@event4u/agent-config 14.12.0 → 14.13.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 (466) 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 +234 -0
  5. package/CONTRIBUTING.md +38 -0
  6. package/CREDITS.md +135 -0
  7. package/NOTICE +13 -0
  8. package/README.md +8 -6
  9. package/dist/agent-src/commands/analyze/inbox.md +109 -2
  10. package/dist/agent-src/commands/feature/dev.md +7 -2
  11. package/dist/agent-src/commands/ghostwriter/write.md +5 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/mission/upgrade.md +1 -1
  14. package/dist/agent-src/commands/orchestrate.md +2 -2
  15. package/dist/agent-src/commands/post-as/me.md +5 -1
  16. package/dist/agent-src/commands/pr/merge.md +19 -6
  17. package/dist/agent-src/commands/roadmap/process-full.md +19 -5
  18. package/dist/agent-src/commands/tdd/red.md +12 -2
  19. package/dist/agent-src/commands/work.md +1 -1
  20. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +40 -0
  21. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -2
  22. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +21 -0
  23. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +1 -0
  24. package/dist/agent-src/contexts/execution/subagent-response-contract.md +40 -0
  25. package/dist/agent-src/contexts/execution/subagent-steering.md +13 -1
  26. package/dist/agent-src/contexts/execution/terminal-states.md +96 -0
  27. package/dist/agent-src/rules/artifact-drafting-protocol.md +1 -1
  28. package/dist/agent-src/rules/broken-access-control.md +4 -0
  29. package/dist/agent-src/rules/code-provenance.md +4 -0
  30. package/dist/agent-src/rules/design-fidelity.md +1 -1
  31. package/dist/agent-src/rules/domain-safety-pii.md +4 -0
  32. package/dist/agent-src/rules/domain-safety-retention.md +4 -0
  33. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  34. package/dist/agent-src/rules/external-code-graph-interop.md +14 -12
  35. package/dist/agent-src/rules/improve-before-implement.md +6 -8
  36. package/dist/agent-src/rules/laravel-routing.md +1 -1
  37. package/dist/agent-src/rules/lethal-trifecta-guard.md +4 -0
  38. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  39. package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
  40. package/dist/agent-src/rules/source-confidentiality.md +12 -34
  41. package/dist/agent-src/rules/symfony-routing.md +1 -1
  42. package/dist/agent-src/rules/token-efficiency.md +14 -0
  43. package/dist/agent-src/rules/tool-safety.md +4 -0
  44. package/dist/agent-src/rules/untrusted-input-defense.md +4 -0
  45. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +247 -3
  46. package/dist/agent-src/scripts/guarded_baseline.ts +328 -0
  47. package/dist/agent-src/scripts/roadmap_set_step.ts +253 -0
  48. package/dist/agent-src/scripts/stub_queue.ts +82 -0
  49. package/dist/agent-src/scripts/stubs_due.ts +400 -0
  50. package/dist/agent-src/scripts/ticket_bundles.ts +84 -0
  51. package/dist/agent-src/scripts/update_roadmap_progress.ts +8 -4
  52. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +17 -4
  53. package/dist/agent-src/skills/brand-audit/SKILL.md +33 -0
  54. package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -0
  55. package/dist/agent-src/skills/database/SKILL.md +8 -0
  56. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +27 -0
  57. package/dist/agent-src/skills/design-review/SKILL.md +1 -1
  58. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +17 -1
  60. package/dist/agent-src/skills/feature-planning/SKILL.md +9 -2
  61. package/dist/agent-src/skills/frontend-render-security/SKILL.md +44 -0
  62. package/dist/agent-src/skills/git-workflow/SKILL.md +1 -1
  63. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +38 -4
  64. package/dist/agent-src/skills/judge-artifact-completeness/rubrics/ticket-quality-score.json +2 -2
  65. package/dist/agent-src/skills/laravel/SKILL.md +35 -0
  66. package/dist/agent-src/skills/laravel-migration/SKILL.md +57 -3
  67. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +26 -0
  68. package/dist/agent-src/skills/playbook-authoring/SKILL.md +28 -1
  69. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  70. package/dist/agent-src/skills/skill-writing/references/procedure.md +5 -5
  71. package/dist/agent-src/skills/sql-writing/SKILL.md +7 -1
  72. package/dist/agent-src/skills/standards-from-config/SKILL.md +27 -4
  73. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +11 -4
  74. package/dist/agent-src/skills/symfony-workflow/SKILL.md +35 -0
  75. package/dist/agent-src/skills/test-case-discovery/SKILL.md +85 -0
  76. package/dist/agent-src/skills/test-driven-development/SKILL.md +33 -9
  77. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  78. package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +37 -0
  79. package/dist/agent-src/skills/ui-component-architect/SKILL.md +45 -2
  80. package/dist/agent-src/skills/upstream-contribute/SKILL.md +38 -1
  81. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +9 -4
  82. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  83. package/dist/agent-src/templates/roadmaps.md +97 -3
  84. package/dist/agent-src/templates/scripts/check_memory.ts +258 -17
  85. package/dist/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
  86. package/dist/agent-src/templates/scripts/memory_lookup.ts +86 -0
  87. package/dist/agent-src/templates/scripts/memory_signal.ts +44 -0
  88. package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
  89. package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
  90. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
  91. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
  92. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
  93. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
  94. package/dist/cli/commands/uiAudit.js +124 -5
  95. package/dist/cli/registry.js +2 -0
  96. package/dist/cli-delegate/{chunk-HYWSRNUX.js → chunk-2DK2VJWG.js} +3 -3
  97. package/dist/cli-delegate/{chunk-JCW7ZLHK.js → chunk-4FS4WRXP.js} +5 -0
  98. package/dist/cli-delegate/{chunk-OMIVVCG2.js → chunk-A4QJ6ENT.js} +24 -0
  99. package/dist/cli-delegate/{chunk-JJA2QEWA.js → chunk-AKJGUQNO.js} +466 -285
  100. package/dist/cli-delegate/{chunk-E46KPN6K.js → chunk-BTWUYDGN.js} +424 -363
  101. package/dist/cli-delegate/{chunk-K4FFODTS.js → chunk-ILMUPLGW.js} +1227 -316
  102. package/dist/cli-delegate/{chunk-FF7WAFMR.js → chunk-KPBWFOPZ.js} +5 -1
  103. package/dist/cli-delegate/cmd_adr_effective.js +102 -0
  104. package/dist/cli-delegate/cmd_conformance.js +42 -10
  105. package/dist/cli-delegate/cmd_doctor.js +3 -3
  106. package/dist/cli-delegate/cmd_export.js +3 -2
  107. package/dist/cli-delegate/cmd_fleet.js +2 -2
  108. package/dist/cli-delegate/cmd_handoff.js +1 -1
  109. package/dist/cli-delegate/cmd_preflight.js +2 -2
  110. package/dist/cli-delegate/cmd_prune.js +3 -2
  111. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  112. package/dist/cli-delegate/cmd_settings_migrate.js +3 -2
  113. package/dist/cli-delegate/cmd_settings_set.js +2 -2
  114. package/dist/cli-delegate/cmd_sync.js +3 -2
  115. package/dist/cli-delegate/cmd_uninstall.js +3 -2
  116. package/dist/cli-delegate/cmd_validate.js +3 -2
  117. package/dist/discovery/deprecation-report.md +1 -1
  118. package/dist/discovery/discovery-manifest.json +57 -57
  119. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  120. package/dist/discovery/discovery-manifest.summary.md +1 -1
  121. package/dist/discovery/dormancy-report.md +1 -1
  122. package/dist/discovery/orphan-report.md +1 -1
  123. package/dist/discovery/packs.json +2 -2
  124. package/dist/discovery/trust-report.md +1 -1
  125. package/dist/discovery/workspaces.json +2 -2
  126. package/dist/hooks/dispatch.js +1836 -712
  127. package/dist/install/claudeRuleRewrite.js +254 -0
  128. package/dist/install/detect.js +29 -16
  129. package/dist/install/detect_php_shape.js +159 -0
  130. package/dist/install/install.mjs +634 -428
  131. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  132. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
  133. package/dist/mcp/registry-manifest.json +3 -3
  134. package/dist/mcp/server.json +3 -3
  135. package/dist/mcp/server.mjs +4818 -2645
  136. package/dist/server/schemas/settings.js +3 -0
  137. package/dist/ui/assets/index-D0jaCRPM.js +29 -0
  138. package/dist/ui/index.html +1 -1
  139. package/docs/MIGRATION.md +1 -1
  140. package/docs/THIRD-PARTY-NOTICES.md +25 -0
  141. package/docs/contracts/provider-lifecycle.md +1 -1
  142. package/docs/contracts/settings-classes.md +10 -4
  143. package/docs/guidelines/agent-infra/5w2h-analysis.md +3 -3
  144. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -0
  145. package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +58 -3
  146. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +2 -2
  147. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +3 -3
  148. package/docs/guidelines/agent-infra/break-glass-usage.md +7 -7
  149. package/docs/guidelines/agent-infra/carve-out-predicates.md +3 -3
  150. package/docs/guidelines/agent-infra/comparison-matrix.md +1 -1
  151. package/docs/guidelines/agent-infra/critical-thinking.md +4 -4
  152. package/docs/guidelines/agent-infra/direct-answers-demos.md +2 -2
  153. package/docs/guidelines/agent-infra/domain-eval-anti-pattern.md +1 -1
  154. package/docs/guidelines/agent-infra/engineering-memory-data-format.md +3 -3
  155. package/docs/guidelines/agent-infra/existence-question-verdicts.md +80 -0
  156. package/docs/guidelines/agent-infra/first-principles.md +2 -2
  157. package/docs/guidelines/agent-infra/framework-neutrality-patterns.md +2 -2
  158. package/docs/guidelines/agent-infra/guarded-baseline.md +39 -0
  159. package/docs/guidelines/agent-infra/inversion-thinking.md +5 -5
  160. package/docs/guidelines/agent-infra/language-and-tone-examples.md +1 -1
  161. package/docs/guidelines/agent-infra/layered-settings.md +10 -10
  162. package/docs/guidelines/agent-infra/memory-access.md +1 -1
  163. package/docs/guidelines/agent-infra/mental-models.md +3 -3
  164. package/docs/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
  165. package/docs/guidelines/agent-infra/review-routing-data-format.md +7 -7
  166. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +13 -7
  167. package/docs/guidelines/agent-infra/role-contracts.md +2 -2
  168. package/docs/guidelines/agent-infra/role-mode-router.md +3 -3
  169. package/docs/guidelines/agent-infra/rule-type-governance.md +2 -2
  170. package/docs/guidelines/agent-infra/scqa-framework.md +5 -5
  171. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +6 -6
  172. package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +2 -2
  173. package/docs/guidelines/agent-infra/six-hats.md +3 -3
  174. package/docs/guidelines/agent-infra/skill-quality-checklist.md +6 -6
  175. package/docs/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
  176. package/docs/guidelines/agent-infra/systems-thinking.md +1 -1
  177. package/docs/guidelines/agent-infra/verify-before-complete-demos.md +2 -2
  178. package/docs/guidelines/augment-portability-patterns.md +6 -4
  179. package/docs/guidelines/component-oriented-and-oop-development.md +3 -2
  180. package/docs/guidelines/cross-role-handoff.md +2 -2
  181. package/docs/guidelines/design-fidelity-mechanics.md +1 -1
  182. package/docs/guidelines/docs/readme-size-and-splitting.md +3 -3
  183. package/docs/guidelines/php/database.md +90 -7
  184. package/docs/guidelines/prompt-templates.md +7 -7
  185. package/llms.txt +2 -2
  186. package/package.json +5 -2
  187. package/src/agent-src/scripts/archive_completed_roadmaps.ts +247 -3
  188. package/src/agent-src/scripts/guarded_baseline.ts +328 -0
  189. package/src/agent-src/scripts/roadmap_set_step.ts +253 -0
  190. package/src/agent-src/scripts/stub_queue.ts +82 -0
  191. package/src/agent-src/scripts/stubs_due.ts +400 -0
  192. package/src/agent-src/scripts/ticket_bundles.ts +84 -0
  193. package/src/agent-src/scripts/update_roadmap_progress.ts +8 -4
  194. package/src/agent-src/templates/scripts/check_memory.ts +258 -17
  195. package/src/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
  196. package/src/agent-src/templates/scripts/memory_lookup.ts +86 -0
  197. package/src/agent-src/templates/scripts/memory_signal.ts +44 -0
  198. package/src/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
  199. package/src/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
  200. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
  201. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
  202. package/src/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
  203. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
  204. package/src/config/agent-settings.template.yml +32 -3
  205. package/src/config/assurance-capability-registry.json +21 -9
  206. package/src/config/canonical-terms.yml +119 -0
  207. package/src/config/capability-native-outcome-bars.json +81 -0
  208. package/src/config/ci-local-parity.yml +80 -0
  209. package/src/config/drafting-channels.yml +43 -0
  210. package/src/config/estate-count-budget.json +38 -36
  211. package/src/config/evaluator-budgets.json +14 -14
  212. package/src/config/gate-coverage.yml +579 -63
  213. package/src/config/gate-reachability-exemptions.json +21 -0
  214. package/src/config/gate-violation-baselines.json +37 -19
  215. package/src/config/harness-evolution-budget.json +25 -0
  216. package/src/config/lapsed-beta-baseline.json +96 -0
  217. package/src/config/memory-twin-verdicts.yml +129 -0
  218. package/src/config/metric-registry.yml +124 -0
  219. package/src/config/pack-size-budget.json +16 -15
  220. package/src/config/preamble-payload-budget.json +8 -8
  221. package/src/config/profiles/balanced.ini +27 -0
  222. package/src/config/profiles/full.ini +27 -0
  223. package/src/config/profiles/minimal.ini +27 -0
  224. package/src/config/publish-surface.json +9 -2
  225. package/src/config/routing-coverage-seed.json +16 -2
  226. package/src/config/rule-stub-ceilings.json +126 -112
  227. package/src/config/turnaround-budget.json +66 -0
  228. package/src/install/claudeRuleRewrite.ts +288 -0
  229. package/src/install/detect.ts +60 -17
  230. package/src/install/detect_php_shape.ts +191 -0
  231. package/src/scripts/_cli/cmd_doctor.ts +27 -112
  232. package/src/scripts/_dispatch.bash +27 -0
  233. package/src/scripts/_lib/activation_ladder.ts +211 -0
  234. package/src/scripts/_lib/activation_states.ts +88 -0
  235. package/src/scripts/_lib/adherence_detectors.ts +143 -0
  236. package/src/scripts/_lib/arith_claims.ts +181 -0
  237. package/src/scripts/_lib/arm_ranking.ts +62 -0
  238. package/src/scripts/_lib/asset_delivery_ledger.ts +303 -0
  239. package/src/scripts/_lib/audit_field_provenance.ts +54 -0
  240. package/src/scripts/_lib/auto_dispatch.ts +32 -3
  241. package/src/scripts/_lib/bench_ab_size_claim.ts +98 -4
  242. package/src/scripts/_lib/benchmark_cache_fields.ts +158 -0
  243. package/src/scripts/_lib/candidate_proposer.ts +402 -0
  244. package/src/scripts/_lib/candidate_record.ts +601 -0
  245. package/src/scripts/_lib/capture_rate.ts +319 -0
  246. package/src/scripts/_lib/collector_denominator.ts +771 -0
  247. package/src/scripts/_lib/collector_record.ts +340 -0
  248. package/src/scripts/_lib/collector_store.ts +664 -0
  249. package/src/scripts/_lib/collector_supervision.ts +827 -0
  250. package/src/scripts/_lib/concern_estate.ts +77 -0
  251. package/src/scripts/_lib/config_chain.ts +477 -0
  252. package/src/scripts/_lib/config_cost.ts +209 -0
  253. package/src/scripts/_lib/council_settings_block.ts +6 -0
  254. package/src/scripts/_lib/curator_ops.ts +172 -0
  255. package/src/scripts/_lib/delivery_arm_experiment.ts +266 -0
  256. package/src/scripts/_lib/delivery_criticality.ts +126 -0
  257. package/src/scripts/_lib/doctor_runtime_checks.ts +70 -0
  258. package/src/scripts/_lib/empty_cycles.ts +88 -0
  259. package/src/scripts/_lib/eval_publication.ts +354 -0
  260. package/src/scripts/_lib/evaluation_vector.ts +283 -0
  261. package/src/scripts/_lib/evaluator_promotion.ts +225 -0
  262. package/src/scripts/_lib/evidence_basis.ts +118 -0
  263. package/src/scripts/_lib/exec_evidence.ts +8 -0
  264. package/src/scripts/_lib/experience_card.ts +272 -0
  265. package/src/scripts/_lib/experience_report.ts +151 -0
  266. package/src/scripts/_lib/experiment_freeze.ts +112 -0
  267. package/src/scripts/_lib/generated_by.ts +66 -0
  268. package/src/scripts/_lib/harness_evolution_guards.ts +229 -0
  269. package/src/scripts/_lib/hook_effect_probe.ts +141 -0
  270. package/src/scripts/_lib/host_denominator.ts +687 -0
  271. package/src/scripts/_lib/host_projection_reach.ts +157 -0
  272. package/src/scripts/_lib/ignored_blocker.ts +148 -0
  273. package/src/scripts/_lib/install_reach_checks.ts +495 -0
  274. package/src/scripts/_lib/json_duplicate_keys.ts +150 -0
  275. package/src/scripts/_lib/judge_hygiene.ts +228 -0
  276. package/src/scripts/_lib/legacy_boundary_map.ts +228 -0
  277. package/src/scripts/_lib/loop_guards.ts +299 -0
  278. package/src/scripts/_lib/loss_class.ts +129 -0
  279. package/src/scripts/_lib/machine_wake.ts +51 -0
  280. package/src/scripts/_lib/md_prose_lines.ts +177 -0
  281. package/src/scripts/_lib/minimality_tiebreak.ts +168 -0
  282. package/src/scripts/_lib/one_resolver_invariant.ts +449 -0
  283. package/src/scripts/_lib/orchestration_record.ts +253 -5
  284. package/src/scripts/_lib/outcome_envelope.ts +227 -0
  285. package/src/scripts/_lib/outcome_vocabularies.ts +125 -0
  286. package/src/scripts/_lib/paired_stats.ts +245 -0
  287. package/src/scripts/_lib/paired_verdict.ts +226 -0
  288. package/src/scripts/_lib/prefix_stable_surfaces.ts +123 -0
  289. package/src/scripts/_lib/privacy_class.ts +47 -0
  290. package/src/scripts/_lib/regression_neighbourhood.ts +259 -0
  291. package/src/scripts/_lib/repeated_failure.ts +105 -0
  292. package/src/scripts/_lib/repo_root.ts +111 -0
  293. package/src/scripts/_lib/review_skipped_record.ts +37 -0
  294. package/src/scripts/_lib/role_split.ts +338 -0
  295. package/src/scripts/_lib/runtime_journal.ts +1499 -0
  296. package/src/scripts/_lib/runtime_wiring_checks.ts +430 -0
  297. package/src/scripts/_lib/skill_catalogue.ts +11 -1
  298. package/src/scripts/_lib/source_digest.ts +200 -0
  299. package/src/scripts/_lib/source_redact.ts +260 -0
  300. package/src/scripts/_lib/source_shape.ts +502 -0
  301. package/src/scripts/_lib/source_snapshot_dedup.ts +199 -0
  302. package/src/scripts/_lib/strip_comments.d.mts +8 -0
  303. package/src/scripts/_lib/strip_comments.mjs +65 -0
  304. package/src/scripts/_lib/test_red_state.ts +154 -0
  305. package/src/scripts/_lib/trigger_shift.ts +144 -0
  306. package/src/scripts/_lib/untrusted_content.ts +82 -7
  307. package/src/scripts/adoption_report.ts +1 -1
  308. package/src/scripts/adr/evidence_census.ts +1 -1
  309. package/src/scripts/adr/regenerate_index.ts +26 -2
  310. package/src/scripts/adr_cite_check.ts +164 -1
  311. package/src/scripts/ai_council/blind_review.ts +11 -1
  312. package/src/scripts/ai_council/clients.ts +4 -0
  313. package/src/scripts/ai_council/config.ts +3 -0
  314. package/src/scripts/ai_council/consensus.ts +28 -8
  315. package/src/scripts/ai_council/consensus_round.ts +226 -0
  316. package/src/scripts/ai_council/disagreement_signal.ts +338 -0
  317. package/src/scripts/ai_council/inline_findings.ts +340 -0
  318. package/src/scripts/ai_council/judge_position_bias.ts +197 -0
  319. package/src/scripts/ai_council/orchestrator.ts +16 -203
  320. package/src/scripts/ai_council/orchestrator_results.ts +23 -3
  321. package/src/scripts/ai_council/pricing.ts +106 -0
  322. package/src/scripts/ai_council/prompts.ts +74 -7
  323. package/src/scripts/ai_council/provider_leakage_bench.ts +327 -0
  324. package/src/scripts/ai_council/py_format.ts +14 -3
  325. package/src/scripts/ai_council/recouncil_guard.ts +293 -0
  326. package/src/scripts/ai_council/response_record.ts +50 -0
  327. package/src/scripts/ai_council/topology_bench_manifest.ts +860 -0
  328. package/src/scripts/ai_council/topology_vocabulary.ts +175 -0
  329. package/src/scripts/asset_delivery_ledger.ts +157 -0
  330. package/src/scripts/attest_artifact.ts +193 -0
  331. package/src/scripts/audit_auto_rules.ts +2 -1
  332. package/src/scripts/audit_command_surface.ts +1 -1
  333. package/src/scripts/audit_mcp_tools.ts +2 -2
  334. package/src/scripts/audit_skill_overlap_allowlist.json +6 -1
  335. package/src/scripts/audit_user_type_axis.ts +2 -1
  336. package/src/scripts/bench_ab_clone.ts +215 -10
  337. package/src/scripts/bench_ab_integrity.ts +119 -5
  338. package/src/scripts/bench_ab_v2_stats.ts +40 -213
  339. package/src/scripts/build_archive_index.ts +1 -1
  340. package/src/scripts/build_linear_digest.ts +1 -1
  341. package/src/scripts/build_proof.ts +11 -4
  342. package/src/scripts/build_rule_trigger_matrix.ts +1 -1
  343. package/src/scripts/build_source_digests.ts +157 -0
  344. package/src/scripts/cache_realization_report.ts +103 -0
  345. package/src/scripts/check_adr_frontmatter.ts +126 -0
  346. package/src/scripts/check_benchmark_report_fields.ts +207 -0
  347. package/src/scripts/check_beta_review_markers.ts +235 -6
  348. package/src/scripts/check_bundle_path_leakage.ts +34 -1
  349. package/src/scripts/check_claims.ts +576 -6
  350. package/src/scripts/check_composite_arming.ts +35 -0
  351. package/src/scripts/check_concern_admissions.ts +365 -0
  352. package/src/scripts/check_enforcement_coverage.ts +28 -0
  353. package/src/scripts/check_estate_count.ts +142 -1
  354. package/src/scripts/check_gate_reachability.ts +383 -0
  355. package/src/scripts/check_host_loadability.ts +104 -0
  356. package/src/scripts/check_loss_class_declared.ts +308 -0
  357. package/src/scripts/check_md_language.ts +15 -98
  358. package/src/scripts/check_memory_proposal.ts +18 -11
  359. package/src/scripts/check_no_currency_in_cost_surfaces.ts +225 -0
  360. package/src/scripts/check_no_external_sources.ts +281 -6
  361. package/src/scripts/check_pack_conformance_fixture.ts +11 -1
  362. package/src/scripts/check_pack_size.ts +59 -2
  363. package/src/scripts/check_preamble_payload_budget.ts +114 -3
  364. package/src/scripts/check_prefix_stable_mutation.ts +372 -0
  365. package/src/scripts/check_publish_surface.ts +10 -1
  366. package/src/scripts/check_references.ts +24 -1
  367. package/src/scripts/check_rule_evidence_declaration.ts +316 -0
  368. package/src/scripts/check_rule_projection_integrity.ts +14 -4
  369. package/src/scripts/check_score_contract.ts +26 -3
  370. package/src/scripts/check_static_parity.ts +285 -0
  371. package/src/scripts/check_supervision_claim_atomicity.ts +498 -0
  372. package/src/scripts/check_suppression_hygiene.ts +15 -0
  373. package/src/scripts/ci_settle.ts +39 -2
  374. package/src/scripts/code_graph/build.ts +60 -3
  375. package/src/scripts/code_graph/extract.ts +44 -0
  376. package/src/scripts/code_graph/types.ts +24 -0
  377. package/src/scripts/collector_daemon.ts +714 -0
  378. package/src/scripts/config_cost_report.ts +302 -0
  379. package/src/scripts/consumer_matrix.ts +74 -1
  380. package/src/scripts/cost/budget.mjs +10 -3
  381. package/src/scripts/cost/track.mjs +10 -3
  382. package/src/scripts/council_cli.ts +37 -54
  383. package/src/scripts/dispatch_r2_reviewer.ts +46 -5
  384. package/src/scripts/evolution_lab.ts +980 -0
  385. package/src/scripts/external_sources_denylist.json +5 -18
  386. package/src/scripts/extract_audit_patterns.ts +21 -0
  387. package/src/scripts/fold_intake.ts +12 -1
  388. package/src/scripts/gen_discovery_baseline.ts +1 -1
  389. package/src/scripts/generate_capabilities_index.ts +12 -5
  390. package/src/scripts/generate_capability_matrix.ts +3 -3
  391. package/src/scripts/generate_command_flows.ts +2 -2
  392. package/src/scripts/generate_cookbook.ts +2 -2
  393. package/src/scripts/generate_index.ts +8 -1
  394. package/src/scripts/generate_knowledge_index.ts +2 -1
  395. package/src/scripts/generate_ownership_matrix.ts +2 -1
  396. package/src/scripts/generate_pack_manifests.ts +10 -2
  397. package/src/scripts/generate_role_experiences_catalog.ts +2 -2
  398. package/src/scripts/generate_settings_reference.ts +1 -1
  399. package/src/scripts/git_authorization_hook.ts +22 -1
  400. package/src/scripts/grade_target_readiness.ts +48 -5
  401. package/src/scripts/hook_effect_doctor.ts +261 -0
  402. package/src/scripts/hook_manifest.json +1 -1
  403. package/src/scripts/hook_manifest.yaml +98 -11
  404. package/src/scripts/hooks/block_speaking_inbox_dir.ts +271 -0
  405. package/src/scripts/hooks/block_unauthorized_git.ts +18 -0
  406. package/src/scripts/hooks/concern_registry.ts +4 -0
  407. package/src/scripts/hooks/dispatch_hook.ts +32 -0
  408. package/src/scripts/hooks/journal_record_hook.ts +345 -0
  409. package/src/scripts/hooks/prepush_metadata_sources.sh +110 -0
  410. package/src/scripts/hooks/run_continuation_hook.ts +44 -9
  411. package/src/scripts/hooks/shims/install.sh +61 -0
  412. package/src/scripts/hooks/shims/php +101 -0
  413. package/src/scripts/hooks/suggestion_capture_hook.ts +10 -0
  414. package/src/scripts/hot_context_hook.ts +16 -0
  415. package/src/scripts/install-hooks.sh +20 -5
  416. package/src/scripts/install.ts +17 -35
  417. package/src/scripts/inventory_frontmatter.ts +2 -1
  418. package/src/scripts/lint_adr_sweep_routing.ts +12 -0
  419. package/src/scripts/lint_canonical_terms.ts +604 -0
  420. package/src/scripts/lint_composition_review.ts +619 -0
  421. package/src/scripts/lint_consolidation_lineage.ts +666 -0
  422. package/src/scripts/lint_eval_specs.ts +593 -0
  423. package/src/scripts/lint_hook_manifest.ts +19 -0
  424. package/src/scripts/lint_host_portability.ts +373 -0
  425. package/src/scripts/lint_judge_prompt_expectation.ts +27 -1
  426. package/src/scripts/lint_memory_twin_parity.ts +335 -0
  427. package/src/scripts/lint_metric_consumers.ts +355 -0
  428. package/src/scripts/lint_skill_descriptions.ts +18 -1
  429. package/src/scripts/lint_skill_frontmatter_safety.ts +58 -0
  430. package/src/scripts/lint_skill_link_reach.ts +252 -0
  431. package/src/scripts/lint_skill_trigger_corpus.ts +426 -23
  432. package/src/scripts/lint_ticket_buildable.ts +17 -1
  433. package/src/scripts/mcp_server/consumer_tool_catalog.json +1 -1
  434. package/src/scripts/mcp_server/tool_catalog_source.ts +1 -1
  435. package/src/scripts/measure_docs_dead_links.ts +348 -0
  436. package/src/scripts/measure_host_capture.ts +461 -0
  437. package/src/scripts/migration_status.ts +3 -3
  438. package/src/scripts/model_rule_injection.ts +60 -0
  439. package/src/scripts/orchestration_record.ts +14 -0
  440. package/src/scripts/preamble_byte_census.ts +139 -10
  441. package/src/scripts/prepack-check.mjs +4 -1
  442. package/src/scripts/probe_turnaround.ts +384 -0
  443. package/src/scripts/probe_unblocked_ask.ts +279 -0
  444. package/src/scripts/render_benchmark_md.ts +1 -1
  445. package/src/scripts/rule_activation_census.ts +70 -9
  446. package/src/scripts/rule_backlinks.ts +1 -1
  447. package/src/scripts/run_lifecycle_suite.ts +208 -0
  448. package/src/scripts/run_skill_evals.ts +40 -0
  449. package/src/scripts/schemas/delivery-manifest.schema.json +43 -0
  450. package/src/scripts/schemas/drafting-channels.schema.json +43 -0
  451. package/src/scripts/schemas/evals.schema.json +24 -1
  452. package/src/scripts/schemas/rule.schema.json +52 -1
  453. package/src/scripts/schemas/skill.schema.json +17 -2
  454. package/src/scripts/skill_overlap.ts +7 -1
  455. package/src/scripts/skill_tools/run_block_d_eval.ts +8 -1
  456. package/src/scripts/skill_tools/score_skill_relevance.ts +84 -3
  457. package/src/scripts/skill_tools/suggest_skill_for_task.ts +8 -1
  458. package/src/scripts/skill_usage_report.ts +2 -1
  459. package/src/scripts/stubs_due.ts +211 -0
  460. package/src/scripts/sweep_source_surfaces.ts +678 -0
  461. package/src/scripts/sync_pr_branch.ts +3 -3
  462. package/src/scripts/trigger_eval_grandfather.json +202 -222
  463. package/src/scripts/utilization_report.ts +22 -2
  464. package/src/server/schemas/settings.ts +5 -0
  465. package/src/templates/marketing-copy.yml +1 -1
  466. package/dist/ui/assets/index-Cxgy63kz.js +0 -26
