agentxchain 0.8.8 → 2.2.0

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.
Files changed (74) hide show
  1. package/README.md +136 -136
  2. package/bin/agentxchain.js +186 -5
  3. package/dashboard/app.js +305 -0
  4. package/dashboard/components/blocked.js +145 -0
  5. package/dashboard/components/cross-repo.js +126 -0
  6. package/dashboard/components/gate.js +311 -0
  7. package/dashboard/components/hooks.js +177 -0
  8. package/dashboard/components/initiative.js +147 -0
  9. package/dashboard/components/ledger.js +165 -0
  10. package/dashboard/components/timeline.js +222 -0
  11. package/dashboard/index.html +352 -0
  12. package/package.json +14 -6
  13. package/scripts/live-api-proxy-preflight-smoke.sh +531 -0
  14. package/scripts/publish-from-tag.sh +88 -0
  15. package/scripts/release-postflight.sh +231 -0
  16. package/scripts/release-preflight.sh +167 -0
  17. package/src/commands/accept-turn.js +160 -0
  18. package/src/commands/approve-completion.js +80 -0
  19. package/src/commands/approve-transition.js +85 -0
  20. package/src/commands/dashboard.js +70 -0
  21. package/src/commands/init.js +516 -0
  22. package/src/commands/migrate.js +348 -0
  23. package/src/commands/multi.js +549 -0
  24. package/src/commands/plugin.js +157 -0
  25. package/src/commands/reject-turn.js +204 -0
  26. package/src/commands/resume.js +389 -0
  27. package/src/commands/status.js +196 -3
  28. package/src/commands/step.js +947 -0
  29. package/src/commands/template-list.js +33 -0
  30. package/src/commands/template-set.js +279 -0
  31. package/src/commands/validate.js +20 -11
  32. package/src/commands/verify.js +71 -0
  33. package/src/lib/adapters/api-proxy-adapter.js +1076 -0
  34. package/src/lib/adapters/local-cli-adapter.js +337 -0
  35. package/src/lib/adapters/manual-adapter.js +169 -0
  36. package/src/lib/blocked-state.js +94 -0
  37. package/src/lib/config.js +97 -1
  38. package/src/lib/context-compressor.js +121 -0
  39. package/src/lib/context-section-parser.js +220 -0
  40. package/src/lib/coordinator-acceptance.js +428 -0
  41. package/src/lib/coordinator-config.js +461 -0
  42. package/src/lib/coordinator-dispatch.js +276 -0
  43. package/src/lib/coordinator-gates.js +487 -0
  44. package/src/lib/coordinator-hooks.js +239 -0
  45. package/src/lib/coordinator-recovery.js +523 -0
  46. package/src/lib/coordinator-state.js +365 -0
  47. package/src/lib/cross-repo-context.js +247 -0
  48. package/src/lib/dashboard/bridge-server.js +284 -0
  49. package/src/lib/dashboard/file-watcher.js +93 -0
  50. package/src/lib/dashboard/state-reader.js +96 -0
  51. package/src/lib/dispatch-bundle.js +568 -0
  52. package/src/lib/dispatch-manifest.js +252 -0
  53. package/src/lib/gate-evaluator.js +285 -0
  54. package/src/lib/governed-state.js +2139 -0
  55. package/src/lib/governed-templates.js +145 -0
  56. package/src/lib/hook-runner.js +788 -0
  57. package/src/lib/normalized-config.js +539 -0
  58. package/src/lib/plugin-config-schema.js +192 -0
  59. package/src/lib/plugins.js +692 -0
  60. package/src/lib/protocol-conformance.js +291 -0
  61. package/src/lib/reference-conformance-adapter.js +858 -0
  62. package/src/lib/repo-observer.js +597 -0
  63. package/src/lib/repo.js +0 -31
  64. package/src/lib/schema.js +121 -0
  65. package/src/lib/schemas/turn-result.schema.json +205 -0
  66. package/src/lib/token-budget.js +206 -0
  67. package/src/lib/token-counter.js +27 -0
  68. package/src/lib/turn-paths.js +67 -0
  69. package/src/lib/turn-result-validator.js +496 -0
  70. package/src/lib/validation.js +137 -0
  71. package/src/templates/governed/api-service.json +31 -0
  72. package/src/templates/governed/cli-tool.json +30 -0
  73. package/src/templates/governed/generic.json +10 -0
  74. package/src/templates/governed/web-app.json +30 -0
