@mutagent/helix 0.1.0-alpha.14

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 (357) hide show
  1. package/.claude/skills/mutagent-helix/SKILL.md +113 -0
  2. package/.claude/skills/mutagent-helix/scripts/cli/codex-transcode.ts +143 -0
  3. package/.claude/skills/mutagent-helix/scripts/cli/doctor.ts +111 -0
  4. package/.claude/skills/mutagent-helix/scripts/cli/init.ts +382 -0
  5. package/.claude/skills/mutagent-helix/scripts/cli/run.sh +61 -0
  6. package/CLAUDE.md +193 -0
  7. package/LICENSE +201 -0
  8. package/NOTICE +22 -0
  9. package/README.md +209 -0
  10. package/assets/adl-loop.svg +162 -0
  11. package/assets/system-status.svg +422 -0
  12. package/bin/mutagent-cli.mjs +9950 -0
  13. package/bin/mutagent-helix.mjs +78 -0
  14. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/CLAUDE.md +28 -0
  15. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/SKILL.md +138 -0
  16. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/assets/templates/agentspec.yaml.tpl +247 -0
  17. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/references/frameworks/doc-pins.md +53 -0
  18. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/references/principles.md +91 -0
  19. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/references/requirements.yaml +295 -0
  20. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/references/workflows/orchestrator-protocol.md +302 -0
  21. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/cli/doctor.ts +116 -0
  22. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/cli/init.ts +230 -0
  23. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/cli/run.sh +60 -0
  24. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/config/resolve-spec-dir.ts +199 -0
  25. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/contract/agentspec.schema.ts +455 -0
  26. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/setup/detect.ts +67 -0
  27. package/mutagent-agentspec/.claude/skills/mutagent-agentspec/scripts/validate/validate-spec.ts +93 -0
  28. package/mutagent-agentspec/LICENSE +201 -0
  29. package/mutagent-agentspec/NOTICE +19 -0
  30. package/mutagent-agentspec/README.md +44 -0
  31. package/mutagent-builder/.claude/skills/mutagent-builder/SKILL.md +100 -0
  32. package/mutagent-builder/.claude/skills/mutagent-builder/assets/agents/ai-architect.md +312 -0
  33. package/mutagent-builder/.claude/skills/mutagent-builder/assets/agents/ai-engineer.md +357 -0
  34. package/mutagent-builder/.claude/skills/mutagent-builder/assets/templates/build-report.md.tpl +39 -0
  35. package/mutagent-builder/.claude/skills/mutagent-builder/references/principles.md +31 -0
  36. package/mutagent-builder/.claude/skills/mutagent-builder/references/workflows/build-protocol.md +61 -0
  37. package/mutagent-builder/.claude/skills/mutagent-builder/references/workflows/improve-handoff.md +23 -0
  38. package/mutagent-builder/.claude/skills/mutagent-builder/scripts/cli/run.sh +60 -0
  39. package/mutagent-builder/.claude/skills/mutagent-builder/scripts/handoff/validate-build-input.ts +72 -0
  40. package/mutagent-builder/.claude/skills/mutagent-builder/scripts/sync-spec/check-sync-spec.ts +499 -0
  41. package/mutagent-builder/.claude/skills/mutagent-builder/scripts/verify/spec-impl-coverage.ts +182 -0
  42. package/mutagent-builder/CLAUDE.md +25 -0
  43. package/mutagent-builder/README.md +26 -0
  44. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/README.md +192 -0
  45. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/SKILL.md +352 -0
  46. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/agents/ai-architect.md +256 -0
  47. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/agents/ai-engineer.md +344 -0
  48. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/agents/diagnostics-analyzer.md +576 -0
  49. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/agents/discovery.md +425 -0
  50. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/audit.json.tpl +24 -0
  51. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/audit.md.tpl +49 -0
  52. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/config.yaml.tpl +100 -0
  53. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/pr-body.md.tpl +56 -0
  54. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/report.html.tpl +327 -0
  55. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/shared/README.md +27 -0
  56. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/shared/iter-N-handover.md.tpl +125 -0
  57. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/shared/self-diagnosis-contract.v0.1.0.yaml.tpl +234 -0
  58. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/shared/spec.yaml.tpl +152 -0
  59. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/shared/team.yaml.tpl +127 -0
  60. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/templates/shared/wave-N-dashboard.html.tpl +1712 -0
  61. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/diagnostics/WF-2.1-nl-query.md +59 -0
  62. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/diagnostics/WF-2.2-tier0-progress.md +58 -0
  63. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/diagnostics/WF-2.3-analysis-progress.md +44 -0
  64. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/diagnostics/WF-2.4-html-report.md +61 -0
  65. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/diagnostics/WF-2.5-rca-detail.md +69 -0
  66. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/diagnostics/WF-2.6-stale-warning.md +53 -0
  67. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.1-welcome.md +60 -0
  68. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.2-source-platform-picker.md +41 -0
  69. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.3-source-auth.md +75 -0
  70. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.4-target-picker.md +48 -0
  71. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.5-ask-tool.md +43 -0
  72. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.6-config-review.md +57 -0
  73. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.7-filter-defaults.md +52 -0
  74. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/assets/wireframes/onboarding/WF-1.8-first-run.md +59 -0
  75. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/examples/sample-findings.json +92 -0
  76. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/examples/sample-meta.json +47 -0
  77. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/adapter-strategy.md +75 -0
  78. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/config-migration.md +84 -0
  79. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/config.md +159 -0
  80. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/filter-search-matrix.md +66 -0
  81. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/harness-knowledge.md +22 -0
  82. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/memory-format.md +78 -0
  83. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/onboarding-decisions.yaml +187 -0
  84. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/operation-inventory.md +59 -0
  85. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/overview.md +167 -0
  86. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/reference.md +119 -0
  87. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/script-index.md +101 -0
  88. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-adapter-contract.md +93 -0
  89. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/claude-code-transcripts.md +66 -0
  90. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/codex-transcripts.md +78 -0
  91. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/codex.md +145 -0
  92. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/install-paths.md +57 -0
  93. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/langfuse.md +178 -0
  94. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/local-jsonl.md +55 -0
  95. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/openobserve.md +102 -0
  96. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/source-platforms/otel.md +63 -0
  97. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/cloud-rest.md +84 -0
  98. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/local-claude.md +65 -0
  99. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/local-cloud-agent-sdk.md +92 -0
  100. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/local-codex.md +72 -0
  101. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/local-cursor.md +108 -0
  102. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/local-mastra.md +77 -0
  103. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/target-platforms/local-opencode.md +70 -0
  104. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/apply-pr-comment-format.md +82 -0
  105. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/diagnostics.md +35 -0
  106. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/handover-contract.md +247 -0
  107. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/onboarding.md +300 -0
  108. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/orchestrator-protocol.md +1861 -0
  109. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/rca.md +331 -0
  110. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/rendering-anatomy.md +314 -0
  111. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/schedule-prep.md +100 -0
  112. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/scope-model.md +128 -0
  113. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/references/workflows/verification-methodology.md +174 -0
  114. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/aggregate/sort-findings.ts +0 -0
  115. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/awareness/blind-spots.ts +167 -0
  116. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/awareness/llm-sample.ts +681 -0
  117. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/cli/doctor.ts +215 -0
  118. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/cli/init.ts +1013 -0
  119. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/cli/install-agents.ts +350 -0
  120. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/cli/run.sh +61 -0
  121. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/cli/shared-templates.ts +79 -0
  122. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/config/load.ts +392 -0
  123. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/config/resolve-credential.ts +120 -0
  124. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/config/schema.ts +641 -0
  125. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/config/validate.ts +134 -0
  126. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/context/build-diagnosis-context.ts +417 -0
  127. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/context/load-context.ts +105 -0
  128. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/contract/types.ts +166 -0
  129. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/enrich/build-render-input.ts +1964 -0
  130. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/enrich/corpus-severity.ts +172 -0
  131. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/enrich/dismissal-match.ts +270 -0
  132. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/enrich/rank-remedies.ts +75 -0
  133. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/hooks/install-hooks.sh +46 -0
  134. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/hooks/pre-push.sh +78 -0
  135. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/invocation/parse-brief.ts +188 -0
  136. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/library/match.ts +114 -0
  137. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/library/paths.ts +95 -0
  138. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/library/store.ts +535 -0
  139. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/library/types.ts +181 -0
  140. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/lint/template-inline-js.ts +267 -0
  141. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/memory/append.ts +188 -0
  142. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/memory/read.ts +218 -0
  143. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/normalize/platforms/entity-context.ts +686 -0
  144. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/normalize/read-unitf.ts +267 -0
  145. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/normalize/trace.ts +1078 -0
  146. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/normalize/unitf-adapter.ts +0 -0
  147. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/normalize/unitf-types.ts +213 -0
  148. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/report/persist-selections.ts +174 -0
  149. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/report/render.ts +3254 -0
  150. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/run/diagnose.ts +64 -0
  151. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/run/session.ts +167 -0
  152. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/sample/caps.ts +345 -0
  153. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/sample/deep-read-gate.ts +424 -0
  154. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/sample/representative.ts +584 -0
  155. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/scan/objection.ts +224 -0
  156. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/scan/trajectory.ts +489 -0
  157. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/self-diagnostics/dispatch.ts +135 -0
  158. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/self-diagnostics/probe.ts +170 -0
  159. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/setup/detect.ts +307 -0
  160. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/setup/ensure-cli.ts +315 -0
  161. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/setup/reconfigure.ts +77 -0
  162. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/setup/verify-agents.ts +207 -0
  163. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/slicer.ts +166 -0
  164. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/adapters/claude-code.ts +17 -0
  165. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/adapters/codex.ts +11 -0
  166. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/adapters/langfuse.ts +17 -0
  167. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/adapters/local-jsonl.ts +12 -0
  168. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/adapters/otel.ts +12 -0
  169. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/index.ts +54 -0
  170. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/registry.ts +45 -0
  171. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/source/types.ts +86 -0
  172. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/stale-detector.ts +74 -0
  173. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/test/render-playwright.cli.ts +477 -0
  174. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/tier0/claude-code.ts +133 -0
  175. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/tier0/langfuse.ts +143 -0
  176. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/tier0-scan.ts +423 -0
  177. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/completeness-check.ts +436 -0
  178. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/doc-refs.ts +190 -0
  179. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/finalize-gate.ts +417 -0
  180. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/findings-contract.ts +628 -0
  181. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/render-js-syntax.ts +188 -0
  182. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/report-checklist.yaml +217 -0
  183. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/trace-shape.ts +262 -0
  184. package/mutagent-diagnostics/.claude/skills/mutagent-diagnostics/scripts/validate/wave6-checklist.ts +326 -0
  185. package/mutagent-diagnostics/CLAUDE.md +41 -0
  186. package/mutagent-diagnostics/LICENSE +201 -0
  187. package/mutagent-diagnostics/NOTICE +18 -0
  188. package/mutagent-diagnostics/README.md +343 -0
  189. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/SKILL.md +548 -0
  190. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/assets/agents/audit-executor.md +169 -0
  191. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/assets/agents/dataset-builder.md +160 -0
  192. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/assets/agents/discovery.md +425 -0
  193. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +1108 -0
  194. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/assets/brand/theme.css +213 -0
  195. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/assets/brand/wordmark.html +9 -0
  196. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/lenses/context-flow-lens.md +85 -0
  197. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/lenses/data-lens.md +47 -0
  198. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/lenses/decision-lens.md +48 -0
  199. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/lenses/methodology-critic-lens.md +53 -0
  200. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/lenses/trajectory-lens.md +44 -0
  201. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/build-review-interface.md +83 -0
  202. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/edd-loop.md +134 -0
  203. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/error-analysis.md +113 -0
  204. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/eval-audit.md +154 -0
  205. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/eval-stage.md +168 -0
  206. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/generate-synthetic-data.md +81 -0
  207. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/grounded-adjudication.md +221 -0
  208. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/memory-format.md +65 -0
  209. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/methodology.md +201 -0
  210. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/operation-inventory.md +197 -0
  211. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/validate-evaluator.md +125 -0
  212. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/workflows/orchestrator-protocol.md +300 -0
  213. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/references/write-judge-prompt.md +123 -0
  214. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/behavior-tree.schema.yaml +73 -0
  215. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/dataset.schema.yaml +66 -0
  216. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/edd-change-request.schema.yaml +114 -0
  217. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/eval-matrix.schema.yaml +74 -0
  218. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/flow-graph.schema.yaml +69 -0
  219. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/flow-profile.schema.yaml +49 -0
  220. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/methodology-review.schema.yaml +40 -0
  221. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/schemas/scorecard.schema.yaml +85 -0
  222. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/agent-dispatch.ts +0 -0
  223. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/aggregate-discover.ts +543 -0
  224. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/artifact-paths.ts +115 -0
  225. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/assemble-scorecard.ts +172 -0
  226. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/build-dataset.ts +186 -0
  227. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/build-evals.ts +93 -0
  228. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/build-review-ui.ts +393 -0
  229. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/check-method-router.ts +170 -0
  230. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/aggregate.ts +112 -0
  231. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/audit-run.ts +182 -0
  232. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/doctor.ts +211 -0
  233. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/dogfood.ts +133 -0
  234. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/init.ts +601 -0
  235. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/methodology-review.ts +122 -0
  236. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/prep.ts +295 -0
  237. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/profile-subject.ts +165 -0
  238. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/run.sh +56 -0
  239. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cli/variance-check.ts +105 -0
  240. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/code-eval.ts +248 -0
  241. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/code-quality-verdict.ts +285 -0
  242. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/codegen-evals.ts +173 -0
  243. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cold-start-project.ts +151 -0
  244. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/cold-start-sampler.ts +267 -0
  245. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/config/load.ts +343 -0
  246. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/config/schema.ts +380 -0
  247. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/agentspec-evals.ts +85 -0
  248. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/dataset.ts +149 -0
  249. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/eval-engine.ts +118 -0
  250. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +577 -0
  251. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/eval-types.ts +1074 -0
  252. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/flow-graph.ts +194 -0
  253. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/types.ts +303 -0
  254. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/contracts/validation.ts +193 -0
  255. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/derive-dataset.ts +152 -0
  256. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/determine-outcome.ts +219 -0
  257. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/diff-discriminate.ts +160 -0
  258. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/discover-criteria.ts +348 -0
  259. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/edd/change-request.ts +232 -0
  260. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/edd/edd-types.ts +210 -0
  261. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/edd/variance-gate.ts +186 -0
  262. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/emit-completeness.ts +111 -0
  263. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/eval-engine.ts +160 -0
  264. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/evaluate.ts +333 -0
  265. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/flow-graph.ts +230 -0
  266. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/judge-prompt-template.ts +253 -0
  267. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/judge-provider.ts +135 -0
  268. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/lint-grounding.ts +229 -0
  269. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/lint-uniformity.ts +168 -0
  270. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/living-suite.ts +109 -0
  271. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/load-bundle.ts +203 -0
  272. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/load-profile-vocab.ts +64 -0
  273. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/load-profile.ts +106 -0
  274. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/mask.ts +138 -0
  275. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/materialize-dataset.ts +113 -0
  276. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/matrix-judge.ts +750 -0
  277. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/memory/append.ts +215 -0
  278. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/memory/read.ts +168 -0
  279. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/persist-eval-criteria.ts +232 -0
  280. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/prep-tasks.ts +125 -0
  281. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/profile-subject.ts +310 -0
  282. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/publish-report.ts +131 -0
  283. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/read-unitf-traces.ts +81 -0
  284. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/render-build-cards.ts +195 -0
  285. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/render-discover-report.ts +1640 -0
  286. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/render-eval-report.ts +3838 -0
  287. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/render-report.ts +212 -0
  288. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/resolve-credential.ts +110 -0
  289. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/resolve-ref.ts +98 -0
  290. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/result-verify.ts +129 -0
  291. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +330 -0
  292. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/run-deterministic.ts +367 -0
  293. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/run-evaluate.ts +719 -0
  294. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/run-judge.ts +155 -0
  295. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/run-pipeline.ts +175 -0
  296. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/sample-traces.ts +210 -0
  297. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/self-audit.ts +387 -0
  298. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/source-map.ts +106 -0
  299. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/subject-profile.ts +134 -0
  300. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/substrate.ts +162 -0
  301. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/sync-eval-criteria.ts +244 -0
  302. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/ui-slots.ts +119 -0
  303. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/unitf-to-evaltrace.ts +284 -0
  304. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/validate-judge.ts +358 -0
  305. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/scripts/variance-compare.ts +177 -0
  306. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/behavior-tree.yaml +140 -0
  307. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/eval-matrix.yaml +1270 -0
  308. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/methodology-review.yaml +105 -0
  309. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/workflows/audit.workflow.js +82 -0
  310. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/workflows/data-leak.workflow.js +236 -0
  311. package/mutagent-evaluator/.claude/skills/mutagent-evaluator/workflows/variance.workflow.js +163 -0
  312. package/mutagent-evaluator/CLAUDE.md +45 -0
  313. package/mutagent-evaluator/LICENSE +201 -0
  314. package/mutagent-evaluator/NOTICE +23 -0
  315. package/mutagent-evaluator/README.md +88 -0
  316. package/mutagent-optimize/.claude/skills/mutagent-optimize/SKILL.md +287 -0
  317. package/mutagent-optimize/.claude/skills/mutagent-optimize/scripts/contracts/amend-request.ts +503 -0
  318. package/mutagent-optimize/.claude/skills/mutagent-optimize/scripts/improve-loop-run.ts +304 -0
  319. package/mutagent-optimize/.claude/skills/mutagent-optimize/scripts/loop-state-cli.ts +300 -0
  320. package/mutagent-optimize/.claude/skills/mutagent-optimize/scripts/loop-state.ts +440 -0
  321. package/mutagent-optimize/CLAUDE.md +49 -0
  322. package/mutagent-optimize/README.md +26 -0
  323. package/mutagent-orchestrator/CLAUDE.md +102 -0
  324. package/mutagent-orchestrator/README.md +20 -0
  325. package/mutagent-orchestrator/assets/agents/discovery.md +425 -0
  326. package/mutagent-orchestrator/assets/agents/dogfood-monitor.md +266 -0
  327. package/mutagent-orchestrator/assets/agents/monitor.md +258 -0
  328. package/mutagent-orchestrator/orchestrator.md +782 -0
  329. package/mutagent-orchestrator/references/MONITORING.md +209 -0
  330. package/mutagent-orchestrator/references/config-fields.md +132 -0
  331. package/mutagent-orchestrator/references/config-migration.md +132 -0
  332. package/mutagent-orchestrator/references/index-registry.md +92 -0
  333. package/mutagent-orchestrator/routing.yaml +391 -0
  334. package/mutagent-orchestrator/scripts/config-schema.ts +1010 -0
  335. package/mutagent-orchestrator/scripts/dispatch.ts +439 -0
  336. package/mutagent-orchestrator/scripts/dogfood/extract-feedback.ts +203 -0
  337. package/mutagent-orchestrator/scripts/dogfood/reconstruct-trajectory.ts +327 -0
  338. package/mutagent-orchestrator/scripts/dogfood/render-dogfood-report.ts +347 -0
  339. package/mutagent-orchestrator/scripts/dogfood/resolve-subagents.ts +160 -0
  340. package/mutagent-orchestrator/scripts/dogfood/run-report.ts +264 -0
  341. package/mutagent-orchestrator/scripts/dogfood/slack-thread.ts +106 -0
  342. package/mutagent-orchestrator/scripts/dogfood/types.ts +257 -0
  343. package/mutagent-orchestrator/scripts/eval-routing.ts +753 -0
  344. package/mutagent-orchestrator/scripts/gate.ts +219 -0
  345. package/mutagent-orchestrator/scripts/handover-contract.ts +362 -0
  346. package/mutagent-orchestrator/scripts/index/build-index.ts +351 -0
  347. package/mutagent-orchestrator/scripts/index/render-index.ts +110 -0
  348. package/mutagent-orchestrator/scripts/monitor/slack-notify.ts +111 -0
  349. package/mutagent-orchestrator/scripts/monitor/triggers.ts +162 -0
  350. package/mutagent-orchestrator/scripts/onboarding-check.ts +369 -0
  351. package/mutagent-orchestrator/scripts/render-roster.ts +308 -0
  352. package/mutagent-orchestrator/scripts/resolve-credential.ts +185 -0
  353. package/mutagent-orchestrator/scripts/resolve-paths.ts +114 -0
  354. package/mutagent-orchestrator/scripts/slack/format.ts +63 -0
  355. package/mutagent-orchestrator/scripts/slack/post.ts +115 -0
  356. package/mutagent-orchestrator/scripts/sync-index.ts +311 -0
  357. package/package.json +64 -0
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * scripts/sync-spec/check-sync-spec.ts
4
+ *
5
+ * Deterministic freshness probe for the `ai-architect #sync-spec` mode. AgentSpec's `*sync-spec`
6
+ * command delegates here (Helix-mediated); `*build` reuses it build-internally on drift.
7
+ *
8
+ * CLI:
9
+ * scripts/cli/run.sh scripts/sync-spec/check-sync-spec.ts --spec <agentspec.yaml?> --target <target-root> [--json]
10
+ */
11
+
12
+ import * as fs from "node:fs";
13
+ import * as path from "node:path";
14
+ import { spawnSync } from "node:child_process";
15
+ import { parse as parseYaml } from "yaml";
16
+
17
+ export interface Freshness {
18
+ updatedAt?: string | null;
19
+ gitCommitEpoch: number | null;
20
+ fileMtimeEpoch: number | null;
21
+ effectiveEpoch: number | null;
22
+ }
23
+
24
+ /**
25
+ * The eval-leg (third leg of the def → impl → eval triad, W2I5) freshness verdict.
26
+ * `not-applicable` = no eval-criteria artifact was requested or located (the 2-leg
27
+ * spec↔impl reconcile is unaffected — additive). The other three MIRROR the spec-leg
28
+ * semantics so both legs agree on "impl is newer → reconcile".
29
+ */
30
+ export type EvalLegStatus = "not-applicable" | "missing-eval" | "in-sync" | "needs-sync";
31
+
32
+ export interface SyncSpecStatus {
33
+ status: "missing-spec" | "in-sync" | "needs-sync" | "error";
34
+ specPath: string | null;
35
+ targetRoot: string;
36
+ specFreshness: { updatedAt: string | null; gitCommitEpoch: number | null; fileMtimeEpoch: number | null; effectiveEpoch: number | null };
37
+ codeFreshness: { gitCommitEpoch: number | null; fileMtimeEpoch: number | null; effectiveEpoch: number | null; newestPath: string | null };
38
+ /**
39
+ * The EVAL leg (W2I5 · KP-003). Detects when the impl amended past the eval criteria
40
+ * that ground the subject's evaluation. Subject-kind-agnostic at THIS layer — the
41
+ * predicate only stats the criteria artifact's freshness vs the impl; WHICH criteria
42
+ * (agent/skill eval-suite vs code code-quality) is the reconcile's concern
43
+ * (`ai-architect #sync-spec` + evaluator `sync-eval-criteria.ts`).
44
+ */
45
+ evalStatus: EvalLegStatus;
46
+ evalFreshness: { path: string | null; updatedAt: string | null; gitCommitEpoch: number | null; fileMtimeEpoch: number | null; effectiveEpoch: number | null };
47
+ reason: string;
48
+ }
49
+
50
+ interface CliArgs {
51
+ spec?: string;
52
+ target?: string;
53
+ evalCriteria?: string;
54
+ json: boolean;
55
+ }
56
+
57
+ function emptySpecFreshness(): SyncSpecStatus["specFreshness"] {
58
+ return { updatedAt: null, gitCommitEpoch: null, fileMtimeEpoch: null, effectiveEpoch: null };
59
+ }
60
+
61
+ function emptyCodeFreshness(targetRoot = ""): SyncSpecStatus["codeFreshness"] {
62
+ void targetRoot;
63
+ return { gitCommitEpoch: null, fileMtimeEpoch: null, effectiveEpoch: null, newestPath: null };
64
+ }
65
+
66
+ function emptyEvalFreshness(): SyncSpecStatus["evalFreshness"] {
67
+ return { path: null, updatedAt: null, gitCommitEpoch: null, fileMtimeEpoch: null, effectiveEpoch: null };
68
+ }
69
+
70
+ /** The eval leg is not-applicable when no criteria artifact was requested/located. */
71
+ function notApplicableEval(): { evalStatus: EvalLegStatus; evalFreshness: SyncSpecStatus["evalFreshness"] } {
72
+ return { evalStatus: "not-applicable", evalFreshness: emptyEvalFreshness() };
73
+ }
74
+
75
+ function maxEpoch(values: Array<number | null | undefined>): number | null {
76
+ const present = values.filter((v): v is number => typeof v === "number" && Number.isFinite(v));
77
+ return present.length ? Math.max(...present) : null;
78
+ }
79
+
80
+ function epochSeconds(ms: number): number | null {
81
+ return Number.isFinite(ms) ? Math.floor(ms / 1000) : null;
82
+ }
83
+
84
+ function normalizeCandidate(base: string, candidate: string): string {
85
+ return path.isAbsolute(candidate) ? candidate : path.resolve(base, candidate);
86
+ }
87
+
88
+ function isPrunedDir(absDir: string, targetRoot: string): boolean {
89
+ const rel = path.relative(targetRoot, absDir);
90
+ if (!rel || rel === "") return false;
91
+ const parts = rel.split(path.sep);
92
+ if (parts.includes("node_modules") || parts.includes(".git") || parts.includes("dist") || parts.includes("coverage")) return true;
93
+ const mutagentIdx = parts.indexOf(".mutagent");
94
+ if (mutagentIdx >= 0 && parts[mutagentIdx + 2] === "runs") return true; // .mutagent/<subject>/runs
95
+ // The evaluator's namespaced artifact root (.mutagent/evaluator/**) is eval OUTPUT, never
96
+ // implementation — pruning it keeps eval-suite criteria / datasets from masquerading as impl
97
+ // drift (they are the EVAL leg, compared separately). W2I5.
98
+ if (mutagentIdx >= 0 && parts[mutagentIdx + 1] === "evaluator") return true;
99
+ return false;
100
+ }
101
+
102
+ function isGeneratedReport(absFile: string, targetRoot: string): boolean {
103
+ const rel = path.relative(targetRoot, absFile);
104
+ const parts = rel.split(path.sep);
105
+ const file = parts.at(-1) ?? "";
106
+ if (!parts.includes(".mutagent")) return false;
107
+ if (/^(build-)?report\.(md|json|html)$/i.test(file)) return true;
108
+ if (/^verdict\.(md|json)$/i.test(file)) return true;
109
+ return false;
110
+ }
111
+
112
+ function walkFiles(root: string, predicate: (abs: string) => boolean): string[] {
113
+ const out: string[] = [];
114
+ const stack = [root];
115
+ while (stack.length) {
116
+ const current = stack.pop()!;
117
+ let entries: fs.Dirent[];
118
+ try {
119
+ entries = fs.readdirSync(current, { withFileTypes: true });
120
+ } catch {
121
+ continue;
122
+ }
123
+ for (const entry of entries) {
124
+ const abs = path.join(current, entry.name);
125
+ if (entry.isDirectory()) {
126
+ if (!isPrunedDir(abs, root)) stack.push(abs);
127
+ } else if (entry.isFile() && predicate(abs)) {
128
+ out.push(abs);
129
+ }
130
+ }
131
+ }
132
+ return out.sort();
133
+ }
134
+
135
+ function locateSpec(targetRoot: string, explicitSpec?: string): string | null {
136
+ if (explicitSpec) {
137
+ const abs = normalizeCandidate(process.cwd(), explicitSpec);
138
+ try {
139
+ const stat = fs.statSync(abs);
140
+ return stat.isFile() ? abs : null;
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
145
+
146
+ const direct = path.join(targetRoot, "agentspec.yaml");
147
+ if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct;
148
+
149
+ const mutagentSpecs = path.join(targetRoot, ".mutagent", "specs");
150
+ if (fs.existsSync(mutagentSpecs)) {
151
+ const found = walkFiles(mutagentSpecs, (abs) => path.basename(abs) === "agentspec.yaml");
152
+ if (found.length) return found[0];
153
+ }
154
+
155
+ const found = walkFiles(targetRoot, (abs) => path.basename(abs) === "agentspec.yaml");
156
+ return found[0] ?? null;
157
+ }
158
+
159
+ function parseUpdatedAt(specPath: string): { updatedAt: string | null; epoch: number | null; error?: string } {
160
+ let parsed: unknown;
161
+ try {
162
+ parsed = parseYaml(fs.readFileSync(specPath, "utf8"));
163
+ } catch (error) {
164
+ const message = error instanceof Error ? error.message : String(error);
165
+ return { updatedAt: null, epoch: null, error: `specPath: failed to parse YAML: ${message}` };
166
+ }
167
+ const updatedAt = (parsed as any)?.meta?.loop_state?.updated_at;
168
+ if (typeof updatedAt !== "string") return { updatedAt: null, epoch: null };
169
+ const ms = Date.parse(updatedAt);
170
+ if (!Number.isFinite(ms)) return { updatedAt: null, epoch: null };
171
+ return { updatedAt, epoch: Math.floor(ms / 1000) };
172
+ }
173
+
174
+ function gitEpochForPaths(targetRoot: string, files: string[]): number | null {
175
+ if (!files.length) return null;
176
+ let best: number | null = null;
177
+ for (let i = 0; i < files.length; i += 100) {
178
+ const batch = files.slice(i, i + 100).map((f) => path.relative(targetRoot, f));
179
+ const result = spawnSync("git", ["-C", targetRoot, "log", "-1", "--format=%ct", "--", ...batch], {
180
+ encoding: "utf8",
181
+ stdio: ["ignore", "pipe", "ignore"],
182
+ });
183
+ if (result.status !== 0) continue;
184
+ const text = result.stdout.trim().split(/\s+/).find(Boolean);
185
+ const n = text ? Number(text) : NaN;
186
+ if (Number.isFinite(n)) best = best == null ? n : Math.max(best, n);
187
+ }
188
+ return best;
189
+ }
190
+
191
+ function fileMtimeEpoch(file: string): number | null {
192
+ try {
193
+ return epochSeconds(fs.statSync(file).mtimeMs);
194
+ } catch {
195
+ return null;
196
+ }
197
+ }
198
+
199
+ function newestMtime(files: string[]): { epoch: number | null; path: string | null } {
200
+ let bestEpoch: number | null = null;
201
+ let bestPath: string | null = null;
202
+ for (const file of files) {
203
+ const epoch = fileMtimeEpoch(file);
204
+ if (epoch != null && (bestEpoch == null || epoch > bestEpoch)) {
205
+ bestEpoch = epoch;
206
+ bestPath = file;
207
+ }
208
+ }
209
+ return { epoch: bestEpoch, path: bestPath };
210
+ }
211
+
212
+ function implementationFiles(targetRoot: string, specPath: string | null, evalPath?: string | null): string[] {
213
+ const normalizedSpec = specPath ? path.resolve(specPath) : null;
214
+ const normalizedEval = evalPath ? path.resolve(evalPath) : null;
215
+ return walkFiles(targetRoot, (abs) => {
216
+ const resolved = path.resolve(abs);
217
+ if (normalizedSpec && resolved === normalizedSpec) return false;
218
+ // The eval-criteria artifact is the EVAL leg, never impl — exclude it so it can never
219
+ // count as its own drift source (mirror of the spec exclusion). W2I5.
220
+ if (normalizedEval && resolved === normalizedEval) return false;
221
+ if (isGeneratedReport(abs, targetRoot)) return false;
222
+ return true;
223
+ });
224
+ }
225
+
226
+ /**
227
+ * Locate the eval-criteria artifact (the EVAL leg's freshness anchor). An explicit path
228
+ * wins; otherwise auto-locate under the evaluator's namespaced root
229
+ * (`.mutagent/evaluator/living-suite/*.{yaml,yml,json}` — the discovered eval-suite
230
+ * criteria) or a conventional criteria file at the target root. Returns `{ path, existed }`:
231
+ * an explicit-but-missing path yields `existed:false` (→ `missing-eval`); no path found
232
+ * yields `path:null` (→ `not-applicable`, additive no-op). W2I5.
233
+ */
234
+ function locateEvalCriteria(targetRoot: string, explicit?: string): { path: string | null; existed: boolean } {
235
+ if (explicit) {
236
+ const abs = normalizeCandidate(process.cwd(), explicit);
237
+ try {
238
+ return { path: abs, existed: fs.statSync(abs).isFile() };
239
+ } catch {
240
+ return { path: abs, existed: false };
241
+ }
242
+ }
243
+ const criteriaExt = (abs: string): boolean => /\.(ya?ml|json)$/i.test(abs);
244
+ const livingSuite = path.join(targetRoot, ".mutagent", "evaluator", "living-suite");
245
+ if (fs.existsSync(livingSuite)) {
246
+ const found = walkFiles(livingSuite, criteriaExt);
247
+ if (found.length) return { path: found[0], existed: true };
248
+ }
249
+ for (const name of ["eval-criteria", "code-quality-criteria"]) {
250
+ for (const ext of ["yaml", "yml", "json"]) {
251
+ const candidate = path.join(targetRoot, `${name}.${ext}`);
252
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return { path: candidate, existed: true };
253
+ }
254
+ }
255
+ return { path: null, existed: false };
256
+ }
257
+
258
+ /** Parse an optional freshness marker from an eval-criteria artifact (best-effort). */
259
+ function parseEvalUpdatedAt(evalPath: string): { updatedAt: string | null; epoch: number | null } {
260
+ let parsed: unknown;
261
+ try {
262
+ parsed = parseYaml(fs.readFileSync(evalPath, "utf8"));
263
+ } catch {
264
+ return { updatedAt: null, epoch: null };
265
+ }
266
+ const raw =
267
+ (parsed as any)?.meta?.loop_state?.updated_at ??
268
+ (parsed as any)?.updatedAt ??
269
+ (parsed as any)?.updated_at ??
270
+ (parsed as any)?.generatedAt;
271
+ if (typeof raw !== "string") return { updatedAt: null, epoch: null };
272
+ const ms = Date.parse(raw);
273
+ if (!Number.isFinite(ms)) return { updatedAt: null, epoch: null };
274
+ return { updatedAt: raw, epoch: Math.floor(ms / 1000) };
275
+ }
276
+
277
+ /**
278
+ * Compute the EVAL leg (status + freshness) given the code (impl) effective epoch. The
279
+ * `not-applicable` / `missing-eval` / `in-sync` / `needs-sync` verdict mirrors the spec
280
+ * leg's semantics so the two legs agree on "impl newer → reconcile". Deterministic. W2I5.
281
+ */
282
+ function evalLeg(
283
+ targetRoot: string,
284
+ explicit: string | undefined,
285
+ codeEffectiveEpoch: number | null,
286
+ ): { evalStatus: EvalLegStatus; evalFreshness: SyncSpecStatus["evalFreshness"] } {
287
+ const located = locateEvalCriteria(targetRoot, explicit);
288
+ if (located.path == null) return notApplicableEval();
289
+ if (!located.existed) {
290
+ return { evalStatus: "missing-eval", evalFreshness: { ...emptyEvalFreshness(), path: located.path } };
291
+ }
292
+ const updated = parseEvalUpdatedAt(located.path);
293
+ const git = gitEpochForPaths(targetRoot, [located.path]);
294
+ const mtime = fileMtimeEpoch(located.path);
295
+ const effectiveEpoch = maxEpoch([updated.epoch, git, mtime]);
296
+ const evalFreshness = {
297
+ path: located.path,
298
+ updatedAt: updated.updatedAt,
299
+ gitCommitEpoch: git,
300
+ fileMtimeEpoch: mtime,
301
+ effectiveEpoch,
302
+ };
303
+ const evalStatus: EvalLegStatus =
304
+ codeEffectiveEpoch != null && effectiveEpoch != null && codeEffectiveEpoch > effectiveEpoch
305
+ ? "needs-sync"
306
+ : "in-sync";
307
+ return { evalStatus, evalFreshness };
308
+ }
309
+
310
+ /**
311
+ * The legs that DRIFTED — the triad's headline surface. `spec` drifts on `missing-spec`
312
+ * (cold construct) or `needs-sync`; `eval` drifts on `missing-eval` or `needs-sync`. The
313
+ * reconcile (ai-architect) reads this to reconcile BOTH legs when an impl amends. W2I5.
314
+ */
315
+ export function driftedLegs(status: SyncSpecStatus): Array<"spec" | "eval"> {
316
+ const legs: Array<"spec" | "eval"> = [];
317
+ if (status.status === "missing-spec" || status.status === "needs-sync") legs.push("spec");
318
+ if (status.evalStatus === "missing-eval" || status.evalStatus === "needs-sync") legs.push("eval");
319
+ return legs;
320
+ }
321
+
322
+ export function checkSyncSpec(args: { targetRoot: string; specPath?: string; evalCriteriaPath?: string }): SyncSpecStatus {
323
+ const targetRoot = path.resolve(args.targetRoot);
324
+ let targetStat: fs.Stats;
325
+ try {
326
+ targetStat = fs.statSync(targetRoot);
327
+ } catch (error) {
328
+ const message = error instanceof Error ? error.message : String(error);
329
+ return {
330
+ status: "error",
331
+ specPath: null,
332
+ targetRoot,
333
+ specFreshness: emptySpecFreshness(),
334
+ codeFreshness: emptyCodeFreshness(),
335
+ ...notApplicableEval(),
336
+ reason: `targetRoot: unreadable target root: ${message}`,
337
+ };
338
+ }
339
+ if (!targetStat.isDirectory()) {
340
+ return {
341
+ status: "error",
342
+ specPath: null,
343
+ targetRoot,
344
+ specFreshness: emptySpecFreshness(),
345
+ codeFreshness: emptyCodeFreshness(),
346
+ ...notApplicableEval(),
347
+ reason: "targetRoot: target root is not a directory",
348
+ };
349
+ }
350
+
351
+ const specPath = locateSpec(targetRoot, args.specPath);
352
+ if (!specPath) {
353
+ const files = implementationFiles(targetRoot, null, locateEvalCriteria(targetRoot, args.evalCriteriaPath).path);
354
+ const newest = newestMtime(files);
355
+ const codeEffective = maxEpoch([gitEpochForPaths(targetRoot, files), newest.epoch]);
356
+ return {
357
+ status: "missing-spec",
358
+ specPath: null,
359
+ targetRoot,
360
+ specFreshness: emptySpecFreshness(),
361
+ codeFreshness: {
362
+ gitCommitEpoch: gitEpochForPaths(targetRoot, files),
363
+ fileMtimeEpoch: newest.epoch,
364
+ effectiveEpoch: codeEffective,
365
+ newestPath: newest.path,
366
+ },
367
+ ...evalLeg(targetRoot, args.evalCriteriaPath, codeEffective),
368
+ reason: "no agentspec.yaml found; derive spec from implementation",
369
+ };
370
+ }
371
+
372
+ const updated = parseUpdatedAt(specPath);
373
+ if (updated.error) {
374
+ return {
375
+ status: "error",
376
+ specPath,
377
+ targetRoot,
378
+ specFreshness: emptySpecFreshness(),
379
+ codeFreshness: emptyCodeFreshness(),
380
+ ...notApplicableEval(),
381
+ reason: updated.error,
382
+ };
383
+ }
384
+
385
+ const specGit = gitEpochForPaths(targetRoot, [specPath]);
386
+ const specMtime = fileMtimeEpoch(specPath);
387
+ const specFreshness = {
388
+ updatedAt: updated.updatedAt,
389
+ gitCommitEpoch: specGit,
390
+ fileMtimeEpoch: specMtime,
391
+ effectiveEpoch: maxEpoch([updated.epoch, specGit, specMtime]),
392
+ };
393
+
394
+ const evalLocated = locateEvalCriteria(targetRoot, args.evalCriteriaPath);
395
+ const files = implementationFiles(targetRoot, specPath, evalLocated.path);
396
+ const codeGit = gitEpochForPaths(targetRoot, files);
397
+ const codeMtime = newestMtime(files);
398
+ const codeFreshness = {
399
+ gitCommitEpoch: codeGit,
400
+ fileMtimeEpoch: codeMtime.epoch,
401
+ effectiveEpoch: maxEpoch([codeGit, codeMtime.epoch]),
402
+ newestPath: codeMtime.path,
403
+ };
404
+ const evalResult = evalLeg(targetRoot, args.evalCriteriaPath, codeFreshness.effectiveEpoch);
405
+
406
+ if (specFreshness.effectiveEpoch == null) {
407
+ return {
408
+ status: "error",
409
+ specPath,
410
+ targetRoot,
411
+ specFreshness,
412
+ codeFreshness,
413
+ ...evalResult,
414
+ reason: "specFreshness.effectiveEpoch: unable to compare spec freshness",
415
+ };
416
+ }
417
+
418
+ if (codeFreshness.effectiveEpoch != null && codeFreshness.effectiveEpoch > specFreshness.effectiveEpoch) {
419
+ return {
420
+ status: "needs-sync",
421
+ specPath,
422
+ targetRoot,
423
+ specFreshness,
424
+ codeFreshness,
425
+ ...evalResult,
426
+ reason: "implementation is newer than agentspec",
427
+ };
428
+ }
429
+
430
+ return {
431
+ status: "in-sync",
432
+ specPath,
433
+ targetRoot,
434
+ specFreshness,
435
+ codeFreshness,
436
+ ...evalResult,
437
+ reason: "agentspec is as fresh as implementation",
438
+ };
439
+ }
440
+
441
+ function parseArgs(argv: string[]): CliArgs {
442
+ const args: CliArgs = { json: false };
443
+ for (let i = 0; i < argv.length; i++) {
444
+ const arg = argv[i];
445
+ if (arg === "--json") args.json = true;
446
+ else if (arg === "--spec") args.spec = argv[++i];
447
+ else if (arg === "--target") args.target = argv[++i];
448
+ else if (arg === "--eval-criteria") args.evalCriteria = argv[++i];
449
+ else if (arg === "--help" || arg === "-h") {
450
+ console.log("Usage: scripts/cli/run.sh scripts/sync-spec/check-sync-spec.ts --spec <agentspec.yaml?> --target <target-root> [--eval-criteria <path?>] [--json]");
451
+ process.exit(0);
452
+ } else {
453
+ throw new Error(`unknown argument: ${arg}`);
454
+ }
455
+ }
456
+ if (!args.target) throw new Error("--target is required");
457
+ return args;
458
+ }
459
+
460
+ function formatSummary(status: SyncSpecStatus): string {
461
+ const lines = [
462
+ `[sync-spec] ${status.status}: ${status.reason}`,
463
+ `targetRoot: ${status.targetRoot}`,
464
+ `specPath: ${status.specPath ?? "<none>"}`,
465
+ `specFreshness: updatedAt=${status.specFreshness.updatedAt ?? "null"} git=${status.specFreshness.gitCommitEpoch ?? "null"} mtime=${status.specFreshness.fileMtimeEpoch ?? "null"} effective=${status.specFreshness.effectiveEpoch ?? "null"}`,
466
+ `codeFreshness: git=${status.codeFreshness.gitCommitEpoch ?? "null"} mtime=${status.codeFreshness.fileMtimeEpoch ?? "null"} effective=${status.codeFreshness.effectiveEpoch ?? "null"} newestPath=${status.codeFreshness.newestPath ?? "null"}`,
467
+ `evalStatus: ${status.evalStatus} (eval leg — W2I5 triad)`,
468
+ `evalFreshness: path=${status.evalFreshness.path ?? "<none>"} updatedAt=${status.evalFreshness.updatedAt ?? "null"} git=${status.evalFreshness.gitCommitEpoch ?? "null"} mtime=${status.evalFreshness.fileMtimeEpoch ?? "null"} effective=${status.evalFreshness.effectiveEpoch ?? "null"}`,
469
+ `driftedLegs: ${driftedLegs(status).join("+") || "<none>"}`,
470
+ ];
471
+ return lines.join("\n");
472
+ }
473
+
474
+ export function runCli(argv: string[]): number {
475
+ let args: CliArgs;
476
+ try {
477
+ args = parseArgs(argv);
478
+ } catch (error) {
479
+ const reason = error instanceof Error ? error.message : String(error);
480
+ const status: SyncSpecStatus = {
481
+ status: "error",
482
+ specPath: null,
483
+ targetRoot: "",
484
+ specFreshness: emptySpecFreshness(),
485
+ codeFreshness: emptyCodeFreshness(),
486
+ ...notApplicableEval(),
487
+ reason: `args: ${reason}`,
488
+ };
489
+ console.error(JSON.stringify(status, null, 2));
490
+ return 1;
491
+ }
492
+ const status = checkSyncSpec({ targetRoot: args.target!, specPath: args.spec, evalCriteriaPath: args.evalCriteria });
493
+ const out = args.json ? JSON.stringify(status, null, 2) : formatSummary(status);
494
+ if (status.status === "error") console.error(out);
495
+ else console.log(out);
496
+ return status.status === "error" ? 1 : 0;
497
+ }
498
+
499
+ if (import.meta.main) process.exit(runCli(process.argv.slice(2)));
@@ -0,0 +1,182 @@
1
+ /**
2
+ * scripts/verify/spec-impl-coverage.ts
3
+ * The BUILD-FAITHFULNESS gate (PR-024) — assert every spec-declared code tool is implemented + tested.
4
+ * Type A — Pure core (computeCoverage, no I/O) + a thin guarded CLI. Mirrors validate-spec.ts.
5
+ *
6
+ * WHY: TDD proves the code that EXISTS passes; it is SILENT on whether all the code the SPEC requires
7
+ * exists (a dropped tool has no test, so nothing fails). A build is GREEN only when TDD passes AND
8
+ * this gate passes. The dogfood sim shipped a scaffold missing `collect-range` with 27 green tests —
9
+ * this gate makes that a STEER instead.
10
+ *
11
+ * CONVENTION: each implementing module carries `// @implements <tool-id>`; ≥1 test references that
12
+ * module (by basename or the same marker). The actor DECLARES the mapping in code; this script VERIFIES
13
+ * completeness against the spec; the Verifier RE-RUNS it (Context-Inversion — never trusts the actor).
14
+ *
15
+ * Usage: scripts/cli/run.sh scripts/verify/spec-impl-coverage.ts <agentspec.yaml> <scaffold-dir>
16
+ * exit 0 = every code tool covered (+ no dangling job.backed_by refs) → "[coverage] PASS"
17
+ * exit 1 = at least one uncovered tool OR a dangling backed_by ref (a STEER)
18
+ */
19
+
20
+ import * as fs from "node:fs";
21
+ import * as path from "node:path";
22
+ import { parse as parseYaml } from "yaml";
23
+
24
+ /** One spec code-tool's coverage status. */
25
+ export interface CoverageEntry {
26
+ id: string;
27
+ module: string | null;
28
+ test: string | null;
29
+ covered: boolean;
30
+ reason?: string;
31
+ }
32
+
33
+ export interface CoverageResult {
34
+ ok: boolean;
35
+ entries: CoverageEntry[];
36
+ /** job.backed_by refs (P2) that don't resolve to a declared code-tool id — a spec defect. */
37
+ danglingBackedBy: string[];
38
+ }
39
+
40
+ /** A scanned file. Pure inputs so the core is unit-testable without disk. */
41
+ export interface ScannedFile {
42
+ path: string;
43
+ content: string;
44
+ }
45
+
46
+ /** `@implements <id>` marker matcher — id is treated literally (regex-escaped). */
47
+ function implementsMarker(id: string): RegExp {
48
+ const esc = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
49
+ return new RegExp(`@implements\\s+${esc}(?:\\s|$)`, "m");
50
+ }
51
+
52
+ /**
53
+ * PURE core. For each declared code-tool id, require (a) a src file carrying `// @implements <id>`
54
+ * AND (b) ≥1 test file referencing that module (by basename) or carrying the same marker. Also flags
55
+ * job `backed_by` refs that don't resolve to a declared tool id. No I/O, never throws.
56
+ */
57
+ export function computeCoverage(
58
+ toolIds: string[],
59
+ srcFiles: ScannedFile[],
60
+ testFiles: ScannedFile[],
61
+ backedByRefs: string[] = [],
62
+ ): CoverageResult {
63
+ const entries: CoverageEntry[] = [];
64
+ for (const id of toolIds) {
65
+ const re = implementsMarker(id);
66
+ const mod = srcFiles.find((f) => re.test(f.content)) ?? null;
67
+ let test: ScannedFile | null = null;
68
+ if (mod) {
69
+ const base = path.basename(mod.path).replace(/\.ts$/, "");
70
+ test =
71
+ testFiles.find((t) => t.content.includes(base) || re.test(t.content)) ?? null;
72
+ }
73
+ const covered = Boolean(mod) && Boolean(test);
74
+ entries.push({
75
+ id,
76
+ module: mod ? mod.path : null,
77
+ test: test ? test.path : null,
78
+ covered,
79
+ reason: !mod
80
+ ? `no module carries \`// @implements ${id}\``
81
+ : !test
82
+ ? `module ${path.basename(mod.path)} has no test referencing it`
83
+ : undefined,
84
+ });
85
+ }
86
+ const declared = new Set(toolIds);
87
+ const danglingBackedBy = [...new Set(backedByRefs)].filter((r) => !declared.has(r));
88
+ const ok = entries.every((e) => e.covered) && danglingBackedBy.length === 0;
89
+ return { ok, entries, danglingBackedBy };
90
+ }
91
+
92
+ /** Recursively collect .ts files under a dir (excluding node_modules / dotdirs). */
93
+ function walkTs(dir: string): ScannedFile[] {
94
+ const out: ScannedFile[] = [];
95
+ const walk = (d: string): void => {
96
+ let ents: fs.Dirent[];
97
+ try {
98
+ ents = fs.readdirSync(d, { withFileTypes: true });
99
+ } catch {
100
+ return;
101
+ }
102
+ for (const e of ents) {
103
+ if (e.name === "node_modules" || e.name.startsWith(".")) continue;
104
+ const full = path.join(d, e.name);
105
+ if (e.isDirectory()) walk(full);
106
+ else if (e.isFile() && e.name.endsWith(".ts"))
107
+ out.push({ path: full, content: fs.readFileSync(full, "utf-8") });
108
+ }
109
+ };
110
+ walk(path.resolve(dir));
111
+ return out;
112
+ }
113
+
114
+ /** Read a spec + scaffold from disk and compute coverage. Throws only on an unreadable spec file. */
115
+ export function coverageForScaffold(
116
+ specPath: string,
117
+ scaffoldDir: string,
118
+ ): CoverageResult {
119
+ const spec = parseYaml(fs.readFileSync(path.resolve(specPath), "utf-8")) as Record<
120
+ string,
121
+ unknown
122
+ >;
123
+ const def = (spec?.definition ?? {}) as Record<string, unknown>;
124
+ const tools = (def?.tools ?? {}) as Record<string, unknown>;
125
+ const codeTools = Array.isArray(tools?.code) ? (tools.code as { id?: string }[]) : [];
126
+ const toolIds = codeTools.map((t) => t?.id).filter((x): x is string => Boolean(x));
127
+ const jobs = Array.isArray(def?.jobs_to_be_done)
128
+ ? (def.jobs_to_be_done as { backed_by?: unknown }[])
129
+ : [];
130
+ const backedBy = jobs.flatMap((j) =>
131
+ Array.isArray(j?.backed_by) ? (j.backed_by as string[]) : [],
132
+ );
133
+ const all = walkTs(scaffoldDir);
134
+ const srcFiles = all.filter((f) => !/\.test\.ts$/.test(f.path));
135
+ const testFiles = all.filter((f) => /\.test\.ts$/.test(f.path));
136
+ return computeCoverage(toolIds, srcFiles, testFiles, backedBy);
137
+ }
138
+
139
+ // ── CLI ─────────────────────────────────────────────────────────────────────────
140
+ function runCli(argv: string[]): number {
141
+ const args = argv.slice(2).filter((a) => !a.startsWith("--"));
142
+ const [specPath, scaffoldDir] = args;
143
+ if (!specPath || !scaffoldDir) {
144
+ process.stderr.write(
145
+ "Usage: scripts/cli/run.sh scripts/verify/spec-impl-coverage.ts <agentspec.yaml> <scaffold-dir>\n" +
146
+ "Asserts every definition.tools.code[].id has an `// @implements <id>` module + a test.\n" +
147
+ "Exit 0 = all covered; exit 1 = an uncovered tool or a dangling backed_by ref (STEER).\n",
148
+ );
149
+ return 1;
150
+ }
151
+ let result: CoverageResult;
152
+ try {
153
+ result = coverageForScaffold(specPath, scaffoldDir);
154
+ } catch (err) {
155
+ process.stderr.write(`Error reading ${specPath}: ${String(err)}\n`);
156
+ return 1;
157
+ }
158
+ console.info("tool-id module test covered");
159
+ console.info("───────────────── ────────────────────────────── ─────────────────── ───────");
160
+ for (const e of result.entries) {
161
+ const mod = e.module ? path.basename(e.module) : "—";
162
+ const tst = e.test ? path.basename(e.test) : "—";
163
+ console.info(
164
+ `${e.id.padEnd(17)} ${mod.padEnd(30)} ${tst.padEnd(19)} ${e.covered ? "✓" : "✗"}` +
165
+ (e.reason ? `\n └─ ${e.reason}` : ""),
166
+ );
167
+ }
168
+ for (const d of result.danglingBackedBy)
169
+ console.info(`✗ dangling job.backed_by → "${d}" is not a declared code tool`);
170
+ if (result.ok) {
171
+ console.info(`[coverage] PASS — all ${result.entries.length} code tool(s) implemented + tested.`);
172
+ return 0;
173
+ }
174
+ const missing = result.entries.filter((e) => !e.covered).map((e) => e.id);
175
+ console.info(
176
+ `[coverage] STEER — uncovered: ${missing.join(", ") || "(none)"}` +
177
+ (result.danglingBackedBy.length ? ` · dangling: ${result.danglingBackedBy.join(", ")}` : ""),
178
+ );
179
+ return 1;
180
+ }
181
+
182
+ if (import.meta.main) process.exit(runCli(process.argv));