@geraldmaron/construct 1.0.21 → 1.0.24

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 (150) hide show
  1. package/README.md +23 -7
  2. package/bin/construct +206 -228
  3. package/bin/construct-postinstall.mjs +25 -15
  4. package/lib/acp/server.mjs +113 -0
  5. package/lib/agent-instructions/inject.mjs +94 -0
  6. package/lib/auto-docs.mjs +10 -2
  7. package/lib/cli-commands.mjs +43 -15
  8. package/lib/comment-lint.mjs +115 -0
  9. package/lib/completions.mjs +7 -1
  10. package/lib/config/schema.mjs +4 -0
  11. package/lib/decisions/enforced-baseline.json +2 -0
  12. package/lib/docs-verify.mjs +15 -18
  13. package/lib/doctor/cli.mjs +8 -1
  14. package/lib/document-export.mjs +124 -0
  15. package/lib/embed/daemon.mjs +1 -1
  16. package/lib/embed/inbox.mjs +31 -10
  17. package/lib/embed/recommendation-store.mjs +7 -289
  18. package/lib/embed/reconcile.mjs +2 -2
  19. package/lib/features.mjs +11 -11
  20. package/lib/git-hooks-path.mjs +61 -0
  21. package/lib/home-namespace.mjs +60 -0
  22. package/lib/hooks/ci-status-check.mjs +62 -40
  23. package/lib/hooks/config-protection.mjs +4 -4
  24. package/lib/hooks/orchestration-dispatch-guard.mjs +153 -0
  25. package/lib/hooks/pre-push-gate.mjs +15 -6
  26. package/lib/hooks/session-reflect.mjs +5 -1
  27. package/lib/hooks/stop-notify.mjs +32 -17
  28. package/lib/hooks/stop-typecheck.mjs +7 -2
  29. package/lib/host-capabilities.mjs +24 -8
  30. package/lib/host-disposition.mjs +76 -0
  31. package/lib/ingest/provider-extract.mjs +1 -1
  32. package/lib/init-docs.mjs +1 -1
  33. package/lib/init-unified.mjs +320 -219
  34. package/lib/init-update.mjs +4 -84
  35. package/lib/init.mjs +9 -25
  36. package/lib/install/stage-project.mjs +8 -2
  37. package/lib/intake/git-queue.mjs +195 -0
  38. package/lib/intake/queue.mjs +9 -16
  39. package/lib/intent-classifier.mjs +1 -1
  40. package/lib/knowledge/search.mjs +52 -3
  41. package/lib/mcp/server.mjs +57 -14
  42. package/lib/mcp/tools/memory.mjs +2 -2
  43. package/lib/mcp/tools/orchestration-run.mjs +125 -0
  44. package/lib/mcp/tools/storage.mjs +2 -3
  45. package/lib/mcp-catalog.json +3 -3
  46. package/lib/mcp-manager.mjs +59 -3
  47. package/lib/model-registry.mjs +40 -33
  48. package/lib/observation-store.mjs +39 -163
  49. package/lib/opencode-config.mjs +1 -1
  50. package/lib/orchestration/events.mjs +66 -0
  51. package/lib/orchestration/runtime.mjs +72 -10
  52. package/lib/orchestration/worker.mjs +69 -21
  53. package/lib/orchestration-policy.mjs +27 -3
  54. package/lib/parity.mjs +46 -24
  55. package/lib/policy/unified-gates.mjs +96 -0
  56. package/lib/project-init-shared.mjs +0 -173
  57. package/lib/reconcile/adapter-prune.mjs +105 -0
  58. package/lib/reconcile/agent-instructions-rewrap.mjs +98 -0
  59. package/lib/reconcile/gitignore-coverage.mjs +88 -0
  60. package/lib/reconcile/index.mjs +169 -0
  61. package/lib/reconcile/legacy-doctrine-strip.mjs +139 -0
  62. package/lib/reconcile/legacy-guide-decommit.mjs +67 -0
  63. package/lib/reconcile/legacy-skills-cleanup.mjs +200 -0
  64. package/lib/reconcile/mcp-entry-reconcile.mjs +142 -0
  65. package/lib/runtime/uv-bootstrap.mjs +27 -3
  66. package/lib/schema-infer.mjs +16 -2
  67. package/lib/server/csrf.mjs +14 -2
  68. package/lib/server/index.mjs +95 -0
  69. package/lib/service-manager.mjs +59 -252
  70. package/lib/setup-prompts.mjs +2 -1
  71. package/lib/setup.mjs +104 -480
  72. package/lib/status.mjs +3 -6
  73. package/lib/storage/admin.mjs +48 -325
  74. package/lib/storage/backend.mjs +10 -43
  75. package/lib/storage/hybrid-query.mjs +15 -196
  76. package/lib/storage/sync.mjs +36 -177
  77. package/lib/storage/vector-client.mjs +256 -235
  78. package/lib/strategy-store.mjs +35 -286
  79. package/lib/template-registry.mjs +73 -0
  80. package/lib/term-format.mjs +75 -0
  81. package/lib/uninstall/uninstall.mjs +180 -7
  82. package/lib/worker/entrypoint.mjs +6 -14
  83. package/package.json +7 -6
  84. package/personas/construct.md +7 -8
  85. package/platforms/claude/settings.template.json +30 -11
  86. package/platforms/opencode/config.template.json +2 -2
  87. package/rules/common/neurodivergent-output.md +66 -0
  88. package/rules/common/tool-invisibility.md +37 -0
  89. package/scripts/sync-specialists.mjs +427 -107
  90. package/skills/operating/orchestration-reference.md +2 -16
  91. package/specialists/policy-inventory.json +14 -0
  92. package/specialists/prompts/cx-accessibility.md +2 -6
  93. package/specialists/prompts/cx-ai-engineer.md +0 -4
  94. package/specialists/prompts/cx-architect.md +3 -5
  95. package/specialists/prompts/cx-business-strategist.md +0 -5
  96. package/specialists/prompts/cx-data-analyst.md +0 -4
  97. package/specialists/prompts/cx-data-engineer.md +0 -4
  98. package/specialists/prompts/cx-debugger.md +2 -6
  99. package/specialists/prompts/cx-designer.md +0 -8
  100. package/specialists/prompts/cx-devil-advocate.md +2 -2
  101. package/specialists/prompts/cx-docs-keeper.md +0 -13
  102. package/specialists/prompts/cx-engineer.md +0 -13
  103. package/specialists/prompts/cx-evaluator.md +2 -2
  104. package/specialists/prompts/cx-explorer.md +4 -5
  105. package/specialists/prompts/cx-legal-compliance.md +4 -5
  106. package/specialists/prompts/cx-operations.md +0 -5
  107. package/specialists/prompts/cx-orchestrator.md +0 -4
  108. package/specialists/prompts/cx-platform-engineer.md +0 -8
  109. package/specialists/prompts/cx-product-manager.md +0 -8
  110. package/specialists/prompts/cx-qa.md +3 -12
  111. package/specialists/prompts/cx-rd-lead.md +0 -5
  112. package/specialists/prompts/cx-release-manager.md +0 -8
  113. package/specialists/prompts/cx-researcher.md +5 -29
  114. package/specialists/prompts/cx-reviewer.md +2 -6
  115. package/specialists/prompts/cx-security.md +2 -11
  116. package/specialists/prompts/cx-sre.md +0 -15
  117. package/specialists/prompts/cx-test-automation.md +0 -4
  118. package/specialists/prompts/cx-trace-reviewer.md +2 -2
  119. package/specialists/prompts/cx-ux-researcher.md +0 -4
  120. package/specialists/registry.json +28 -36
  121. package/templates/distribution/run.mjs +36 -7
  122. package/templates/docs/accessibility-audit.md +56 -0
  123. package/templates/docs/architecture-review.md +59 -0
  124. package/templates/docs/code-review-report.md +46 -0
  125. package/templates/docs/construct_guide.md +15 -15
  126. package/templates/docs/debug-investigation.md +53 -0
  127. package/templates/docs/qa-report.md +48 -0
  128. package/templates/docs/security-audit-report.md +48 -0
  129. package/templates/docs/task-packet.md +49 -0
  130. package/templates/docs/verdict.md +40 -0
  131. package/db/schema/001_init.sql +0 -40
  132. package/db/schema/002_pgvector.sql +0 -182
  133. package/db/schema/003_intake.sql +0 -47
  134. package/db/schema/003_observation_reconciliation.sql +0 -14
  135. package/db/schema/004_recommendations.sql +0 -46
  136. package/db/schema/005_strategy.sql +0 -21
  137. package/db/schema/006_graph.sql +0 -24
  138. package/db/schema/007_tags.sql +0 -30
  139. package/db/schema/008_skill_usage.sql +0 -24
  140. package/db/schema/009_scheduler.sql +0 -14
  141. package/db/schema/010_cx_scores.sql +0 -51
  142. package/lib/intake/postgres-queue.mjs +0 -240
  143. package/lib/server/static/index.html +0 -1
  144. package/lib/services/local-postgres.mjs +0 -15
  145. package/lib/storage/backup.mjs +0 -347
  146. package/lib/storage/migrations.mjs +0 -187
  147. package/lib/storage/postgres-backup.mjs +0 -124
  148. package/lib/storage/sql-store.mjs +0 -45
  149. package/lib/storage/store-version.mjs +0 -115
  150. package/lib/storage/vector-store.mjs +0 -100
