Skip to content

Commit 4141c88

Browse files
Merge pull request #253 from zoidbergclawd/feature/agent-quality-sprint
Agent output quality + game framework bundling
2 parents 3140b8c + 31aa43f commit 4141c88

42 files changed

Lines changed: 1417 additions & 311 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,27 +84,32 @@ Planning Mode (conversational specification assistant):
8484
a. AUTO-TEST: autoMatchTests() auto-generates behavioral tests for when_then
8585
requirements missing test_id (Explorer level only; no-op at Builder/Architect)
8686
b. PLAN: MetaPlanner calls Claude API to decompose spec into task DAG
87-
c. MEETINGS: evaluateAndInvite('plan_ready') checks meeting triggers
87+
MetaPlanner auto-selects game framework (Phaser/p5/Three.js) if not specified
88+
Multi-file task planning enforced: each task owns one file, no concurrent edits
89+
c. FRAMEWORK: Copy selected framework lib to workspace lib/ (if applicable)
90+
d. MEETINGS: evaluateAndInvite('plan_ready') checks meeting triggers
8891
(currently no meetings fire at plan_ready; all moved to per-task/deploy events)
89-
d. TRACE: Build traceability map from spec requirements <-> behavioral tests
90-
e. PORTALS: Initialize MCP/CLI portal adapters if spec declares portals
91-
f. EXECUTE: Streaming-parallel pool (Promise.race, up to 3 concurrent tasks)
92-
Each agent gets: role prompt + task description + context from prior tasks
92+
e. TRACE: Build traceability map from spec requirements <-> behavioral tests
93+
f. PORTALS: Initialize MCP/CLI portal adapters if spec declares portals
94+
g. EXECUTE: Streaming-parallel pool (Promise.race, up to 3 concurrent tasks)
95+
Each agent gets: role prompt + framework context + task description + context from prior tasks
9396
Agent output streams via SDK -> WebSocket events to frontend
9497
Git commit after each completed task (serialized via mutex)
9598
Token budget tracked per agent; warning at 80%, halt on exceed
9699
Per-task meeting triggers: design review (task_starting), mid-build
97100
meetings at 25%/50%/60% completion (task_completed)
98-
g. TEST: TestRunner executes pytest/node, parses results + coverage
101+
h. TEST: TestRunner executes pytest/node, parses results + coverage
99102
Traceability tracker updates requirement-test status
100-
h. HEALTH: HealthTracker computes score (0-100) and grade (A-F)
103+
i. GATE: Test pass rate gate -- auto-fix if below threshold
104+
(explorer: no gate, builder: 50%/1 fix, architect: 80%/2 fixes)
105+
j. HEALTH: HealthTracker computes score (0-100) and grade (A-F)
101106
HealthHistoryService persists trend data for Architect level
102-
i. DEPLOY: Surface before_deploy rules as deploy_checklist event
107+
k. DEPLOY: Surface before_deploy rules as deploy_checklist event
103108
evaluateAndInvite('deploy_started') for device/web deploy meetings
104109
If web: build -> find serve dir -> start local HTTP server -> open browser
105110
If devices: resolveDeployOrder -> flash wizard per device
106111
If CLI portals: execute via CliPortalAdapter (no shell)
107-
j. COMPLETE: evaluateAndInvite('session_complete') -> Architecture agent meeting
112+
l. COMPLETE: evaluateAndInvite('session_complete') -> Architecture agent meeting
108113
5. session_complete event with summary
109114
6. Post-build actions (optional):
110115
- FIX: POST /api/sessions/:id/fix with bugReport -> Orchestrator.runFix()
@@ -158,7 +163,7 @@ In dev mode, Vite (port 5173) proxies `/api/*` and `/ws/*` to backend (port 8000
158163
## Core Abstractions
159164

160165
### NuggetSpec
161-
JSON schema produced by blockInterpreter from Blockly workspace. Drives the entire pipeline. Contains: goal, requirements (with test traceability), style, agents, deployment target, workflow flags (feedback loops, system level, behavioral tests), skills, rules, portals, devices, runtime config (agent name, voice, greeting), knowledge (backpack sources, study mode), composition (provides/requires interfaces for Spec Graph).
166+
JSON schema produced by blockInterpreter from Blockly workspace. Drives the entire pipeline. Contains: goal, requirements (with test traceability), style, agents, deployment target, workflow flags (feedback loops, system level, behavioral tests), skills, rules, portals, devices, runtime config (agent name, voice, greeting), knowledge (backpack sources, study mode), composition (provides/requires interfaces for Spec Graph), framework (optional: phaser/p5/threejs/none -- auto-selected by MetaPlanner if not set).
162167

163168
### Task DAG
164169
Directed acyclic graph of tasks with dependencies. Generated by MetaPlanner. Executed in topological order by Orchestrator. Uses Kahn's algorithm (`utils/dag.ts`).

backend/CLAUDE.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ src/
104104
planningAgent.ts System prompt for Planning Mode conversational agent
105105
metaPlanner.ts System prompt for task decomposition
106106
builderAgent.ts Builder role prompt template
107+
frameworks.ts Framework-specific prompt context (Phaser, p5.js, Three.js): multi-file architecture rules, API patterns, MetaPlanner task planning with per-framework decomposition (scenes/modules/components)
107108
testerAgent.ts Tester role prompt template
108109
reviewerAgent.ts Reviewer role prompt template
109110
teaching.ts Teaching moment curriculum and templates
@@ -121,6 +122,7 @@ src/
121122
pathValidator.ts Workspace path validation (blocklist for system/sensitive dirs)
122123
safeEnv.ts Sanitized process.env copy (strips ANTHROPIC_API_KEY)
123124
findFreePort.ts Scans for available TCP port from a starting port
125+
frameworkLoader.ts Copies selected game framework (Phaser/p5/Three.js) from bundled resources to nugget workspace lib/
124126
anthropicClient.ts Singleton factory for the Anthropic SDK client
125127
```
126128

@@ -139,6 +141,7 @@ src/
139141
| POST | /api/sessions/:id/planning/generate | Generate canvas blocks from plan |
140142
| GET | /api/sessions/:id/planning | Get current planning state |
141143
| POST | /api/sessions/:id/fix | Targeted bug fix (requires session in 'done' state, body: bugReport + failingTests) |
144+
| POST | /api/sessions/:id/screenshot | Visual smoke test (body: { base64 }, returns { passed, summary, issues }) |
142145
| POST | /api/sessions/:id/launch | Serve built nugget without rebuild (finds index.html, spawns local server, returns URL) |
143146
| POST | /api/sessions/:id/gate | Human gate response |
144147
| POST | /api/sessions/:id/question | Answer agent question |
@@ -195,7 +198,7 @@ src/
195198
| GET | /api/spec-graph/:id/interfaces | Resolve interface contracts among nodes |
196199

197200
### WebSocket Events (server -> client)
198-
`planning_started`, `plan_ready`, `planning_mode_started`, `planning_turn`, `planning_question`, `planning_plan_updated`, `planning_ready`, `planning_canvas_generated`, `planning_teaching`, `planning_learning_summary`, `planning_error`, `task_started`, `task_completed`, `task_failed`, `agent_output`, `commit_created`, `token_usage`, `budget_warning`, `test_expectations` (task_id, tests[] with name/description -- pre-generated pending tests), `test_result`, `test_phase_complete` (passed, failed, total -- emitted after all `test_result` events), `coverage_update`, `deploy_started`, `deploy_progress`, `deploy_checklist`, `deploy_complete` (includes `url?` for web deploys), `serial_data`, `human_gate`, `user_question`, `skill_*`, `teaching_moment`, `narrator_message`, `permission_auto_resolved`, `minion_state_change`, `workspace_created`, `flash_prompt`, `flash_progress`, `flash_complete`, `context_flow` (from_task_id, to_task_ids, summary_preview), `documentation_ready`, `meeting_invite`, `meeting_started`, `meeting_message`, `meeting_canvas_update`, `meeting_outcome`, `meeting_ended`, `traceability_update`, `traceability_summary`, `correction_cycle_started`, `correction_cycle_progress`, `convergence_update`, `composition_started` (graph_id, node_ids), `composition_impact` (graph_id, changed_node_id, affected_nodes, severity), `decomposition_narrated`, `impact_estimate`, `boundary_analysis`, `system_health_update`, `system_health_summary`, `health_history` (entries array for Architect trend tracking), `fix_started` (bugReport), `fix_task_completed` (taskId, success), `fix_tests_completed` (passed, failed, total), `meeting_blocking_task` (task_id, meeting_type_id), `meeting_unblocking_task` (task_id), `error`, `session_complete`
201+
`planning_started`, `plan_ready`, `planning_mode_started`, `planning_turn`, `planning_question`, `planning_plan_updated`, `planning_ready`, `planning_canvas_generated`, `planning_teaching`, `planning_learning_summary`, `planning_error`, `task_started`, `task_completed`, `task_failed`, `agent_output`, `commit_created`, `token_usage`, `budget_warning`, `test_expectations` (task_id, tests[] with name/description -- pre-generated pending tests), `test_result`, `test_phase_complete` (passed, failed, total -- emitted after all `test_result` events), `coverage_update`, `deploy_started`, `deploy_progress`, `deploy_checklist`, `deploy_complete` (includes `url?` for web deploys), `serial_data`, `human_gate`, `user_question`, `skill_*`, `teaching_moment`, `narrator_message`, `permission_auto_resolved`, `minion_state_change`, `workspace_created`, `flash_prompt`, `flash_progress`, `flash_complete`, `context_flow` (from_task_id, to_task_ids, summary_preview), `documentation_ready`, `meeting_invite`, `meeting_started`, `meeting_message`, `meeting_canvas_update`, `meeting_outcome`, `meeting_ended`, `traceability_update`, `traceability_summary`, `correction_cycle_started`, `correction_cycle_progress`, `convergence_update`, `composition_started` (graph_id, node_ids), `composition_impact` (graph_id, changed_node_id, affected_nodes, severity), `decomposition_narrated`, `impact_estimate`, `boundary_analysis`, `system_health_update`, `system_health_summary`, `health_history` (entries array for Architect trend tracking), `auto_fix_started` (passRate, threshold, attempt), `visual_smoke_test` (passed, summary, issues[]), `fix_started` (bugReport, isAutoFix?), `fix_task_completed` (taskId, success), `fix_tests_completed` (passed, failed, total), `meeting_blocking_task` (task_id, meeting_type_id), `meeting_unblocking_task` (task_id), `error`, `session_complete`
199202

200203
### Agent Runtime WebSocket Events (/v1/agents/:id/stream)
201204
Client sends `turn` (text) or `audio_turn` (audio) messages. Server responds with:
@@ -208,7 +211,7 @@ Client sends `turn` (text) or `audio_turn` (audio) messages. Server responds wit
208211

209212
- **Session state**: In-memory Maps with optional JSON persistence for checkpoint/recovery. Cleanup timer (5 min) starts on WS connect; cancelled at build start, re-armed in `.finally()` after build completes. Meeting activity (accept, message, end), kid-initiated meeting starts, and fix/launch requests also reset the timer. `impactEstimate` and `boundaryAnalysis` persisted on session during plan phase for Blueprint meeting context.
210213
- **NuggetSpec validation**: Zod schema validates at `/api/sessions/:id/start` (string caps, array limits, portal command allowlist).
211-
- **SDK query per task**: Each agent task calls `query()` from `@anthropic-ai/claude-agent-sdk` with `permissionMode: 'bypassPermissions'` and explicit `pathToClaudeCodeExecutable` (resolved once at module load by `agentRunner.ts`). The SDK uses system `node` to spawn `cli.js`. Node.js is a prerequisite; Electron main process shows a dialog prompting install if missing. Default `maxTurns=25` (`MAX_TURNS_DEFAULT`). On retry, grants 10 additional turns per attempt (`MAX_TURNS_RETRY_INCREMENT`), so retries progress: 25 → 35 → 45.
214+
- **SDK query per task**: Each agent task calls `query()` from `@anthropic-ai/claude-agent-sdk` with `permissionMode: 'bypassPermissions'` and explicit `pathToClaudeCodeExecutable` (resolved once at module load by `agentRunner.ts`). The SDK uses system `node` to spawn `cli.js`. Node.js is a prerequisite; Electron main process shows a dialog prompting install if missing. Default `maxTurns=25` (`MAX_TURNS_DEFAULT`). On retry, grants 10 additional turns per attempt (`MAX_TURNS_RETRY_INCREMENT`), so retries progress: 25 → 35 → 45. Builder agents are instructed to self-verify by running `node tests/test_{task_id}.js` before writing their summary.
212215
- **API key propagation**: `POST /api/internal/config` sets `process.env.ANTHROPIC_API_KEY` then calls `resetAnthropicClient()` to invalidate the singleton. Module-level services (`meetingAgentService`, `planningService`) call `getAnthropicClient()` per-use (no cached `this.client`) so key changes propagate immediately.
213216
- **Stale metadata cleanup**: On each build, `setupWorkspace()` removes `.elisa/{comms,context,status}` from previous sessions before recreating them. Preserves `.elisa/logs/`, source files, and `.git/`.
214217
- **Structural digest injection**: Agent task prompts include function/class signatures extracted from workspace source files (via `ContextManager.buildStructuralDigest()`), allowing agents to orient without reading each file.
@@ -218,6 +221,7 @@ Client sends `turn` (text) or `audio_turn` (audio) messages. Server responds wit
218221
- **Context chain**: After each task, summary + structural digest written to `.elisa/context/nugget_context.md`.
219222
- **Cancellation**: `Orchestrator.cancel()` via AbortController; signal propagated to Agent SDK `query()` calls. Session state set to `done` on error.
220223
- **Content safety**: All agent prompts enforce age-appropriate output (8-14). Placeholder values sanitized before interpolation (`sanitizePlaceholder()`).
224+
- **Game frameworks**: Phaser 3, p5.js, Three.js bundled in `build/frameworks/` (downloaded by `scripts/bundle-frameworks.mjs`). MetaPlanner auto-selects or kid picks via Goal block dropdown. Selected library copied to workspace `lib/` after planning. Multi-file architecture enforced: scaffold creates all files as stubs, each feature task owns one module file, no concurrent edits to same file.
221225
- **Flash mutex**: `HardwareService.flash()` serializes concurrent calls via Promise-chain mutex.
222226
- **WebSocket heartbeat + send queue**: Server sends protocol-level pings every 30s (`WS_PING_INTERVAL_MS`). Connections that miss a pong are terminated via `ws.terminate()`. All `sendEvent()` calls are serialized through a per-session FIFO queue with `setImmediate` yield between each frame, preventing burst-flooding the Vite proxy even when concurrent fire-and-forget callers (agent_output streaming, meeting triggers) overlap. Queue depth warnings at 10/50/100; drain summaries logged for batches >5. `wsAlive` WeakMap tracks per-connection liveness.
223227
- **Graceful shutdown**: SIGTERM/SIGINT handlers cancel orchestrators, close WS server, 10s force-exit. `SessionStore.onCleanup` invokes `ConnectionManager.cleanup()` for WS teardown.

backend/src/models/session.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export interface MetaPlannerPlan {
4343
tasks: Task[];
4444
agents: Agent[];
4545
plan_explanation?: string;
46+
framework?: string;
4647
}
4748

4849
export interface BuildSession {
@@ -58,6 +59,7 @@ export interface BuildSession {
5859
impactEstimate?: { estimated_tasks: number; complexity: string; heaviest_requirements: string[] };
5960
boundaryAnalysis?: { inputs: Array<{ name: string; type: string; source?: string }>; outputs: Array<{ name: string; type: string; source?: string }>; boundary_portals: string[] };
6061
planningSession?: PlanningSession;
62+
testGatePassed?: boolean;
6163
}
6264

6365
export interface AgentResult {

backend/src/prompts/builderAgent.test.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ describe('builderAgent SYSTEM_PROMPT', () => {
3838
expect(SYSTEM_PROMPT).toContain('BUILDER');
3939
});
4040

41-
it('contains Thinking Steps section', () => {
41+
it('contains Thinking Steps section with self-verification', () => {
4242
expect(SYSTEM_PROMPT).toContain('## Thinking Steps');
4343
expect(SYSTEM_PROMPT).toContain('file manifest and structural digest');
44+
expect(SYSTEM_PROMPT).toContain('Run your tests');
45+
expect(SYSTEM_PROMPT).toContain('node tests/test_{task_id}.js');
4446
});
4547

4648
it('contains Turn Efficiency section', () => {
@@ -56,6 +58,19 @@ describe('builderAgent SYSTEM_PROMPT', () => {
5658
it('contains wind-down instruction referencing turn limit', () => {
5759
expect(SYSTEM_PROMPT).toContain('wind down');
5860
});
61+
62+
it('requires running test file before summary', () => {
63+
expect(SYSTEM_PROMPT).toContain('MUST run');
64+
expect(SYSTEM_PROMPT).toContain('tests/test_{task_id}.js');
65+
});
66+
67+
it('instructs agents not to leave FAIL stubs', () => {
68+
expect(SYSTEM_PROMPT).toContain('do NOT leave FAIL stubs');
69+
});
70+
71+
it('instructs honesty when node is unavailable', () => {
72+
expect(SYSTEM_PROMPT).toContain('do not claim manual verification');
73+
});
5974
});
6075

6176
describe('formatStyle', () => {
@@ -99,8 +114,12 @@ describe('formatTaskPrompt', () => {
99114
role: 'builder',
100115
persona: 'A friendly robot',
101116
task: {
117+
id: 'task-1',
102118
name: 'Build UI',
103119
description: 'Create the main UI',
120+
status: 'pending' as const,
121+
agent_name: 'Builder Bot',
122+
dependencies: [],
104123
acceptance_criteria: ['Page renders', 'Button works'],
105124
},
106125
spec: {
@@ -126,15 +145,15 @@ describe('formatTaskPrompt', () => {
126145
it('omits acceptance criteria section when empty array', () => {
127146
const result = formatTaskPrompt({
128147
...baseParams,
129-
task: { name: 'Test', description: 'Desc', acceptance_criteria: [] },
148+
task: { ...baseParams.task, name: 'Test', description: 'Desc', acceptance_criteria: [] },
130149
});
131150
expect(result).not.toContain('Acceptance Criteria');
132151
});
133152

134153
it('omits acceptance criteria section when undefined', () => {
135154
const result = formatTaskPrompt({
136155
...baseParams,
137-
task: { name: 'Test', description: 'Desc' },
156+
task: { ...baseParams.task, name: 'Test', description: 'Desc', acceptance_criteria: undefined as any },
138157
});
139158
expect(result).not.toContain('Acceptance Criteria');
140159
});
@@ -423,6 +442,12 @@ describe('formatTaskPrompt', () => {
423442
expect(result).not.toContain('## Available Portals');
424443
});
425444

445+
it('includes test file path in user prompt', () => {
446+
const result = formatTaskPrompt(baseParams);
447+
expect(result).toContain('## Your Test File');
448+
expect(result).toContain('node tests/test_task-1.js');
449+
});
450+
426451
it('includes multiple portals', () => {
427452
const result = formatTaskPrompt({
428453
...baseParams,

backend/src/prompts/builderAgent.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ relative to cwd automatically. Do not reference paths outside this workspace.
3636
1. Scan the file manifest and structural digest below to understand what exists. Only Read specific files when you need implementation details not visible in signatures.
3737
2. Plan your changes: identify which files to create or modify and how they fit together.
3838
3. Implement the task, writing or editing files one at a time.
39-
4. Verify your work: re-read changed files to confirm correctness, then write your summary.
39+
4. Run your tests: execute \`node tests/test_{task_id}.js\` and check the output.
40+
5. Fix any FAIL lines -- update your code or fix the test assertions. Re-run until all PASS.
41+
6. Write your summary to .elisa/comms/{task_id}_summary.md.
4042
4143
## Turn Efficiency
4244
You have a limited turn budget of {max_turns} turns. Prioritize implementation over exploration:
@@ -53,6 +55,10 @@ Rules:
5355
- Output format: console.log('PASS: test_name') or console.log('FAIL: test_name')
5456
- Do not delete test files or remove test entries
5557
- Write assertions that genuinely verify the acceptance criteria
58+
- After implementing, you MUST run \`node tests/test_{task_id}.js\` before writing your summary
59+
- If a test requires browser APIs (DOM, canvas), convert it to a Node.js-compatible check \
60+
(file existence, string matching, syntax validation) -- do NOT leave FAIL stubs
61+
- If node is unavailable, state this clearly in your summary -- do not claim manual verification
5662
5763
## Rules
5864
- Write clean, well-structured code appropriate for the nugget type.
@@ -108,6 +114,10 @@ export function formatTaskPrompt(params: {
108114
}
109115
}
110116

117+
if (task.id) {
118+
parts.push(`\n## Your Test File\nRun before finishing: node tests/test_${task.id}.js`);
119+
}
120+
111121
const nugget = spec.nugget ?? {};
112122
parts.push(`\n## Nugget Context\nGoal: ${nugget.goal ?? 'Not specified'}`);
113123
if (nugget.description) {

0 commit comments

Comments
 (0)