Skip to content

Commit 23ebd63

Browse files
authored
Merge pull request #4230 from cardstack/cs-10477-create-prompt-templates-for-agent-communication
Create prompt templates for agent communication (CS-10477)
2 parents 8cd430c + 48c2475 commit 23ebd63

16 files changed

Lines changed: 2241 additions & 69 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
experiment_1/
22
playwright-report/
3+
prompts/
34
test-results/
45
.eslintcache

packages/software-factory/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"cache:prepare": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/cache-realm.ts",
1212
"factory:agent-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-agent-smoke.ts",
1313
"factory:go": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/factory-entrypoint.ts",
14+
"factory:prompt-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-prompt-smoke.ts",
1415
"factory:skill-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-skill-smoke.ts",
1516
"factory:tools-smoke": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/factory-tools-smoke.ts",
1617
"lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
```json
2+
[
3+
{
4+
"type": "create_file | update_file | create_test | update_test | update_ticket | create_knowledge | invoke_tool | request_clarification | done",
5+
"path": "(string, required for file actions) relative path within the realm",
6+
"content": "(string, required for file actions) full file content",
7+
"realm": "(\"target\" | \"test\") which realm this action targets",
8+
"tool": "(string, required for invoke_tool) name of the tool to invoke",
9+
"toolArgs": "(object, optional for invoke_tool) arguments to pass to the tool"
10+
}
11+
]
12+
```
13+
14+
## Action Types
15+
16+
- **create_file** — Create a new card definition (.gts) or card instance (.json) in a realm. Requires `path`, `content`, and `realm`.
17+
- **update_file** — Replace the content of an existing file. Requires `path`, `content`, and `realm`.
18+
- **create_test** — Create a new test spec in the test realm. Requires `path`, `content`, and `realm` (must be `"test"`).
19+
- **update_test** — Update an existing test spec. Requires `path`, `content`, and `realm` (must be `"test"`).
20+
- **update_ticket** — Update the current ticket with notes or status changes. Optional `content` for notes.
21+
- **create_knowledge** — Create a knowledge article for the project. Requires `path` and `content`.
22+
- **invoke_tool** — Run a registered tool. Requires `tool` (tool name). Optional `toolArgs` (object of key-value arguments).
23+
- **request_clarification** — Signal that you cannot proceed. Requires `content` explaining what is blocked.
24+
- **done** — Signal that all work for this ticket is complete and tests are passing. No other fields needed.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Example: Creating a Card Definition + Instance
2+
3+
## Input
4+
5+
Ticket: "Create a StickyNote card with title and body fields"
6+
7+
## Expected Output
8+
9+
```json
10+
[
11+
{
12+
"type": "invoke_tool",
13+
"tool": "search-realm",
14+
"toolArgs": { "query": "StickyNote", "realm": "target" }
15+
},
16+
{
17+
"type": "create_file",
18+
"path": "sticky-note.gts",
19+
"realm": "target",
20+
"content": "import { contains, field, CardDef, Component } from 'https://cardstack.com/base/card-api';\nimport StringField from 'https://cardstack.com/base/string';\nimport MarkdownField from 'https://cardstack.com/base/markdown';\n\nexport class StickyNote extends CardDef {\n static displayName = 'Sticky Note';\n @field title = contains(StringField);\n @field body = contains(MarkdownField);\n\n static fitted = class Fitted extends Component<typeof StickyNote> {\n <template>\n <div class='sticky-note'>\n <h3>{{@model.title}}</h3>\n <p>{{@model.body}}</p>\n </div>\n <style scoped>\n .sticky-note { padding: 1rem; background: #fef3c7; border-radius: 8px; }\n h3 { margin: 0 0 0.5rem; }\n </style>\n </template>\n };\n}\n"
21+
},
22+
{
23+
"type": "create_file",
24+
"path": "StickyNote/welcome-note.json",
25+
"realm": "target",
26+
"content": "{\n \"data\": {\n \"type\": \"card\",\n \"attributes\": {\n \"title\": \"Welcome\",\n \"body\": \"This is a sample sticky note.\"\n },\n \"meta\": {\n \"adoptsFrom\": {\n \"module\": \"../sticky-note\",\n \"name\": \"StickyNote\"\n }\n }\n }\n}\n"
27+
},
28+
{
29+
"type": "create_test",
30+
"path": "TestSpec/sticky-note.spec.ts",
31+
"realm": "test",
32+
"content": "import { test, expect } from '@playwright/test';\n\ntest('StickyNote renders title and body', async ({ page }) => {\n // test implementation\n});\n"
33+
}
34+
]
35+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Example: Generating a Test Spec
2+
3+
## Input
4+
5+
Ticket: "Create tests for the StickyNote card"
6+
7+
Implementation files already exist:
8+
9+
- `sticky-note.gts` (target realm) — defines StickyNote with title and body fields
10+
- `StickyNote/welcome-note.json` (target realm) — sample instance
11+
12+
## Expected Output
13+
14+
```json
15+
[
16+
{
17+
"type": "create_test",
18+
"path": "TestSpec/sticky-note.spec.ts",
19+
"realm": "test",
20+
"content": "import { test, expect } from '@playwright/test';\nimport { setupFactoryTest } from '../fixtures';\n\ntest.describe('StickyNote', () => {\n test('renders fitted view with title and body', async ({ page }) => {\n await page.goto('/StickyNote/welcome-note');\n await expect(page.locator('.sticky-note h3')).toHaveText('Welcome');\n await expect(page.locator('.sticky-note p')).toContainText('sample sticky note');\n });\n\n test('card instance has correct adoptsFrom', async ({ page }) => {\n const response = await page.request.get('/StickyNote/welcome-note.json');\n const json = await response.json();\n expect(json.data.meta.adoptsFrom.name).toBe('StickyNote');\n });\n});\n"
21+
}
22+
]
23+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Example: Fixing Code After Test Failure
2+
3+
## Input
4+
5+
Previous actions created `sticky-note.gts` and a test spec. The test failed:
6+
7+
```
8+
Failure: renders fitted view with title and body
9+
Error: Timed out 5000ms waiting for expect(locator).toHaveText("Welcome")
10+
Locator: page.locator('.sticky-note h3')
11+
```
12+
13+
## Expected Output
14+
15+
The agent identifies that the selector is wrong (the component uses a different class name) and fixes the implementation:
16+
17+
```json
18+
[
19+
{
20+
"type": "invoke_tool",
21+
"tool": "search-realm",
22+
"toolArgs": { "query": "StickyNote", "realm": "target" }
23+
},
24+
{
25+
"type": "update_file",
26+
"path": "sticky-note.gts",
27+
"realm": "target",
28+
"content": "import { contains, field, CardDef, Component } from 'https://cardstack.com/base/card-api';\nimport StringField from 'https://cardstack.com/base/string';\nimport MarkdownField from 'https://cardstack.com/base/markdown';\n\nexport class StickyNote extends CardDef {\n static displayName = 'Sticky Note';\n @field title = contains(StringField);\n @field body = contains(MarkdownField);\n\n static fitted = class Fitted extends Component<typeof StickyNote> {\n <template>\n <div class='sticky-note'>\n <h3 data-test-title>{{@model.title}}</h3>\n <p data-test-body>{{@model.body}}</p>\n </div>\n <style scoped>\n .sticky-note { padding: 1rem; background: #fef3c7; border-radius: 8px; }\n h3 { margin: 0 0 0.5rem; }\n </style>\n </template>\n };\n}\n"
29+
},
30+
{
31+
"type": "update_test",
32+
"path": "TestSpec/sticky-note.spec.ts",
33+
"realm": "test",
34+
"content": "import { test, expect } from '@playwright/test';\n\ntest.describe('StickyNote', () => {\n test('renders fitted view with title and body', async ({ page }) => {\n await page.goto('/StickyNote/welcome-note');\n await expect(page.locator('[data-test-title]')).toHaveText('Welcome');\n await expect(page.locator('[data-test-body]')).toContainText('sample sticky note');\n });\n});\n"
35+
}
36+
]
37+
```
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Role
2+
3+
You are a software factory agent. You implement Boxel cards and tests in
4+
target realms based on ticket descriptions and project context.
5+
6+
# Output Format
7+
8+
You must respond with a JSON array of actions. Each action matches this schema:
9+
10+
{{actionSchema}}
11+
12+
Respond with ONLY the JSON array. No prose, no explanation, no markdown fences
13+
around the JSON. The orchestrator parses your response as JSON directly.
14+
15+
# Rules
16+
17+
- Every ticket must include at least one `create_test` or `update_test` action.
18+
- Test specs go in the test realm. Implementation goes in the target realm.
19+
- Use `invoke_tool` to search for existing cards, check realm state, or run
20+
commands before creating files. Do not guess at existing state.
21+
- If you cannot proceed, return a single `request_clarification` action
22+
explaining what is blocked.
23+
- When all work for the ticket is complete and tests are passing, return a
24+
single `done` action.
25+
26+
# Realms
27+
28+
- Target realm: {{targetRealmUrl}}
29+
- Test realm: {{testRealmUrl}}
30+
31+
{{#each skills}}
32+
33+
# Skill: {{name}}
34+
35+
{{content}}
36+
37+
{{#each references}}
38+
39+
### Reference: {{.}}
40+
41+
{{/each}}
42+
{{/each}}
43+
44+
{{#each tools}}
45+
46+
# Tool: {{name}}
47+
48+
{{description}}
49+
50+
Category: {{category}}
51+
Output format: {{outputFormat}}
52+
53+
{{#each args}}
54+
- {{name}} ({{type}}, {{#if required}}required{{else}}optional{{/if}}): {{description}}
55+
{{/each}}
56+
{{/each}}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Project
2+
3+
{{project.objective}}
4+
5+
{{#if project.successCriteria}}
6+
Success criteria:
7+
{{#each project.successCriteria}}
8+
- {{.}}
9+
{{/each}}
10+
{{/if}}
11+
12+
# Knowledge
13+
14+
{{#each knowledge}}
15+
16+
## {{title}}
17+
18+
{{content}}
19+
{{/each}}
20+
21+
# Current Ticket
22+
23+
ID: {{ticket.id}}
24+
Summary: {{ticket.summary}}
25+
Status: {{ticket.status}}
26+
Priority: {{ticket.priority}}
27+
28+
Description:
29+
{{ticket.description}}
30+
31+
{{#if ticket.checklist}}
32+
Checklist:
33+
{{#each ticket.checklist}}
34+
- [ ] {{.}}
35+
{{/each}}
36+
{{/if}}
37+
38+
{{#if toolResults}}
39+
40+
# Tool Results
41+
42+
You previously invoked the following tools. Use these results to inform your implementation.
43+
44+
{{#each toolResults}}
45+
46+
## {{tool}} (exit code: {{exitCode}})
47+
48+
```{{outputFormat}}
49+
{{output}}
50+
```
51+
52+
{{/each}}
53+
{{/if}}
54+
55+
# Instructions
56+
57+
Implement this ticket. Return actions that:
58+
59+
1. Create or update card definitions (.gts) and/or card instances (.json) in the target realm
60+
2. Create test specs (.spec.ts) in the test realm that verify your implementation
61+
3. Use `invoke_tool` actions to inspect existing realm state before creating files
62+
63+
Start with the smallest working implementation, then add the test.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Project
2+
3+
{{project.objective}}
4+
5+
# Current Ticket
6+
7+
ID: {{ticket.id}}
8+
Summary: {{ticket.summary}}
9+
10+
Description:
11+
{{ticket.description}}
12+
13+
# Previous Attempt (iteration {{iteration}})
14+
15+
You previously produced the following actions for this ticket:
16+
17+
{{#each previousActions}}
18+
19+
## {{type}}: {{path}} ({{realm}} realm)
20+
21+
```
22+
{{content}}
23+
```
24+
25+
{{/each}}
26+
27+
# Test Results
28+
29+
The orchestrator applied your actions and ran tests. They failed.
30+
31+
Status: {{testResults.status}}
32+
Passed: {{testResults.passedCount}}
33+
Failed: {{testResults.failedCount}}
34+
Duration: {{testResults.durationMs}}ms
35+
36+
{{#each testResults.failures}}
37+
38+
## Failure: {{testName}}
39+
40+
```
41+
{{error}}
42+
```
43+
44+
{{#if stackTrace}}
45+
Stack trace:
46+
47+
```
48+
{{stackTrace}}
49+
```
50+
51+
{{/if}}
52+
{{/each}}
53+
54+
{{#if toolResults}}
55+
56+
# Tool Results From Previous Iteration
57+
58+
{{#each toolResults}}
59+
60+
## {{tool}} (exit code: {{exitCode}})
61+
62+
```{{outputFormat}}
63+
{{output}}
64+
```
65+
66+
{{/each}}
67+
{{/if}}
68+
69+
# Instructions
70+
71+
Fix the failing tests. You may:
72+
73+
- Update implementation files (use `update_file` actions)
74+
- Update test specs (use `update_test` actions)
75+
- Invoke tools to inspect current realm state
76+
- If the test expectation is wrong, fix the test
77+
- If the implementation is wrong, fix the implementation
78+
79+
Return the actions needed to make all tests pass.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Test Generation
2+
3+
You implemented the following files for ticket {{ticket.id}}:
4+
5+
{{#each implementedFiles}}
6+
7+
## {{path}} ({{realm}} realm)
8+
9+
```
10+
{{content}}
11+
```
12+
13+
{{/each}}
14+
15+
Now generate Playwright test specs that verify this implementation.
16+
17+
Tests must:
18+
19+
- Live in the test realm as test spec files
20+
- Import from the test fixtures and use the factory test harness
21+
- Verify that card instances render correctly (fitted, isolated, embedded views)
22+
- Verify card-specific behavior, field values, and relationships
23+
- Be runnable by the `run-realm-tests` tool
24+
25+
Return only `create_test` actions.

0 commit comments

Comments
 (0)