@@ -0,0 +1,619 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * The authoring-search record — `composition_review` (road-to-composition-before-creation).
4
+ *
5
+ * The estate is 299 skills, 120 rules and 114 guidelines. Two gates measure its
6
+ * SIZE and three tools measure overlap AFTER the fact. None of them holds the
7
+ * authoring-time record: `artifact-drafting-protocol` Phase B mandates a
8
+ * four-surface overlap scan and ships `instruction-only`, so a skill authored
9
+ * without one and a skill authored after a thorough one are indiscernible in
10
+ * the tree.
11
+ *
12
+ * Measured 2026-08-27 over the twenty most recently added skills and rules
13
+ * (`agents/evidence/analysis/authoring-search-record-sample-2026-08-27.md`):
14
+ * **1 of 20** carried a machine-readable record, **9 of 20** carried a strict
15
+ * prose one, **18 of 20** carried something loose enough to count if you
16
+ * squinted. The kill criterion in that roadmap did not fire on either reading a
17
+ * two-seat council was willing to adopt.
18
+ *
19
+ * ## What this gate does, and the split that matters
20
+ *
21
+ * **HARD (exit 1) — a `composition_review` that is present and malformed.** A
22
+ * `candidate` that resolves to no artifact in the tree, a `none` candidate
23
+ * paired with a disposition other than `none_found` (or the reverse), or a
24
+ * duplicate candidate inside one block. A record pointing at an incumbent that
25
+ * does not exist is worse than no record: the reference is checkable and wrong.
26
+ *
27
+ * **What that certifies is REFERENTIAL CONSISTENCY, never that a search
28
+ * happened.** Narrowed after an independent review, where both seats made the
29
+ * point independently: an author can name any existing artifact, write
30
+ * `create_separate` and a sixteen-character rationale, and pass every check
31
+ * here. The earlier wording said such a record "reads as evidence of a search
32
+ * that cannot have happened", which is a claim about the search — and this gate
33
+ * has none to make. It buys that a record cannot point at nothing; whether it
34
+ * points at the RIGHT thing is a reviewer's judgement, and the roadmap's own
35
+ * Risk 1 is that the field becomes a pro-forma line.
36
+ *
37
+ * **ADVISORY (never fails) — an artifact ADDED relative to a base ref carrying
38
+ * no record at all.** Reported by count and by path, exit 0. Flipping this to a
39
+ * block needs a false-positive rate from at least one release of advisory
40
+ * operation, which this roadmap does not have and deliberately does not
41
+ * pre-authorise: a measurement is not a gate.
42
+ *
43
+ * ## What it cannot do, stated rather than implied
44
+ *
45
+ * It checks that a delta was WRITTEN. It cannot check that the search happened,
46
+ * that the rationale is true, or that the incumbent named is the nearest one.
47
+ * The council that scoped it said so in both seats, and the roadmap's own Risk 1
48
+ * is that the field becomes a pro-forma line. Nothing here refutes that risk;
49
+ * the advisory-first sequencing is what is supposed to measure it.
50
+ *
51
+ * ## Why the corpus is the scan scope and the diff is only the finding
52
+ *
53
+ * A diff-scoped gate legitimately scans 0 on most runs and can carry no honest
54
+ * `min_scanned` floor — `lint_evidence_artifacts` declines registration in
55
+ * `gate-coverage.yml` for exactly that reason. This gate walks the whole
56
+ * skill+rule corpus for its structural half, so its floor is real, and narrows
57
+ * to the diff only for the advisory half.
58
+ *
59
+ * The addition set unions `git ls-files` with `git ls-files --others
60
+ * --exclude-standard`: a create-only canary plants an UNTRACKED file, and a
61
+ * plain `ls-files` cannot see it, so the gate would report green on its own
62
+ * canary.
63
+ *
64
+ * Exit codes: 0 = pass, 1 = policy violation (a dead scan scope included — a
65
+ * gate that read nothing has not passed), 2 = internal error. `scanned:` is
66
+ * emitted on every path.
67
+ */
68
+
69
+ import { execFileSync } from 'node:child_process';
70
+ import * as fs from 'node:fs';
71
+ import * as os from 'node:os';
72
+ import * as path from 'node:path';
73
+ import { fileURLToPath, pathToFileURL } from 'node:url';
74
+
75
+ import { GateLedger } from './_lib/gate_ledger.js';
76
+ import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
77
+ import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
78
+
79
+ const GATE = 'lint_composition_review';
80
+
81
+ /** The four values `composition_review[].disposition` may take. */
82
+ export const COMPOSITION_DISPOSITIONS = [
83
+ 'extend_incumbent',
84
+ 'compose_with_incumbent',
85
+ 'create_separate',
86
+ 'none_found',
87
+ ] as const;
88
+ export type CompositionDisposition = (typeof COMPOSITION_DISPOSITIONS)[number];
89
+
90
+ /** The candidate value that records "searched, found no credible incumbent". */
91
+ export const NO_INCUMBENT = 'none';
92
+
93
+ export interface Violation {
94
+ kind: string;
95
+ file: string;
96
+ detail: string;
97
+ }
98
+
99
+ export interface Advisory {
100
+ file: string;
101
+ }
102
+
103
+ /** Thrown when a diff-scoped read was requested and git could not answer it. */
104
+ export class GitScopeError extends Error {
105
+ constructor(command: string, cause: unknown) {
106
+ super(
107
+ `git ${command} failed, so the added-artifact set is UNKNOWN rather than empty — a ` +
108
+ 'diff-scoped reading that silently reports zero is indistinguishable from a clean ' +
109
+ `one. Underlying error: ${String(cause)}`,
110
+ );
111
+ this.name = 'GitScopeError';
112
+ }
113
+ }
114
+
115
+ /** Sentinel for a field whose value was a YAML block scalar this parser refuses. */
116
+ export const BLOCK_SCALAR = '\u0000block-scalar';
117
+
118
+ /** `skill:foo`, `rule:bar`, `command:baz`, `guideline:a/b`, or the literal `none`. */
119
+ // Path segments are non-empty, so `guideline:/foo`, `guideline:foo/` and
120
+ // `guideline:foo//bar` are rejected rather than accepted-then-unresolvable.
121
+ // Found by review: the previous class admitted all three.
122
+ const CANDIDATE_RE = /^(skill|rule|command|guideline):([A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*)$/;
123
+
124
+ function _frontmatter(text: string): string | null {
125
+ if (!text.startsWith('---')) return null;
126
+ const end = text.indexOf('\n---', 3);
127
+ return end === -1 ? null : text.slice(text.indexOf('\n') + 1, end + 1);
128
+ }
129
+
130
+ export interface CompositionEntry {
131
+ candidate: string;
132
+ disposition: string;
133
+ rationale: string;
134
+ }
135
+
136
+ /**
137
+ * Read `composition_review` out of a frontmatter block.
138
+ *
139
+ * A deliberately small YAML reader rather than a dependency: the block shape is
140
+ * pinned by the JSON schema (`skill.schema.json` / `rule.schema.json`), which is
141
+ * what `validate_frontmatter` enforces. This parser exists to find the entries,
142
+ * not to re-validate their types — a malformed block that the schema rejects is
143
+ * that gate's finding, not this one's, and duplicating the check would produce
144
+ * two error messages for one defect.
145
+ *
146
+ * @returns `null` when the artifact carries no block at all.
147
+ */
148
+ export function parseCompositionReview(text: string): CompositionEntry[] | null {
149
+ const fm = _frontmatter(text);
150
+ if (fm === null || !/^composition_review:/m.test(fm)) return null;
151
+
152
+ const lines = fm.split('\n');
153
+ const start = lines.findIndex((l) => /^composition_review:/.test(l));
154
+ const entries: CompositionEntry[] = [];
155
+ let cur: Partial<CompositionEntry> | null = null;
156
+
157
+ for (let i = start + 1; i < lines.length; i++) {
158
+ const line = lines[i] as string;
159
+ if (/^\S/.test(line)) break; // dedented back to a sibling key
160
+ const item = /^\s*-\s+(\w+):\s*(.*)$/.exec(line);
161
+ if (item) {
162
+ if (cur !== null) entries.push(cur as CompositionEntry);
163
+ cur = {};
164
+ cur[item[1] as keyof CompositionEntry] = _unquote(item[2] as string);
165
+ continue;
166
+ }
167
+ const field = /^\s+(\w+):\s*(.*)$/.exec(line);
168
+ if (field && cur !== null) {
169
+ const raw = (field[2] as string).trim();
170
+ // A YAML block scalar (`rationale: |`) would otherwise capture the
171
+ // `|` itself and yield a one-character value — valid YAML read
172
+ // wrongly, which is worse than a parse error because it looks like
173
+ // data. Found by review. Marked, so the caller reports it.
174
+ cur[field[1] as keyof CompositionEntry] = /^[|>][-+0-9]*$/.test(raw)
175
+ ? BLOCK_SCALAR
176
+ : _unquote(field[2] as string);
177
+ }
178
+ }
179
+ if (cur !== null) entries.push(cur as CompositionEntry);
180
+ return entries;
181
+ }
182
+
183
+ function _unquote(s: string): string {
184
+ const t = s.trim();
185
+ if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
186
+ return t.slice(1, -1);
187
+ }
188
+ return t;
189
+ }
190
+
191
+ /**
192
+ * Every artifact id the tree defines, across all four kinds.
193
+ *
194
+ * **All four, and that is a correction.** The first version resolved only
195
+ * `skill:` and `rule:`, and carried a comment saying a `command:` or
196
+ * `guideline:` id "lives in a tree this gate does not walk" — while this
197
+ * module's own contract and BOTH schema descriptions said a lint checks that
198
+ * `candidate` resolves. An independent cross-model review found the
199
+ * contradiction: `candidate: command:this-does-not-exist` passed every check.
200
+ * Two ways to close it — narrow the claim, or walk the trees. The trees are
201
+ * right here, so the claim stands and the code now earns it.
202
+ */
203
+ export function artefactIds(repo: string): Set<string> {
204
+ const ids = new Set<string>();
205
+ const skills = path.join(repo, 'src', 'skills');
206
+ if (fs.existsSync(skills)) {
207
+ for (const e of fs.readdirSync(skills, { withFileTypes: true })) {
208
+ if (e.isDirectory() && fs.existsSync(path.join(skills, e.name, 'SKILL.md'))) {
209
+ ids.add(`skill:${e.name}`);
210
+ }
211
+ }
212
+ }
213
+ const rules = path.join(repo, 'src', 'rules');
214
+ if (fs.existsSync(rules)) {
215
+ for (const e of fs.readdirSync(rules, { withFileTypes: true })) {
216
+ if (e.isFile() && e.name.endsWith('.md')) ids.add(`rule:${e.name.slice(0, -3)}`);
217
+ }
218
+ }
219
+
220
+ // Commands are `src/domains/<pack>/<path...>/command.md`, addressed by the
221
+ // path BELOW the pack — `refine-ticket`, `roadmap/create`. The pack is a
222
+ // distribution detail, not part of the name a rule or skill cites.
223
+ const domains = path.join(repo, 'src', 'domains');
224
+ for (const pack of _dirs(domains)) {
225
+ const walk = (rel: string): void => {
226
+ for (const e of fs.readdirSync(path.join(domains, pack, rel), { withFileTypes: true })) {
227
+ if (e.isDirectory()) walk(path.join(rel, e.name));
228
+ else if (e.name === 'command.md' && rel !== '') {
229
+ ids.add(`command:${rel.split(path.sep).join('/')}`);
230
+ }
231
+ }
232
+ };
233
+ try {
234
+ walk('');
235
+ } catch {
236
+ // An unreadable pack resolves no command — the conservative
237
+ // direction, since the candidate then fails rather than passing.
238
+ }
239
+ }
240
+
241
+ // Guidelines are `docs/guidelines/<path>.md`, addressed without the
242
+ // extension — `code-clarity`, `agent-infra/rule-type-governance`.
243
+ const guidelines = path.join(repo, 'docs', 'guidelines');
244
+ const walkG = (rel: string): void => {
245
+ for (const e of fs.readdirSync(path.join(guidelines, rel), { withFileTypes: true })) {
246
+ if (e.isDirectory()) walkG(path.join(rel, e.name));
247
+ else if (e.name.endsWith('.md')) {
248
+ ids.add(`guideline:${path.join(rel, e.name.slice(0, -3)).split(path.sep).join('/')}`);
249
+ }
250
+ }
251
+ };
252
+ try {
253
+ walkG('');
254
+ } catch {
255
+ // Same direction: an unreadable tree resolves nothing, never everything.
256
+ }
257
+
258
+ return ids;
259
+ }
260
+
261
+ function _dirs(root: string): string[] {
262
+ try {
263
+ return fs.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
264
+ } catch {
265
+ return [];
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Structural check on one artifact's record. Returns [] when it carries none —
271
+ * absence is the ADVISORY half and is never a violation here.
272
+ */
273
+ export function checkArtefact(rel: string, text: string, known: ReadonlySet<string>): Violation[] {
274
+ const entries = parseCompositionReview(text);
275
+ if (entries === null) return [];
276
+
277
+ // `composition_review: []` is PRESENT and says nothing — neither of the two
278
+ // states this gate distinguishes. The schema's `minItems: 1` rejects it, but
279
+ // a gate whose comments claim "present and malformed → exit 1" has to deliver
280
+ // that itself rather than rely on another gate having run first. Found by
281
+ // review: the parser returned `[]` and this returned no violation.
282
+ if (entries.length === 0) {
283
+ return [
284
+ {
285
+ kind: 'empty-record',
286
+ file: rel,
287
+ detail:
288
+ 'a `composition_review:` block is present but has no entries — record at least one ' +
289
+ 'candidate, or omit the block entirely. Absence is advisory; an empty claim is not.',
290
+ },
291
+ ];
292
+ }
293
+
294
+ const out: Violation[] = [];
295
+ const seen = new Set<string>();
296
+ for (const [i, e] of entries.entries()) {
297
+ const where = `entry ${String(i + 1)}`;
298
+ const cand = (e.candidate ?? '').trim();
299
+ const disp = (e.disposition ?? '').trim();
300
+
301
+ if (cand === '') {
302
+ out.push({ kind: 'missing-candidate', file: rel, detail: `${where}: no \`candidate\`.` });
303
+ continue;
304
+ }
305
+ const blockScalar = [e.candidate, e.disposition, e.rationale].filter((v) => v === BLOCK_SCALAR);
306
+ if (blockScalar.length > 0) {
307
+ out.push({
308
+ kind: 'block-scalar-field',
309
+ file: rel,
310
+ detail: `${where}: a field uses a YAML block scalar (\`|\` / \`>\`). This gate reads one line per field, so a block scalar would be read as its marker character rather than its text — write the value inline.`,
311
+ });
312
+ continue;
313
+ }
314
+ if (seen.has(cand)) {
315
+ out.push({
316
+ kind: 'duplicate-candidate',
317
+ file: rel,
318
+ detail: `${where}: \`${cand}\` is recorded twice — one candidate, one disposition.`,
319
+ });
320
+ }
321
+ seen.add(cand);
322
+
323
+ if (!(COMPOSITION_DISPOSITIONS as readonly string[]).includes(disp)) {
324
+ out.push({
325
+ kind: 'unknown-disposition',
326
+ file: rel,
327
+ detail: `${where}: disposition \`${disp}\` is not one of ${COMPOSITION_DISPOSITIONS.join(', ')}.`,
328
+ });
329
+ }
330
+
331
+ // `none` and `none_found` are the same fact stated twice; either one
332
+ // without the other is a record whose meaning cannot be recovered.
333
+ if (cand === NO_INCUMBENT && disp !== 'none_found') {
334
+ out.push({
335
+ kind: 'none-mismatch',
336
+ file: rel,
337
+ detail: `${where}: candidate \`none\` requires disposition \`none_found\`, got \`${disp}\`.`,
338
+ });
339
+ } else if (cand !== NO_INCUMBENT && disp === 'none_found') {
340
+ out.push({
341
+ kind: 'none-mismatch',
342
+ file: rel,
343
+ detail: `${where}: disposition \`none_found\` requires candidate \`none\`, got \`${cand}\`.`,
344
+ });
345
+ }
346
+
347
+ if (cand !== NO_INCUMBENT) {
348
+ const m = CANDIDATE_RE.exec(cand);
349
+ if (m === null) {
350
+ out.push({
351
+ kind: 'malformed-candidate',
352
+ file: rel,
353
+ detail: `${where}: \`${cand}\` is not \`skill:<name>\` / \`rule:<name>\` / \`command:<name>\` / \`guideline:<path>\` / \`none\`.`,
354
+ });
355
+ } else if (!known.has(cand)) {
356
+ // All four kinds, since `artefactIds` now walks all four. The
357
+ // carve-out that used to sit here let `command:` and
358
+ // `guideline:` ids through unchecked while the contract claimed
359
+ // otherwise — found by review, closed rather than documented.
360
+ out.push({
361
+ kind: 'unresolvable-candidate',
362
+ file: rel,
363
+ detail: `${where}: \`${cand}\` names no artifact in the tree. The reference is checkable and wrong — which is all this gate can tell you; it cannot tell you whether a search happened.`,
364
+ });
365
+ }
366
+ }
367
+ }
368
+ return out;
369
+ }
370
+
371
+ function _corpus(repo: string): string[] {
372
+ const out: string[] = [];
373
+ const skills = path.join(repo, 'src', 'skills');
374
+ if (fs.existsSync(skills)) {
375
+ for (const e of fs.readdirSync(skills, { withFileTypes: true })) {
376
+ const f = path.join('src', 'skills', e.name, 'SKILL.md');
377
+ if (e.isDirectory() && fs.existsSync(path.join(repo, f))) out.push(f);
378
+ }
379
+ }
380
+ const rules = path.join(repo, 'src', 'rules');
381
+ if (fs.existsSync(rules)) {
382
+ for (const e of fs.readdirSync(rules, { withFileTypes: true })) {
383
+ if (e.isFile() && e.name.endsWith('.md')) out.push(path.join('src', 'rules', e.name));
384
+ }
385
+ }
386
+ return out.sort();
387
+ }
388
+
389
+ /**
390
+ * Artifacts ADDED relative to `baseRef`, unioned with untracked files.
391
+ *
392
+ * The union is load-bearing: a create-only canary plants an untracked artifact,
393
+ * and `git diff --name-only` against a base ref cannot see it, so the gate would
394
+ * report green on its own canary and the advisory half would be blind exactly
395
+ * where it is being tested.
396
+ */
397
+ export function addedArtefacts(repo: string, baseRef: string, corpus: readonly string[]): string[] {
398
+ const inCorpus = new Set(corpus);
399
+ const added = new Set<string>();
400
+ const run = (args: readonly string[]): string[] => {
401
+ try {
402
+ return execFileSync('git', args, { cwd: repo, encoding: 'utf-8' })
403
+ .split('\n')
404
+ .map((s) => s.trim())
405
+ .filter((s) => s !== '');
406
+ } catch (exc) {
407
+ throw new GitScopeError(args.join(' '), exc);
408
+ }
409
+ };
410
+ for (const f of run(['diff', '--name-only', '--diff-filter=A', `${baseRef}...HEAD`])) {
411
+ if (inCorpus.has(f)) added.add(f);
412
+ }
413
+ for (const f of run(['ls-files', '--others', '--exclude-standard'])) {
414
+ if (inCorpus.has(f)) added.add(f);
415
+ }
416
+ return [...added].sort();
417
+ }
418
+
419
+ export function main(argv?: readonly string[]): number {
420
+ const args = argv ?? process.argv.slice(2);
421
+ let repo = process.cwd();
422
+ let baseRef = '';
423
+ let quiet = false;
424
+ for (let i = 0; i < args.length; i++) {
425
+ const a = args[i] as string;
426
+ if (a === '--repo' || a === '--root') repo = args[++i] as string;
427
+ else if (a === '--base-ref') baseRef = args[++i] as string;
428
+ else if (a === '--quiet') quiet = true;
429
+ else if (a === '--self-test') return selfTest();
430
+ else if (a === '-h' || a === '--help') {
431
+ process.stdout.write(
432
+ 'usage: lint_composition_review [--repo PATH] [--base-ref REF] [--quiet] [--self-test]\n',
433
+ );
434
+ process.stdout.write('scanned: 0\n');
435
+ return 0;
436
+ }
437
+ }
438
+
439
+ const corpus = _corpus(repo);
440
+ try {
441
+ reportScanned({
442
+ gate: GATE,
443
+ scanned: corpus.length,
444
+ units: 'skill/rule artifact(s)',
445
+ roots: ['src/skills', 'src/rules'],
446
+ });
447
+ } catch (exc) {
448
+ // A dead scope is a POLICY violation, never an internal error: exit 2 is
449
+ // warn-and-allow at every call site, so a moved corpus root would
450
+ // silently degrade this gate to advisory on its HARD half too.
451
+ process.stderr.write(`❌ ${exc instanceof DeadScopeError ? exc.message : String(exc)}\n`);
452
+ return 1;
453
+ }
454
+
455
+ const known = artefactIds(repo);
456
+ const ledger = new GateLedger(GATE);
457
+ ledger.plan(corpus);
458
+ const violations: Violation[] = [];
459
+ const advisories: Advisory[] = [];
460
+ const added = baseRef === '' ? [] : new Set(addedArtefacts(repo, baseRef, corpus));
461
+
462
+ let scanned = 0;
463
+ try {
464
+ for (const rel of corpus) {
465
+ const text = fs.readFileSync(path.join(repo, rel), 'utf-8');
466
+ scanned += 1;
467
+ const found = checkArtefact(rel, text, known);
468
+ if (found.length > 0) ledger.fail(rel, `${String(found.length)} record violation(s)`);
469
+ else ledger.complete(rel);
470
+ violations.push(...found);
471
+ if (added !== null && added instanceof Set && added.has(rel) && parseCompositionReview(text) === null) {
472
+ advisories.push({ file: rel });
473
+ }
474
+ }
475
+ } catch (exc) {
476
+ process.stderr.write(
477
+ `❌ ${GATE}: internal error after ${String(scanned)} artifact(s): ${String(exc)}\n`,
478
+ );
479
+ return 2;
480
+ }
481
+ ledger.report();
482
+
483
+ if (!quiet) {
484
+ for (const v of violations) process.stderr.write(`❌ ${v.file} — ${v.detail}\n`);
485
+ for (const a of advisories) {
486
+ process.stdout.write(
487
+ `⚠️ ${a.file}: added without a \`composition_review\` record — advisory, not a block.\n`,
488
+ );
489
+ }
490
+ }
491
+
492
+ if (violations.length > 0) {
493
+ process.stderr.write(
494
+ `❌ ${GATE}: ${String(violations.length)} malformed record(s) across ${String(corpus.length)} artifact(s).\n`,
495
+ );
496
+ return 1;
497
+ }
498
+ process.stdout.write(
499
+ `✅ ${GATE}: ${String(corpus.length)} artifact(s) — records well-formed; ` +
500
+ `${String(advisories.length)} addition(s) without one (advisory).\n`,
501
+ );
502
+ return 0;
503
+ }
504
+
505
+ // ── self-test ──────────────────────────────────────────────────────────────
506
+
507
+ function _fixture(files: Record<string, string>): string {
508
+ const d = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'lcr-'));
509
+ for (const [rel, body] of Object.entries(files)) {
510
+ const f = path.join(d, rel);
511
+ fs.mkdirSync(path.dirname(f), { recursive: true });
512
+ fs.writeFileSync(f, body);
513
+ }
514
+ return d;
515
+ }
516
+
517
+ function _fm(block: string): string {
518
+ return `---\nname: probe\n${block}---\n\n# probe\n`;
519
+ }
520
+
521
+ function selfTest(): number {
522
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
523
+ const rel = 'src/scripts/lint_composition_review.ts';
524
+ const run = (files: Record<string, string>): number =>
525
+ runGateCli(repoRoot, rel, ['--quiet'], _fixture(files));
526
+
527
+ const incumbent = { 'src/rules/incumbent.md': _fm('') };
528
+
529
+ return runSelfTest({
530
+ gate: GATE,
531
+ minCases: 6,
532
+ minRejectCases: 4,
533
+ cases: [
534
+ {
535
+ name: 'a well-formed record naming a real incumbent is accepted',
536
+ expect: 'accept',
537
+ run: () =>
538
+ run({
539
+ ...incumbent,
540
+ 'src/rules/probe.md': _fm(
541
+ 'composition_review:\n - candidate: rule:incumbent\n disposition: create_separate\n rationale: the incumbent cannot express this case at all\n',
542
+ ),
543
+ }),
544
+ },
545
+ {
546
+ name: 'candidate `none` with `none_found` is accepted — a search that found nothing is a record',
547
+ expect: 'accept',
548
+ run: () =>
549
+ run({
550
+ 'src/rules/probe.md': _fm(
551
+ 'composition_review:\n - candidate: none\n disposition: none_found\n rationale: no artifact in the tree covers this surface\n',
552
+ ),
553
+ }),
554
+ },
555
+ {
556
+ name: 'an artifact with NO record is accepted — absence is advisory, never a block',
557
+ expect: 'accept',
558
+ run: () => run({ 'src/rules/probe.md': _fm('') }),
559
+ },
560
+ {
561
+ name: 'a candidate naming no artifact in the tree is REJECTED',
562
+ expect: 'reject',
563
+ run: () =>
564
+ run({
565
+ 'src/rules/probe.md': _fm(
566
+ 'composition_review:\n - candidate: rule:does-not-exist\n disposition: create_separate\n rationale: this incumbent was never in the tree at all\n',
567
+ ),
568
+ }),
569
+ },
570
+ {
571
+ name: '`none` paired with a non-`none_found` disposition is REJECTED',
572
+ expect: 'reject',
573
+ run: () =>
574
+ run({
575
+ 'src/rules/probe.md': _fm(
576
+ 'composition_review:\n - candidate: none\n disposition: extend_incumbent\n rationale: cannot extend an incumbent that was never found\n',
577
+ ),
578
+ }),
579
+ },
580
+ {
581
+ name: '`none_found` paired with a real candidate is REJECTED — the reverse direction',
582
+ expect: 'reject',
583
+ run: () =>
584
+ run({
585
+ ...incumbent,
586
+ 'src/rules/probe.md': _fm(
587
+ 'composition_review:\n - candidate: rule:incumbent\n disposition: none_found\n rationale: an incumbent was named and also declared absent\n',
588
+ ),
589
+ }),
590
+ },
591
+ {
592
+ name: 'a disposition outside the enum is REJECTED',
593
+ expect: 'reject',
594
+ run: () =>
595
+ run({
596
+ ...incumbent,
597
+ 'src/rules/probe.md': _fm(
598
+ 'composition_review:\n - candidate: rule:incumbent\n disposition: superseded\n rationale: borrowing a value from another vocabulary entirely\n',
599
+ ),
600
+ }),
601
+ },
602
+ {
603
+ name: 'the same candidate recorded twice is REJECTED',
604
+ expect: 'reject',
605
+ run: () =>
606
+ run({
607
+ ...incumbent,
608
+ 'src/rules/probe.md': _fm(
609
+ 'composition_review:\n - candidate: rule:incumbent\n disposition: create_separate\n rationale: the first of two contradictory dispositions\n - candidate: rule:incumbent\n disposition: extend_incumbent\n rationale: the second of two contradictory dispositions\n',
610
+ ),
611
+ }),
612
+ },
613
+ ],
614
+ });
615
+ }
616
+
617
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
618
+ process.exit(main());
619
+ }