@event4u/agent-config 14.10.0 → 14.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +335 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  45. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  46. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  47. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  52. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  53. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  54. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  55. package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
  56. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  57. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
  58. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  59. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  60. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  61. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  62. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  63. package/dist/agent-src/skills/design-review/SKILL.md +79 -1
  64. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  65. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  66. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  67. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  68. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  69. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  70. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  72. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  73. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  74. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  75. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  76. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  77. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  80. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  81. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  82. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  83. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  84. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  85. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  86. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  87. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  88. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  89. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  90. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  91. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  92. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  93. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  94. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  95. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  96. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  97. package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
  98. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  99. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  100. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  101. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  102. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  103. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  104. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  105. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  106. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  107. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  108. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  109. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  110. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  111. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  112. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  113. package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
  114. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  115. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  116. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  117. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  118. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  119. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  120. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  121. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  122. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  123. package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
  124. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  125. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  126. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  127. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  128. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  129. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  130. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  131. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  132. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  133. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  134. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  135. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  136. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  137. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  138. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  139. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  140. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  141. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  142. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  143. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  144. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  145. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  146. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  147. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  148. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  149. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  150. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  151. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  152. package/dist/agent-src/templates/playbook.md +75 -0
  153. package/dist/agent-src/templates/roadmaps.md +37 -0
  154. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  155. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  156. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  157. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  158. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  159. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  160. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  161. package/dist/cli/commands/uiAudit.js +221 -0
  162. package/dist/cli/commands/uiAudit.js.map +1 -0
  163. package/dist/cli/commands/uiRender.js +206 -0
  164. package/dist/cli/commands/uiRender.js.map +1 -0
  165. package/dist/cli/main.js +22 -1
  166. package/dist/cli/main.js.map +1 -1
  167. package/dist/cli/mcp/content.js +64 -1
  168. package/dist/cli/mcp/content.js.map +1 -1
  169. package/dist/cli/mcp/dispatch.js +175 -10
  170. package/dist/cli/mcp/dispatch.js.map +1 -1
  171. package/dist/cli/registry.js +3 -0
  172. package/dist/cli/registry.js.map +1 -1
  173. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  174. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  175. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  176. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  177. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  178. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  179. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  180. package/dist/cli-delegate/cmd_conformance.js +3 -3
  181. package/dist/cli-delegate/cmd_doctor.js +3 -3
  182. package/dist/cli-delegate/cmd_export.js +4 -4
  183. package/dist/cli-delegate/cmd_handoff.js +1 -1
  184. package/dist/cli-delegate/cmd_preflight.js +2 -2
  185. package/dist/cli-delegate/cmd_prune.js +4 -4
  186. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  187. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  188. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  189. package/dist/cli-delegate/cmd_sync.js +4 -4
  190. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  191. package/dist/cli-delegate/cmd_validate.js +4 -4
  192. package/dist/discovery/deprecation-report.md +1 -1
  193. package/dist/discovery/discovery-manifest.json +359 -124
  194. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  195. package/dist/discovery/discovery-manifest.summary.md +4 -4
  196. package/dist/discovery/dormancy-report.md +1 -1
  197. package/dist/discovery/orphan-report.md +1 -1
  198. package/dist/discovery/packs.json +18 -8
  199. package/dist/discovery/trust-report.md +3 -3
  200. package/dist/discovery/workspaces.json +16 -6
  201. package/dist/hooks/dispatch.js +2337 -1436
  202. package/dist/install/install.mjs +795 -708
  203. package/dist/mcp/registry-manifest.json +3 -3
  204. package/dist/mcp/server.json +2 -2
  205. package/dist/mcp/server.mjs +1685 -1250
  206. package/dist/router.json +1 -1
  207. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  208. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  209. package/dist/scripts/_lib/scoped_projection.js +84 -0
  210. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  211. package/dist/scripts/_lib/ui_surface.js +89 -0
  212. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  213. package/dist/scripts/condense.js +6 -10
  214. package/dist/scripts/condense.js.map +1 -1
  215. package/dist/scripts/project_thin_rules.js +40 -2
  216. package/dist/scripts/project_thin_rules.js.map +1 -1
  217. package/dist/server/schemas/settings.js +3 -0
  218. package/dist/server/schemas/settings.js.map +1 -1
  219. package/dist/shared/skillRanking.js +159 -0
  220. package/dist/shared/skillRanking.js.map +1 -0
  221. package/docs/contracts/provider-lifecycle.md +47 -18
  222. package/docs/contracts/settings-classes.md +14 -4
  223. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  224. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  225. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  226. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  227. package/docs/guidelines/design-antipatterns.md +4 -2
  228. package/docs/guidelines/design-asset-discipline.md +38 -0
  229. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  230. package/docs/guidelines/design-handover-extraction.md +35 -5
  231. package/docs/guidelines/design-modes.md +41 -1
  232. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  233. package/llms.txt +50 -39
  234. package/package.json +1 -1
  235. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  236. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  237. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  238. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  239. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  240. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  241. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  242. package/src/config/agent-settings.template.yml +28 -0
  243. package/src/config/assurance-capability-registry.json +223 -0
  244. package/src/config/assurance-threshold-budget.json +65 -0
  245. package/src/config/ci-local-parity.yml +14 -0
  246. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  247. package/src/config/dispatch-economy-metrics.json +4 -2
  248. package/src/config/evaluator-budgets.json +14 -14
  249. package/src/config/gate-coverage.yml +207 -8
  250. package/src/config/gate-violation-baselines.json +76 -72
  251. package/src/config/hook-latency-budget.json +1 -0
  252. package/src/config/hook-token-budget.json +137 -125
  253. package/src/config/pack-size-budget.json +4 -2
  254. package/src/config/quorum-attendance-budget.json +12 -4
  255. package/src/config/rule-activation-census.json +7 -3
  256. package/src/config/rule-enforcement-baseline.json +0 -1
  257. package/src/config/rule-stub-ceilings.json +112 -91
  258. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  259. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  260. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  261. package/src/scripts/_dispatch.bash +17 -0
  262. package/src/scripts/_lib/as_of.ts +412 -0
  263. package/src/scripts/_lib/exec_evidence.ts +7 -0
  264. package/src/scripts/_lib/hook_settings.ts +33 -0
  265. package/src/scripts/_lib/host_listing_model.ts +192 -0
  266. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  267. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  268. package/src/scripts/_lib/review_independence.ts +62 -10
  269. package/src/scripts/_lib/rule_injection.ts +239 -0
  270. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  271. package/src/scripts/_lib/scoped_projection.ts +96 -0
  272. package/src/scripts/_lib/session_register.ts +44 -2
  273. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  274. package/src/scripts/_lib/transcript_reads.ts +207 -0
  275. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  276. package/src/scripts/_lib/ui_authority.ts +343 -0
  277. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  278. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  279. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  280. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  281. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  282. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  283. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  284. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  285. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  286. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  287. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  288. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  289. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  290. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  291. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  292. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  293. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  294. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  295. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  296. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  297. package/src/scripts/ai-video/stitch.sh +191 -10
  298. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  299. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  300. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  301. package/src/scripts/ai_council/clients.ts +3 -2
  302. package/src/scripts/ai_council/consensus.ts +63 -6
  303. package/src/scripts/ai_council/events_log.ts +46 -1
  304. package/src/scripts/ai_council/orchestrator.ts +103 -108
  305. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  306. package/src/scripts/ai_council/prompts.ts +12 -5
  307. package/src/scripts/ai_council/quorum.ts +82 -1
  308. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  309. package/src/scripts/ai_council/stance_tally.ts +22 -0
  310. package/src/scripts/bench_hook_latency.ts +62 -0
  311. package/src/scripts/build_proof.ts +12 -1
  312. package/src/scripts/capture_skill_catalogue.ts +21 -0
  313. package/src/scripts/check_always_budget.ts +2 -1
  314. package/src/scripts/check_augmentignore.ts +2 -1
  315. package/src/scripts/check_beta_review_markers.ts +2 -1
  316. package/src/scripts/check_composite_arming.ts +364 -0
  317. package/src/scripts/check_corpus_staleness.ts +2 -1
  318. package/src/scripts/check_council_pin_staleness.ts +3 -1
  319. package/src/scripts/check_enforcement_coverage.ts +116 -3
  320. package/src/scripts/check_enforcement_denominator.ts +365 -0
  321. package/src/scripts/check_gate_coverage.ts +2 -1
  322. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  323. package/src/scripts/check_knowledge_cards.ts +2 -1
  324. package/src/scripts/check_knowledge_pages.ts +3 -2
  325. package/src/scripts/check_memory.ts +2 -1
  326. package/src/scripts/check_no_automerge_key.ts +287 -0
  327. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  328. package/src/scripts/check_proposal.ts +2 -1
  329. package/src/scripts/check_reach_staleness.ts +2 -1
  330. package/src/scripts/check_release_adjacent_health.ts +2 -1
  331. package/src/scripts/check_release_pr_shape.ts +5 -0
  332. package/src/scripts/check_review_schema.ts +34 -1
  333. package/src/scripts/check_roadmap_trackable.ts +48 -3
  334. package/src/scripts/check_source_size_budget.ts +2 -1
  335. package/src/scripts/check_standing_payload_delta.ts +505 -0
  336. package/src/scripts/check_trigger_evals.ts +2 -1
  337. package/src/scripts/classify_change_risk.ts +281 -0
  338. package/src/scripts/compute_skill_tiers.ts +186 -0
  339. package/src/scripts/condense.ts +8 -13
  340. package/src/scripts/council_attendance_metrics.ts +183 -0
  341. package/src/scripts/council_cli.ts +10 -8
  342. package/src/scripts/council_parse_rate.ts +116 -0
  343. package/src/scripts/derive_playbooks.ts +406 -0
  344. package/src/scripts/design_slop_rules.test.ts +7 -0
  345. package/src/scripts/design_slop_rules.ts +56 -2
  346. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  347. package/src/scripts/frontend_corpus_hash.ts +116 -0
  348. package/src/scripts/grade_target_readiness.ts +478 -0
  349. package/src/scripts/hook_manifest.json +1 -1
  350. package/src/scripts/hook_manifest.yaml +97 -16
  351. package/src/scripts/hooks/concern_registry.ts +4 -0
  352. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  353. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  354. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  355. package/src/scripts/hot_context_hook.ts +74 -7
  356. package/src/scripts/install.ts +40 -89
  357. package/src/scripts/lint_adapter_tier.ts +663 -0
  358. package/src/scripts/lint_archived_skills.ts +93 -0
  359. package/src/scripts/lint_budget_ownership.ts +2 -1
  360. package/src/scripts/lint_deterministic_time.ts +470 -0
  361. package/src/scripts/lint_framework_leakage.ts +5 -0
  362. package/src/scripts/lint_handoffs.ts +525 -1
  363. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  364. package/src/scripts/lint_marketplace.ts +88 -0
  365. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  366. package/src/scripts/lint_one_off_age.ts +6 -1
  367. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  368. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  369. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  370. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  371. package/src/scripts/lint_spawn_payload.ts +42 -5
  372. package/src/scripts/lint_symptom_intake.ts +2 -1
  373. package/src/scripts/mcp_parity_smoke.ts +30 -4
  374. package/src/scripts/mcp_render.ts +66 -1
  375. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  376. package/src/scripts/measure_risk_class_drift.ts +140 -0
  377. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  378. package/src/scripts/media/lib/adapter-common.sh +211 -1
  379. package/src/scripts/media/lib/adapter-contract.md +74 -6
  380. package/src/scripts/model_rule_injection.ts +907 -0
  381. package/src/scripts/project_thin_rules.ts +45 -2
  382. package/src/scripts/release.ts +66 -860
  383. package/src/scripts/release_drill.ts +59 -3
  384. package/src/scripts/release_env.ts +298 -0
  385. package/src/scripts/release_publication.ts +720 -0
  386. package/src/scripts/roadmap_context.ts +778 -0
  387. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  388. package/src/scripts/schemas/rule.schema.json +24 -2
  389. package/src/scripts/schemas/skill.schema.json +39 -0
  390. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  391. package/src/scripts/session_register_hook.ts +29 -3
  392. package/src/scripts/sessions_cli.ts +67 -3
  393. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  394. package/src/scripts/token_sink_report.ts +239 -0
  395. package/src/scripts/trigger_coverage.ts +122 -3
  396. package/src/scripts/update_counts.ts +7 -3
  397. package/src/scripts/validate_frontmatter.ts +66 -0
  398. package/src/server/schemas/settings.ts +5 -0
  399. package/src/shared/skillRanking.ts +183 -0