@@ -0,0 +1,85 @@
1
+ import chalk from 'chalk';
2
+ import { loadProjectContext, loadProjectState } from '../lib/config.js';
3
+ import { approvePhaseTransition } from '../lib/governed-state.js';
4
+ import { deriveRecoveryDescriptor } from '../lib/blocked-state.js';
5
+
6
+ export async function approveTransitionCommand(opts) {
7
+ const context = loadProjectContext();
8
+ if (!context) {
9
+ console.log(chalk.red('No agentxchain.json found. Run `agentxchain init` first.'));
10
+ process.exit(1);
11
+ }
12
+
13
+ if (context.config.protocol_mode !== 'governed') {
14
+ console.log(chalk.red('approve-transition is only available in governed mode.'));
15
+ process.exit(1);
16
+ }
17
+
18
+ const { root, config } = context;
19
+ const state = loadProjectState(root, config);
20
+
21
+ if (!state?.pending_phase_transition) {
22
+ console.log(chalk.yellow('No pending phase transition to approve.'));
23
+ if (state?.phase) {
24
+ console.log(chalk.dim(` Current phase: ${state.phase}`));
25
+ }
26
+ process.exit(1);
27
+ }
28
+
29
+ const pt = state.pending_phase_transition;
30
+ console.log('');
31
+ console.log(chalk.bold(' Approving Phase Transition'));
32
+ console.log(chalk.dim(' ' + '─'.repeat(44)));
33
+ console.log(` ${chalk.dim('From:')} ${pt.from}`);
34
+ console.log(` ${chalk.dim('To:')} ${pt.to}`);
35
+ console.log(` ${chalk.dim('Gate:')} ${pt.gate}`);
36
+ console.log(` ${chalk.dim('Turn:')} ${pt.requested_by_turn}`);
37
+ console.log('');
38
+
39
+ const result = approvePhaseTransition(root, config);
40
+
41
+ if (!result.ok) {
42
+ if (result.error_code?.startsWith('hook_') || result.error_code === 'hook_blocked') {
43
+ printGateHookFailure(result, 'phase_transition', pt);
44
+ } else {
45
+ console.log(chalk.red(` Failed: ${result.error}`));
46
+ }
47
+ process.exit(1);
48
+ }
49
+
50
+ console.log(chalk.green(` ✓ Phase advanced: ${pt.from} → ${pt.to}`));
51
+ console.log(chalk.dim(` Run status: ${result.state.status}`));
52
+ console.log('');
53
+ console.log(chalk.dim(` Next: agentxchain step (to run the first turn in ${pt.to} phase)`));
54
+ console.log('');
55
+ }
56
+
57
+ function printGateHookFailure(result, gateType, gateInfo) {
58
+ const recovery = deriveRecoveryDescriptor(result.state);
59
+ const hookName = result.hookResults?.blocker?.hook_name
60
+ || result.hookResults?.results?.find((entry) => entry.hook_name)?.hook_name
61
+ || '(unknown)';
62
+
63
+ console.log('');
64
+ console.log(chalk.yellow(` ${gateType === 'phase_transition' ? 'Phase Transition' : 'Run Completion'} Blocked By Hook`));
65
+ console.log(chalk.dim(' ' + '-'.repeat(44)));
66
+ console.log('');
67
+ if (gateType === 'phase_transition') {
68
+ console.log(` ${chalk.dim('From:')} ${gateInfo.from}`);
69
+ console.log(` ${chalk.dim('To:')} ${gateInfo.to}`);
70
+ }
71
+ console.log(` ${chalk.dim('Gate:')} ${gateInfo.gate}`);
72
+ console.log(` ${chalk.dim('Hook:')} ${hookName}`);
73
+ console.log(` ${chalk.dim('Error:')} ${result.error}`);
74
+ if (recovery) {
75
+ console.log(` ${chalk.dim('Reason:')} ${recovery.typed_reason}`);
76
+ console.log(` ${chalk.dim('Owner:')} ${recovery.owner}`);
77
+ console.log(` ${chalk.dim('Action:')} ${recovery.recovery_action}`);
78
+ if (recovery.detail) {
79
+ console.log(` ${chalk.dim('Detail:')} ${recovery.detail}`);
80
+ }
81
+ } else {
82
+ console.log(` ${chalk.dim('Action:')} Fix or reconfigure hook "${hookName}", then rerun agentxchain approve-transition`);
83
+ }
84
+ console.log('');
85
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * CLI command: agentxchain dashboard
3
+ *
4
+ * Starts the read-only dashboard bridge server and opens a browser.
5
+ * See: V2_DASHBOARD_SPEC.md, DEC-DASH-002 (read-only in v2.0).
6
+ */
7
+
8
+ import { existsSync } from 'fs';
9
+ import { join, dirname } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import { createBridgeServer } from '../lib/dashboard/bridge-server.js';
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const DEFAULT_PORT = 3847;
15
+
16
+ export async function dashboardCommand(options) {
17
+ const cwd = process.cwd();
18
+ const agentxchainDir = join(cwd, '.agentxchain');
19
+ const dashboardDir = join(__dirname, '..', '..', 'dashboard');
20
+
21
+ if (!existsSync(agentxchainDir)) {
22
+ console.error('Error: No .agentxchain/ directory found in the current directory.');
23
+ console.error('Run "agentxchain init --governed" first to create a governed project.');
24
+ process.exit(1);
25
+ }
26
+
27
+ if (!existsSync(dashboardDir)) {
28
+ console.error('Error: Dashboard assets not found at', dashboardDir);
29
+ process.exit(1);
30
+ }
31
+
32
+ const port = parseInt(options.port, 10) || DEFAULT_PORT;
33
+ const bridge = createBridgeServer({ agentxchainDir, dashboardDir, port });
34
+
35
+ try {
36
+ const { port: actualPort } = await bridge.start();
37
+ const url = `http://localhost:${actualPort}`;
38
+
39
+ console.log(`Dashboard running at ${url}`);
40
+ console.log('Press Ctrl+C to stop.\n');
41
+
42
+ if (options.open !== false) {
43
+ try {
44
+ const { exec } = await import('child_process');
45
+ const openCmd = process.platform === 'darwin' ? 'open'
46
+ : process.platform === 'win32' ? 'start'
47
+ : 'xdg-open';
48
+ exec(`${openCmd} ${url}`);
49
+ } catch {
50
+ // Browser open is best-effort
51
+ }
52
+ }
53
+
54
+ // Keep running until interrupted
55
+ const shutdown = async () => {
56
+ console.log('\nShutting down dashboard...');
57
+ await bridge.stop();
58
+ process.exit(0);
59
+ };
60
+ process.on('SIGINT', shutdown);
61
+ process.on('SIGTERM', shutdown);
62
+
63
+ } catch (err) {
64
+ if (err.code === 'EADDRINUSE') {
65
+ console.error(`Error: Port ${port} is already in use. Try --port <number>.`);
66
+ process.exit(1);
67
+ }
68
+ throw err;
69
+ }
70
+ }
@@ -5,6 +5,7 @@ import chalk from 'chalk';
5
5
  import inquirer from 'inquirer';
6
6
  import { CONFIG_FILE, LOCK_FILE, STATE_FILE } from '../lib/config.js';
7
7
  import { generateVSCodeFiles } from '../lib/generate-vscode.js';
8
+ import { loadGovernedTemplate, VALID_GOVERNED_TEMPLATE_IDS } from '../lib/governed-templates.js';
8
9
 
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
11
  const TEMPLATES_DIR = join(__dirname, '../templates');
@@ -45,7 +46,522 @@ function loadTemplates() {
45
46
  return templates;
46
47
  }
47
48
 
49
+ function interpolateTemplateContent(contentTemplate, projectName) {
50
+ return contentTemplate.replaceAll('{{project_name}}', projectName);
51
+ }
52
+
53
+ function appendPromptOverride(basePrompt, override) {
54
+ if (!override || !override.trim()) return basePrompt;
55
+ return `${basePrompt}\n\n---\n\n## Project-Type-Specific Guidance\n\n${override.trim()}\n`;
56
+ }
57
+
58
+ function appendAcceptanceHints(baseMatrix, acceptanceHints) {
59
+ if (!Array.isArray(acceptanceHints) || acceptanceHints.length === 0) {
60
+ return baseMatrix;
61
+ }
62
+
63
+ const hintLines = acceptanceHints.map((hint) => `- [ ] ${hint}`).join('\n');
64
+ return `${baseMatrix}\n\n## Template Guidance\n${hintLines}\n`;
65
+ }
66
+
67
+ // ── Governed init ───────────────────────────────────────────────────────────
68
+
69
+ const GOVERNED_ROLES = {
70
+ pm: {
71
+ title: 'Product Manager',
72
+ mandate: 'Protect user value, scope clarity, and acceptance criteria.',
73
+ write_authority: 'review_only',
74
+ runtime: 'manual-pm'
75
+ },
76
+ dev: {
77
+ title: 'Developer',
78
+ mandate: 'Implement approved work safely and verify behavior.',
79
+ write_authority: 'authoritative',
80
+ runtime: 'local-dev'
81
+ },
82
+ qa: {
83
+ title: 'QA',
84
+ mandate: 'Challenge correctness, acceptance coverage, and ship readiness.',
85
+ write_authority: 'review_only',
86
+ runtime: 'api-qa'
87
+ },
88
+ eng_director: {
89
+ title: 'Engineering Director',
90
+ mandate: 'Resolve tactical deadlocks and enforce technical coherence.',
91
+ write_authority: 'review_only',
92
+ runtime: 'manual-director'
93
+ }
94
+ };
95
+
96
+ const GOVERNED_RUNTIMES = {
97
+ 'manual-pm': { type: 'manual' },
98
+ 'local-dev': { type: 'local_cli', command: ['claude', '--print', '-p', '{prompt}'], cwd: '.', prompt_transport: 'argv' },
99
+ 'api-qa': { type: 'api_proxy', provider: 'anthropic', model: 'claude-sonnet-4-6', auth_env: 'ANTHROPIC_API_KEY' },
100
+ 'manual-director': { type: 'manual' }
101
+ };
102
+
103
+ const GOVERNED_ROUTING = {
104
+ planning: {
105
+ entry_role: 'pm',
106
+ allowed_next_roles: ['pm', 'eng_director', 'human'],
107
+ exit_gate: 'planning_signoff'
108
+ },
109
+ implementation: {
110
+ entry_role: 'dev',
111
+ allowed_next_roles: ['dev', 'qa', 'eng_director', 'human'],
112
+ exit_gate: 'implementation_complete'
113
+ },
114
+ qa: {
115
+ entry_role: 'qa',
116
+ allowed_next_roles: ['dev', 'qa', 'eng_director', 'human'],
117
+ exit_gate: 'qa_ship_verdict'
118
+ }
119
+ };
120
+
121
+ const GOVERNED_GATES = {
122
+ planning_signoff: {
123
+ requires_files: ['.planning/PM_SIGNOFF.md', '.planning/ROADMAP.md'],
124
+ requires_human_approval: true
125
+ },
126
+ implementation_complete: {
127
+ requires_verification_pass: true
128
+ },
129
+ qa_ship_verdict: {
130
+ requires_files: ['.planning/acceptance-matrix.md', '.planning/ship-verdict.md'],
131
+ requires_human_approval: true
132
+ }
133
+ };
134
+
135
+ function buildGovernedPrompt(roleId, role) {
136
+ const rolePrompts = {
137
+ pm: buildPmPrompt,
138
+ dev: buildDevPrompt,
139
+ qa: buildQaPrompt,
140
+ eng_director: buildEngDirectorPrompt,
141
+ };
142
+
143
+ const builder = rolePrompts[roleId];
144
+ if (builder) return builder(role);
145
+
146
+ // Fallback for custom roles
147
+ return buildGenericPrompt(roleId, role);
148
+ }
149
+
150
+ function buildPmPrompt(role) {
151
+ return `# Product Manager — Role Prompt
152
+
153
+ You are the Product Manager. Your mandate: **${role.mandate}**
154
+
155
+ ## What You Do Each Turn
156
+
157
+ 1. **Read the previous turn** (from CONTEXT.md). Understand what was done and what decisions were made.
158
+ 2. **Challenge it.** Even if the work looks correct, identify at least one risk, scope gap, or assumption worth questioning. Rubber-stamping violates the protocol.
159
+ 3. **Create or refine planning artifacts:**
160
+ - \`.planning/ROADMAP.md\` — what will be built, in what order, with acceptance criteria
161
+ - \`.planning/PM_SIGNOFF.md\` — your formal sign-off when planning is complete
162
+ - \`.planning/acceptance-matrix.md\` — the acceptance criteria checklist for QA
163
+ 4. **Propose the next role.** Typically \`dev\` after planning is complete, or \`eng_director\` if there's a technical deadlock.
164
+
165
+ ## Planning Phase Exit
166
+
167
+ To exit the planning phase, you must:
168
+ - Ensure \`.planning/PM_SIGNOFF.md\` exists with your explicit sign-off
169
+ - Ensure \`.planning/ROADMAP.md\` exists with clear acceptance criteria
170
+ - Set \`phase_transition_request: "implementation"\` in your turn result
171
+
172
+ The orchestrator will evaluate the gate and may require human approval.
173
+
174
+ ## Scope Authority
175
+
176
+ You define **what** gets built and **why**. You do not define **how** — that's the developer's domain. If you disagree with a technical approach, raise an objection with rationale, but do not override implementation decisions.
177
+
178
+ ## Acceptance Criteria Quality
179
+
180
+ Every roadmap item must have acceptance criteria that are:
181
+ - **Observable** — can be verified by running code or inspecting output
182
+ - **Specific** — not "works well" but "returns 200 for GET /api/users with valid token"
183
+ - **Complete** — covers happy path, error cases, and edge cases worth testing
184
+ `;
185
+ }
186
+
187
+ function buildDevPrompt(role) {
188
+ return `# Developer — Role Prompt
189
+
190
+ You are the Developer. Your mandate: **${role.mandate}**
191
+
192
+ ## What You Do Each Turn
193
+
194
+ 1. **Read the previous turn and ROADMAP.md.** Understand what you're building and what the acceptance criteria are.
195
+ 2. **Challenge the previous turn.** If the PM's acceptance criteria are ambiguous, flag it. If QA's objections are unfounded, explain why. Never skip this.
196
+ 3. **Implement the work.**
197
+ - Write clean, correct code that meets the acceptance criteria
198
+ - Run tests and include the results as verification evidence
199
+ - Accurately list every file you changed in \`files_changed\`
200
+ 4. **Verify your work.** Run the test suite, linter, or build command. Record the commands and exit codes in \`verification.machine_evidence\`.
201
+
202
+ ## Implementation Rules
203
+
204
+ - Only implement what the roadmap and acceptance criteria require. Do not add unrequested features.
205
+ - If acceptance criteria are unclear, set \`status: "needs_human"\` and explain what needs clarification in \`needs_human_reason\`.
206
+ - If you encounter a technical blocker, set \`status: "blocked"\` and describe it.
207
+
208
+ ## Verification Is Mandatory
209
+
210
+ You must run verification commands and report them honestly:
211
+ - \`verification.status\` must be \`"pass"\` only if all commands exited with code 0
212
+ - \`verification.machine_evidence\` must list every command you ran with its actual exit code
213
+ - Do NOT claim \`"pass"\` if you did not run the tests
214
+
215
+ ## Phase Transition
216
+
217
+ When your implementation is complete and verified:
218
+ - If the implementation phase gate requires verification pass: ensure tests pass
219
+ - Set \`phase_transition_request: "qa"\` to advance to QA
220
+ - The gate may auto-advance or require human approval depending on config
221
+
222
+ ## Artifact Type
223
+
224
+ Your artifact type is \`workspace\` (direct file modifications). The orchestrator will diff your changes against the pre-turn snapshot to verify \`files_changed\` accuracy.
225
+ `;
226
+ }
227
+
228
+ function buildQaPrompt(role) {
229
+ return `# QA — Role Prompt
230
+
231
+ You are QA. Your mandate: **${role.mandate}**
232
+
233
+ ## What You Do Each Turn
234
+
235
+ 1. **Read the previous turn, the ROADMAP, and the acceptance matrix.** Understand what was built and what the acceptance criteria are.
236
+ 2. **Challenge the implementation.** You MUST raise at least one objection — this is a protocol requirement for review_only roles. If the code is perfect, challenge the test coverage, the edge cases, or the documentation.
237
+ 3. **Evaluate against acceptance criteria.** Go through each criterion and determine pass/fail.
238
+ 4. **Create review artifacts:**
239
+ - \`.planning/acceptance-matrix.md\` — updated with pass/fail verdicts per criterion
240
+ - \`.planning/ship-verdict.md\` — your overall ship/no-ship recommendation
241
+
242
+ ## You Cannot Modify Code
243
+
244
+ You have \`review_only\` write authority. You may NOT modify product files. You may only create/modify files under \`.planning/\` and \`.agentxchain/reviews/\`. Your artifact type must be \`review\`.
245
+
246
+ ## Objection Requirement
247
+
248
+ You MUST raise at least one objection in your turn result. An empty \`objections\` array is a protocol violation and will be rejected by the validator. If the work is genuinely excellent, raise a low-severity observation about test coverage, documentation, or future risk.
249
+
250
+ Each objection must have:
251
+ - \`id\`: pattern \`OBJ-NNN\`
252
+ - \`severity\`: \`low\`, \`medium\`, \`high\`, or \`blocking\`
253
+ - \`against_turn_id\`: the turn you're challenging
254
+ - \`statement\`: clear description of the issue
255
+ - \`status\`: \`"raised"\`
256
+
257
+ ## Blocking vs. Non-Blocking
258
+
259
+ - \`blocking\` severity means the work cannot ship. Use sparingly and only for real defects.
260
+ - \`high\` severity means significant risk but potentially shippable with mitigation.
261
+ - \`medium\` and \`low\` are observations that improve quality but don't block.
262
+
263
+ ## Ship Verdict & Run Completion
264
+
265
+ When you are satisfied the work meets acceptance criteria:
266
+ 1. Create \`.planning/ship-verdict.md\` with your verdict
267
+ 2. Create/update \`.planning/acceptance-matrix.md\` with all criteria checked
268
+ 3. Set \`run_completion_request: true\` in your turn result
269
+
270
+ **Only set \`run_completion_request: true\` when:**
271
+ - All blocking objections from prior turns are resolved
272
+ - The acceptance matrix shows all critical criteria passing
273
+ - \`.planning/ship-verdict.md\` exists with an affirmative verdict
274
+
275
+ **Do NOT set \`run_completion_request: true\` if:**
276
+ - You have unresolved blocking objections
277
+ - Critical acceptance criteria are failing
278
+ - You need the developer to fix issues first (propose \`dev\` as next role instead)
279
+
280
+ ## Routing After QA
281
+
282
+ - If issues found → propose \`dev\` as next role (they fix, then you re-review)
283
+ - If ship-ready → set \`run_completion_request: true\`
284
+ - If deadlocked → propose \`eng_director\` or \`human\`
285
+ `;
286
+ }
287
+
288
+ function buildEngDirectorPrompt(role) {
289
+ return `# Engineering Director — Role Prompt
290
+
291
+ You are the Engineering Director. Your mandate: **${role.mandate}**
292
+
293
+ ## When You Are Called
294
+
295
+ You are invoked when the normal PM → Dev → QA loop is stuck:
296
+ - Repeated QA/Dev cycles without convergence
297
+ - Technical disagreement between roles
298
+ - Scope dispute that the PM cannot resolve
299
+ - Budget or timeline pressure requiring trade-offs
300
+
301
+ ## What You Do Each Turn
302
+
303
+ 1. **Read the full context.** Review the escalation reason, unresolved objections, and the decision history.
304
+ 2. **Make a binding decision.** Your role is to break deadlocks, not to add more opinions. State your decision clearly with rationale.
305
+ 3. **Challenge what led to the deadlock.** Identify the root cause — unclear acceptance criteria? Wrong technical approach? Scope creep?
306
+ 4. **Route back to the appropriate role.** After your decision, the normal loop should resume.
307
+
308
+ ## Decision Authority
309
+
310
+ - You may override QA objections if they are unreasonable or out of scope
311
+ - You may override PM scope decisions if they create technical impossibility
312
+ - You may NOT override human decisions — escalate to \`human\` if needed
313
+ - Every override must be recorded as a decision with clear rationale
314
+
315
+ ## You Cannot Modify Code
316
+
317
+ You have \`review_only\` write authority. Like QA, you must raise at least one objection (protocol requirement). Your artifact type is \`review\`.
318
+
319
+ ## Objection Requirement
320
+
321
+ You MUST raise at least one objection. Typically this will be about the process failure that led to your involvement — why did the loop deadlock? What should be done differently next time?
322
+
323
+ ## Escalation to Human
324
+
325
+ If you cannot resolve the deadlock:
326
+ - Set \`status: "needs_human"\`
327
+ - Explain the situation in \`needs_human_reason\`
328
+ - The orchestrator will pause the run for human input
329
+ `;
330
+ }
331
+
332
+ function buildGenericPrompt(roleId, role) {
333
+ return `# ${role.title} — Role Prompt
334
+
335
+ You are the **${role.title}** on this project.
336
+
337
+ **Mandate:** ${role.mandate}
338
+ **Write authority:** ${role.write_authority}
339
+
340
+ ## What You Do Each Turn
341
+
342
+ 1. Read the previous turn and challenge it explicitly.
343
+ 2. Do your work according to your mandate.
344
+ 3. Write your structured turn result to the turn-scoped staging path printed by the orchestrator (\`.agentxchain/staging/<turn_id>/turn-result.json\`).
345
+
346
+ ## File Access
347
+
348
+ ${role.write_authority === 'authoritative'
349
+ ? 'You may modify product files directly.'
350
+ : role.write_authority === 'proposed'
351
+ ? 'You may propose changes via patches.'
352
+ : 'You may NOT modify product files. Only create review artifacts under `.planning/` and `.agentxchain/reviews/`.'}
353
+ `;
354
+ }
355
+
356
+ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'generic') {
357
+ const template = loadGovernedTemplate(templateId);
358
+ const config = {
359
+ schema_version: '1.0',
360
+ template: template.id,
361
+ project: {
362
+ id: projectId,
363
+ name: projectName,
364
+ default_branch: 'main'
365
+ },
366
+ roles: GOVERNED_ROLES,
367
+ runtimes: GOVERNED_RUNTIMES,
368
+ routing: GOVERNED_ROUTING,
369
+ gates: GOVERNED_GATES,
370
+ budget: {
371
+ per_turn_max_usd: 2.0,
372
+ per_run_max_usd: 50.0,
373
+ on_exceed: 'pause_and_escalate'
374
+ },
375
+ retention: {
376
+ talk_strategy: 'append_only',
377
+ history_strategy: 'jsonl_append_only'
378
+ },
379
+ prompts: {
380
+ pm: '.agentxchain/prompts/pm.md',
381
+ dev: '.agentxchain/prompts/dev.md',
382
+ qa: '.agentxchain/prompts/qa.md',
383
+ eng_director: '.agentxchain/prompts/eng_director.md'
384
+ },
385
+ rules: {
386
+ challenge_required: true,
387
+ max_turn_retries: 2,
388
+ max_deadlock_cycles: 2
389
+ }
390
+ };
391
+
392
+ const state = {
393
+ schema_version: '1.1',
394
+ run_id: null,
395
+ project_id: projectId,
396
+ status: 'idle',
397
+ phase: 'planning',
398
+ accepted_integration_ref: null,
399
+ active_turns: {},
400
+ turn_sequence: 0,
401
+ last_completed_turn_id: null,
402
+ blocked_on: null,
403
+ blocked_reason: null,
404
+ escalation: null,
405
+ queued_phase_transition: null,
406
+ queued_run_completion: null,
407
+ phase_gate_status: {
408
+ planning_signoff: 'pending',
409
+ implementation_complete: 'pending',
410
+ qa_ship_verdict: 'pending'
411
+ },
412
+ budget_reservations: {},
413
+ budget_status: {
414
+ spent_usd: 0,
415
+ remaining_usd: config.budget.per_run_max_usd
416
+ }
417
+ };
418
+
419
+ // Create directories
420
+ mkdirSync(join(dir, '.agentxchain', 'staging'), { recursive: true });
421
+ mkdirSync(join(dir, '.agentxchain', 'prompts'), { recursive: true });
422
+ mkdirSync(join(dir, '.agentxchain', 'reviews'), { recursive: true });
423
+ mkdirSync(join(dir, '.agentxchain', 'dispatch'), { recursive: true });
424
+ mkdirSync(join(dir, '.planning'), { recursive: true });
425
+
426
+ // Core governed files
427
+ writeFileSync(join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + '\n');
428
+ writeFileSync(join(dir, '.agentxchain', 'state.json'), JSON.stringify(state, null, 2) + '\n');
429
+ writeFileSync(join(dir, '.agentxchain', 'history.jsonl'), '');
430
+ writeFileSync(join(dir, '.agentxchain', 'decision-ledger.jsonl'), '');
431
+
432
+ // Prompt templates
433
+ for (const [roleId, role] of Object.entries(GOVERNED_ROLES)) {
434
+ const basePrompt = buildGovernedPrompt(roleId, role);
435
+ const prompt = appendPromptOverride(basePrompt, template.prompt_overrides?.[roleId]);
436
+ writeFileSync(join(dir, '.agentxchain', 'prompts', `${roleId}.md`), prompt);
437
+ }
438
+
439
+ // Planning artifacts
440
+ writeFileSync(join(dir, '.planning', 'PM_SIGNOFF.md'), `# PM Signoff — ${projectName}\n\nApproved: NO\n\n## Discovery Checklist\n- [ ] Target user defined\n- [ ] Core pain point defined\n- [ ] Core workflow defined\n- [ ] MVP scope defined\n- [ ] Out-of-scope list defined\n- [ ] Success metric defined\n\n## Notes for team\n(PM and human add final kickoff notes here.)\n`);
441
+ writeFileSync(join(dir, '.planning', 'ROADMAP.md'), `# Roadmap — ${projectName}\n\n## Phases\n\n| Phase | Goal | Status |\n|-------|------|--------|\n| Planning | Align scope, requirements, acceptance criteria | In progress |\n| Implementation | Build and verify | Pending |\n| QA | Challenge correctness and ship readiness | Pending |\n`);
442
+ const baseAcceptanceMatrix = `# Acceptance Matrix — ${projectName}\n\n| Req # | Requirement | Acceptance criteria | Test status | Last tested | Status |\n|-------|-------------|-------------------|-------------|-------------|--------|\n| (QA fills this from ROADMAP.md) | | | | | |\n`;
443
+ writeFileSync(
444
+ join(dir, '.planning', 'acceptance-matrix.md'),
445
+ appendAcceptanceHints(baseAcceptanceMatrix, template.acceptance_hints)
446
+ );
447
+ writeFileSync(join(dir, '.planning', 'ship-verdict.md'), `# Ship Verdict — ${projectName}\n\n## Verdict: PENDING\n\n## QA Summary\n\n(QA writes the final ship/no-ship assessment here.)\n\n## Open Blockers\n\n(List any blocking issues.)\n\n## Conditions\n\n(List any conditions for shipping.)\n`);
448
+ for (const artifact of template.planning_artifacts) {
449
+ writeFileSync(
450
+ join(dir, '.planning', artifact.filename),
451
+ interpolateTemplateContent(artifact.content_template, projectName)
452
+ );
453
+ }
454
+
455
+ // TALK.md
456
+ writeFileSync(join(dir, 'TALK.md'), `# ${projectName} — Team Talk File\n\nCanonical human-readable handoff log for all agents.\n\n---\n\n`);
457
+
458
+ // .gitignore additions
459
+ const gitignorePath = join(dir, '.gitignore');
460
+ const requiredIgnores = ['.env', '.agentxchain/staging/', '.agentxchain/dispatch/'];
461
+ if (!existsSync(gitignorePath)) {
462
+ writeFileSync(gitignorePath, requiredIgnores.join('\n') + '\n');
463
+ } else {
464
+ const existingIgnore = readFileSync(gitignorePath, 'utf8');
465
+ const missing = requiredIgnores.filter(entry => !existingIgnore.split(/\r?\n/).includes(entry));
466
+ if (missing.length > 0) {
467
+ const prefix = existingIgnore.endsWith('\n') ? '' : '\n';
468
+ writeFileSync(gitignorePath, existingIgnore + prefix + missing.join('\n') + '\n');
469
+ }
470
+ }
471
+
472
+ return { config, state };
473
+ }
474
+
475
+ async function initGoverned(opts) {
476
+ let projectName, folderName;
477
+ const templateId = opts.template || 'generic';
478
+
479
+ if (!VALID_GOVERNED_TEMPLATE_IDS.includes(templateId)) {
480
+ console.error(chalk.red(` Error: Unknown template "${templateId}".`));
481
+ console.error('');
482
+ console.error(' Available templates:');
483
+ console.error(' generic Default governed scaffold');
484
+ console.error(' api-service Governed scaffold for a backend service');
485
+ console.error(' cli-tool Governed scaffold for a CLI tool');
486
+ console.error(' web-app Governed scaffold for a web application');
487
+ process.exit(1);
488
+ }
489
+
490
+ if (opts.yes) {
491
+ projectName = 'My AgentXchain Project';
492
+ folderName = slugify(projectName);
493
+ } else {
494
+ const { name } = await inquirer.prompt([{
495
+ type: 'input',
496
+ name: 'name',
497
+ message: 'Project name:',
498
+ default: 'My AgentXchain Project'
499
+ }]);
500
+ projectName = name;
501
+ folderName = slugify(projectName);
502
+
503
+ const { folder } = await inquirer.prompt([{
504
+ type: 'input',
505
+ name: 'folder',
506
+ message: 'Folder name:',
507
+ default: folderName
508
+ }]);
509
+ folderName = folder;
510
+ }
511
+
512
+ const dir = resolve(process.cwd(), folderName);
513
+ const projectId = slugify(projectName);
514
+
515
+ if (existsSync(dir) && existsSync(join(dir, CONFIG_FILE))) {
516
+ if (!opts.yes) {
517
+ const { overwrite } = await inquirer.prompt([{
518
+ type: 'confirm',
519
+ name: 'overwrite',
520
+ message: `${folderName}/ already has agentxchain.json. Overwrite?`,
521
+ default: false
522
+ }]);
523
+ if (!overwrite) {
524
+ console.log(chalk.yellow(' Aborted.'));
525
+ return;
526
+ }
527
+ }
528
+ }
529
+
530
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
531
+
532
+ scaffoldGoverned(dir, projectName, projectId, templateId);
533
+
534
+ console.log('');
535
+ console.log(chalk.green(` ✓ Created governed project ${chalk.bold(folderName)}/`));
536
+ console.log('');
537
+ console.log(` ${chalk.dim('├──')} agentxchain.json ${chalk.dim('(governed)')}`);
538
+ console.log(` ${chalk.dim('├──')} .agentxchain/`);
539
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} state.json / history.jsonl / decision-ledger.jsonl`);
540
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} staging/`);
541
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} prompts/ ${chalk.dim('(pm, dev, qa, eng_director)')}`);
542
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} reviews/`);
543
+ console.log(` ${chalk.dim('│')} ${chalk.dim('└──')} dispatch/`);
544
+ console.log(` ${chalk.dim('├──')} .planning/`);
545
+ console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} PM_SIGNOFF.md / ROADMAP.md`);
546
+ console.log(` ${chalk.dim('│')} ${chalk.dim('└──')} acceptance-matrix.md / ship-verdict.md`);
547
+ console.log(` ${chalk.dim('└──')} TALK.md`);
548
+ console.log('');
549
+ console.log(` ${chalk.dim('Roles:')} pm, dev, qa, eng_director`);
550
+ console.log(` ${chalk.dim('Template:')} ${templateId}`);
551
+ console.log(` ${chalk.dim('Protocol:')} governed convergence`);
552
+ console.log('');
553
+ console.log(` ${chalk.cyan('Next:')}`);
554
+ console.log(` ${chalk.bold(`cd ${folderName}`)}`);
555
+ console.log(` ${chalk.bold('agentxchain step')} ${chalk.dim('# run the first governed turn')}`);
556
+ console.log(` ${chalk.bold('agentxchain status')} ${chalk.dim('# inspect phase, gate, and turn state')}`);
557
+ console.log('');
558
+ }
559
+
48
560
  export async function initCommand(opts) {
561
+ if (opts.governed || opts.schemaVersion === '4') {
562
+ return initGoverned(opts);
563
+ }
564
+
49
565
  let project, agents, folderName, rules;
50
566
 
51
567
  if (opts.yes) {