@@ -12,7 +12,8 @@
12
12
  "permissions": {
13
13
  "edit": "allow",
14
14
  "bash": "allow",
15
- "webfetch": "allow"
15
+ "webfetch": "allow",
16
+ "websearch": "allow"
16
17
  },
17
18
  "claudeTools": "Read,Grep,Glob,LS,Edit,Write,Bash,WebSearch,WebFetch,summarize_diff,scan_file,project_context,memory_create_entities,list_skills,get_skill,search_skills,list_teams,get_team,memory_search,memory_add_observations,cx_trace,cx_score",
18
19
  "promptFile": "personas/construct.md",
@@ -417,7 +418,7 @@
417
418
  "modelTier": "standard",
418
419
  "reasoningEffort": "medium",
419
420
  "codexSandbox": "read-only",
420
- "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score",
421
+ "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score,get_template,list_templates",
421
422
  "promptFile": "specialists/prompts/cx-evaluator.md",
422
423
  "embedOrientation": {
423
424
  "focusAreas": [
@@ -628,7 +629,7 @@
628
629
  "modelTier": "reasoning",
629
630
  "reasoningEffort": "high",
630
631
  "codexSandbox": "read-only",
631
- "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations",
632
+ "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,get_template,list_templates",
632
633
  "promptFile": "specialists/prompts/cx-devil-advocate.md",
633
634
  "embedOrientation": {
634
635
  "focusAreas": [
@@ -673,7 +674,7 @@
673
674
  "modelTier": "reasoning",
674
675
  "reasoningEffort": "high",
675
676
  "codexSandbox": "read-only",
676
- "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score",
677
+ "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score,get_template,list_templates",
677
678
  "promptFile": "specialists/prompts/cx-reviewer.md",
678
679
  "embedOrientation": {
679
680
  "focusAreas": [
@@ -716,7 +717,7 @@
716
717
  "modelTier": "reasoning",
717
718
  "reasoningEffort": "high",
718
719
  "codexSandbox": "read-only",
719
- "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score",
720
+ "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score,get_template,list_templates",
720
721
  "promptFile": "specialists/prompts/cx-security.md",
721
722
  "embedOrientation": {
722
723
  "focusAreas": [
@@ -776,7 +777,7 @@
776
777
  "modelTier": "standard",
777
778
  "reasoningEffort": "medium",
778
779
  "codexSandbox": "workspace-write",
779
- "claudeTools": "Read,Grep,Glob,LS,Edit,Write,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score",
780
+ "claudeTools": "Read,Grep,Glob,LS,Edit,Write,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score,get_template,list_templates",
780
781
  "promptFile": "specialists/prompts/cx-qa.md",
781
782
  "embedOrientation": {
782
783
  "focusAreas": [
@@ -824,7 +825,7 @@
824
825
  "modelTier": "reasoning",
825
826
  "reasoningEffort": "high",
826
827
  "codexSandbox": "workspace-write",
827
- "claudeTools": "Read,Grep,Glob,LS,Bash,Edit,WebSearch,WebFetch,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score",
828
+ "claudeTools": "Read,Grep,Glob,LS,Bash,Edit,WebSearch,WebFetch,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score,get_template,list_templates",
828
829
  "promptFile": "specialists/prompts/cx-debugger.md",
829
830
  "embedOrientation": {
830
831
  "focusAreas": [
@@ -987,7 +988,7 @@
987
988
  "modelTier": "standard",
988
989
  "reasoningEffort": "medium",
989
990
  "codexSandbox": "read-only",
990
- "claudeTools": "Read,Grep,Glob,LS,WebSearch,WebFetch,list_skills,get_skill,search_skills,memory_search,memory_add_observations",
991
+ "claudeTools": "Read,Grep,Glob,LS,WebSearch,WebFetch,list_skills,get_skill,search_skills,memory_search,memory_add_observations,get_template,list_templates",
991
992
  "promptFile": "specialists/prompts/cx-legal-compliance.md",
992
993
  "embedOrientation": {
993
994
  "focusAreas": [
@@ -1179,7 +1180,7 @@
1179
1180
  "modelTier": "standard",
1180
1181
  "reasoningEffort": "medium",
1181
1182
  "codexSandbox": "read-only",
1182
- "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations",
1183
+ "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,get_template,list_templates",
1183
1184
  "promptFile": "specialists/prompts/cx-accessibility.md",
1184
1185
  "embedOrientation": {
1185
1186
  "focusAreas": [
@@ -1227,7 +1228,7 @@
1227
1228
  "modelTier": "fast",
1228
1229
  "reasoningEffort": "medium",
1229
1230
  "codexSandbox": "read-only",
1230
- "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations",
1231
+ "claudeTools": "Read,Grep,Glob,LS,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,get_template,list_templates",
1231
1232
  "promptFile": "specialists/prompts/cx-explorer.md",
1232
1233
  "embedOrientation": {
1233
1234
  "focusAreas": [
@@ -1266,7 +1267,7 @@
1266
1267
  "description": "Tracks fleet-level performance patterns — stable median scores can hide high-variance agents that are failing silently.",
1267
1268
  "modelTier": "reasoning",
1268
1269
  "reasoningEffort": "high",
1269
- "claudeTools": "Read,Write,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score",
1270
+ "claudeTools": "Read,Write,Bash,list_skills,get_skill,search_skills,memory_search,memory_add_observations,cx_trace,cx_score,get_template,list_templates",
1270
1271
  "promptFile": "specialists/prompts/cx-trace-reviewer.md",
1271
1272
  "embedOrientation": {
1272
1273
  "focusAreas": [
@@ -1427,8 +1428,8 @@
1427
1428
  "options": {
1428
1429
  "baseURL": "https://openrouter.ai/api/v1",
1429
1430
  "headers": {
1430
- "HTTP-Referer": "__OPENROUTER_REFERER__",
1431
- "X-Title": "__OPENROUTER_TITLE__"
1431
+ "HTTP-Referer": "https://github.com/geraldmaron/construct",
1432
+ "X-Title": "Construct"
1432
1433
  }
1433
1434
  },
1434
1435
  "models": {
@@ -1462,23 +1463,15 @@
1462
1463
  "command": "npx",
1463
1464
  "args": [
1464
1465
  "-y",
1465
- "@upstash/context7-mcp@latest"
1466
+ "@upstash/context7-mcp@3.1.0"
1466
1467
  ],
1467
1468
  "description": "Library and framework documentation lookup"
1468
1469
  },
1469
- "playwright": {
1470
- "command": "npx",
1471
- "args": [
1472
- "-y",
1473
- "@playwright/mcp@latest"
1474
- ],
1475
- "description": "Browser automation and E2E testing"
1476
- },
1477
1470
  "github": {
1478
1471
  "type": "url",
1479
1472
  "url": "https://api.githubcopilot.com/mcp/",
1480
1473
  "headers": {
1481
- "Authorization": "Bearer __GITHUB_TOKEN__"
1474
+ "Authorization": "Bearer {env:GITHUB_TOKEN}"
1482
1475
  },
1483
1476
  "description": "GitHub API: issues, PRs, code search"
1484
1477
  },
@@ -1486,7 +1479,7 @@
1486
1479
  "command": "npx",
1487
1480
  "args": [
1488
1481
  "-y",
1489
- "@modelcontextprotocol/server-sequential-thinking"
1482
+ "@modelcontextprotocol/server-sequential-thinking@2025.12.18"
1490
1483
  ],
1491
1484
  "description": "Complex multi-step reasoning chains"
1492
1485
  },
@@ -1500,17 +1493,18 @@
1500
1493
  },
1501
1494
  "sharedGuidance": [
1502
1495
  "Session Resumption: start every new chat by grounding in project_context, memory_search, AGENTS.md, plan.md, and the relevant repo docs. Apply results silently — never ask the user to repeat information that memory already holds.",
1503
- "Memory recording: after completing meaningful work, call memory_add_observations to record what you learned. Record: patterns discovered (category: pattern), decisions made with rationale (category: decision), anti-patterns found (category: anti-pattern), and open questions (category: insight). Summaries must be ≥30 characters and specific — avoid placeholders like 'task done' or 'completed'.",
1504
- "Project Identification: identify the current project from the workspace (basename of CWD) or memory MCP. Focus your expertise on the target repository you are currently in.",
1505
- "Loop guard: if you observe the same command or tool call being attempted more than 3 times with identical arguments and no state change, stop immediately and surface what blocked progress and what decision is needed.",
1496
+ "Memory recording: after meaningful work, call memory_add_observations to record patterns, decisions with rationale, anti-patterns, and open questions. Summaries must be specific and ≥30 characters — avoid placeholders.",
1497
+ "Project Identification: identify the current project from the workspace or memory MCP, and focus on that target repository.",
1498
+ "Loop guard: if the same command or tool call is attempted 3+ times with identical arguments and no state change, stop and surface what blocked progress and what decision is needed.",
1506
1499
  "Circuit breaker: after 3 failed attempts at the same approach, stop all edits. Revert to last known working state. Document what was tried and escalate to the user with full context.",
1507
- "Worker task contract: specialists return exactly one terminal state: DONE (with files changed and verification evidence), BLOCKED (with concrete blocker and attempted steps), or NEEDS_MAIN_INPUT ({ taskKey, worker, blocker, question, safeDefault, context }). Never conduct user interviews from child sessions.",
1508
- "Tool naming contract: use the exact tool names exposed by the current host never emit names from other vendors catalogs. Claude-trained models map edits→edit, writes→write, shell→bash. OpenAI-trained models map python/code interpreter→unavailable, shell→bash, apply_patch→edit/write. The bash tool requires both command and description string fields: {\"command\":\"...\",\"description\":\"why this command is needed\"}. Prefer rg over find/grep. If a needed tool is not listed, use the closest available primitive.",
1509
- "Observability: you MUST call cx_trace at the start of every new task using your agent name as the name field and the user goal as input. Construct attaches promptName/promptFile/promptHash automatically. If the tool is unavailable, log the failure and continue — telemetry must never block execution.",
1510
- "Quality scoring: after producing a significant deliverable, you MUST call cx_score with name=quality, value 0.0-1.0, and a brief comment explaining the score. Use the trace_id returned by cx_trace.",
1511
- "Model failover: if you hit a rate-limit error (HTTP 429), provider quota message, or model unavailability, the PostToolUseFailure hook may run construct models --apply. Surface whether fallback ran, then retry only after the model assignment changed.",
1500
+ "Worker task contract: specialists return exactly one terminal state DONE (files + verification), BLOCKED (blocker + attempts), or NEEDS_MAIN_INPUT (per the `construct-to-orchestrator` shape in specialists/contracts.json). Never interview the user from a child session.",
1501
+ "Tool naming contract: use the exact tool names exposed by the current host; never emit names from other vendors' catalogs. The bash tool requires both command and description string fields. Prefer rg over find/grep. If a needed tool is missing, use the closest available primitive.",
1502
+ "Observability: you MUST call cx_trace at the start of every new task with your agent name and the user goal. Construct attaches prompt provenance automatically. If the tool is unavailable, log and continue — telemetry must never block execution.",
1503
+ "Quality scoring: after a significant deliverable, you MUST call cx_score with `name=quality`, a 0.01.0 value, a brief comment, and the `trace_id` returned by cx_trace.",
1504
+ "Model failover: on rate-limit / quota / model-unavailability errors, the PostToolUseFailure hook may switch model assignments. Surface whether fallback ran; retry only after assignments changed.",
1512
1505
  "Detailed orchestration reference (execution contract, handoff template, intent/category classification, parallel dispatch, horizontal routing, efficiency discipline) is in skills/operating/orchestration-reference.md — call get_skill(\"operating/orchestration-reference\") when you need it.",
1513
- "Agent contracts: explicit producer→consumer service contracts live in specialists/contracts.json (loaded via lib/specialist-contracts.mjs). Call the agent_contract MCP tool at handoff time to look up input.mustContain, preconditions, output shape, and postconditions for the current pair. routeRequest returns contractChain alongside specialists."
1506
+ "Agent contracts: typed producer→consumer contracts live in `specialists/contracts.json`. Call the `agent_contract` MCP tool at handoff to fetch the current shape; `routeRequest` returns `contractChain` alongside `specialists`.",
1507
+ "Tool invisibility: a deliverable artifact is about the user's project, never about the tooling that produced it. In artifact content never name Construct, the `cx-*` specialist role ids, or internal orchestration mechanics (task-packets, orchestration_run, dispatch chains) — unless the subject project IS Construct itself. A metric owner is a real role on the user's team, never a `cx-*` id. Provenance goes in a comment or handoff, not the deliverable's prose. See rules/common/tool-invisibility.md."
1514
1508
  ],
1515
1509
  "platformGuidance": {
1516
1510
  "opencode": [
@@ -1519,9 +1513,7 @@
1519
1513
  "OpenCode subagents are not primary chat surfaces. If you are running as a subagent and you need user input, do not prompt the user from the child session. Return a concise NEEDS_MAIN_INPUT packet containing the blocker, exact question, safe default, and current task/session context so the primary persona can ask the user and resume.",
1520
1514
  "If a subagent returns NEEDS_MAIN_INPUT, the primary persona must ask the user in the main session, update the tracker or plan with the blocker, then resume or re-dispatch the specialist with the answer.",
1521
1515
  "Provider fallback is not guaranteed inside OpenCode provider/session failures. If you see a provider quota or usage-limit error, run or ask the main session to run `construct models --apply` after confirming an OpenRouter key is available, then retry after sync changes model assignments.",
1522
- "Use OpenCode's native plugin, agent, command, and hook surfaces as the orchestration substrate. Visible handoff blocks are fallback/debug artifacts, not the workflow itself.",
1523
- "Loop guard: if you observe the same command or tool call being attempted more than 3 times with identical arguments and no state change, stop immediately and surface what blocked progress.",
1524
- "If the memory MCP is available, call search_nodes with the current project name before starting work to retrieve prior session context."
1516
+ "Use OpenCode's native plugin, agent, command, and hook surfaces as the orchestration substrate. Visible handoff blocks are fallback/debug artifacts, not the workflow itself."
1525
1517
  ],
1526
1518
  "claude": [
1527
1519
  "Follow workspace instructions, project-level CLAUDE.md, agent definitions in ~/.claude/specialists/, slash commands, and hooks. Project-level instructions override global ones.",
@@ -14,30 +14,38 @@
14
14
  * and the launcher invokes its `bin/construct`. Used for development
15
15
  * and smoke tests; not consulted in normal runs.
16
16
  *
17
- * 1. Workspace `node_modules/@geraldmaron/construct/bin/construct` — if a
17
+ * 1. Self-repo — if this launcher is staged inside the Construct source
18
+ * checkout itself (the project's own `package.json` is
19
+ * `@geraldmaron/construct` and `bin/construct` is present), invoke that
20
+ * `bin/construct` directly. Without this, the Construct repo's own hooks
21
+ * would dead-end at npx/global resolution and fail every session.
22
+ *
23
+ * 2. Workspace `node_modules/@geraldmaron/construct/bin/construct` — if a
18
24
  * `npm install` has materialised Construct in this project's node_modules,
19
25
  * use that. Fastest, no network.
20
26
  *
21
- * 2. `npx -p @geraldmaron/construct@<version> construct …` — if Node ≥ 18
27
+ * 3. `npx -p @geraldmaron/construct@<version> construct …` — if Node ≥ 18
22
28
  * is on PATH but the package isn't installed locally. npx caches the
23
29
  * package after the first run. The version pin comes from
24
- * `.construct/version` next to this file.
30
+ * `.construct/version` next to this file. The pin is probed first; if it
31
+ * cannot be resolved (offline, or the version is unpublished/yanked) the
32
+ * launcher defers to the resolvers below instead of dead-ending here.
25
33
  *
26
- * 3. Globally installed `construct` on PATH — if neither of the above
34
+ * 4. Globally installed `construct` on PATH — if neither of the above
27
35
  * hit but the user has installed Construct globally.
28
36
  *
29
- * 4. Cached binary at `.construct/cache/bin/construct-<os>-<arch>` — if
37
+ * 5. Cached binary at `.construct/cache/bin/construct-<os>-<arch>` — if
30
38
  * a previous bootstrap downloaded the matching single-file binary
31
39
  * from GitHub Releases.
32
40
  *
33
- * 5. Docker container — if the docker daemon is reachable, invoke
41
+ * 6. Docker container — if the docker daemon is reachable, invoke
34
42
  * `ghcr.io/geraldmaron/construct:<pinned-version>` with the project
35
43
  * bind-mounted at /work. Lets language-agnostic projects (no Node,
36
44
  * no global construct, no binary) still execute hooks. Disable with
37
45
  * `CONSTRUCT_DISABLE_DOCKER=1` for environments where docker is
38
46
  * reachable but undesirable (e.g. CI runners that prefer fail-fast).
39
47
  *
40
- * 6. Print a precise error with the exact install commands and exit
48
+ * 7. Print a precise error with the exact install commands and exit
41
49
  * with the documented exit code (127 = command not found).
42
50
  *
43
51
  * The launcher is intentionally tiny and dependency-free so it never
@@ -99,6 +107,18 @@ function tryDevPath() {
99
107
  return true;
100
108
  }
101
109
 
110
+ function trySelfRepo() {
111
+ const candidate = join(PROJECT_ROOT, 'bin', 'construct');
112
+ if (!isExecutable(candidate)) return false;
113
+ let pkgName = null;
114
+ try {
115
+ pkgName = JSON.parse(readFileSync(join(PROJECT_ROOT, 'package.json'), 'utf8')).name;
116
+ } catch { return false; }
117
+ if (pkgName !== '@geraldmaron/construct') return false;
118
+ runForeground(process.execPath, [candidate, ...process.argv.slice(2)]);
119
+ return true;
120
+ }
121
+
102
122
  function tryNodeModules() {
103
123
  const candidate = join(
104
124
  PROJECT_ROOT,
@@ -112,6 +132,14 @@ function tryNodeModules() {
112
132
  function tryNpx(version) {
113
133
  if (!commandOnPath('npx')) return false;
114
134
  const spec = version ? `@geraldmaron/construct@${version}` : '@geraldmaron/construct';
135
+
136
+ // npx exits non-zero when the pinned version is unpublished/yanked or the registry
137
+ // is unreachable. runForeground hands the process to npx and exits with its status,
138
+ // so that failure can never fall through to the global/cache/Docker resolvers below.
139
+ // Confirm the spec resolves first; on a miss, defer instead of dead-ending the chain.
140
+
141
+ const probe = spawnSync('npm', ['view', spec, 'version'], { stdio: 'ignore', timeout: 8000 });
142
+ if (probe.status !== 0) return false;
115
143
  runForeground('npx', ['-p', spec, '--', 'construct', ...process.argv.slice(2)]);
116
144
  return true;
117
145
  }
@@ -179,6 +207,7 @@ function fail() {
179
207
  const version = readPinnedVersion();
180
208
 
181
209
  if (tryDevPath()) { /* spawned */ }
210
+ else if (trySelfRepo()) { /* spawned */ }
182
211
  else if (tryNodeModules()) { /* spawned */ }
183
212
  else if (tryNpx(version)) { /* spawned */ }
184
213
  else if (tryGlobal()) { /* spawned */ }
@@ -0,0 +1,56 @@
1
+ # Accessibility Audit: {scope-title}
2
+
3
+ - **Date**: {YYYY-MM-DD}
4
+ - **Auditor**: cx-accessibility (or named human)
5
+ - **Scope**: {pages / flows / components / PR}
6
+ - **Baseline**: WCAG 2.1 AA (unless otherwise specified)
7
+ - **Assistive technology used**: {VoiceOver / NVDA / JAWS / TalkBack / Switch Control / Voice Control / keyboard-only}
8
+ - **Verdict**: ACCESSIBLE | ISSUES_FOUND | BLOCKED
9
+ - **Status**: draft | final
10
+
11
+ <!--
12
+ Accessibility is measured by using the product, not reading the spec. Every finding cites a
13
+ WCAG criterion AND a concrete repro step (keyboard sequence, screen-reader output,
14
+ contrast measurement). A claim about assistive-tech behavior you didn't actually exercise
15
+ is `unverified` — not a finding.
16
+ -->
17
+
18
+ ## Flows tested
19
+ <!-- The user flows actually walked through, end-to-end, with the assistive tech named above. A flow that wasn't walked isn't "passed"; it's "not tested." -->
20
+
21
+ ## Findings
22
+
23
+ | WCAG Criterion | Severity | Element / Location | Repro Steps | Recommended Fix |
24
+ |---|---|---|---|---|
25
+ | {1.4.3 Contrast, 2.1.1 Keyboard, 4.1.2 Name/Role/Value, ...} | critical / high / medium / low | `{selector or path:line}` | {keystroke sequence + observed result} | {smallest change that restores compliance} |
26
+
27
+ ## High-impact areas
28
+
29
+ - [ ] **Forms**: labels, error association, required-field signaling, autocomplete, focus management on validation
30
+ - [ ] **Images and media**: alt text correctness, captions, transcripts, decorative-vs-informative classification
31
+ - [ ] **Navigation**: keyboard order, skip links, landmarks, focus traps in modals
32
+ - [ ] **Motion and animation**: prefers-reduced-motion honored, no flashing >3 Hz, no purely-motion conveyors of meaning
33
+ - [ ] **Dynamic content**: live regions, status messages, async update announcements
34
+ - [ ] **Color and contrast**: text contrast meets AA, meaning not carried by color alone
35
+ - [ ] **Keyboard navigation**: every interactive element reachable and operable; no keyboard traps
36
+ - [ ] **Screen reader**: every interactive element exposes a name, role, and value; relationships announced
37
+
38
+ <!-- Tick what was actually exercised. An unticked box is "not audited," not "passed." -->
39
+
40
+ ## Contrast measurements
41
+ <!-- Where contrast was checked, the measured ratio, the threshold, and pass / fail. Include the tool used (browser devtools, axe, manual measurement). -->
42
+
43
+ ## Keyboard navigation
44
+ <!-- Sequence walked, traps found, focus-visible state. A keyboard audit that doesn't report focus indicators is incomplete. -->
45
+
46
+ ## Screen reader behavior
47
+ <!-- For each non-trivial element: what was announced vs. what should have been. Quote the verbatim AT output where possible. -->
48
+
49
+ ## Out of scope
50
+ <!-- Flows or surfaces not exercised. Future audits will need to cover them. -->
51
+
52
+ ## Handoff
53
+
54
+ - design fixes → `next:cx-designer`
55
+ - code fixes → `next:cx-engineer`
56
+ - review of remediation → `next:cx-reviewer`
@@ -0,0 +1,59 @@
1
+ # Architecture Review: {system-or-component}
2
+
3
+ - **Date**: {YYYY-MM-DD}
4
+ - **Reviewer**: cx-architect (or named human)
5
+ - **Subject**: {existing design / proposed change / PR}
6
+ - **Related**: {ADR, RFC, PRD ids — link the design this review evaluates}
7
+ - **Verdict**: APPROVE | APPROVE_WITH_CONDITIONS | REJECT | NEEDS_REVISION
8
+ - **Status**: draft | final
9
+
10
+ <!--
11
+ An architecture review evaluates an existing or proposed design — distinct from an ADR (a
12
+ new decision record) or RFC (a proposal). Every concern cites the design property it
13
+ violates and the failure mode it enables. "I don't like it" is not a concern; "this couples
14
+ X and Y in a way that prevents Z" is.
15
+ -->
16
+
17
+ ## Summary
18
+ <!-- 2–4 sentences: what was reviewed, the top-level verdict, the single most important concern. -->
19
+
20
+ ## Design intent
21
+ <!-- Restate what the design is trying to achieve, in the reviewer's words. If the design's intent can't be stated clearly from the artifact, that itself is a finding. -->
22
+
23
+ ## Trade-offs evaluated
24
+
25
+ | Trade-off | Option chosen | Rationale | Cost | Reversibility |
26
+ |---|---|---|---|---|
27
+ | {axis — e.g. consistency vs. availability} | {what the design picked} | {why} | {what's given up} | reversible / costly to reverse / one-way door |
28
+
29
+ ## Interface contracts
30
+
31
+ | Interface | Inputs | Outputs | Error cases | Backwards compatibility |
32
+ |---|---|---|---|---|
33
+ | `{name}` | {types / shapes} | {types / shapes} | {failure modes + retry semantics} | {breaking / non-breaking + version policy} |
34
+
35
+ ## Non-functional assessment
36
+
37
+ - **Scalability**: {expected load → actual capacity, with bottleneck}
38
+ - **Reliability**: {failure modes, blast radius, recovery}
39
+ - **Observability**: {what is traced, what is alerted, what isn't and why}
40
+ - **Security**: {trust boundaries, attack surface, sensitive data flow}
41
+ - **Operability**: {who runs this, how do they intervene, what's the runbook}
42
+
43
+ ## Concerns
44
+
45
+ | Severity | Concern | Property violated | Failure mode enabled | Recommended change |
46
+ |---|---|---|---|---|
47
+ | critical / high / medium / low | {one-line statement} | {the design property — e.g. "single-writer per partition"} | {what breaks when this is violated} | {smallest change that restores the property} |
48
+
49
+ ## Conditions for approval
50
+ <!-- If APPROVE_WITH_CONDITIONS, the conditions go here. Each condition is verifiable; the verdict flips back to NEEDS_REVISION if any condition isn't met. -->
51
+
52
+ ## Rejected alternatives considered
53
+ <!-- Designs evaluated but not chosen, with the reason. An ADR-worthy review names the alternatives the original artifact didn't consider. -->
54
+
55
+ ## Handoff
56
+
57
+ - changes to implement → `next:cx-engineer` or `next:cx-platform-engineer`
58
+ - ADR follow-up → `next:cx-architect` (capture the decision)
59
+ - security re-review → `next:cx-security`
@@ -0,0 +1,46 @@
1
+ # Code Review Report: {pr-title-or-bd-id}
2
+
3
+ - **Date**: {YYYY-MM-DD}
4
+ - **Reviewer**: cx-reviewer (or named human)
5
+ - **Scope**: {files / commits / PR / bd-id}
6
+ - **Verdict**: APPROVED | CHANGES_REQUESTED | BLOCKED
7
+ - **Status**: draft | final
8
+
9
+ <!--
10
+ A review is not approval theater. Every finding cites file:line from the diff and a concrete
11
+ failure scenario; severity is the impact, not the surface. Happy-path checks are not review.
12
+ -->
13
+
14
+ ## Summary
15
+ <!-- 2–4 sentences: what was reviewed, the top-level verdict, what most matters. Plain language. If there are no findings, say so explicitly. -->
16
+
17
+ ## Findings
18
+
19
+ | Severity | File:Line | Issue | Recommended Fix |
20
+ |---|---|---|---|
21
+ | critical / high / medium / low / nit | `path/to/file.ext:NNN` | {what's wrong, with the failure mode it enables} | {smallest safe change, or "discuss"} |
22
+
23
+ <!--
24
+ Severity contract:
25
+ - critical: shipping this regresses correctness, security, or data integrity. Block.
26
+ - high: likely to cause defects in production within weeks. Fix before merge.
27
+ - medium: real risk under specific conditions. Fix before merge, or open a tracked follow-up.
28
+ - low: code-quality / maintainability cost. Optional.
29
+ - nit: cosmetic; reviewer preference. Always optional.
30
+ -->
31
+
32
+ ## Conditions the author did not test for
33
+ <!-- The point of review: name the inputs, states, and edge cases the diff doesn't account for. One bullet each, with file:line where the gap lives. -->
34
+
35
+ ## Required changes
36
+ <!-- The minimum delta before this can merge. Empty if APPROVED. -->
37
+
38
+ ## Verification
39
+ <!-- How a reviewer (or CI) would confirm the changes land safely. Reference the test name, the trace id, or the command that proves it. -->
40
+
41
+ ## Handoff
42
+ <!-- Where this report goes next. Bare bd-label form so it works across hosts:
43
+ - findings to address → `next:cx-engineer`
44
+ - security concern → `next:cx-security`
45
+ - design / contract concern → `next:cx-architect`
46
+ -->
@@ -14,22 +14,22 @@ You added Construct as a dev dependency. That gave you:
14
14
 
15
15
  You don't need to manage any of that: it all runs locally.
16
16
 
17
- ## Files Construct created in your project
17
+ ## Files Construct created or manages
18
18
 
19
- You'll see these in `git status`:
19
+ Generated tooling (`.construct/`, `.claude/`, `.cx/`, `plan.md`) is gitignored; the user-owned files (`AGENTS.md`, `docs/`) are the ones committed as project content.
20
20
 
21
21
  | Path | What it is | Touch? |
22
22
  |---|---|---|
23
- | `.construct/` | Launcher for hooks. Don't edit. Regenerated on `npm install`. | No |
24
- | `.claude/agents/` | The 29 persona definitions. Regenerated on `npm install`. | No |
25
- | `.claude/commands/` | Slash commands. Regenerated on `npm install`. | No |
26
- | `.claude/settings.json` | Hooks + MCP server config. Construct keys are managed; your additions are preserved. | Carefully |
27
- | `.cx/` | Per-project session state (active work, recent decisions). Construct writes here. | Read-mostly |
23
+ | `.construct/` | Launcher for hooks. Don't edit. Regenerated on `npm install`. Gitignored. | No |
24
+ | `.claude/agents/` | The persona definitions. Regenerated on `npm install`. Gitignored. | No |
25
+ | `.claude/commands/` | Slash commands. Regenerated on `npm install`. Gitignored. | No |
26
+ | `.claude/settings.json` | Hooks + MCP server config. Construct keys are managed; your additions are preserved. Gitignored. | Carefully |
27
+ | `.cx/` | Per-project session state (active work, decisions, this guide). Construct writes here. Gitignored. | Read-mostly |
28
28
  | `.beads/` | Issue tracker data. Use `bd <cmd>` to interact. | Via CLI |
29
- | `AGENTS.md` | Operating rules the agent reads at session start. | Yes: your house rules go here |
29
+ | `AGENTS.md` | Agent guide. Construct owns only its fenced integration block; the rest is yours. | Yes: your house rules go here |
30
30
  | `plan.md` | Active work plan. Local-only (gitignored). | Yes: your current focus |
31
31
  | `docs/` | Documentation lanes (ADRs, PRDs, memos, …). | Yes: this is your team's docs |
32
- | `construct_guide.md` | This file. Friendly orientation. | Yes: adapt to your team |
32
+ | `.cx/construct_guide.md` | This file. Friendly orientation. Gitignored. | Yes: adapt to your team |
33
33
 
34
34
  ## Where to drop information you want Construct to use
35
35
 
@@ -67,7 +67,7 @@ The full list of personas lives in `.claude/agents/`. Run `construct list` to se
67
67
 
68
68
  Two scopes:
69
69
 
70
- **Per-project** (committed, shared with peers): edit `.claude/settings.json`. Construct owns the `hooks` block and a known set of `mcpServers` keys (memory, context7, playwright, github, sequential-thinking, construct-mcp); your additions are preserved on `npm install`. Other top-level keys are yours to control.
70
+ **Per-project** (committed, shared with peers): edit `.claude/settings.json`. Construct owns the `hooks` block and a known set of `mcpServers` keys (memory, context7, github, sequential-thinking, construct-mcp); your additions — including opt-in MCPs like `playwright` (`construct mcp add playwright`) — are preserved on `npm install`. Other top-level keys are yours to control.
71
71
 
72
72
  **Per-machine** (private to you): edit `~/.construct/config.env`. This is where your API keys, consent flags, and local-service credentials live. Standard `KEY=value` format.
73
73
 
@@ -80,16 +80,15 @@ construct doctor # verify everything's healthy
80
80
 
81
81
  ## Local services
82
82
 
83
- If you ran `construct init` and have Docker, you have three things running locally on Construct's reserved port block (`54329-54339`, chosen to avoid colliding with Next.js, Postgres, Redis, etc.):
83
+ Once services are set up (`construct install`) and you have Docker, three things run locally. Ports are derived per home directory to avoid collisions, so run `construct status` for the live endpoints rather than assuming fixed numbers.
84
84
 
85
85
  **Telemetry backend** (LLM observability: see your traces, costs, and quality scores)
86
86
 
87
- - URL: <http://localhost:54330>
87
+ - Dashboard URL: shown by `construct status`
88
88
 
89
89
  **Postgres + pgvector** (the database that backs hybrid retrieval)
90
90
 
91
- - URL: `postgresql://construct:construct@127.0.0.1:54329/construct`
92
- - Saved as `DATABASE_URL` in `~/.construct/config.env`
91
+ - Connection string (`DATABASE_URL`): shown by `construct status`, saved in `~/.construct/config.env`
93
92
 
94
93
  **Vector storage** (semantic search over your project + ingested docs)
95
94
 
@@ -105,7 +104,8 @@ All ports bind to `127.0.0.1` only; nothing is reachable from other machines on
105
104
 
106
105
  | Command | What it does |
107
106
  |---|---|
108
- | `construct init` | One-time per-machine: spins up local services, writes config |
107
+ | `construct install` | Machine setup, once per machine: Docker, local services, `~/.construct` config |
108
+ | `construct init` | Project setup, once per repo: scaffold `.cx/`, AGENTS.md, plan.md, adapters |
109
109
  | `construct config [mode <m>]` | Show active deployment mode (solo / team / enterprise) or set a new one |
110
110
  | `construct doctor` | Health check across config, services, agents, hooks |
111
111
  | `construct sync` | Regenerate platform adapters (Claude Code, OpenCode, Codex, Cursor) |
@@ -0,0 +1,53 @@
1
+ # Debug Investigation: {symptom-or-bd-id}
2
+
3
+ - **Date**: {YYYY-MM-DD}
4
+ - **Debugger**: cx-debugger (or named human)
5
+ - **Severity**: critical / high / medium / low
6
+ - **Status**: in-progress | root-cause-identified | fix-proposed | resolved
7
+
8
+ <!--
9
+ The real bug is always one layer deeper than where it presents. Every diagnostic claim cites
10
+ a stack trace, log line, test failure, or repro step. If the trace doesn't show the cause,
11
+ the cause is `unknown` — not a guess.
12
+ -->
13
+
14
+ ## Capture
15
+ <!-- The exact error message, stack trace, log output, and repro steps as observed. Verbatim. Do not paraphrase or "clean up" the symptom. Include a path to the run log if one exists. -->
16
+
17
+ ## Reproduce
18
+ <!-- The smallest deterministic recipe that triggers the symptom. Confirm the recipe reproduces on at least two runs. If you can't reproduce, the investigation isn't ready for a fix — say so. -->
19
+
20
+ ```
21
+ {minimal repro: command, input, expected vs. actual output}
22
+ ```
23
+
24
+ ## Isolate
25
+ <!-- Reduce the failing case until the smallest change toggles the bug. Cite the bisect range (`git bisect` boundary), the test that exhibits the failure, or the input dimension. -->
26
+
27
+ ## Trace
28
+ <!-- The execution path from trigger to symptom. file:line citations for each hop. A trace without source pointers is a story, not a trace. -->
29
+
30
+ | Hop | Location | Observation |
31
+ |---|---|---|
32
+ | {1, 2, 3...} | `path/to/file.ext:NNN` | {what the code does here that's relevant} |
33
+
34
+ ## Invariant violated
35
+ <!-- One sentence: the property the system was supposed to uphold, and the specific way this run violated it. The invariant is the contract; the bug is the violation. -->
36
+
37
+ ## Root cause
38
+ <!-- The system / design condition that lets the trigger cause harm. A root cause is a system gap, never a person and never a single line of code unless that line is the design decision. -->
39
+
40
+ ## Proposed fix
41
+ <!-- The smallest safe change that restores the invariant. If the smallest change is large, the root cause is wider than originally thought — restate it. -->
42
+
43
+ ## Verification
44
+ <!-- The specific run, test, or check that proves the fix works. The repro from above must now pass. Cite the test name and the trace id. -->
45
+
46
+ ## Follow-ups
47
+ <!-- Adjacent issues surfaced during the investigation. Each gets a bd id, not a paragraph. -->
48
+
49
+ ## Handoff
50
+
51
+ - fix to implement → `next:cx-engineer`
52
+ - additional test coverage → `next:cx-qa`
53
+ - design review of the invariant → `next:cx-architect`
@@ -0,0 +1,48 @@
1
+ # QA Report: {feature-or-bd-id}
2
+
3
+ - **Date**: {YYYY-MM-DD}
4
+ - **QA**: cx-qa (or named human)
5
+ - **Scope**: {feature / PR / bd-id / acceptance-criteria source}
6
+ - **Verdict**: PASS | FAIL | BLOCKED
7
+ - **Status**: draft | final
8
+
9
+ <!--
10
+ Coverage is a hypothesis about quality, not proof of it. Every PASS / FAIL verdict cites the
11
+ test name + run log line. Every coverage claim cites the report file. If a report isn't
12
+ available, the verdict is `unknown` — not an estimate.
13
+ -->
14
+
15
+ ## Acceptance criteria
16
+
17
+ | Criterion | Verdict | Test name | Log line / artifact |
18
+ |---|---|---|---|
19
+ | {restated from the source — PRD, bd issue, design doc} | PASS / FAIL / unknown | `tests/path/to/test.mjs > "name"` | `{path or trace id}` |
20
+
21
+ ## Coverage
22
+
23
+ | Layer | % | Source |
24
+ |---|---|---|
25
+ | Unit | {NN}% | `{coverage report path}` |
26
+ | Integration | {NN}% | `{report or "not run"}` |
27
+ | E2E | {NN}% | `{report or "not run"}` |
28
+
29
+ ## Test pyramid assessment
30
+ <!-- Target ratio is unit:integration:E2E ≈ 70:20:10. Note the actual mix and the gap. Tests that exist but don't exercise the acceptance criterion are coverage without behavior — name them. -->
31
+
32
+ ## Findings
33
+
34
+ | Test | Result | Log line / repro | Edge case it exercises |
35
+ |---|---|---|---|
36
+ | `{test name}` | PASS / FAIL / flaky | `{log line or repro steps}` | {what could break in production} |
37
+
38
+ ## Coverage gaps
39
+ <!-- Acceptance criteria that lack a corresponding assertion, or assertions that don't exercise the acceptance criterion. Each gap names the missing test and what it should assert. -->
40
+
41
+ ## Determinism
42
+ <!-- Tests that re-run with different results. Cite the run history (pass rate over N runs). A single failure is not flake; a single pass is not stable. -->
43
+
44
+ ## Handoff
45
+
46
+ - test failures to fix → `next:cx-engineer`
47
+ - root cause investigation → `next:cx-debugger`
48
+ - flaky tests to stabilize → `next:cx-test-automation`