@@ -0,0 +1,778 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * `agent-config roadmap:context` — the one deterministic situational-awareness
4
+ * probe behind every `/roadmap:*` entry point.
5
+ *
6
+ * ## Why this exists
7
+ *
8
+ * The ingredients of situational awareness were already in the tree and none of
9
+ * them reached a run. The four-command live screen was hand-written inside ONE
10
+ * command file (`/roadmap:next` § 1) and existed for **selection** only; the
11
+ * loop's own screen is claim-triggered — it fires when a message would
12
+ * *describe* a roadmap as in-flight, not before the work starts; file-overlap
13
+ * derivation lived inside the multi-roadmap set contract, which a
14
+ * single-roadmap run never enters. This file is the extraction: one probe, one
15
+ * report, callable from all five entry points.
16
+ *
17
+ * Measured on the population that motivated it: 4 of 24 active roadmaps were
18
+ * already closed in an open PR at `33d7f74af`, and 2 of 22 six days later at
19
+ * `f6703b78a` — the sample halved itself in a week. That decay is the argument
20
+ * for a refresh cadence rather than a better one-shot screen, and it is why
21
+ * every read below is live and none is cached.
22
+ *
23
+ * ## Honesty boundary — the probe is deterministic, the invocation is not
24
+ *
25
+ * ```
26
+ * THE PROBE IS DETERMINISTIC ONCE INVOKED. THE INVOCATION IS MODEL-CARRIED.
27
+ * NOTHING FIRES THIS SCRIPT, AND NOTHING NOTICES WHEN IT IS SKIPPED.
28
+ * ```
29
+ *
30
+ * Same shape as the boundary `/roadmap:next` already states over its own
31
+ * screen, and stated here rather than left as prose that reads like a
32
+ * guarantee. What IS deterministic: what the probe reports once it runs.
33
+ *
34
+ * ## Failure posture
35
+ *
36
+ * Every read degrades rather than throws. No network, no `gh`, no
37
+ * authentication → `network: "unavailable"`, an empty PR set, an explicit
38
+ * `scanned: 0 PRs (network unavailable)` line, and exit 0. A probe that exits
39
+ * non-zero offline would make the call site conditional, which is the one thing
40
+ * this change is trying to remove.
41
+ */
42
+
43
+ import { execFileSync } from 'node:child_process';
44
+ import * as fs from 'node:fs';
45
+ import * as path from 'node:path';
46
+ import process from 'node:process';
47
+ import { fileURLToPath } from 'node:url';
48
+
49
+ import { gitEnv } from './_lib/git_env.js';
50
+ import { read_live_records, register_dir } from './_lib/session_register.js';
51
+ import { other_worktree_branches_detailed } from './sessions_cli.js';
52
+
53
+ /** One open pull request, with the file set it changes. */
54
+ export interface PullRequestInfo {
55
+ number: number;
56
+ title: string;
57
+ headRefName: string;
58
+ files: string[];
59
+ /** Head commit of the PR branch. Feeds the refresh fingerprint. */
60
+ headRefOid?: string;
61
+ }
62
+
63
+ /** A roadmap file found in one of the four roadmap directories. */
64
+ export interface RoadmapEntry {
65
+ slug: string;
66
+ /** Repo-relative path. */
67
+ path: string;
68
+ title: string;
69
+ dir: RoadmapDir;
70
+ }
71
+
72
+ export type RoadmapDir = 'active' | 'later' | 'stubs' | 'archive';
73
+
74
+ /** A sibling roadmap whose slug or title shares keywords with the subject. */
75
+ export interface KeywordHit {
76
+ slug: string;
77
+ path: string;
78
+ dir: RoadmapDir;
79
+ matched: string[];
80
+ }
81
+
82
+ /** A remote branch whose name carries a roadmap slug. */
83
+ export interface RoadmapBranch {
84
+ branch: string;
85
+ slug: string;
86
+ }
87
+
88
+ /** One (roadmap, open PR) pair that touches at least one path in common. */
89
+ export interface OverlapPair {
90
+ roadmap: string;
91
+ pr: number;
92
+ paths: string[];
93
+ /** Which owned-path set the roadmap side came from. */
94
+ source: OverlapSource;
95
+ }
96
+
97
+ /**
98
+ * Where the roadmap's owned-path set came from.
99
+ *
100
+ * `pre-scan` is the set `roadmap-process-loop § 3d` already derives and is
101
+ * authoritative. `cited-path` is the fallback heuristic and is labelled as one:
102
+ * backticked paths in roadmap prose include examples and files the roadmap does
103
+ * not own, so it over-reports. The conservative direction is the one the set
104
+ * contract already states — overlap resolves toward serial — so a false pair
105
+ * costs ordering, never a skip.
106
+ */
107
+ export type OverlapSource = 'pre-scan' | 'cited-path';
108
+
109
+ export interface SessionsView {
110
+ records: unknown[];
111
+ other_worktree_branches: unknown[];
112
+ }
113
+
114
+ export interface RoadmapContext {
115
+ generated_at: string;
116
+ network: 'live' | 'unavailable';
117
+ roadmap: string | null;
118
+ /** `origin/main` at probe time, or `null` when the ref was unreadable. */
119
+ base_sha: string | null;
120
+ /**
121
+ * Stable digest of everything a refresh decision depends on. Compare, never
122
+ * parse: two probes with the same fingerprint saw the same world.
123
+ */
124
+ fingerprint: string;
125
+ scanned: {
126
+ prs: number;
127
+ roadmaps: number;
128
+ remote_branches: number;
129
+ inbox_files: number;
130
+ sessions: number;
131
+ };
132
+ open_prs: PullRequestInfo[];
133
+ roadmap_branches: RoadmapBranch[];
134
+ sessions: SessionsView;
135
+ /** File NAMES only — never contents. `agents/tmp/` is a private scratch area. */
136
+ inbox_files: string[];
137
+ hits: KeywordHit[];
138
+ overlaps: OverlapPair[];
139
+ }
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // Pure helpers — every one of these is exercised without a network.
143
+ // ---------------------------------------------------------------------------
144
+
145
+ /** Roadmap directories the probe walks, in report order. */
146
+ export const ROADMAP_DIRS: ReadonlyArray<readonly [RoadmapDir, string]> = [
147
+ ['active', 'agents/roadmaps'],
148
+ ['later', 'agents/roadmaps/later'],
149
+ ['stubs', 'agents/roadmaps/stubs'],
150
+ ['archive', 'agents/roadmaps/archive'],
151
+ ];
152
+
153
+ const NON_ROADMAP_NAMES: ReadonlySet<string> = new Set([
154
+ 'template.md',
155
+ 'README.md',
156
+ 'progress.md',
157
+ 'roadmaps-progress.md',
158
+ ]);
159
+
160
+ /**
161
+ * Parse `gh pr list --json number,title,headRefName,files`.
162
+ *
163
+ * Tolerant on purpose: a `gh` version that omits `files`, or a PR whose file
164
+ * list is truncated, yields an empty set for that PR rather than dropping the
165
+ * PR. Losing the overlap signal for one PR is recoverable; losing the PR from
166
+ * the screen is the failure this whole probe exists to prevent.
167
+ */
168
+ export function parsePrList(raw: string): PullRequestInfo[] {
169
+ let parsed: unknown;
170
+ try {
171
+ parsed = JSON.parse(raw);
172
+ } catch {
173
+ return [];
174
+ }
175
+ if (!Array.isArray(parsed)) return [];
176
+ const out: PullRequestInfo[] = [];
177
+ for (const item of parsed) {
178
+ if (item === null || typeof item !== 'object') continue;
179
+ const rec = item as Record<string, unknown>;
180
+ const num = rec['number'];
181
+ if (typeof num !== 'number') continue;
182
+ const files: string[] = [];
183
+ const rawFiles = rec['files'];
184
+ if (Array.isArray(rawFiles)) {
185
+ for (const f of rawFiles) {
186
+ if (typeof f === 'string') files.push(f);
187
+ else if (f !== null && typeof f === 'object') {
188
+ const p = (f as Record<string, unknown>)['path'];
189
+ if (typeof p === 'string') files.push(p);
190
+ }
191
+ }
192
+ }
193
+ const oid = rec['headRefOid'];
194
+ out.push({
195
+ number: num,
196
+ title: typeof rec['title'] === 'string' ? rec['title'] : '',
197
+ headRefName: typeof rec['headRefName'] === 'string' ? rec['headRefName'] : '',
198
+ files: files.sort(),
199
+ ...(typeof oid === 'string' && oid !== '' ? { headRefOid: oid } : {}),
200
+ });
201
+ }
202
+ return out.sort((a, b) => a.number - b.number);
203
+ }
204
+
205
+ /**
206
+ * Remote branches whose name carries a roadmap slug — the "roadmap tail".
207
+ *
208
+ * The branch axis needs no claim to have been written: a checkout is on disk
209
+ * from the first minute, and a peer that never claimed is invisible to every
210
+ * other axis.
211
+ */
212
+ export function roadmapTailBranches(
213
+ branches: readonly string[],
214
+ slugs: readonly string[],
215
+ ): RoadmapBranch[] {
216
+ const out: RoadmapBranch[] = [];
217
+ for (const b of branches) {
218
+ const ref = b.trim();
219
+ if (ref === '' || ref.endsWith('/HEAD')) continue;
220
+ for (const slug of slugs) {
221
+ if (slug !== '' && ref.includes(slug)) {
222
+ out.push({ branch: ref, slug });
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ return out;
228
+ }
229
+
230
+ const STOPWORDS: ReadonlySet<string> = new Set([
231
+ 'road',
232
+ 'to',
233
+ 'the',
234
+ 'and',
235
+ 'for',
236
+ 'of',
237
+ 'a',
238
+ 'an',
239
+ 'in',
240
+ 'on',
241
+ 'with',
242
+ 'over',
243
+ 'from',
244
+ ]);
245
+
246
+ /** Keyword set for a slug: kebab segments minus stopwords and 2-char noise. */
247
+ export function slugKeywords(slug: string): string[] {
248
+ const seen = new Set<string>();
249
+ for (const part of slug.toLowerCase().split(/[^a-z0-9]+/)) {
250
+ if (part.length < 3 || STOPWORDS.has(part)) continue;
251
+ seen.add(part);
252
+ }
253
+ return [...seen].sort();
254
+ }
255
+
256
+ /**
257
+ * Sibling roadmaps sharing at least `minMatches` keywords with the subject.
258
+ *
259
+ * This is the axis `/roadmap:create`'s collision check does NOT cover: that
260
+ * check is a recursive `find -iname`, so it already sees `later/`, `stubs/` and
261
+ * `archive/` — lexically. Same topic under a different name passes it.
262
+ */
263
+ export function keywordHits(
264
+ entries: readonly RoadmapEntry[],
265
+ keywords: readonly string[],
266
+ subjectSlug: string,
267
+ minMatches = 2,
268
+ ): KeywordHit[] {
269
+ if (keywords.length === 0) return [];
270
+ const out: KeywordHit[] = [];
271
+ for (const e of entries) {
272
+ if (e.slug === subjectSlug) continue;
273
+ const hay = `${e.slug} ${e.title}`.toLowerCase();
274
+ const matched = keywords.filter((k) => hay.includes(k));
275
+ if (matched.length >= minMatches) {
276
+ out.push({ slug: e.slug, path: e.path, dir: e.dir, matched });
277
+ }
278
+ }
279
+ return out.sort((a, b) => b.matched.length - a.matched.length || a.slug.localeCompare(b.slug));
280
+ }
281
+
282
+ /**
283
+ * Stable digest of everything a refresh decision depends on.
284
+ *
285
+ * `road-to-roadmap-situational-awareness` § 5.1, as re-scoped: the roadmap asked
286
+ * for a `roadmap.context_refresh_cadence` enum and the tree's settings contract
287
+ * refused it — `derivable` is a deletion queue whose size may only fall, and a
288
+ * fixed beat is exactly the flag the contract says a mechanism should replace
289
+ * (`agents/evidence/analysis/situational-awareness-cadence-key-decision.md`).
290
+ * So the trigger is a comparison, not a cadence: re-probe when the world moved.
291
+ *
292
+ * **`origin/main` alone is not enough**, and this is the case a SHA-only trigger
293
+ * misses: a peer pushing to their OWN open PR branch mid-run can add a file that
294
+ * now overlaps this run's owned paths, and `origin/main` has not moved. So the
295
+ * PR head SHAs are in the digest too, at the cost of one extra `gh` field.
296
+ *
297
+ * Not a security hash — a cheap, order-independent digest. It is compared, never
298
+ * parsed, and a collision costs one skipped re-probe rather than a wrong answer.
299
+ */
300
+ export function contextFingerprint(
301
+ base_sha: string | null,
302
+ prs: readonly PullRequestInfo[],
303
+ ): string {
304
+ const parts = [
305
+ `base:${base_sha ?? 'none'}`,
306
+ ...prs
307
+ .map((p) => `pr:${p.number}@${p.headRefOid ?? 'unknown'}`)
308
+ .sort((a, b) => a.localeCompare(b)),
309
+ ];
310
+ let h1 = 0x811c9dc5;
311
+ let h2 = 0x01000193;
312
+ const joined = parts.join('|');
313
+ for (let i = 0; i < joined.length; i++) {
314
+ const c = joined.charCodeAt(i);
315
+ h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0;
316
+ h2 = Math.imul(h2 + c, 0x85ebca6b) >>> 0;
317
+ }
318
+ return `${h1.toString(16).padStart(8, '0')}${h2.toString(16).padStart(8, '0')}`;
319
+ }
320
+
321
+ const CITED_PATH_RE = /`([A-Za-z0-9_][A-Za-z0-9_./-]*\/[A-Za-z0-9_./-]+\.[A-Za-z0-9]+)`/g;
322
+
323
+ /**
324
+ * Paths a roadmap cites in backticks — the FALLBACK owned-path set.
325
+ *
326
+ * Deliberately labelled everywhere it is used: backticked paths include
327
+ * examples, evidence pointers and files the roadmap merely reads. It
328
+ * over-reports, and over-reporting resolves toward serial execution, which is
329
+ * the direction the set contract already declares safe.
330
+ */
331
+ export function citedPaths(text: string): string[] {
332
+ const out = new Set<string>();
333
+ CITED_PATH_RE.lastIndex = 0;
334
+ let m: RegExpExecArray | null;
335
+ while ((m = CITED_PATH_RE.exec(text)) !== null) {
336
+ const p = m[1]!;
337
+ if (p.includes('..')) continue;
338
+ out.add(p);
339
+ }
340
+ return [...out].sort();
341
+ }
342
+
343
+ /**
344
+ * `(roadmap, open PR)` pairs with a non-empty file intersection.
345
+ *
346
+ * Pure over two maps so the assertion in the test suite pins a committed
347
+ * fixture and never a live PR number — the property D1b showed to be
348
+ * essential, since the live population halved inside six days.
349
+ */
350
+ export function computeOverlaps(
351
+ owned: ReadonlyMap<string, { paths: readonly string[]; source: OverlapSource }>,
352
+ prs: readonly PullRequestInfo[],
353
+ ): OverlapPair[] {
354
+ const out: OverlapPair[] = [];
355
+ for (const [roadmap, set] of [...owned.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
356
+ const mine = new Set(set.paths);
357
+ if (mine.size === 0) continue;
358
+ for (const pr of prs) {
359
+ const shared = pr.files.filter((f) => mine.has(f)).sort();
360
+ if (shared.length > 0) {
361
+ out.push({ roadmap, pr: pr.number, paths: shared, source: set.source });
362
+ }
363
+ }
364
+ }
365
+ return out;
366
+ }
367
+
368
+ // ---------------------------------------------------------------------------
369
+ // I/O — injected so every test above runs offline.
370
+ // ---------------------------------------------------------------------------
371
+
372
+ export interface ExecResult {
373
+ code: number;
374
+ stdout: string;
375
+ }
376
+
377
+ export type Exec = (cmd: string, args: readonly string[]) => ExecResult;
378
+
379
+ export const realExec: Exec = (cmd, args) => {
380
+ try {
381
+ const stdout = execFileSync(cmd, [...args], {
382
+ encoding: 'utf-8',
383
+ stdio: ['ignore', 'pipe', 'ignore'],
384
+ env: gitEnv(),
385
+ timeout: 30_000,
386
+ maxBuffer: 32 * 1024 * 1024,
387
+ });
388
+ return { code: 0, stdout };
389
+ } catch (exc) {
390
+ const e = exc as { status?: number; stdout?: string | Buffer };
391
+ return { code: typeof e.status === 'number' ? e.status : 1, stdout: String(e.stdout ?? '') };
392
+ }
393
+ };
394
+
395
+ function readTitle(text: string, slug: string): string {
396
+ const m = /^#[ \t]+(.+)$/m.exec(text);
397
+ return m ? m[1]!.trim() : slug;
398
+ }
399
+
400
+ /** Enumerate the four roadmap directories. Non-recursive per directory. */
401
+ export function enumerateRoadmaps(repoRoot: string): RoadmapEntry[] {
402
+ const out: RoadmapEntry[] = [];
403
+ for (const [dir, rel] of ROADMAP_DIRS) {
404
+ const abs = path.join(repoRoot, rel);
405
+ let names: string[];
406
+ try {
407
+ names = fs.readdirSync(abs);
408
+ } catch {
409
+ continue;
410
+ }
411
+ for (const name of names.sort()) {
412
+ if (!name.endsWith('.md') || NON_ROADMAP_NAMES.has(name)) continue;
413
+ const full = path.join(abs, name);
414
+ try {
415
+ if (!fs.statSync(full).isFile()) continue;
416
+ } catch {
417
+ continue;
418
+ }
419
+ const slug = name.slice(0, -3);
420
+ let text = '';
421
+ try {
422
+ text = fs.readFileSync(full, 'utf-8');
423
+ } catch {
424
+ /* unreadable file still counts as present */
425
+ }
426
+ out.push({ slug, path: path.posix.join(rel, name), title: readTitle(text, slug), dir });
427
+ }
428
+ }
429
+ return out;
430
+ }
431
+
432
+ /** `agents/tmp/` file NAMES. Never contents — the directory is private scratch. */
433
+ export function inboxNames(repoRoot: string): string[] {
434
+ try {
435
+ return fs
436
+ .readdirSync(path.join(repoRoot, 'agents/tmp'), { withFileTypes: true })
437
+ .filter((d) => d.isFile() && !d.name.startsWith('.'))
438
+ .map((d) => d.name)
439
+ .sort();
440
+ } catch {
441
+ return [];
442
+ }
443
+ }
444
+
445
+ /** Verdict for a step whose cited artefacts were checked against the tree. */
446
+ export type ArtefactVerdict = 'present' | 'unverified';
447
+
448
+ /**
449
+ * Which of a step's cited paths are missing from the tree.
450
+ *
451
+ * `road-to-roadmap-situational-awareness` § 5.5, harvested from a dropped draft:
452
+ * **absence of the file is absence of evidence, not evidence of completion.** The
453
+ * failure mode is specific and quiet — a step cites an artefact, the artefact is
454
+ * gone, and "nothing to do here" reads identically to "already done". One of
455
+ * those is a closed step and the other is a lost one.
456
+ */
457
+ export function absentCitedPaths(repoRoot: string, paths: readonly string[]): string[] {
458
+ return paths.filter((rel) => !fs.existsSync(path.join(repoRoot, rel))).sort();
459
+ }
460
+
461
+ /**
462
+ * `unverified` when ANY cited artefact is missing, `present` only when all are.
463
+ *
464
+ * Deliberately not a ratio and not a majority: one missing artefact is enough,
465
+ * because the whole point is that the step's evidence cannot be checked. A step
466
+ * with no cited paths is `present` — there was nothing to check, and inventing a
467
+ * doubt would fire this on every prose step.
468
+ */
469
+ export function staleArtefactVerdict(
470
+ repoRoot: string,
471
+ paths: readonly string[],
472
+ ): { verdict: ArtefactVerdict; absent: string[] } {
473
+ const absent = absentCitedPaths(repoRoot, paths);
474
+ return { verdict: absent.length > 0 ? 'unverified' : 'present', absent };
475
+ }
476
+
477
+ export interface ProbeOptions {
478
+ repoRoot: string;
479
+ roadmap?: string | null;
480
+ exec?: Exec;
481
+ /** Pre-scan owned-path sets, slug → paths. Authoritative where present. */
482
+ ownedPaths?: ReadonlyMap<string, readonly string[]>;
483
+ now?: Date;
484
+ }
485
+
486
+ /** Run the probe. Never throws; degrades to `network: "unavailable"`. */
487
+ export function probe(opts: ProbeOptions): RoadmapContext {
488
+ const exec = opts.exec ?? realExec;
489
+ const root = opts.repoRoot;
490
+ const subject = opts.roadmap ?? null;
491
+
492
+ // A pruning fetch first: every read below is only as live as the refs.
493
+ const fetched = exec('git', ['-C', root, 'fetch', 'origin', '--prune']);
494
+
495
+ const prRun = exec('gh', [
496
+ 'pr',
497
+ 'list',
498
+ '--state',
499
+ 'open',
500
+ '--limit',
501
+ '100',
502
+ '--json',
503
+ 'number,title,headRefName,headRefOid,files',
504
+ ]);
505
+ const online = prRun.code === 0 && fetched.code === 0;
506
+ const open_prs = prRun.code === 0 ? parsePrList(prRun.stdout) : [];
507
+
508
+ const baseRun = exec('git', ['-C', root, 'rev-parse', 'origin/main']);
509
+ const base_sha = baseRun.code === 0 ? baseRun.stdout.trim() || null : null;
510
+
511
+ const roadmaps = enumerateRoadmaps(root);
512
+ const activeSlugs = roadmaps.filter((r) => r.dir === 'active').map((r) => r.slug);
513
+
514
+ const branchRun = exec('git', [
515
+ '-C',
516
+ root,
517
+ 'for-each-ref',
518
+ '--format=%(refname:short)',
519
+ 'refs/remotes/origin',
520
+ ]);
521
+ const branches = branchRun.stdout.split('\n').filter((l) => l.trim() !== '');
522
+ const roadmap_branches = roadmapTailBranches(branches, activeSlugs);
523
+
524
+ const sessions = readSessions(root);
525
+
526
+ const inbox_files = inboxNames(root);
527
+
528
+ const hits =
529
+ subject === null ? [] : keywordHits(roadmaps, slugKeywords(subject), subject);
530
+
531
+ // Pre-scan sets, unioned from two sources and NEVER re-derived. The loop's
532
+ // § 3d overlap derivation runs in the model's head; the register is where it
533
+ // becomes readable by anything else — a session that ran
534
+ // `sessions:claim --paths` has published exactly that set. An explicit
535
+ // `--owned-paths` file wins, because a caller that names a set means it.
536
+ const fromRegister = registerOwnedPaths(sessions.records);
537
+ const owned = new Map<string, { paths: readonly string[]; source: OverlapSource }>();
538
+ const subjects = subject === null ? roadmaps.filter((r) => r.dir === 'active') : roadmaps.filter((r) => r.slug === subject);
539
+ for (const r of subjects) {
540
+ const pre = opts.ownedPaths?.get(r.slug) ?? fromRegister.get(r.slug);
541
+ if (pre !== undefined && pre.length > 0) {
542
+ owned.set(r.slug, { paths: pre, source: 'pre-scan' });
543
+ continue;
544
+ }
545
+ let text = '';
546
+ try {
547
+ text = fs.readFileSync(path.join(root, r.path), 'utf-8');
548
+ } catch {
549
+ continue;
550
+ }
551
+ owned.set(r.slug, { paths: citedPaths(text), source: 'cited-path' });
552
+ }
553
+ const overlaps = computeOverlaps(owned, open_prs);
554
+
555
+ return {
556
+ generated_at: (opts.now ?? new Date()).toISOString(),
557
+ network: online ? 'live' : 'unavailable',
558
+ roadmap: subject,
559
+ base_sha,
560
+ fingerprint: contextFingerprint(base_sha, open_prs),
561
+ scanned: {
562
+ prs: open_prs.length,
563
+ roadmaps: roadmaps.length,
564
+ remote_branches: branches.length,
565
+ inbox_files: inbox_files.length,
566
+ sessions: sessions.records.length,
567
+ },
568
+ open_prs,
569
+ roadmap_branches,
570
+ sessions,
571
+ inbox_files,
572
+ hits,
573
+ overlaps,
574
+ };
575
+ }
576
+
577
+ /**
578
+ * The session register, read through the same library `sessions:list` uses.
579
+ *
580
+ * Both axes, because the register alone reports the half that can be silent: a
581
+ * peer that never claimed is visible only as a branch checked out in another
582
+ * worktree.
583
+ */
584
+ function readSessions(root: string): SessionsView {
585
+ let records: unknown[] = [];
586
+ let other: unknown[] = [];
587
+ try {
588
+ const dir = register_dir(root);
589
+ records = dir === null ? [] : read_live_records(dir, { prune: false });
590
+ } catch {
591
+ /* no register is the normal pre-first-session state, never an error */
592
+ }
593
+ try {
594
+ other = other_worktree_branches_detailed(root).rows;
595
+ } catch {
596
+ /* a git failure costs the branch axis, not the whole report */
597
+ }
598
+ return { records, other_worktree_branches: other };
599
+ }
600
+
601
+ /**
602
+ * Owned-path sets published by live sessions, slug → paths.
603
+ *
604
+ * This is the "extend before create" half of § 3.3: no second derivation exists
605
+ * anywhere: the set is whatever a session declared through
606
+ * `sessions:claim --paths`, and a slug with no declaration simply is not in the
607
+ * map, so the caller falls through to the labelled cited-path heuristic.
608
+ */
609
+ export function registerOwnedPaths(records: readonly unknown[]): Map<string, string[]> {
610
+ const out = new Map<string, string[]>();
611
+ for (const r of records) {
612
+ if (r === null || typeof r !== 'object') continue;
613
+ const rec = r as Record<string, unknown>;
614
+ const slug = rec['roadmap_slug'];
615
+ const paths = rec['owned_paths'];
616
+ if (typeof slug !== 'string' || slug === '' || !Array.isArray(paths)) continue;
617
+ const clean = paths.filter((x): x is string => typeof x === 'string' && x.trim() !== '');
618
+ if (clean.length === 0) continue;
619
+ const prior = out.get(slug) ?? [];
620
+ out.set(slug, [...new Set([...prior, ...clean])].sort());
621
+ }
622
+ return out;
623
+ }
624
+
625
+ /** The four relations rule 18 allows. Closed set — an unknown value reds the lint. */
626
+ export const RELATES_RELATIONS = ['extends', 'supersedes', 'depends', 'disjoint'] as const;
627
+ export type RelatesRelation = (typeof RELATES_RELATIONS)[number];
628
+
629
+ /**
630
+ * The `relates: []` frontmatter line for a probe that found nothing.
631
+ *
632
+ * Fully determined, which is why it is emitted rather than asked about: zero
633
+ * hits leaves nothing for a numbered question to be about. The `scanned:` count
634
+ * rides along because that is what separates "somebody looked and found
635
+ * nothing" from "nobody looked" — the whole absent-versus-empty distinction
636
+ * template rule 18 rests on.
637
+ */
638
+ export function emptyRelatesBlock(scanned: number): string {
639
+ return `relates: [] # scanned: ${scanned} roadmap file(s), 0 sibling hits`;
640
+ }
641
+
642
+ /**
643
+ * The `relates:` frontmatter block for a probe that found hits.
644
+ *
645
+ * One row per hit. The RELATION is not inferred: `answers` carries the choice
646
+ * for each slug, because "extends" and "supersedes" are the same lexical
647
+ * evidence and opposite decisions — guessing would manufacture the reflex-empty
648
+ * failure in a louder form. A hit with no answer is emitted with its own note
649
+ * saying so rather than silently dropped, so an unanswered question cannot
650
+ * disappear into a clean-looking block.
651
+ */
652
+ export function relatesRowsFromHits(
653
+ hits: readonly KeywordHit[],
654
+ answers: ReadonlyMap<string, RelatesRelation>,
655
+ ): string {
656
+ if (hits.length === 0) return 'relates: []';
657
+ const lines = ['relates:'];
658
+ for (const h of hits) {
659
+ const rel = answers.get(h.slug);
660
+ lines.push(` - slug: ${h.slug}`);
661
+ lines.push(` relation: ${rel ?? 'disjoint'}`);
662
+ lines.push(
663
+ rel === undefined
664
+ ? ` note: "UNANSWERED — probe hit in ${h.dir}/ on [${h.matched.join(' ')}]; confirm the relation"`
665
+ : ` note: "probe hit in ${h.dir}/ on [${h.matched.join(' ')}]"`,
666
+ );
667
+ }
668
+ return lines.join('\n');
669
+ }
670
+
671
+ /** The human report. One compact block — a refresh runs at every phase boundary. */
672
+ export function renderText(ctx: RoadmapContext): string {
673
+ const L: string[] = [];
674
+ L.push(`roadmap:context — ${ctx.generated_at}${ctx.roadmap ? ` · subject: ${ctx.roadmap}` : ''}`);
675
+ L.push('');
676
+ if (ctx.network === 'unavailable') {
677
+ L.push('scanned: 0 PRs (network unavailable)');
678
+ } else {
679
+ L.push(`scanned: ${ctx.scanned.prs} PRs`);
680
+ }
681
+ L.push(`scanned: ${ctx.scanned.roadmaps} roadmap file(s) across active/later/stubs/archive`);
682
+ L.push(`scanned: ${ctx.scanned.remote_branches} remote branch(es)`);
683
+ L.push(`scanned: ${ctx.scanned.sessions} live session record(s)`);
684
+ L.push(`scanned: ${ctx.scanned.inbox_files} inbox file name(s) in agents/tmp/`);
685
+ L.push('');
686
+ L.push('open PRs:');
687
+ if (ctx.open_prs.length === 0) L.push(' (none)');
688
+ for (const pr of ctx.open_prs) {
689
+ L.push(` #${pr.number} ${pr.title}`);
690
+ L.push(` head: ${pr.headRefName} · ${pr.files.length} changed file(s)`);
691
+ }
692
+ L.push('');
693
+ L.push('remote branches carrying a roadmap slug:');
694
+ if (ctx.roadmap_branches.length === 0) L.push(' (none)');
695
+ for (const b of ctx.roadmap_branches) L.push(` ${b.branch} → ${b.slug}`);
696
+ L.push('');
697
+ L.push('roadmap ↔ open-PR file overlap:');
698
+ if (ctx.overlaps.length === 0) L.push(' (none)');
699
+ for (const o of ctx.overlaps) {
700
+ L.push(` ${o.roadmap} × #${o.pr} [${o.source}] ${o.paths.length} path(s)`);
701
+ for (const p of o.paths.slice(0, 5)) L.push(` ${p}`);
702
+ }
703
+ L.push('');
704
+ L.push('sibling roadmaps on the same topic (slug/title keywords):');
705
+ if (ctx.hits.length === 0) L.push(' (none)');
706
+ for (const h of ctx.hits) L.push(` ${h.dir.padEnd(7)} ${h.slug} [${h.matched.join(' ')}]`);
707
+ L.push('');
708
+ L.push('inbox notes (names only, never contents):');
709
+ if (ctx.inbox_files.length === 0) L.push(' (none)');
710
+ for (const n of ctx.inbox_files) L.push(` agents/tmp/${n}`);
711
+ L.push(`context fingerprint: ${ctx.fingerprint} (base ${ctx.base_sha ?? 'unknown'})`);
712
+ L.push('A refresh re-probes when this value differs from the one the run holds.');
713
+ L.push('');
714
+ L.push('This probe is deterministic once invoked; the invocation is model-carried.');
715
+ return `${L.join('\n')}\n`;
716
+ }
717
+
718
+ function readOwnedPathsFile(p: string): Map<string, string[]> {
719
+ const out = new Map<string, string[]>();
720
+ try {
721
+ const parsed = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
722
+ for (const [slug, v] of Object.entries(parsed)) {
723
+ if (Array.isArray(v)) out.set(slug, v.filter((x): x is string => typeof x === 'string'));
724
+ }
725
+ } catch {
726
+ /* an unreadable pre-scan file falls back to the cited-path heuristic */
727
+ }
728
+ return out;
729
+ }
730
+
731
+ export function main(argv: string[] = process.argv.slice(2)): number {
732
+ if (argv.includes('--help') || argv.includes('-h')) {
733
+ process.stdout.write(
734
+ 'usage: agent-config roadmap:context [--roadmap <slug>] [--owned-paths <file.json>] [--json] [--relates] [--fingerprint]\n',
735
+ );
736
+ return 0;
737
+ }
738
+ const at = (flag: string): string | null => {
739
+ const i = argv.indexOf(flag);
740
+ return i >= 0 && argv[i + 1] !== undefined ? argv[i + 1]! : null;
741
+ };
742
+ const root = at('--root') ?? process.cwd();
743
+ const ownedFile = at('--owned-paths');
744
+ const ctx = probe({
745
+ repoRoot: root,
746
+ roadmap: at('--roadmap'),
747
+ ...(ownedFile !== null ? { ownedPaths: readOwnedPathsFile(ownedFile) } : {}),
748
+ });
749
+ if (argv.includes('--fingerprint')) {
750
+ // Just the digest, for the loop's phase-boundary comparison and for the
751
+ // resume checkpoint's `context_fingerprint` (§ 5.6 — same value).
752
+ process.stdout.write(`${ctx.fingerprint}\n`);
753
+ return 0;
754
+ }
755
+ if (argv.includes('--relates')) {
756
+ // The frontmatter block, ready to paste. Zero hits is fully determined
757
+ // and printed as-is; hits print with every relation UNANSWERED, because
758
+ // that is a question for the author and not something to infer.
759
+ process.stdout.write(
760
+ ctx.hits.length === 0
761
+ ? `${emptyRelatesBlock(ctx.scanned.roadmaps)}\n`
762
+ : `${relatesRowsFromHits(ctx.hits, new Map())}\n`,
763
+ );
764
+ return 0;
765
+ }
766
+ process.stdout.write(
767
+ argv.includes('--json') ? `${JSON.stringify(ctx, null, 2)}\n` : renderText(ctx),
768
+ );
769
+ return 0;
770
+ }
771
+
772
+ // Bundle-safety: never auto-run when inlined into an esbuild bundle, where
773
+ // every module shares the bundle's `import.meta.url` (see sessions_cli.ts).
774
+ declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
775
+ const _bundled = typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__;
776
+ if (!_bundled && fileURLToPath(import.meta.url) === path.resolve(process.argv[1] ?? '')) {
777
+ process.exit(main());
778
+ }