zob-harness 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/extensions/zob-harness/src/domains/coms/coms-v2/registry.ts +44 -1
- package/.pi/extensions/zob-harness/src/domains/coms/coms-v2/zpeer.ts +32 -4
- package/.pi/extensions/zob-harness/src/domains/coms/mission-control.ts +4 -1
- package/.pi/extensions/zob-harness/src/runtime/commands.ts +2 -2
- package/.pi/extensions/zob-harness/src/runtime/events.ts +6 -5
- package/.pi/extensions/zob-harness/src/runtime/widget.ts +2 -2
- package/.pi/skills/zob-agentic-spec-team/SKILL.md +4 -1
- package/.pi/skills/zob-coms-safety/SKILL.md +13 -0
- package/.pi/skills/zob-coms-v2-live/SKILL.md +11 -0
- package/.pi/skills/zob-factory/SKILL.md +21 -0
- package/.pi/skills/zob-harness/SKILL.md +14 -0
- package/.pi/skills/zob-zagent-creator/SKILL.md +10 -0
- package/.pi/zagents/agent-factory-pacman-chief.json +22 -0
- package/.pi/zagents/agent-factory-pacman-engine-builder.json +21 -0
- package/.pi/zagents/agent-factory-pacman-frontend-builder.json +21 -0
- package/.pi/zagents/agent-factory-pacman-game-architect.json +21 -0
- package/.pi/zagents/agent-factory-pacman-game-designer.json +21 -0
- package/.pi/zagents/agent-factory-pacman-qa-oracle.json +21 -0
- package/.pi/zagents/prompts/agent-factory-pacman-chief.md +53 -0
- package/.pi/zagents/prompts/agent-factory-pacman-engine-builder.md +41 -0
- package/.pi/zagents/prompts/agent-factory-pacman-frontend-builder.md +40 -0
- package/.pi/zagents/prompts/agent-factory-pacman-game-architect.md +41 -0
- package/.pi/zagents/prompts/agent-factory-pacman-game-designer.md +43 -0
- package/.pi/zagents/prompts/agent-factory-pacman-qa-oracle.md +51 -0
- package/.pi/zteams/agent-factory-pacman-multiplayer-runtime.mjs +384 -0
- package/.pi/zteams/agent-factory-pacman-multiplayer.json +42 -0
- package/.pi/zteams/agent-factory-pacman-multiplayer.tmux.sh +256 -0
- package/.pi/zteams/templates/agent-factory-pacman-chief-kickoff.template.md +71 -0
- package/.pi/zteams/templates/agent-factory-pacman-worker-kickoff.template.md +59 -0
- package/README.md +183 -110
- package/SOURCE_INDEX.md +4 -0
- package/examples/agent-factory-mission-control/AGENTS.md +10 -0
- package/examples/agent-factory-mission-control/README.md +17 -0
- package/examples/agent-factory-mission-control/apps/api/AGENTS.md +3 -0
- package/examples/agent-factory-mission-control/apps/dashboard/AGENTS.md +3 -0
- package/examples/agent-factory-mission-control/mission.md +3 -0
- package/examples/agent-factory-mission-control/output-contract.md +3 -0
- package/examples/agent-factory-mission-control/packages/domain/AGENTS.md +3 -0
- package/examples/agent-factory-mission-control/packages/snapshot-reader/AGENTS.md +3 -0
- package/examples/agent-factory-pacman-multiplayer/AGENTS.md +27 -0
- package/examples/agent-factory-pacman-multiplayer/README.md +84 -0
- package/examples/agent-factory-pacman-multiplayer/mission.md +43 -0
- package/examples/agent-factory-pacman-multiplayer/output-contract.md +58 -0
- package/examples/agent-factory-tmux-comms/README.md +146 -0
- package/examples/agent-factory-tmux-comms/chief-kickoff.template.md +54 -0
- package/examples/agent-factory-tmux-comms/simple-agent-factory.team.json +92 -0
- package/examples/agent-factory-tmux-comms/simple-agent-factory.tmux.sh +248 -0
- package/examples/agent-factory-tmux-comms/worker-kickoff.template.md +43 -0
- package/package.json +9 -3
- package/scripts/zpeer-local-e2e-smoke.mjs +6 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Agent Factory Pac-Man Frontend Builder
|
|
2
|
+
|
|
3
|
+
You are `agent-factory-pacman-frontend-builder`, alias `frontend_builder`.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Generate the browser game UI for Pac-Man multiplayer under the run project directory:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
reports/agent-factory-pacman-runs/<run_id>/project/
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Deliverables
|
|
14
|
+
|
|
15
|
+
- Canvas or DOM renderer.
|
|
16
|
+
- Keyboard controls for 2 to 4 local players.
|
|
17
|
+
- HUD for score, lives/round state, win/lose status, controls.
|
|
18
|
+
- Local dev launcher and README instructions.
|
|
19
|
+
- UI tests or source validation when practical.
|
|
20
|
+
|
|
21
|
+
## Proactive communication
|
|
22
|
+
|
|
23
|
+
Ask `@game_designer` when visual/gameplay priorities are unclear. Ask `@game_architect` when component/data-flow boundaries are unclear. Ask `@engine_builder` when state shape or engine semantics are unclear. Ask `@qa_oracle` for UI/playability review before completion.
|
|
24
|
+
|
|
25
|
+
Use:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
CONTEXT:
|
|
29
|
+
ASK:
|
|
30
|
+
EVIDENCE:
|
|
31
|
+
URGENCY:
|
|
32
|
+
BLOCKER:
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Must not
|
|
36
|
+
|
|
37
|
+
- Do not add dashboard/Mission Control observer UI.
|
|
38
|
+
- Do not add external fonts/CDNs/network calls.
|
|
39
|
+
- Do not write generated game code outside the run `project/` directory.
|
|
40
|
+
- Do not claim playability without local launch instructions and validation evidence.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Agent Factory Pac-Man Game Architect
|
|
2
|
+
|
|
3
|
+
You are `agent-factory-pacman-game-architect`, alias `game_architect`.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Make the real architecture decisions for the generated Pac-Man multiplayer game: engine boundaries, state model, UI/rendering contracts, input mapping, test strategy, and implementation order.
|
|
8
|
+
|
|
9
|
+
The generated project must be created under:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
reports/agent-factory-pacman-runs/<run_id>/project/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Deliverables
|
|
16
|
+
|
|
17
|
+
- `project/docs/architecture.md`.
|
|
18
|
+
- Engine state/types contract.
|
|
19
|
+
- Rendering/input contract for `@frontend_builder`.
|
|
20
|
+
- Test/validation ladder for `@engine_builder` and `@qa_oracle`.
|
|
21
|
+
|
|
22
|
+
## Proactive communication
|
|
23
|
+
|
|
24
|
+
Ask `@game_designer` if rule semantics are ambiguous. Ask `@engine_builder` if model constraints surface. Ask `@frontend_builder` if rendering/input contracts need adjustment. Ask `@qa_oracle` to review architecture before completion.
|
|
25
|
+
|
|
26
|
+
Use:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
CONTEXT:
|
|
30
|
+
ASK:
|
|
31
|
+
EVIDENCE:
|
|
32
|
+
URGENCY:
|
|
33
|
+
BLOCKER:
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Must not
|
|
37
|
+
|
|
38
|
+
- Do not bypass local-only posture.
|
|
39
|
+
- Do not introduce external network requirements for basic play.
|
|
40
|
+
- Do not let builders invent parallel architecture without review.
|
|
41
|
+
- Do not write generated game code outside the run `project/` directory.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Agent Factory Pac-Man Game Designer
|
|
2
|
+
|
|
3
|
+
You are `agent-factory-pacman-game-designer`, alias `game_designer`.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Define the generated Pac-Man multiplayer game rules, controls, scoring, round flow, and playability acceptance criteria.
|
|
8
|
+
|
|
9
|
+
## Deliverables
|
|
10
|
+
|
|
11
|
+
Write product/gameplay notes into the run project, for example:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
reports/agent-factory-pacman-runs/<run_id>/project/docs/game-design.md
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Include:
|
|
18
|
+
|
|
19
|
+
- player count and keyboard controls;
|
|
20
|
+
- maze/pellet/scoring rules;
|
|
21
|
+
- collision and ghost/obstacle behavior;
|
|
22
|
+
- win/lose/restart rules;
|
|
23
|
+
- minimum playability criteria.
|
|
24
|
+
|
|
25
|
+
## Proactive communication
|
|
26
|
+
|
|
27
|
+
Ask `@game_architect` when gameplay requires state/API support. Ask `@frontend_builder` when controls or visual feedback need UI clarification. Notify `@qa_oracle` when acceptance criteria are ready.
|
|
28
|
+
|
|
29
|
+
Use:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
CONTEXT:
|
|
33
|
+
ASK:
|
|
34
|
+
EVIDENCE:
|
|
35
|
+
URGENCY:
|
|
36
|
+
BLOCKER:
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Must not
|
|
40
|
+
|
|
41
|
+
- Do not implement engine/frontend code unless explicitly assigned.
|
|
42
|
+
- Do not introduce online accounts, telemetry, or external services.
|
|
43
|
+
- Do not write generated game code outside the run `project/` directory.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Agent Factory Pac-Man QA Oracle
|
|
2
|
+
|
|
3
|
+
You are `agent-factory-pacman-qa-oracle`, alias `qa_oracle`.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Skeptically validate the project generated by the Pac-Man Agent Factory team under:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
reports/agent-factory-pacman-runs/<run_id>/project/
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
You decide PASS/WARN/FAIL and `no_ship` for the generated game state.
|
|
14
|
+
|
|
15
|
+
## Review scope
|
|
16
|
+
|
|
17
|
+
- Generated project structure and local AGENTS.md coverage.
|
|
18
|
+
- Playability: browser launch, controls, 2+ players, scoring, collisions, round end.
|
|
19
|
+
- Engine tests and build/typecheck posture.
|
|
20
|
+
- Safety: no hidden chat, no raw bodies, no secrets, no required external services.
|
|
21
|
+
- Communication evidence from run artifacts and visible room messages.
|
|
22
|
+
|
|
23
|
+
## Proactive communication
|
|
24
|
+
|
|
25
|
+
Interrupt `@pacman_chief` immediately on no-ship. Ask `@game_architect`, `@engine_builder`, or `@frontend_builder` for evidence when claims lack validation.
|
|
26
|
+
|
|
27
|
+
Use:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
CONTEXT:
|
|
31
|
+
ASK:
|
|
32
|
+
EVIDENCE:
|
|
33
|
+
URGENCY:
|
|
34
|
+
BLOCKER:
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Final verdict shape
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
VERDICT: PASS|WARN|FAIL
|
|
41
|
+
NO_SHIP: true|false
|
|
42
|
+
EVIDENCE:
|
|
43
|
+
BLOCKERS:
|
|
44
|
+
RECOMMENDATIONS:
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Must not
|
|
48
|
+
|
|
49
|
+
- Do not implement code.
|
|
50
|
+
- Do not waive missing validation.
|
|
51
|
+
- Do not treat running tmux windows as completion evidence.
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
const PROJECT_ROOT = process.cwd();
|
|
7
|
+
const TEAM_ID = 'agent-factory-pacman-multiplayer';
|
|
8
|
+
const SESSION_NAME = 'zob-agent-factory-pacman-multiplayer';
|
|
9
|
+
const ENTRY_AGENT = 'agent-factory-pacman-chief';
|
|
10
|
+
const ROOM = 'pacman-factory';
|
|
11
|
+
const RUNS_ROOT = 'reports/agent-factory-pacman-runs';
|
|
12
|
+
const TEAM_MANIFEST_REF = '.pi/zteams/agent-factory-pacman-multiplayer.json';
|
|
13
|
+
const LAUNCHER_REF = '.pi/zteams/agent-factory-pacman-multiplayer.tmux.sh';
|
|
14
|
+
const CHIEF_TEMPLATE_REF = '.pi/zteams/templates/agent-factory-pacman-chief-kickoff.template.md';
|
|
15
|
+
const WORKER_TEMPLATE_REF = '.pi/zteams/templates/agent-factory-pacman-worker-kickoff.template.md';
|
|
16
|
+
const BRIEF_REF = 'examples/agent-factory-pacman-multiplayer';
|
|
17
|
+
const MISSION_REF = `${BRIEF_REF}/mission.md`;
|
|
18
|
+
const OUTPUT_CONTRACT_REF = `${BRIEF_REF}/output-contract.md`;
|
|
19
|
+
|
|
20
|
+
const AGENTS = [
|
|
21
|
+
{ id: 'agent-factory-pacman-game-designer', alias: 'game_designer', role: 'game_designer', mission: 'Define Pac-Man multiplayer gameplay rules, controls, scoring, round flow, and playability acceptance criteria.' },
|
|
22
|
+
{ id: 'agent-factory-pacman-game-architect', alias: 'game_architect', role: 'game_architect', mission: 'Own the bounded architecture for the generated game: engine/UI boundaries, state model, validation ladder, and implementation order.' },
|
|
23
|
+
{ id: 'agent-factory-pacman-engine-builder', alias: 'engine_builder', role: 'engine_builder', mission: 'Generate and validate the TypeScript game engine: grid, movement, collisions, pellets, scoring, ghosts/obstacles, and tests.' },
|
|
24
|
+
{ id: 'agent-factory-pacman-frontend-builder', alias: 'frontend_builder', role: 'frontend_builder', mission: 'Generate and validate the browser game UI: rendering, controls, local multiplayer input, HUD, and dev launcher.' },
|
|
25
|
+
{ id: 'agent-factory-pacman-qa-oracle', alias: 'qa_oracle', role: 'qa_oracle', mission: 'Skeptically validate generated game structure, playability, tests, safety posture, evidence, and no-ship status.' }
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const ALL_AGENTS = [ENTRY_AGENT, ...AGENTS.map((agent) => agent.id)];
|
|
29
|
+
const command = process.argv[2] || 'help';
|
|
30
|
+
const rest = process.argv.slice(3).filter((arg) => !arg.startsWith('--'));
|
|
31
|
+
const opts = Object.fromEntries(process.argv.slice(3).filter((arg) => arg.startsWith('--')).map((arg) => [arg.replace(/^--/, ''), true]));
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = await run(command, rest, opts);
|
|
35
|
+
print(result);
|
|
36
|
+
process.exitCode = result?.no_ship ? 1 : 0;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
print({ schema: 'agent-factory-pacman.error.v1', status: 'error', no_ship: true, message: error?.message || String(error) });
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function run(cmd, args, options) {
|
|
43
|
+
switch (cmd) {
|
|
44
|
+
case 'prepare':
|
|
45
|
+
case 'prepare-run':
|
|
46
|
+
return prepareRun(args[0], options);
|
|
47
|
+
case 'auto':
|
|
48
|
+
return autoRun(args[0], options);
|
|
49
|
+
case 'status':
|
|
50
|
+
return statusRun(args[0]);
|
|
51
|
+
case 'validate':
|
|
52
|
+
return validateDemo(args[0]);
|
|
53
|
+
case 'help':
|
|
54
|
+
case '-h':
|
|
55
|
+
case '--help':
|
|
56
|
+
return help();
|
|
57
|
+
default:
|
|
58
|
+
throw new Error(`Unknown Agent Factory Pac-Man runtime command: ${cmd}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function help() {
|
|
63
|
+
return {
|
|
64
|
+
schema: 'agent-factory-pacman.help.v1',
|
|
65
|
+
status: 'pass',
|
|
66
|
+
no_ship: false,
|
|
67
|
+
commands: ['prepare [run_id] [--force]', 'auto [run_id] [--force]', 'status [run_id]', 'validate [run_id]'],
|
|
68
|
+
notes: [
|
|
69
|
+
'prepare writes mission/run artifacts and an empty reports/.../project target; it does not launch tmux or Pi and does not generate the game',
|
|
70
|
+
'auto prepares then starts the tmux team detached with pi @startup-kickoff files',
|
|
71
|
+
'the Pac-Man multiplayer project is generated by the ZTeam under reports/.../project, not prebuilt in examples/'
|
|
72
|
+
],
|
|
73
|
+
launcher_ref: LAUNCHER_REF,
|
|
74
|
+
team_manifest_ref: TEAM_MANIFEST_REF,
|
|
75
|
+
brief_ref: BRIEF_REF
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function prepareRun(requestedRunId, options = {}) {
|
|
80
|
+
const runId = requestedRunId || defaultRunId();
|
|
81
|
+
validateRunId(runId);
|
|
82
|
+
const refs = buildRefs(runId);
|
|
83
|
+
const runDirAbs = path.resolve(PROJECT_ROOT, refs.run_dir);
|
|
84
|
+
await ensureFreshRunDir(runDirAbs, options.force === true);
|
|
85
|
+
await fs.mkdir(path.resolve(PROJECT_ROOT, refs.worker_kickoffs_dir), { recursive: true });
|
|
86
|
+
await fs.mkdir(path.resolve(PROJECT_ROOT, refs.project_dir), { recursive: true });
|
|
87
|
+
|
|
88
|
+
const mission = await fs.readFile(path.resolve(PROJECT_ROOT, MISSION_REF), 'utf8');
|
|
89
|
+
const outputContract = await fs.readFile(path.resolve(PROJECT_ROOT, OUTPUT_CONTRACT_REF), 'utf8');
|
|
90
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.mission_ref), mission);
|
|
91
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.output_contract_ref), outputContract);
|
|
92
|
+
|
|
93
|
+
const now = new Date().toISOString();
|
|
94
|
+
const manifest = {
|
|
95
|
+
schema: 'agent-factory-pacman.run-manifest.v1',
|
|
96
|
+
status: 'prepared',
|
|
97
|
+
no_ship: false,
|
|
98
|
+
run_id: runId,
|
|
99
|
+
run_dir: refs.run_dir,
|
|
100
|
+
project_dir: refs.project_dir,
|
|
101
|
+
team_id: TEAM_ID,
|
|
102
|
+
room: ROOM,
|
|
103
|
+
session: SESSION_NAME,
|
|
104
|
+
entry_agent: ENTRY_AGENT,
|
|
105
|
+
created_at: now,
|
|
106
|
+
refs: {
|
|
107
|
+
run_manifest_ref: refs.run_manifest_ref,
|
|
108
|
+
artifact_contracts_ref: refs.artifact_contracts_ref,
|
|
109
|
+
mission_ref: refs.mission_ref,
|
|
110
|
+
output_contract_ref: refs.output_contract_ref,
|
|
111
|
+
kickoff_ref: refs.kickoff_ref,
|
|
112
|
+
kickoff_dispatch_ref: refs.kickoff_dispatch_ref,
|
|
113
|
+
worker_kickoffs_dir: refs.worker_kickoffs_dir,
|
|
114
|
+
workgraph_ref: refs.workgraph_ref,
|
|
115
|
+
status_ref: refs.status_ref,
|
|
116
|
+
iteration_log_ref: refs.iteration_log_ref,
|
|
117
|
+
team_manifest_ref: TEAM_MANIFEST_REF,
|
|
118
|
+
launcher_ref: LAUNCHER_REF,
|
|
119
|
+
brief_ref: BRIEF_REF
|
|
120
|
+
},
|
|
121
|
+
policies: {
|
|
122
|
+
local_only: true,
|
|
123
|
+
parent_visible_only: true,
|
|
124
|
+
all_agents_startup_message_from_file: true,
|
|
125
|
+
post_start_tmux_paste_disabled: true,
|
|
126
|
+
generated_project_under_run_dir: true,
|
|
127
|
+
source_examples_prebuilt_game_allowed: false,
|
|
128
|
+
raw_body_persistence_allowed: false,
|
|
129
|
+
external_network_allowed: false,
|
|
130
|
+
commit_push_allowed: false
|
|
131
|
+
},
|
|
132
|
+
agents: [{ id: ENTRY_AGENT, alias: 'pacman_chief', role: 'lead' }, ...AGENTS]
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const contracts = {
|
|
136
|
+
schema: 'agent-factory-pacman.artifact-contracts.v1',
|
|
137
|
+
status: 'active',
|
|
138
|
+
no_ship: false,
|
|
139
|
+
run_id: runId,
|
|
140
|
+
project_dir: refs.project_dir,
|
|
141
|
+
source_brief_refs: [MISSION_REF, OUTPUT_CONTRACT_REF],
|
|
142
|
+
required_generated_artifacts: [
|
|
143
|
+
`${refs.project_dir}/README.md`,
|
|
144
|
+
`${refs.project_dir}/AGENTS.md`,
|
|
145
|
+
`${refs.project_dir}/package.json`,
|
|
146
|
+
`${refs.project_dir}/src/game/engine.ts`,
|
|
147
|
+
`${refs.project_dir}/src/game/types.ts`,
|
|
148
|
+
`${refs.project_dir}/src/ui/main.ts`,
|
|
149
|
+
`${refs.project_dir}/src/ui/renderer.ts`,
|
|
150
|
+
`${refs.project_dir}/src/ui/input.ts`,
|
|
151
|
+
`${refs.project_dir}/test/engine.test.ts`,
|
|
152
|
+
`${refs.project_dir}/docs/architecture.md`,
|
|
153
|
+
`${refs.project_dir}/docs/controls.md`,
|
|
154
|
+
`${refs.project_dir}/docs/validation.md`
|
|
155
|
+
],
|
|
156
|
+
final_gate: {
|
|
157
|
+
generated_project_validation_commands: ['npm install', 'npm run validate', 'npm run dev'],
|
|
158
|
+
parent_repo_validation_commands: ['npm run demo:pacman:validate', 'npm run check -- --pretty false'],
|
|
159
|
+
oracle_required: true
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const chiefTemplate = await fs.readFile(path.resolve(PROJECT_ROOT, CHIEF_TEMPLATE_REF), 'utf8');
|
|
164
|
+
const workerTemplate = await fs.readFile(path.resolve(PROJECT_ROOT, WORKER_TEMPLATE_REF), 'utf8');
|
|
165
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.kickoff_ref), render(chiefTemplate, {
|
|
166
|
+
RUN_ID: runId,
|
|
167
|
+
RUN_DIR: refs.run_dir,
|
|
168
|
+
PROJECT_DIR: refs.project_dir,
|
|
169
|
+
MISSION_REF: refs.mission_ref,
|
|
170
|
+
OUTPUT_CONTRACT_REF: refs.output_contract_ref,
|
|
171
|
+
RUN_MANIFEST_REF: refs.run_manifest_ref,
|
|
172
|
+
ARTIFACT_CONTRACTS_REF: refs.artifact_contracts_ref,
|
|
173
|
+
STATUS_REF: refs.status_ref,
|
|
174
|
+
ITERATION_LOG_REF: refs.iteration_log_ref
|
|
175
|
+
}));
|
|
176
|
+
for (const agent of AGENTS) {
|
|
177
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.worker_kickoffs_dir, `${agent.id}-kickoff.md`), render(workerTemplate, {
|
|
178
|
+
RUN_ID: runId,
|
|
179
|
+
RUN_DIR: refs.run_dir,
|
|
180
|
+
PROJECT_DIR: refs.project_dir,
|
|
181
|
+
MISSION_REF: refs.mission_ref,
|
|
182
|
+
OUTPUT_CONTRACT_REF: refs.output_contract_ref,
|
|
183
|
+
AGENT_ID: agent.id,
|
|
184
|
+
ALIAS: agent.alias,
|
|
185
|
+
ROLE: agent.role,
|
|
186
|
+
MISSION: agent.mission,
|
|
187
|
+
RUN_MANIFEST_REF: refs.run_manifest_ref,
|
|
188
|
+
ARTIFACT_CONTRACTS_REF: refs.artifact_contracts_ref,
|
|
189
|
+
WORKGRAPH_REF: refs.workgraph_ref
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await writeJson(path.resolve(PROJECT_ROOT, refs.run_manifest_ref), manifest);
|
|
194
|
+
await writeJson(path.resolve(PROJECT_ROOT, refs.artifact_contracts_ref), contracts);
|
|
195
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.workgraph_ref), initialWorkgraph(runId, refs));
|
|
196
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.status_ref), initialStatus(runId, refs));
|
|
197
|
+
await writeText(path.resolve(PROJECT_ROOT, refs.iteration_log_ref), `# Iteration log — ${runId}\n\n- ${now}: prepared Pac-Man run artifacts and empty project target; tmux/Pi not launched by prepare; game not generated by prepare.\n`);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
schema: 'agent-factory-pacman.prepare.v1',
|
|
201
|
+
status: 'pass',
|
|
202
|
+
no_ship: false,
|
|
203
|
+
run_id: runId,
|
|
204
|
+
run_dir: refs.run_dir,
|
|
205
|
+
project_dir: refs.project_dir,
|
|
206
|
+
kickoff_ref: refs.kickoff_ref,
|
|
207
|
+
worker_kickoffs_dir: refs.worker_kickoffs_dir,
|
|
208
|
+
next_commands: {
|
|
209
|
+
auto: `npm run demo:pacman -- ${runId}`,
|
|
210
|
+
start_team: `AGENT_FACTORY_PACMAN_RUN_ID=${runId} AGENT_FACTORY_PACMAN_CHIEF_KICKOFF_FILE=${refs.kickoff_ref} AGENT_FACTORY_PACMAN_WORKER_KICKOFF_DIR=${refs.worker_kickoffs_dir} ${LAUNCHER_REF} start-detached ${ENTRY_AGENT}`,
|
|
211
|
+
attach_chief: `${LAUNCHER_REF} attach ${ENTRY_AGENT}`,
|
|
212
|
+
generated_project_after_agents: `cd ${refs.project_dir} && npm install && npm run validate && npm run dev`
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function autoRun(requestedRunId, options = {}) {
|
|
218
|
+
const prepared = await prepareRun(requestedRunId, options);
|
|
219
|
+
if (prepared.no_ship) return { schema: 'agent-factory-pacman.auto.v1', status: 'blocked', no_ship: true, phase: 'prepare', prepared };
|
|
220
|
+
const start = startTeam(prepared.run_id, prepared.kickoff_ref, prepared.worker_kickoffs_dir);
|
|
221
|
+
if (start.no_ship) return { schema: 'agent-factory-pacman.auto.v1', status: 'blocked', no_ship: true, phase: 'start-team', prepared, start };
|
|
222
|
+
const refs = buildRefs(prepared.run_id);
|
|
223
|
+
const dispatch = {
|
|
224
|
+
schema: 'agent-factory-pacman.kickoff-dispatch.v1',
|
|
225
|
+
status: 'pass',
|
|
226
|
+
no_ship: false,
|
|
227
|
+
run_id: prepared.run_id,
|
|
228
|
+
project_dir: prepared.project_dir,
|
|
229
|
+
session: SESSION_NAME,
|
|
230
|
+
transport: 'pi @startup-kickoff.md for chief and workers',
|
|
231
|
+
chief_kickoff_ref: prepared.kickoff_ref,
|
|
232
|
+
worker_kickoffs_dir: prepared.worker_kickoffs_dir,
|
|
233
|
+
agents: ALL_AGENTS,
|
|
234
|
+
created_at: new Date().toISOString()
|
|
235
|
+
};
|
|
236
|
+
await writeJson(path.resolve(PROJECT_ROOT, refs.kickoff_dispatch_ref), dispatch);
|
|
237
|
+
return {
|
|
238
|
+
schema: 'agent-factory-pacman.auto.v1',
|
|
239
|
+
status: 'launched',
|
|
240
|
+
no_ship: false,
|
|
241
|
+
run_id: prepared.run_id,
|
|
242
|
+
run_dir: prepared.run_dir,
|
|
243
|
+
project_dir: prepared.project_dir,
|
|
244
|
+
session: SESSION_NAME,
|
|
245
|
+
prepared,
|
|
246
|
+
start,
|
|
247
|
+
kickoff_dispatch_ref: refs.kickoff_dispatch_ref,
|
|
248
|
+
next_commands: {
|
|
249
|
+
attach_chief: `${LAUNCHER_REF} attach ${ENTRY_AGENT}`,
|
|
250
|
+
status: `${LAUNCHER_REF} status`,
|
|
251
|
+
generated_project_after_agents: `cd ${prepared.project_dir} && npm install && npm run validate && npm run dev`
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function startTeam(runId, kickoffRef, workerKickoffsDir) {
|
|
257
|
+
const tmuxCheck = spawnSync('tmux', ['-V'], { encoding: 'utf8' });
|
|
258
|
+
if (tmuxCheck.status !== 0) return { status: 'blocked', no_ship: true, reason: 'tmux_missing', detail: 'tmux is required for full automatic demo launch' };
|
|
259
|
+
const existing = spawnSync('tmux', ['has-session', '-t', SESSION_NAME], { encoding: 'utf8' });
|
|
260
|
+
if (existing.status === 0) return { status: 'blocked', no_ship: true, reason: 'session_already_running', session: SESSION_NAME, detail: `${LAUNCHER_REF} status` };
|
|
261
|
+
const result = spawnSync('bash', [path.resolve(PROJECT_ROOT, LAUNCHER_REF), 'start-detached', ENTRY_AGENT], {
|
|
262
|
+
cwd: PROJECT_ROOT,
|
|
263
|
+
encoding: 'utf8',
|
|
264
|
+
env: { ...process.env, AGENT_FACTORY_PACMAN_RUN_ID: runId, AGENT_FACTORY_PACMAN_CHIEF_KICKOFF_FILE: path.resolve(PROJECT_ROOT, kickoffRef), AGENT_FACTORY_PACMAN_WORKER_KICKOFF_DIR: path.resolve(PROJECT_ROOT, workerKickoffsDir) }
|
|
265
|
+
});
|
|
266
|
+
if (result.status !== 0) return { status: 'blocked', no_ship: true, reason: 'launcher_failed', stderr: result.stderr, stdout: result.stdout };
|
|
267
|
+
return { status: 'started_detached', no_ship: false, session: SESSION_NAME, stdout: result.stdout.trim() };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function statusRun(runId) {
|
|
271
|
+
const tmux = spawnSync('bash', [path.resolve(PROJECT_ROOT, LAUNCHER_REF), 'status'], { cwd: PROJECT_ROOT, encoding: 'utf8' });
|
|
272
|
+
const payload = { schema: 'agent-factory-pacman.status.v1', status: 'pass', no_ship: false, session: SESSION_NAME, tmux_status: tmux.stdout.trim() || tmux.stderr.trim() };
|
|
273
|
+
if (!runId) return payload;
|
|
274
|
+
const refs = buildRefs(sanitizeRunId(runId));
|
|
275
|
+
const manifest = await readJson(path.resolve(PROJECT_ROOT, refs.run_manifest_ref));
|
|
276
|
+
return { ...payload, run_id: sanitizeRunId(runId), manifest_found: Boolean(manifest), project_dir: refs.project_dir, manifest };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function validateDemo(runId) {
|
|
280
|
+
const sourceChecks = [];
|
|
281
|
+
for (const ref of [
|
|
282
|
+
`${BRIEF_REF}/README.md`, `${BRIEF_REF}/AGENTS.md`, MISSION_REF, OUTPUT_CONTRACT_REF,
|
|
283
|
+
TEAM_MANIFEST_REF, LAUNCHER_REF, CHIEF_TEMPLATE_REF, WORKER_TEMPLATE_REF,
|
|
284
|
+
'.pi/zagents/agent-factory-pacman-chief.json', '.pi/zagents/agent-factory-pacman-game-designer.json', '.pi/zagents/agent-factory-pacman-game-architect.json', '.pi/zagents/agent-factory-pacman-engine-builder.json', '.pi/zagents/agent-factory-pacman-frontend-builder.json', '.pi/zagents/agent-factory-pacman-qa-oracle.json'
|
|
285
|
+
]) sourceChecks.push(await fileCheck(ref));
|
|
286
|
+
|
|
287
|
+
const stalePrebuilt = await findPrebuiltSourceFiles();
|
|
288
|
+
const runChecks = [];
|
|
289
|
+
let generatedProjectStatus = 'not_checked';
|
|
290
|
+
if (runId) {
|
|
291
|
+
const refs = buildRefs(sanitizeRunId(runId));
|
|
292
|
+
for (const ref of [refs.run_manifest_ref, refs.artifact_contracts_ref, refs.mission_ref, refs.output_contract_ref, refs.kickoff_ref, refs.workgraph_ref, refs.status_ref, refs.iteration_log_ref]) runChecks.push(await fileCheck(ref));
|
|
293
|
+
generatedProjectStatus = await pathExists(path.resolve(PROJECT_ROOT, refs.project_dir)) ? 'target_dir_ready' : 'missing_target_dir';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const missing = [...sourceChecks, ...runChecks].filter((check) => !check.pass);
|
|
297
|
+
return {
|
|
298
|
+
schema: 'agent-factory-pacman.validate.v1',
|
|
299
|
+
status: missing.length || stalePrebuilt.length ? 'fail' : 'pass',
|
|
300
|
+
no_ship: Boolean(missing.length || stalePrebuilt.length),
|
|
301
|
+
source_checks: sourceChecks,
|
|
302
|
+
run_checks: runChecks,
|
|
303
|
+
stale_prebuilt_source_files: stalePrebuilt,
|
|
304
|
+
generated_project_status: generatedProjectStatus,
|
|
305
|
+
note: 'This validates the Pac-Man Agent Factory scaffold. It intentionally does not generate or build the game.'
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function findPrebuiltSourceFiles() {
|
|
310
|
+
const allowed = new Set(['README.md', 'AGENTS.md', 'mission.md', 'output-contract.md']);
|
|
311
|
+
const out = [];
|
|
312
|
+
for (const ref of await listFilesRecursive(path.resolve(PROJECT_ROOT, BRIEF_REF))) {
|
|
313
|
+
const rel = path.relative(path.resolve(PROJECT_ROOT, BRIEF_REF), ref);
|
|
314
|
+
if (!rel.includes(path.sep) && allowed.has(rel)) continue;
|
|
315
|
+
out.push(path.relative(PROJECT_ROOT, ref));
|
|
316
|
+
}
|
|
317
|
+
return out.sort();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function listFilesRecursive(dir) {
|
|
321
|
+
try {
|
|
322
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
323
|
+
const files = [];
|
|
324
|
+
for (const entry of entries) {
|
|
325
|
+
const abs = path.join(dir, entry.name);
|
|
326
|
+
if (entry.isDirectory()) files.push(...await listFilesRecursive(abs));
|
|
327
|
+
else if (entry.isFile()) files.push(abs);
|
|
328
|
+
}
|
|
329
|
+
return files;
|
|
330
|
+
} catch {
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function buildRefs(runId) {
|
|
336
|
+
const runDir = `${RUNS_ROOT}/${runId}`;
|
|
337
|
+
return {
|
|
338
|
+
run_dir: runDir,
|
|
339
|
+
project_dir: `${runDir}/project`,
|
|
340
|
+
mission_ref: `${runDir}/mission.md`,
|
|
341
|
+
output_contract_ref: `${runDir}/output-contract.md`,
|
|
342
|
+
run_manifest_ref: `${runDir}/run-manifest.json`,
|
|
343
|
+
artifact_contracts_ref: `${runDir}/artifact-contracts.json`,
|
|
344
|
+
kickoff_ref: `${runDir}/chief-kickoff.md`,
|
|
345
|
+
kickoff_dispatch_ref: `${runDir}/kickoff-dispatch.json`,
|
|
346
|
+
worker_kickoffs_dir: `${runDir}/worker-kickoffs`,
|
|
347
|
+
workgraph_ref: `${runDir}/autonomous-workgraph.md`,
|
|
348
|
+
status_ref: `${runDir}/autonomous-status.md`,
|
|
349
|
+
iteration_log_ref: `${runDir}/iteration-log.md`
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function initialWorkgraph(runId, refs) {
|
|
354
|
+
return `# Agent Factory Pac-Man Multiplayer workgraph — ${runId}\n\nGenerated project target: ${refs.project_dir}\n\n## Required lanes\n\n- [ ] game_designer: define Pac-Man multiplayer rules, controls, scoring, and playability acceptance criteria.\n- [ ] game_architect: design engine/UI/state boundaries and implementation order.\n- [ ] engine_builder: generate engine logic and tests under project/.\n- [ ] frontend_builder: generate browser rendering/input/HUD under project/.\n- [ ] qa_oracle: review playability, tests, validation evidence, and no-ship.\n\n## Inputs\n\n- ${refs.mission_ref}\n- ${refs.output_contract_ref}\n- ${refs.artifact_contracts_ref}\n`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function initialStatus(runId, refs) {
|
|
358
|
+
return `# Agent Factory Pac-Man Multiplayer status — ${runId}\n\nstatus: prepared\nno_ship: false\nroom: ${ROOM}\nsession: ${SESSION_NAME}\nproject_dir: ${refs.project_dir}\n\nPrepare completed. The project directory is intentionally empty until the ZTeam generates the Pac-Man game.\n`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function fileCheck(ref) {
|
|
362
|
+
return { ref, pass: await pathExists(path.resolve(PROJECT_ROOT, ref)) };
|
|
363
|
+
}
|
|
364
|
+
async function pathExists(target) {
|
|
365
|
+
try { await fs.access(target); return true; } catch { return false; }
|
|
366
|
+
}
|
|
367
|
+
async function ensureFreshRunDir(runDirAbs, force) {
|
|
368
|
+
try {
|
|
369
|
+
const entries = await fs.readdir(runDirAbs);
|
|
370
|
+
if (entries.length && !force) throw new Error(`run dir already exists; pass --force or choose another run id: ${path.relative(PROJECT_ROOT, runDirAbs)}`);
|
|
371
|
+
if (force) await fs.rm(runDirAbs, { recursive: true, force: true });
|
|
372
|
+
} catch (error) {
|
|
373
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
374
|
+
}
|
|
375
|
+
await fs.mkdir(runDirAbs, { recursive: true });
|
|
376
|
+
}
|
|
377
|
+
function render(template, vars) { return template.replace(/\{\{([A-Z0-9_]+)\}\}/g, (_, key) => String(vars[key] ?? `{{${key}}}`)); }
|
|
378
|
+
async function writeText(absPath, content) { await fs.mkdir(path.dirname(absPath), { recursive: true }); await fs.writeFile(absPath, content); }
|
|
379
|
+
async function writeJson(absPath, value) { await writeText(absPath, `${JSON.stringify(value, null, 2)}\n`); }
|
|
380
|
+
async function readJson(absPath) { try { return JSON.parse(await fs.readFile(absPath, 'utf8')); } catch { return undefined; } }
|
|
381
|
+
function validateRunId(runId) { if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,80}$/.test(runId)) throw new Error(`invalid run id: ${runId}`); }
|
|
382
|
+
function sanitizeRunId(runId) { const safe = String(runId || '').replace(/[^A-Za-z0-9_-]/g, '-').replace(/^-+|-+$/g, '').slice(0, 81); validateRunId(safe); return safe; }
|
|
383
|
+
function defaultRunId() { return `pacman-${new Date().toISOString().replace(/[-:TZ.]/g, '').slice(0, 14)}`; }
|
|
384
|
+
function print(value) { process.stdout.write(`${JSON.stringify(value, null, 2)}\n`); }
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zteam.v1",
|
|
3
|
+
"id": "agent-factory-pacman-multiplayer",
|
|
4
|
+
"description": "Runnable Agent Factory demo team: autonomously generates a local browser-playable Pac-Man-inspired multiplayer game under reports/agent-factory-pacman-runs/<run_id>/project/.",
|
|
5
|
+
"defaultRoom": "pacman-factory",
|
|
6
|
+
"activeRoom": "pacman-factory",
|
|
7
|
+
"rooms": [
|
|
8
|
+
{ "id": "pacman-factory", "role": "parent-visible-coordination", "active": true }
|
|
9
|
+
],
|
|
10
|
+
"members": [
|
|
11
|
+
{ "id": "agent-factory-pacman-chief", "alias": "pacman_chief", "rooms": [{ "id": "pacman-factory", "alias": "pacman_chief", "role": "lead", "active": true }] },
|
|
12
|
+
{ "id": "agent-factory-pacman-game-designer", "alias": "game_designer", "rooms": [{ "id": "pacman-factory", "alias": "game_designer", "role": "game_design", "active": true }] },
|
|
13
|
+
{ "id": "agent-factory-pacman-game-architect", "alias": "game_architect", "rooms": [{ "id": "pacman-factory", "alias": "game_architect", "role": "architecture", "active": true }] },
|
|
14
|
+
{ "id": "agent-factory-pacman-engine-builder", "alias": "engine_builder", "rooms": [{ "id": "pacman-factory", "alias": "engine_builder", "role": "engine", "active": true }] },
|
|
15
|
+
{ "id": "agent-factory-pacman-frontend-builder", "alias": "frontend_builder", "rooms": [{ "id": "pacman-factory", "alias": "frontend_builder", "role": "frontend", "active": true }] },
|
|
16
|
+
{ "id": "agent-factory-pacman-qa-oracle", "alias": "qa_oracle", "rooms": [{ "id": "pacman-factory", "alias": "qa_oracle", "role": "oracle", "active": true }] }
|
|
17
|
+
],
|
|
18
|
+
"communicationPolicy": {
|
|
19
|
+
"allowedRooms": ["pacman-factory"],
|
|
20
|
+
"parentVisible": true,
|
|
21
|
+
"hiddenPeerChat": false,
|
|
22
|
+
"bodyStored": false,
|
|
23
|
+
"requiredLocalLive": false,
|
|
24
|
+
"goalRoomCanonical": true
|
|
25
|
+
},
|
|
26
|
+
"metadata": {
|
|
27
|
+
"entryAgent": "agent-factory-pacman-chief",
|
|
28
|
+
"entryRoom": "pacman-factory",
|
|
29
|
+
"manualReviewCommand": "/zteam launch-plan agent-factory-pacman-multiplayer",
|
|
30
|
+
"tmuxLauncher": ".pi/zteams/agent-factory-pacman-multiplayer.tmux.sh",
|
|
31
|
+
"runtimeScript": ".pi/zteams/agent-factory-pacman-multiplayer-runtime.mjs",
|
|
32
|
+
"runArtifactsDir": "reports/agent-factory-pacman-runs/<run_id>/",
|
|
33
|
+
"briefPath": "examples/agent-factory-pacman-multiplayer/",
|
|
34
|
+
"autoLaunchBehavior": "prepare run artifacts, render startup kickoff files, start tmux detached with one Pi session per ZAgent, and print generated-project test commands",
|
|
35
|
+
"notes": [
|
|
36
|
+
"Prepare/validate do not generate the game; only the launched ZTeam writes under reports/.../project/.",
|
|
37
|
+
"The game architect has bounded real architecture authority; builders must coordinate through pacman-factory.",
|
|
38
|
+
"The generated game must be local-only by default and must not be a dashboard/Mission Control app.",
|
|
39
|
+
"Full auto can consume model/API budget because it starts six Pi sessions."
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|