@event4u/agent-config 14.12.0 → 14.14.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 (521) 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 +118 -155
  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 +145 -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-comment-discipline.md +6 -0
  30. package/dist/agent-src/rules/code-provenance.md +4 -0
  31. package/dist/agent-src/rules/design-fidelity.md +1 -1
  32. package/dist/agent-src/rules/domain-safety-pii.md +4 -0
  33. package/dist/agent-src/rules/domain-safety-retention.md +4 -0
  34. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  35. package/dist/agent-src/rules/external-code-graph-interop.md +14 -12
  36. package/dist/agent-src/rules/improve-before-implement.md +6 -8
  37. package/dist/agent-src/rules/laravel-routing.md +1 -1
  38. package/dist/agent-src/rules/lethal-trifecta-guard.md +4 -0
  39. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  40. package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
  41. package/dist/agent-src/rules/source-confidentiality.md +12 -34
  42. package/dist/agent-src/rules/symfony-routing.md +1 -1
  43. package/dist/agent-src/rules/token-efficiency.md +14 -0
  44. package/dist/agent-src/rules/tool-safety.md +4 -0
  45. package/dist/agent-src/rules/untrusted-input-defense.md +4 -0
  46. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +252 -8
  47. package/dist/agent-src/scripts/guarded_baseline.ts +343 -0
  48. package/dist/agent-src/scripts/roadmap_set_step.ts +253 -0
  49. package/dist/agent-src/scripts/stub_queue.ts +82 -0
  50. package/dist/agent-src/scripts/stubs_due.ts +516 -0
  51. package/dist/agent-src/scripts/ticket_bundles.ts +84 -0
  52. package/dist/agent-src/scripts/update_roadmap_progress.ts +16 -12
  53. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +17 -4
  54. package/dist/agent-src/skills/brand-audit/SKILL.md +33 -0
  55. package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -0
  56. package/dist/agent-src/skills/database/SKILL.md +8 -0
  57. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +27 -0
  58. package/dist/agent-src/skills/design-review/SKILL.md +1 -1
  59. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  60. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +17 -1
  61. package/dist/agent-src/skills/feature-planning/SKILL.md +9 -2
  62. package/dist/agent-src/skills/frontend-render-security/SKILL.md +44 -0
  63. package/dist/agent-src/skills/git-workflow/SKILL.md +1 -1
  64. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +38 -4
  65. package/dist/agent-src/skills/judge-artifact-completeness/rubrics/ticket-quality-score.json +2 -2
  66. package/dist/agent-src/skills/laravel/SKILL.md +35 -0
  67. package/dist/agent-src/skills/laravel-migration/SKILL.md +57 -3
  68. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +26 -0
  69. package/dist/agent-src/skills/playbook-authoring/SKILL.md +28 -1
  70. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  71. package/dist/agent-src/skills/skill-writing/references/procedure.md +5 -5
  72. package/dist/agent-src/skills/sql-writing/SKILL.md +7 -1
  73. package/dist/agent-src/skills/standards-from-config/SKILL.md +27 -4
  74. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +11 -4
  75. package/dist/agent-src/skills/symfony-workflow/SKILL.md +35 -0
  76. package/dist/agent-src/skills/test-case-discovery/SKILL.md +85 -0
  77. package/dist/agent-src/skills/test-driven-development/SKILL.md +33 -9
  78. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  79. package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +37 -0
  80. package/dist/agent-src/skills/ui-component-architect/SKILL.md +45 -2
  81. package/dist/agent-src/skills/upstream-contribute/SKILL.md +38 -1
  82. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +9 -4
  83. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  84. package/dist/agent-src/templates/roadmaps.md +97 -3
  85. package/dist/agent-src/templates/scripts/check_memory.ts +258 -17
  86. package/dist/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
  87. package/dist/agent-src/templates/scripts/memory_lookup.ts +86 -0
  88. package/dist/agent-src/templates/scripts/memory_signal.ts +44 -0
  89. package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
  90. package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
  91. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
  92. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
  93. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
  94. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
  95. package/dist/cli/commands/uiAudit.js +124 -5
  96. package/dist/cli/registry.js +2 -0
  97. package/dist/cli-delegate/{chunk-HYWSRNUX.js → chunk-2DK2VJWG.js} +3 -3
  98. package/dist/cli-delegate/{chunk-JCW7ZLHK.js → chunk-4FS4WRXP.js} +5 -0
  99. package/dist/cli-delegate/{chunk-OMIVVCG2.js → chunk-A4QJ6ENT.js} +24 -0
  100. package/dist/cli-delegate/{chunk-JJA2QEWA.js → chunk-AKJGUQNO.js} +466 -285
  101. package/dist/cli-delegate/{chunk-E46KPN6K.js → chunk-BTWUYDGN.js} +424 -363
  102. package/dist/cli-delegate/{chunk-K4FFODTS.js → chunk-ILMUPLGW.js} +1227 -316
  103. package/dist/cli-delegate/{chunk-FF7WAFMR.js → chunk-KPBWFOPZ.js} +5 -1
  104. package/dist/cli-delegate/cmd_adr_effective.js +102 -0
  105. package/dist/cli-delegate/cmd_conformance.js +80 -11
  106. package/dist/cli-delegate/cmd_doctor.js +3 -3
  107. package/dist/cli-delegate/cmd_export.js +3 -2
  108. package/dist/cli-delegate/cmd_fleet.js +2 -2
  109. package/dist/cli-delegate/cmd_handoff.js +1 -1
  110. package/dist/cli-delegate/cmd_preflight.js +2 -2
  111. package/dist/cli-delegate/cmd_prune.js +3 -2
  112. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  113. package/dist/cli-delegate/cmd_settings_migrate.js +3 -2
  114. package/dist/cli-delegate/cmd_settings_set.js +2 -2
  115. package/dist/cli-delegate/cmd_sync.js +3 -2
  116. package/dist/cli-delegate/cmd_uninstall.js +3 -2
  117. package/dist/cli-delegate/cmd_validate.js +3 -2
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +58 -58
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +1 -1
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +2 -2
  125. package/dist/discovery/trust-report.md +1 -1
  126. package/dist/discovery/workspaces.json +2 -2
  127. package/dist/hooks/dispatch.js +2900 -1049
  128. package/dist/install/claudeRuleRewrite.js +254 -0
  129. package/dist/install/detect.js +29 -16
  130. package/dist/install/detect_php_shape.js +159 -0
  131. package/dist/install/install.mjs +634 -428
  132. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  133. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
  134. package/dist/mcp/registry-manifest.json +3 -3
  135. package/dist/mcp/server.json +3 -3
  136. package/dist/mcp/server.mjs +4895 -2627
  137. package/dist/server/schemas/settings.js +3 -0
  138. package/dist/ui/assets/index-D0jaCRPM.js +29 -0
  139. package/dist/ui/index.html +1 -1
  140. package/docs/MIGRATION.md +1 -1
  141. package/docs/THIRD-PARTY-NOTICES.md +25 -0
  142. package/docs/contracts/provider-lifecycle.md +1 -1
  143. package/docs/contracts/settings-classes.md +10 -4
  144. package/docs/guidelines/agent-infra/5w2h-analysis.md +3 -3
  145. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -0
  146. package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +58 -3
  147. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +2 -2
  148. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +3 -3
  149. package/docs/guidelines/agent-infra/break-glass-usage.md +7 -7
  150. package/docs/guidelines/agent-infra/carve-out-predicates.md +3 -3
  151. package/docs/guidelines/agent-infra/comparison-matrix.md +1 -1
  152. package/docs/guidelines/agent-infra/critical-thinking.md +4 -4
  153. package/docs/guidelines/agent-infra/direct-answers-demos.md +2 -2
  154. package/docs/guidelines/agent-infra/domain-eval-anti-pattern.md +1 -1
  155. package/docs/guidelines/agent-infra/engineering-memory-data-format.md +3 -3
  156. package/docs/guidelines/agent-infra/existence-question-verdicts.md +80 -0
  157. package/docs/guidelines/agent-infra/first-principles.md +2 -2
  158. package/docs/guidelines/agent-infra/framework-neutrality-patterns.md +2 -2
  159. package/docs/guidelines/agent-infra/guarded-baseline.md +39 -0
  160. package/docs/guidelines/agent-infra/inversion-thinking.md +5 -5
  161. package/docs/guidelines/agent-infra/language-and-tone-examples.md +1 -1
  162. package/docs/guidelines/agent-infra/layered-settings.md +10 -10
  163. package/docs/guidelines/agent-infra/memory-access.md +1 -1
  164. package/docs/guidelines/agent-infra/mental-models.md +3 -3
  165. package/docs/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
  166. package/docs/guidelines/agent-infra/review-routing-data-format.md +7 -7
  167. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +13 -7
  168. package/docs/guidelines/agent-infra/role-contracts.md +2 -2
  169. package/docs/guidelines/agent-infra/role-mode-router.md +3 -3
  170. package/docs/guidelines/agent-infra/rule-type-governance.md +2 -2
  171. package/docs/guidelines/agent-infra/scqa-framework.md +5 -5
  172. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +6 -6
  173. package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +2 -2
  174. package/docs/guidelines/agent-infra/six-hats.md +3 -3
  175. package/docs/guidelines/agent-infra/skill-quality-checklist.md +6 -6
  176. package/docs/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
  177. package/docs/guidelines/agent-infra/systems-thinking.md +1 -1
  178. package/docs/guidelines/agent-infra/verify-before-complete-demos.md +2 -2
  179. package/docs/guidelines/augment-portability-patterns.md +6 -4
  180. package/docs/guidelines/code-clarity.md +18 -0
  181. package/docs/guidelines/component-oriented-and-oop-development.md +3 -2
  182. package/docs/guidelines/cross-role-handoff.md +2 -2
  183. package/docs/guidelines/design-fidelity-mechanics.md +1 -1
  184. package/docs/guidelines/docs/readme-size-and-splitting.md +3 -3
  185. package/docs/guidelines/php/database.md +90 -7
  186. package/docs/guidelines/prompt-templates.md +7 -7
  187. package/llms.txt +2 -2
  188. package/package.json +5 -2
  189. package/src/agent-src/scripts/archive_completed_roadmaps.ts +252 -8
  190. package/src/agent-src/scripts/guarded_baseline.ts +343 -0
  191. package/src/agent-src/scripts/roadmap_set_step.ts +253 -0
  192. package/src/agent-src/scripts/stub_queue.ts +82 -0
  193. package/src/agent-src/scripts/stubs_due.ts +516 -0
  194. package/src/agent-src/scripts/ticket_bundles.ts +84 -0
  195. package/src/agent-src/scripts/update_roadmap_progress.ts +16 -12
  196. package/src/agent-src/templates/scripts/check_memory.ts +258 -17
  197. package/src/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
  198. package/src/agent-src/templates/scripts/memory_lookup.ts +86 -0
  199. package/src/agent-src/templates/scripts/memory_signal.ts +44 -0
  200. package/src/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
  201. package/src/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
  202. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
  203. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
  204. package/src/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
  205. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
  206. package/src/config/agent-settings.template.yml +32 -3
  207. package/src/config/assurance-capability-registry.json +21 -9
  208. package/src/config/canonical-terms.yml +119 -0
  209. package/src/config/capability-native-outcome-bars.json +81 -0
  210. package/src/config/ci-local-parity.yml +80 -0
  211. package/src/config/drafting-channels.yml +43 -0
  212. package/src/config/estate-count-budget.json +57 -36
  213. package/src/config/evaluator-budgets.json +14 -14
  214. package/src/config/gate-coverage.yml +729 -63
  215. package/src/config/gate-reachability-exemptions.json +21 -0
  216. package/src/config/gate-violation-baselines.json +42 -19
  217. package/src/config/harness-evolution-budget.json +25 -0
  218. package/src/config/lapsed-beta-baseline.json +96 -0
  219. package/src/config/memory-twin-verdicts.yml +129 -0
  220. package/src/config/metric-registry.yml +124 -0
  221. package/src/config/pack-size-budget.json +16 -15
  222. package/src/config/preamble-payload-budget.json +12 -4
  223. package/src/config/profiles/balanced.ini +27 -0
  224. package/src/config/profiles/full.ini +27 -0
  225. package/src/config/profiles/minimal.ini +27 -0
  226. package/src/config/publish-surface.json +9 -2
  227. package/src/config/routing-coverage-seed.json +16 -2
  228. package/src/config/rule-stub-ceilings.json +133 -112
  229. package/src/config/turnaround-budget.json +66 -0
  230. package/src/install/claudeRuleRewrite.ts +288 -0
  231. package/src/install/detect.ts +60 -17
  232. package/src/install/detect_php_shape.ts +191 -0
  233. package/src/scripts/_cli/cmd_doctor.ts +27 -112
  234. package/src/scripts/_dispatch.bash +23 -0
  235. package/src/scripts/_lib/activation_ladder.ts +255 -0
  236. package/src/scripts/_lib/activation_receipt_producer.ts +341 -0
  237. package/src/scripts/_lib/activation_states.ts +88 -0
  238. package/src/scripts/_lib/adherence_detectors.ts +143 -0
  239. package/src/scripts/_lib/adr_frontmatter.ts +26 -0
  240. package/src/scripts/_lib/arith_claims.ts +181 -0
  241. package/src/scripts/_lib/arm_ranking.ts +62 -0
  242. package/src/scripts/_lib/asset_delivery_ledger.ts +303 -0
  243. package/src/scripts/_lib/audit_field_provenance.ts +54 -0
  244. package/src/scripts/_lib/auto_dispatch.ts +32 -3
  245. package/src/scripts/_lib/bench_ab_size_claim.ts +98 -4
  246. package/src/scripts/_lib/benchmark_cache_fields.ts +158 -0
  247. package/src/scripts/_lib/candidate_pair_delta.ts +349 -0
  248. package/src/scripts/_lib/candidate_proposer.ts +402 -0
  249. package/src/scripts/_lib/candidate_record.ts +601 -0
  250. package/src/scripts/_lib/capture_rate.ts +319 -0
  251. package/src/scripts/_lib/carrier_status.ts +118 -0
  252. package/src/scripts/_lib/cascade_stage_enumeration.ts +92 -0
  253. package/src/scripts/_lib/collector_denominator.ts +771 -0
  254. package/src/scripts/_lib/collector_record.ts +340 -0
  255. package/src/scripts/_lib/collector_store.ts +664 -0
  256. package/src/scripts/_lib/collector_supervision.ts +827 -0
  257. package/src/scripts/_lib/concern_estate.ts +77 -0
  258. package/src/scripts/_lib/config_chain.ts +477 -0
  259. package/src/scripts/_lib/config_cost.ts +209 -0
  260. package/src/scripts/_lib/corpus_manifest.ts +726 -0
  261. package/src/scripts/_lib/council_settings_block.ts +6 -0
  262. package/src/scripts/_lib/curator_ops.ts +172 -0
  263. package/src/scripts/_lib/delivery_arm_experiment.ts +282 -0
  264. package/src/scripts/_lib/delivery_criticality.ts +126 -0
  265. package/src/scripts/_lib/doctor_runtime_checks.ts +70 -0
  266. package/src/scripts/_lib/empty_cycles.ts +88 -0
  267. package/src/scripts/_lib/estate_carrier_cases.ts +72 -0
  268. package/src/scripts/_lib/eval_publication.ts +354 -0
  269. package/src/scripts/_lib/evaluation_cascade.ts +452 -0
  270. package/src/scripts/_lib/evaluation_vector.ts +283 -0
  271. package/src/scripts/_lib/evaluator_promotion.ts +225 -0
  272. package/src/scripts/_lib/evidence_basis.ts +118 -0
  273. package/src/scripts/_lib/evolution_roi.ts +562 -0
  274. package/src/scripts/_lib/exec_evidence.ts +8 -0
  275. package/src/scripts/_lib/experience_card.ts +272 -0
  276. package/src/scripts/_lib/experience_report.ts +151 -0
  277. package/src/scripts/_lib/experiment_freeze.ts +112 -0
  278. package/src/scripts/_lib/generated_by.ts +66 -0
  279. package/src/scripts/_lib/harness_evolution_guards.ts +239 -0
  280. package/src/scripts/_lib/hook_effect_probe.ts +141 -0
  281. package/src/scripts/_lib/host_denominator.ts +687 -0
  282. package/src/scripts/_lib/host_projection_reach.ts +157 -0
  283. package/src/scripts/_lib/ignored_blocker.ts +148 -0
  284. package/src/scripts/_lib/install_reach_checks.ts +495 -0
  285. package/src/scripts/_lib/json_duplicate_keys.ts +150 -0
  286. package/src/scripts/_lib/judge_hygiene.ts +228 -0
  287. package/src/scripts/_lib/legacy_boundary_map.ts +228 -0
  288. package/src/scripts/_lib/lexical_shortlist.ts +215 -0
  289. package/src/scripts/_lib/llm_candidate_proposer.ts +461 -0
  290. package/src/scripts/_lib/llm_proposer_transport.ts +153 -0
  291. package/src/scripts/_lib/loop_guards.ts +299 -0
  292. package/src/scripts/_lib/loss_class.ts +129 -0
  293. package/src/scripts/_lib/machine_wake.ts +51 -0
  294. package/src/scripts/_lib/md_prose_lines.ts +177 -0
  295. package/src/scripts/_lib/minimality_tiebreak.ts +168 -0
  296. package/src/scripts/_lib/one_resolver_invariant.ts +449 -0
  297. package/src/scripts/_lib/orchestration_record.ts +253 -5
  298. package/src/scripts/_lib/outcome_envelope.ts +227 -0
  299. package/src/scripts/_lib/outcome_vocabularies.ts +125 -0
  300. package/src/scripts/_lib/paired_stats.ts +245 -0
  301. package/src/scripts/_lib/paired_verdict.ts +226 -0
  302. package/src/scripts/_lib/pathology_archive.ts +393 -0
  303. package/src/scripts/_lib/prefix_stable_surfaces.ts +123 -0
  304. package/src/scripts/_lib/privacy_class.ts +47 -0
  305. package/src/scripts/_lib/promotion_capability.ts +315 -0
  306. package/src/scripts/_lib/promotion_evidence.ts +414 -0
  307. package/src/scripts/_lib/promotion_review.ts +294 -0
  308. package/src/scripts/_lib/prompt_shape.ts +92 -0
  309. package/src/scripts/_lib/regression_neighbourhood.ts +259 -0
  310. package/src/scripts/_lib/release_highlights.ts +70 -1
  311. package/src/scripts/_lib/release_material.ts +125 -2
  312. package/src/scripts/_lib/repeated_failure.ts +105 -0
  313. package/src/scripts/_lib/repo_root.ts +111 -0
  314. package/src/scripts/_lib/review_skipped_record.ts +37 -0
  315. package/src/scripts/_lib/role_split.ts +338 -0
  316. package/src/scripts/_lib/routing_corpus.ts +307 -0
  317. package/src/scripts/_lib/routing_index_input.ts +104 -0
  318. package/src/scripts/_lib/rule_injection.ts +22 -1
  319. package/src/scripts/_lib/runtime_journal.ts +1499 -0
  320. package/src/scripts/_lib/runtime_wiring_checks.ts +430 -0
  321. package/src/scripts/_lib/semantic_noop.ts +167 -0
  322. package/src/scripts/_lib/skill_catalogue.ts +11 -1
  323. package/src/scripts/_lib/source_digest.ts +200 -0
  324. package/src/scripts/_lib/source_redact.ts +260 -0
  325. package/src/scripts/_lib/source_shape.ts +502 -0
  326. package/src/scripts/_lib/source_snapshot_dedup.ts +199 -0
  327. package/src/scripts/_lib/strip_comments.d.mts +8 -0
  328. package/src/scripts/_lib/strip_comments.mjs +65 -0
  329. package/src/scripts/_lib/test_red_state.ts +154 -0
  330. package/src/scripts/_lib/trigger_shift.ts +144 -0
  331. package/src/scripts/_lib/untrusted_content.ts +82 -7
  332. package/src/scripts/activation_receipt.ts +258 -0
  333. package/src/scripts/adoption_report.ts +1 -1
  334. package/src/scripts/adr/evidence_census.ts +1 -1
  335. package/src/scripts/adr/regenerate_index.ts +26 -2
  336. package/src/scripts/adr_cite_check.ts +164 -1
  337. package/src/scripts/ai_council/blind_review.ts +11 -1
  338. package/src/scripts/ai_council/clients.ts +4 -0
  339. package/src/scripts/ai_council/config.ts +3 -0
  340. package/src/scripts/ai_council/consensus.ts +28 -8
  341. package/src/scripts/ai_council/consensus_round.ts +226 -0
  342. package/src/scripts/ai_council/cross_exam.ts +128 -0
  343. package/src/scripts/ai_council/disagreement_signal.ts +338 -0
  344. package/src/scripts/ai_council/explain_route.ts +157 -0
  345. package/src/scripts/ai_council/information_gain.ts +293 -0
  346. package/src/scripts/ai_council/inline_findings.ts +340 -0
  347. package/src/scripts/ai_council/judge_position_bias.ts +197 -0
  348. package/src/scripts/ai_council/leakage_corpus.ts +385 -0
  349. package/src/scripts/ai_council/leakage_patterns.ts +453 -0
  350. package/src/scripts/ai_council/minority_retention.ts +171 -0
  351. package/src/scripts/ai_council/orchestrator.ts +16 -203
  352. package/src/scripts/ai_council/orchestrator_results.ts +23 -3
  353. package/src/scripts/ai_council/pricing.ts +106 -0
  354. package/src/scripts/ai_council/prompts.ts +74 -7
  355. package/src/scripts/ai_council/provider_leakage_bench.ts +327 -0
  356. package/src/scripts/ai_council/py_format.ts +14 -3
  357. package/src/scripts/ai_council/recouncil_guard.ts +293 -0
  358. package/src/scripts/ai_council/recouncil_savings.ts +299 -0
  359. package/src/scripts/ai_council/replay_route.ts +223 -0
  360. package/src/scripts/ai_council/response_record.ts +50 -0
  361. package/src/scripts/ai_council/reviewer_assignment.ts +279 -0
  362. package/src/scripts/ai_council/routing_training_row.ts +197 -0
  363. package/src/scripts/ai_council/synthesis_strategy.ts +159 -0
  364. package/src/scripts/ai_council/topology_bench_manifest.ts +860 -0
  365. package/src/scripts/ai_council/topology_vocabulary.ts +175 -0
  366. package/src/scripts/asset_delivery_ledger.ts +157 -0
  367. package/src/scripts/attest_artifact.ts +193 -0
  368. package/src/scripts/audit_auto_rules.ts +2 -1
  369. package/src/scripts/audit_command_surface.ts +1 -1
  370. package/src/scripts/audit_mcp_tools.ts +2 -2
  371. package/src/scripts/audit_skill_overlap_allowlist.json +6 -1
  372. package/src/scripts/audit_user_type_axis.ts +2 -1
  373. package/src/scripts/bench_ab_clone.ts +215 -10
  374. package/src/scripts/bench_ab_integrity.ts +119 -5
  375. package/src/scripts/bench_ab_v2_stats.ts +40 -213
  376. package/src/scripts/build_archive_index.ts +1 -1
  377. package/src/scripts/build_linear_digest.ts +1 -1
  378. package/src/scripts/build_proof.ts +11 -4
  379. package/src/scripts/build_rule_trigger_matrix.ts +1 -1
  380. package/src/scripts/build_source_digests.ts +157 -0
  381. package/src/scripts/cache_realization_report.ts +103 -0
  382. package/src/scripts/check_adr_frontmatter.ts +131 -10
  383. package/src/scripts/check_benchmark_report_fields.ts +207 -0
  384. package/src/scripts/check_beta_review_markers.ts +235 -6
  385. package/src/scripts/check_bundle_path_leakage.ts +34 -1
  386. package/src/scripts/check_claims.ts +576 -6
  387. package/src/scripts/check_composite_arming.ts +35 -0
  388. package/src/scripts/check_concern_admissions.ts +365 -0
  389. package/src/scripts/check_enforcement_coverage.ts +28 -0
  390. package/src/scripts/check_estate_count.ts +347 -25
  391. package/src/scripts/check_gate_reachability.ts +383 -0
  392. package/src/scripts/check_host_loadability.ts +104 -0
  393. package/src/scripts/check_loss_class_declared.ts +308 -0
  394. package/src/scripts/check_md_language.ts +15 -98
  395. package/src/scripts/check_memory_proposal.ts +18 -11
  396. package/src/scripts/check_no_currency_in_cost_surfaces.ts +225 -0
  397. package/src/scripts/check_no_external_sources.ts +281 -6
  398. package/src/scripts/check_pack_conformance_fixture.ts +11 -1
  399. package/src/scripts/check_pack_size.ts +59 -2
  400. package/src/scripts/check_preamble_payload_budget.ts +114 -3
  401. package/src/scripts/check_prefix_stable_mutation.ts +372 -0
  402. package/src/scripts/check_publish_surface.ts +10 -1
  403. package/src/scripts/check_references.ts +24 -1
  404. package/src/scripts/check_release_highlights.ts +96 -13
  405. package/src/scripts/check_roadmap_trackable.ts +17 -2
  406. package/src/scripts/check_rule_evidence_declaration.ts +316 -0
  407. package/src/scripts/check_rule_projection_integrity.ts +14 -4
  408. package/src/scripts/check_score_contract.ts +26 -3
  409. package/src/scripts/check_static_parity.ts +285 -0
  410. package/src/scripts/check_supervision_claim_atomicity.ts +498 -0
  411. package/src/scripts/check_suppression_hygiene.ts +15 -0
  412. package/src/scripts/ci_settle.ts +39 -2
  413. package/src/scripts/code_graph/build.ts +60 -3
  414. package/src/scripts/code_graph/extract.ts +44 -0
  415. package/src/scripts/code_graph/types.ts +24 -0
  416. package/src/scripts/collector_daemon.ts +714 -0
  417. package/src/scripts/config_cost_report.ts +302 -0
  418. package/src/scripts/consumer_matrix.ts +74 -1
  419. package/src/scripts/corpus_manifest.ts +235 -0
  420. package/src/scripts/cost/budget.mjs +10 -3
  421. package/src/scripts/cost/track.mjs +10 -3
  422. package/src/scripts/council_cli.ts +37 -54
  423. package/src/scripts/dispatch_r2_reviewer.ts +46 -5
  424. package/src/scripts/evolution_lab.ts +1153 -0
  425. package/src/scripts/external_sources_denylist.json +5 -18
  426. package/src/scripts/extract_audit_patterns.ts +21 -0
  427. package/src/scripts/fold_intake.ts +12 -1
  428. package/src/scripts/gen_discovery_baseline.ts +1 -1
  429. package/src/scripts/generate_capabilities_index.ts +12 -5
  430. package/src/scripts/generate_capability_matrix.ts +3 -3
  431. package/src/scripts/generate_command_flows.ts +2 -2
  432. package/src/scripts/generate_cookbook.ts +2 -2
  433. package/src/scripts/generate_index.ts +8 -1
  434. package/src/scripts/generate_knowledge_index.ts +2 -1
  435. package/src/scripts/generate_ownership_matrix.ts +2 -1
  436. package/src/scripts/generate_pack_manifests.ts +10 -2
  437. package/src/scripts/generate_role_experiences_catalog.ts +2 -2
  438. package/src/scripts/generate_settings_reference.ts +1 -1
  439. package/src/scripts/git_authorization_hook.ts +22 -1
  440. package/src/scripts/grade_target_readiness.ts +48 -5
  441. package/src/scripts/hook_effect_doctor.ts +261 -0
  442. package/src/scripts/hook_manifest.json +1 -1
  443. package/src/scripts/hook_manifest.yaml +125 -16
  444. package/src/scripts/hooks/block_speaking_inbox_dir.ts +271 -0
  445. package/src/scripts/hooks/block_unauthorized_git.ts +18 -0
  446. package/src/scripts/hooks/comment_discipline_hook.ts +146 -0
  447. package/src/scripts/hooks/concern_registry.ts +6 -0
  448. package/src/scripts/hooks/dispatch_hook.ts +32 -0
  449. package/src/scripts/hooks/journal_record_hook.ts +345 -0
  450. package/src/scripts/hooks/prepush_metadata_sources.sh +110 -0
  451. package/src/scripts/hooks/run_continuation_hook.ts +44 -9
  452. package/src/scripts/hooks/shims/install.sh +61 -0
  453. package/src/scripts/hooks/shims/php +101 -0
  454. package/src/scripts/hooks/suggestion_capture_hook.ts +10 -0
  455. package/src/scripts/hooks/turn_end_gate_hook.ts +9 -1
  456. package/src/scripts/hot_context_hook.ts +16 -0
  457. package/src/scripts/install-hooks.sh +20 -5
  458. package/src/scripts/install.ts +17 -35
  459. package/src/scripts/inventory_frontmatter.ts +2 -1
  460. package/src/scripts/language_mirror_hook.ts +14 -2
  461. package/src/scripts/lint_adr_sweep_routing.ts +12 -0
  462. package/src/scripts/lint_canonical_terms.ts +604 -0
  463. package/src/scripts/lint_carrier_integrity.ts +820 -0
  464. package/src/scripts/lint_code_comments.ts +643 -0
  465. package/src/scripts/lint_composition_review.ts +619 -0
  466. package/src/scripts/lint_consolidation_lineage.ts +666 -0
  467. package/src/scripts/lint_eval_specs.ts +593 -0
  468. package/src/scripts/lint_hook_manifest.ts +19 -0
  469. package/src/scripts/lint_host_portability.ts +373 -0
  470. package/src/scripts/lint_judge_prompt_expectation.ts +27 -1
  471. package/src/scripts/lint_memory_twin_parity.ts +335 -0
  472. package/src/scripts/lint_metric_consumers.ts +355 -0
  473. package/src/scripts/lint_plan_risk_register.ts +39 -19
  474. package/src/scripts/lint_promotion_paths.ts +779 -0
  475. package/src/scripts/lint_skill_descriptions.ts +18 -1
  476. package/src/scripts/lint_skill_frontmatter_safety.ts +58 -0
  477. package/src/scripts/lint_skill_link_reach.ts +252 -0
  478. package/src/scripts/lint_skill_trigger_corpus.ts +426 -23
  479. package/src/scripts/lint_ticket_buildable.ts +17 -1
  480. package/src/scripts/llm_propose.ts +256 -0
  481. package/src/scripts/mcp_server/consumer_tool_catalog.json +1 -1
  482. package/src/scripts/mcp_server/tool_catalog_source.ts +1 -1
  483. package/src/scripts/measure_delivery_sets.ts +361 -0
  484. package/src/scripts/measure_docs_dead_links.ts +348 -0
  485. package/src/scripts/measure_host_capture.ts +461 -0
  486. package/src/scripts/measure_routing_signal.ts +307 -0
  487. package/src/scripts/migration_status.ts +3 -3
  488. package/src/scripts/model_rule_injection.ts +76 -0
  489. package/src/scripts/orchestration_record.ts +14 -0
  490. package/src/scripts/preamble_byte_census.ts +139 -10
  491. package/src/scripts/prepack-check.mjs +4 -1
  492. package/src/scripts/probe_council_retention.ts +495 -0
  493. package/src/scripts/probe_turnaround.ts +384 -0
  494. package/src/scripts/probe_unblocked_ask.ts +279 -0
  495. package/src/scripts/release.ts +21 -37
  496. package/src/scripts/release_drill.ts +143 -0
  497. package/src/scripts/release_env.ts +35 -0
  498. package/src/scripts/release_publication.ts +84 -0
  499. package/src/scripts/render_benchmark_md.ts +1 -1
  500. package/src/scripts/rule_activation_census.ts +70 -9
  501. package/src/scripts/rule_backlinks.ts +1 -1
  502. package/src/scripts/run_lifecycle_suite.ts +208 -0
  503. package/src/scripts/run_skill_evals.ts +40 -0
  504. package/src/scripts/schemas/delivery-manifest.schema.json +43 -0
  505. package/src/scripts/schemas/drafting-channels.schema.json +43 -0
  506. package/src/scripts/schemas/evals.schema.json +24 -1
  507. package/src/scripts/schemas/rule.schema.json +52 -1
  508. package/src/scripts/schemas/skill.schema.json +17 -2
  509. package/src/scripts/skill_overlap.ts +7 -1
  510. package/src/scripts/skill_tools/run_block_d_eval.ts +8 -1
  511. package/src/scripts/skill_tools/score_skill_relevance.ts +84 -3
  512. package/src/scripts/skill_tools/suggest_skill_for_task.ts +8 -1
  513. package/src/scripts/skill_usage_report.ts +2 -1
  514. package/src/scripts/stubs_due.ts +211 -0
  515. package/src/scripts/sweep_source_surfaces.ts +678 -0
  516. package/src/scripts/sync_pr_branch.ts +3 -3
  517. package/src/scripts/trigger_eval_grandfather.json +202 -222
  518. package/src/scripts/utilization_report.ts +22 -2
  519. package/src/server/schemas/settings.ts +5 -0
  520. package/src/templates/marketing-copy.yml +1 -1
  521. package/dist/ui/assets/index-Cxgy63kz.js +0 -26
@@ -0,0 +1,604 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Canonical-terms lint — the house dialect, held by a ratchet.
4
+ *
5
+ * `src/config/canonical-terms.yml` records which side of nine measured spelling
6
+ * pairs this repository writes. Until this gate existed, nothing held that
7
+ * decision: the map was a document, and a document cannot stop the next commit
8
+ * from reintroducing `behaviour`. `road-to-canonical-terms` 2.2.
9
+ *
10
+ * ── What it reads, and what it deliberately does not ────────────────────────
11
+ * Prose only, under `src/` and `docs/` — the SHIPPED surface, which is the
12
+ * surface the map was decided on (22/78 American in `src/`; the British lead in
13
+ * the published aggregate is produced almost entirely by `agents/`, this
14
+ * repository's own working notes). Scanning `agents/` would judge roadmaps,
15
+ * evidence files and an archive nobody will edit again against a convention
16
+ * chosen for the shipped tree.
17
+ *
18
+ * Skipped, via the shared classifier in `_lib/md_prose_lines.ts` rather than a
19
+ * second copy of it: YAML frontmatter, fenced code, indented code, inline code
20
+ * spans, and any line carrying `<!-- canonical-terms: ignore -->`.
21
+ *
22
+ * ── Protected text ──────────────────────────────────────────────────────────
23
+ * Licence TITLES are exact external strings and are never rewritten — `MIT
24
+ * License`, `Apache License`, `GNU General Public License` and their family.
25
+ * The council was explicit on the limit of that carve-out: **proximity to a
26
+ * protected name does not exempt the surrounding prose.** So the exemption is
27
+ * span-scoped, not line-scoped — the title is blanked and the rest of the line
28
+ * is still read.
29
+ *
30
+ * URLs, link targets and paths are blanked for the same reason: `licence` in a
31
+ * URL is an address, not a word.
32
+ *
33
+ * ── Ratchet, not a hard zero ────────────────────────────────────────────────
34
+ * `sweep_authorised: false` in the map is a live constraint, not a formality:
35
+ * the AI council refused an unconditional ~5000-occurrence rewrite and required
36
+ * a classified inventory plus a bounded pilot first. So the corpus is NOT clean
37
+ * on day one, and a hard-zero gate would either fail `main` immediately or have
38
+ * to suppress the findings it exists to surface. `checkRatchet` records the
39
+ * remaining count; the gate fails only when it RISES. Lowering the baseline is
40
+ * a normal commit, raising it is a defect, and the entry expires so the number
41
+ * cannot harden into configuration.
42
+ *
43
+ * ── Inventory mode ──────────────────────────────────────────────────────────
44
+ * `--inventory` classifies every occurrence across a wider root set into the
45
+ * four categories the council named — repository-authored prose, protected
46
+ * exact text, generated or externally synchronised content, and ambiguous —
47
+ * and prints the table the migration is designed from. A frequency count cannot
48
+ * tell (a) from (b); that distinction is the whole point of the mode.
49
+ *
50
+ * Exit codes: 0 clean or within ratchet · 1 regression · 2 usage / dead scope.
51
+ *
52
+ * Usage:
53
+ * ./scripts-run src/scripts/lint_canonical_terms
54
+ * ./scripts-run src/scripts/lint_canonical_terms --inventory
55
+ * ./scripts-run src/scripts/lint_canonical_terms --inventory --format json
56
+ */
57
+ import { execFileSync } from 'node:child_process';
58
+ import * as fs from 'node:fs';
59
+ import * as os from 'node:os';
60
+ import * as path from 'node:path';
61
+ import process from 'node:process';
62
+ import { fileURLToPath, pathToFileURL } from 'node:url';
63
+
64
+ import { parse as parseYaml } from 'yaml';
65
+
66
+ import { checkRatchet } from './_lib/gate_baseline.js';
67
+ import { GateLedger } from './_lib/gate_ledger.js';
68
+ import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
69
+ import { classifyMarkdownLines } from './_lib/md_prose_lines.js';
70
+ import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
71
+
72
+ const _HERE = fileURLToPath(import.meta.url);
73
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
74
+
75
+ /** Where the house dialect is recorded. */
76
+ export const MAP_REL = 'src/config/canonical-terms.yml';
77
+
78
+ /** The gate's enforcing scope — the shipped surface the map was decided on. */
79
+ export const GATE_ROOTS = ['src/', 'docs/'] as const;
80
+
81
+ /** The inventory's scope — everything the sweep would ever touch. */
82
+ export const INVENTORY_ROOTS = ['src/', 'docs/', 'agents/', 'dist/'] as const;
83
+
84
+ /** Per-line opt-out. */
85
+ const IGNORE_RE = /<!--\s*canonical-terms:\s*ignore\s*-->/i;
86
+
87
+ /**
88
+ * Generated or externally synchronised trees. An occurrence here is category
89
+ * (c): rewriting it is a no-op that the next `task sync` reverts.
90
+ */
91
+ const GENERATED_PREFIXES = [
92
+ 'dist/',
93
+ '.augment/',
94
+ '.claude/',
95
+ '.cursor/',
96
+ '.clinerules/',
97
+ 'node_modules/',
98
+ // The retired uncondensed source container ADR-051 abandoned is deliberately
99
+ // NOT listed. `check_no_new_legacy_path` refuses new references to it under
100
+ // src/, and no scan root here reaches it — so an entry would be a dead
101
+ // literal reintroducing a path this repository has spent a ratchet removing.
102
+ // Built by `src/scripts/build_proof.ts` from the Claims Ledger; editing it
103
+ // is a no-op that the next build reverts.
104
+ 'docs/proof.md',
105
+ ] as const;
106
+
107
+ /**
108
+ * Immutable records. Repository-authored, but rewriting them edits history:
109
+ * an archived roadmap and a dated evidence file are what the repository SAID,
110
+ * and a sweep that changes them makes the record disagree with the measurement
111
+ * it published. Category (d) — a human decides, per file.
112
+ */
113
+ const HISTORICAL_PREFIXES = [
114
+ 'agents/roadmaps/archive/',
115
+ 'agents/roadmaps/skipped/',
116
+ 'agents/evidence/',
117
+ 'docs/decisions/',
118
+ // Frozen changelog slices. `docs/archive/CHANGELOG-pre-*.md` is what the
119
+ // repository published at that version; rewriting it makes the record
120
+ // disagree with the release it describes.
121
+ 'docs/archive/',
122
+ 'CHANGELOG.md',
123
+ ] as const;
124
+
125
+ /** Licence titles — exact external strings, never rewritten. */
126
+ const LICENCE_TITLE_RE =
127
+ /\b(?:MIT|Apache(?:\s+\d+(?:\.\d+)?)?|BSD(?:[- ]\d[- ]Clause)?|ISC|MPL|Mozilla\s+Public|GNU\s+(?:Lesser\s+|Affero\s+)?General\s+Public|Creative\s+Commons|Unlicense|EUPL|Eclipse\s+Public)\s+Licen[cs]e\b/gi;
128
+
129
+ /** Link targets, URLs and bare paths — addresses, not words. */
130
+ const ADDRESS_RE = /\]\([^)]*\)|<[^>\s]+>|\bhttps?:\/\/\S+|\b[\w./-]+\.(?:md|ts|js|json|ya?ml|py|sh|html)\b/g;
131
+
132
+ export type TermCategory = 'authored-prose' | 'protected-text' | 'generated' | 'ambiguous';
133
+
134
+ export interface TermPair {
135
+ canonical: string;
136
+ variant: string;
137
+ confidence: string;
138
+ }
139
+
140
+ export interface TermFinding {
141
+ file: string;
142
+ line: number;
143
+ variant: string;
144
+ canonical: string;
145
+ category: TermCategory;
146
+ context: string;
147
+ }
148
+
149
+ interface CanonicalTermsMap {
150
+ sweep_authorised?: boolean;
151
+ terms?: { canonical?: string | null; variant?: string; confidence?: string }[];
152
+ }
153
+
154
+ /** Read the decided pairs. Undecided entries (`canonical: null`) are skipped. */
155
+ export function loadPairs(repoRoot: string = REPO_ROOT, rel: string = MAP_REL): TermPair[] {
156
+ const raw = fs.readFileSync(path.join(repoRoot, rel), 'utf8');
157
+ const doc = parseYaml(raw) as CanonicalTermsMap;
158
+ const out: TermPair[] = [];
159
+ for (const t of doc.terms ?? []) {
160
+ if (t.canonical === null || t.canonical === undefined) continue;
161
+ if (typeof t.variant !== 'string' || t.variant === '') continue;
162
+ out.push({ canonical: t.canonical, variant: t.variant, confidence: t.confidence ?? 'unknown' });
163
+ }
164
+ return out;
165
+ }
166
+
167
+ function escapeRegExp(s: string): string {
168
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
169
+ }
170
+
171
+ /**
172
+ * Match the variant as a whole word, and also as the stem of its own
173
+ * inflections — `behaviour` must catch `behaviours` and `behavioural`, which is
174
+ * where a naive `\b…\b` silently under-reports. The leading boundary stays
175
+ * strict so `licence` does not fire inside `unlicenced-source`… it does, and
176
+ * that is correct: that is the variant.
177
+ */
178
+ function variantRegExp(variant: string): RegExp {
179
+ return new RegExp(`(?<![\\p{L}\\p{N}_])${escapeRegExp(variant)}(?:s|es|d|al|ally|ing)?(?![\\p{L}\\p{N}_])`, 'giu');
180
+ }
181
+
182
+ function blankSpans(line: string, re: RegExp): string {
183
+ re.lastIndex = 0;
184
+ return line.replace(re, (m) => ' '.repeat(m.length));
185
+ }
186
+
187
+ function fileCategory(file: string): TermCategory | null {
188
+ if (GENERATED_PREFIXES.some((p) => file.startsWith(p))) return 'generated';
189
+ if (HISTORICAL_PREFIXES.some((p) => file.startsWith(p))) return 'ambiguous';
190
+ return null;
191
+ }
192
+
193
+ /**
194
+ * Classify every variant occurrence in one document.
195
+ *
196
+ * Pure over the content string, so the four states the roadmap's verify clause
197
+ * names — prose, fence, frontmatter, quoted licence title — are demonstrable
198
+ * from literals with no repo.
199
+ */
200
+ export function scanContent(file: string, content: string, pairs: readonly TermPair[]): TermFinding[] {
201
+ const findings: TermFinding[] = [];
202
+ const forcedCategory = fileCategory(file);
203
+
204
+ for (const line of classifyMarkdownLines(content, { markers: [IGNORE_RE] })) {
205
+ if (line.kind !== 'prose') continue;
206
+
207
+ // `text` already has inline code spans blanked. Blank the protected
208
+ // spans on top, keeping the line length so a category decision made on
209
+ // the blanked text still lines up with the raw one.
210
+ const protectedBlanked = blankSpans(blankSpans(line.text, LICENCE_TITLE_RE), ADDRESS_RE);
211
+
212
+ for (const pair of pairs) {
213
+ const re = variantRegExp(pair.variant);
214
+ re.lastIndex = 0;
215
+ let m: RegExpExecArray | null;
216
+ while ((m = re.exec(line.text)) !== null) {
217
+ const at = m.index;
218
+ const survives = protectedBlanked.slice(at, at + m[0].length).trim() !== '';
219
+ const category: TermCategory = !survives
220
+ ? 'protected-text'
221
+ : (forcedCategory ?? 'authored-prose');
222
+ findings.push({
223
+ file,
224
+ line: line.lineno,
225
+ variant: m[0],
226
+ canonical: pair.canonical,
227
+ category,
228
+ context: line.raw.trim(),
229
+ });
230
+ }
231
+ }
232
+ }
233
+ return findings;
234
+ }
235
+
236
+ /**
237
+ * Every markdown file under the given roots — TRACKED and UNTRACKED.
238
+ *
239
+ * `--others --exclude-standard` is load-bearing, not thoroughness. The gate's
240
+ * canary in `gate-coverage.yml` is create-only by contract, so it plants a NEW,
241
+ * untracked file; a plain `git ls-files` would not see it and the gate would
242
+ * report green on its own canary — the exact "gate that cannot fail" failure
243
+ * `check_gate_coverage --canary` exists to catch.
244
+ */
245
+ function listMarkdown(repoRoot: string, roots: readonly string[]): string[] {
246
+ const patterns = [...roots.map((r) => `${r}**/*.md`), ...roots.map((r) => `${r}*.md`)];
247
+ const run = (args: string[]): string[] =>
248
+ execFileSync('git', args, { cwd: repoRoot, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 })
249
+ .split('\n')
250
+ .filter((l) => l.endsWith('.md'));
251
+ const tracked = run(['ls-files', '--', ...patterns]);
252
+ const untracked = run(['ls-files', '--others', '--exclude-standard', '--', ...patterns]);
253
+ return Array.from(new Set([...tracked, ...untracked])).sort();
254
+ }
255
+
256
+ export interface ScanResult {
257
+ findings: TermFinding[];
258
+ filesScanned: number;
259
+ }
260
+
261
+ /** Every `.md` under a plain directory — the fixture path, where there is no git index. */
262
+ function walkMarkdown(root: string): string[] {
263
+ const out: string[] = [];
264
+ const visit = (dir: string): void => {
265
+ for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
266
+ const abs = path.join(dir, ent.name);
267
+ if (ent.isDirectory()) visit(abs);
268
+ else if (ent.name.endsWith('.md')) out.push(path.relative(root, abs));
269
+ }
270
+ };
271
+ visit(root);
272
+ return out.sort();
273
+ }
274
+
275
+ /**
276
+ * Scan a tree, accounting for every file through a {@link GateLedger}.
277
+ *
278
+ * The ledger is not decoration here. This gate `continue`s past unreadable
279
+ * files, and a `continue` with no record is precisely the silent-skip the
280
+ * ledger exists to make visible — a gate that read 1,400 of 1,577 files and
281
+ * printed a green checkmark is indistinguishable from one that read all of them.
282
+ */
283
+ export function scanTree(
284
+ repoRoot: string,
285
+ roots: readonly string[],
286
+ pairs: readonly TermPair[],
287
+ ledger?: GateLedger,
288
+ ): ScanResult {
289
+ const files = roots.length === 1 && roots[0] === '' ? walkMarkdown(repoRoot) : listMarkdown(repoRoot, roots);
290
+ ledger?.plan(files);
291
+ const findings: TermFinding[] = [];
292
+ for (const f of files) {
293
+ let content: string;
294
+ try {
295
+ content = fs.readFileSync(path.join(repoRoot, f), 'utf8');
296
+ } catch {
297
+ ledger?.skip(f, 'binary_content');
298
+ continue;
299
+ }
300
+ const hits = scanContent(f, content, pairs);
301
+ findings.push(...hits);
302
+ const actionable = hits.filter((h) => h.category === 'authored-prose');
303
+ if (actionable.length > 0) ledger?.fail(f, `${actionable.length} non-canonical occurrence(s)`);
304
+ else ledger?.complete(f);
305
+ }
306
+ return { findings, filesScanned: files.length };
307
+ }
308
+
309
+ function countBy(items: readonly { [k: string]: unknown }[], key: string): Record<string, number> {
310
+ const out: Record<string, number> = {};
311
+ for (const it of items) {
312
+ const k = String(it[key]);
313
+ out[k] = (out[k] ?? 0) + 1;
314
+ }
315
+ return out;
316
+ }
317
+
318
+ function renderInventory(result: ScanResult, pairs: readonly TermPair[]): string {
319
+ const byCat = countBy(result.findings as unknown as Record<string, unknown>[], 'category');
320
+ const lines: string[] = [];
321
+ lines.push(`files scanned: ${result.filesScanned}`);
322
+ lines.push(`occurrences: ${result.findings.length}`);
323
+ lines.push('');
324
+ lines.push('| category | occurrences | what it means |');
325
+ lines.push('|---|---:|---|');
326
+ const meaning: Record<TermCategory, string> = {
327
+ 'authored-prose': 'repository-authored prose, eligible for normalisation',
328
+ 'protected-text': 'exact external text — licence titles, addresses, code spans',
329
+ generated: 'generated or externally synchronised; a rewrite is reverted by `task sync`',
330
+ ambiguous: 'immutable record (archive, evidence, ADRs, changelog) — a human decides per file',
331
+ };
332
+ for (const c of ['authored-prose', 'protected-text', 'generated', 'ambiguous'] as TermCategory[]) {
333
+ lines.push(`| ${c} | ${byCat[c] ?? 0} | ${meaning[c]} |`);
334
+ }
335
+ lines.push('');
336
+ lines.push('| pair | authored-prose | files | blast radius |');
337
+ lines.push('|---|---:|---:|---|');
338
+ for (const p of pairs) {
339
+ const hits = result.findings.filter((f) => f.canonical === p.canonical && f.category === 'authored-prose');
340
+ const files = new Set(hits.map((f) => f.file));
341
+ lines.push(`| \`${p.variant}\` → \`${p.canonical}\` | ${hits.length} | ${files.size} | ${files.size <= 5 ? 'small' : files.size <= 25 ? 'medium' : 'large'} |`);
342
+ }
343
+ return lines.join('\n');
344
+ }
345
+
346
+
347
+ /**
348
+ * Rewrite one matched token to the canonical side, preserving inflection and
349
+ * capitalization. `Sub-agents` → `Subagents`, `behavioural` → `behavioral`,
350
+ * `canonicalised` → `canonicalized`.
351
+ *
352
+ * The suffix is whatever the variant regex matched beyond the base, so the
353
+ * inflection set is defined in exactly one place ({@link variantRegExp}) and a
354
+ * new suffix added there is automatically handled here.
355
+ */
356
+ export function canonicalizeToken(token: string, pair: TermPair): string {
357
+ const lower = token.toLowerCase();
358
+ const base = pair.variant.toLowerCase();
359
+ if (!lower.startsWith(base)) return token;
360
+ const suffix = lower.slice(base.length);
361
+ const replacement = pair.canonical + suffix;
362
+ if (token === token.toUpperCase() && token !== token.toLowerCase()) return replacement.toUpperCase();
363
+ if (token[0] === token[0]?.toUpperCase() && token[0] !== token[0]?.toLowerCase()) {
364
+ return replacement.charAt(0).toUpperCase() + replacement.slice(1);
365
+ }
366
+ return replacement;
367
+ }
368
+
369
+ /**
370
+ * Apply the canonical side to the `authored-prose` occurrences of the selected
371
+ * pairs, and only those.
372
+ *
373
+ * The rewrite is driven by the SAME classification the gate reports, so a
374
+ * category the scanner protects is a category the sweep cannot touch — there is
375
+ * no second find-and-replace with its own idea of what a fence is. That is the
376
+ * property the council's "classified inventory, not a frequency count"
377
+ * condition was asking for.
378
+ */
379
+ export function applyFix(
380
+ repoRoot: string,
381
+ findings: readonly TermFinding[],
382
+ pairs: readonly TermPair[],
383
+ ): { files: number; occurrences: number } {
384
+ const byCanonical = new Map(pairs.map((p) => [p.canonical, p]));
385
+ const byFile = new Map<string, TermFinding[]>();
386
+ for (const f of findings) {
387
+ if (f.category !== 'authored-prose') continue;
388
+ const list = byFile.get(f.file) ?? [];
389
+ list.push(f);
390
+ byFile.set(f.file, list);
391
+ }
392
+ let occurrences = 0;
393
+ for (const [file, list] of byFile) {
394
+ const abs = path.join(repoRoot, file);
395
+ const lines = fs.readFileSync(abs, 'utf8').split('\n');
396
+ // Group per line so several hits on one line are rewritten together.
397
+ const lineNos = Array.from(new Set(list.map((f) => f.line)));
398
+ for (const lineNo of lineNos) {
399
+ const idx = lineNo - 1;
400
+ let text = lines[idx] ?? '';
401
+ for (const pair of new Set(list.filter((f) => f.line === lineNo).map((f) => byCanonical.get(f.canonical)!))) {
402
+ const re = variantRegExp(pair.variant);
403
+ text = text.replace(re, (tok) => canonicalizeToken(tok, pair));
404
+ }
405
+ if (text !== lines[idx]) {
406
+ occurrences += list.filter((f) => f.line === lineNo).length;
407
+ lines[idx] = text;
408
+ }
409
+ }
410
+ fs.writeFileSync(abs, lines.join('\n'), 'utf8');
411
+ }
412
+ return { files: byFile.size, occurrences };
413
+ }
414
+
415
+
416
+ /**
417
+ * Self-test — the gate proving it can still fail, on a fixture tree.
418
+ *
419
+ * The `--root` seam exists for exactly this: a fixture must never be judged
420
+ * against the repository's recorded debt, so `--root` bypasses the ratchet and
421
+ * uses the plain "any authored-prose occurrence is a failure" rule. The four
422
+ * cases are the four states the roadmap's verify clause names.
423
+ */
424
+ export function selfTest(): number {
425
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'lct-selftest-'));
426
+ const write = (rel: string, body: string): string => {
427
+ const p = path.join(tmp, rel);
428
+ fs.mkdirSync(path.dirname(p), { recursive: true });
429
+ fs.writeFileSync(p, body, 'utf-8');
430
+ return path.dirname(p);
431
+ };
432
+ const run = (dir: string): number =>
433
+ runGateCli(REPO_ROOT, 'src/scripts/lint_canonical_terms.ts', ['--root', dir, '--quiet'], REPO_ROOT);
434
+
435
+ try {
436
+ return runSelfTest({
437
+ gate: 'lint_canonical_terms',
438
+ minCases: 4,
439
+ minRejectCases: 1,
440
+ cases: [
441
+ {
442
+ name: 'a non-canonical spelling in prose is rejected',
443
+ expect: 'reject',
444
+ run: () => run(write('prose/doc.md', 'The observed behaviour is wrong.\n')),
445
+ },
446
+ {
447
+ name: 'the same word inside a fence passes',
448
+ expect: 'accept',
449
+ run: () => run(write('fence/doc.md', 'Prose.\n\n```ts\n// behaviour\n```\n')),
450
+ },
451
+ {
452
+ name: 'the same word inside a frontmatter value passes',
453
+ expect: 'accept',
454
+ run: () => run(write('fm/doc.md', '---\ndescription: behaviour\n---\n\nProse.\n')),
455
+ },
456
+ {
457
+ name: 'the same word inside a quoted licence title passes',
458
+ expect: 'accept',
459
+ run: () => run(write('lic/doc.md', 'Shipped under the Mozilla Public Licence.\n')),
460
+ },
461
+ ],
462
+ });
463
+ } finally {
464
+ fs.rmSync(tmp, { recursive: true, force: true });
465
+ }
466
+ }
467
+
468
+ export function main(argv: string[] = process.argv.slice(2)): number {
469
+ if (argv.includes('-h') || argv.includes('--help')) {
470
+ process.stdout.write(
471
+ 'usage: lint_canonical_terms [--inventory] [--format {text,json}] [--quiet]\n' +
472
+ ' Default: ratchet over authored prose under src/ and docs/.\n' +
473
+ ' --inventory: classified occurrence table across src/, docs/, agents/ and dist/.\n' +
474
+ ' --fix --pair <canonical>[,<canonical>]: rewrite authored-prose occurrences of\n' +
475
+ ' the named pairs only. Refuses without --pair: the map carries\n' +
476
+ ' `sweep_authorised: false`, so an unbounded sweep is not available here.\n' +
477
+ ' --root <dir>: judge a fixture tree instead of the repo (no ratchet).\n' +
478
+ ' --self-test: prove the gate can still fail.\n',
479
+ );
480
+ return 0;
481
+ }
482
+ if (argv.includes('--self-test')) return selfTest();
483
+
484
+ const rootIdx = argv.indexOf('--root');
485
+ const fixtureRoot = rootIdx !== -1 && rootIdx + 1 < argv.length ? (argv[rootIdx + 1] as string) : null;
486
+
487
+ const quiet = argv.includes('--quiet');
488
+ const asJson = argv.includes('--format=json') || (argv.includes('--format') && argv[argv.indexOf('--format') + 1] === 'json');
489
+ const inventory = argv.includes('--inventory');
490
+ const fix = argv.includes('--fix');
491
+ const pairArgIdx = argv.indexOf('--pair');
492
+ const selected =
493
+ pairArgIdx !== -1 && pairArgIdx + 1 < argv.length
494
+ ? new Set((argv[pairArgIdx + 1] as string).split(',').map((s) => s.trim()).filter(Boolean))
495
+ : null;
496
+
497
+ if (fix && (selected === null || selected.size === 0)) {
498
+ process.stderr.write(
499
+ 'lint_canonical_terms: --fix requires --pair. `sweep_authorised: false` in ' +
500
+ `${MAP_REL} means the corpus-wide sweep is not authorised; the pilot is bounded ` +
501
+ 'by naming the pairs it may touch.\n',
502
+ );
503
+ return 2;
504
+ }
505
+
506
+ const pairs = loadPairs();
507
+
508
+ if (fixtureRoot !== null) {
509
+ // Fixture mode: no git index, no ratchet. Any authored-prose occurrence
510
+ // fails, which is what makes an accept/reject self-test meaningful.
511
+ const fixture = scanTree(path.resolve(fixtureRoot), [''], pairs);
512
+ const bad = fixture.findings.filter((f) => f.category === 'authored-prose');
513
+ if (!quiet) {
514
+ for (const f of bad) process.stderr.write(` ${f.file}:${f.line} \`${f.variant}\` -> \`${f.canonical}\`\n`);
515
+ process.stdout.write(`scanned: ${fixture.filesScanned}\n`);
516
+ }
517
+ return bad.length > 0 ? 1 : 0;
518
+ }
519
+
520
+ const roots = inventory ? INVENTORY_ROOTS : GATE_ROOTS;
521
+ const ledger = inventory ? undefined : new GateLedger('lint_canonical_terms');
522
+ const result = scanTree(REPO_ROOT, roots, pairs, ledger);
523
+
524
+ try {
525
+ assertScanned({
526
+ gate: 'lint_canonical_terms',
527
+ scanned: result.filesScanned,
528
+ units: 'markdown file(s)',
529
+ roots,
530
+ });
531
+ } catch (e) {
532
+ if (e instanceof DeadScopeError) {
533
+ process.stderr.write(`❌ ${e.message}\n`);
534
+ return 2;
535
+ }
536
+ throw e;
537
+ }
538
+
539
+ if (fix) {
540
+ const scoped = result.findings.filter((f) => selected!.has(f.canonical));
541
+ const applied = applyFix(REPO_ROOT, scoped, pairs);
542
+ process.stdout.write(
543
+ `lint_canonical_terms --fix: ${applied.occurrences} occurrence(s) in ${applied.files} file(s) ` +
544
+ `for pair(s) ${[...selected!].join(', ')}.\n`,
545
+ );
546
+ return 0;
547
+ }
548
+
549
+ if (inventory) {
550
+ if (asJson) {
551
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
552
+ } else {
553
+ process.stdout.write(`${renderInventory(result, pairs)}\n`);
554
+ }
555
+ return 0;
556
+ }
557
+
558
+ const actionable = result.findings.filter((f) => f.category === 'authored-prose');
559
+ if (asJson) {
560
+ process.stdout.write(`${JSON.stringify({ findings: actionable, filesScanned: result.filesScanned }, null, 2)}\n`);
561
+ }
562
+
563
+ const verdict = checkRatchet({
564
+ gate: 'lint_canonical_terms',
565
+ actual: actionable.length,
566
+ repoRoot: REPO_ROOT,
567
+ });
568
+
569
+ if (!verdict.ok) {
570
+ ledger?.report();
571
+ // The machine-readable count is emitted on the RED path too. Rule 1 of
572
+ // the gate-coverage manifest asks for exactly one `scanned:` line, and a
573
+ // gate that publishes its coverage only when it passes cannot be caught
574
+ // going blind at the moment that matters most. This was missing when the
575
+ // gate landed, and `check_gate_coverage` reported it the first time the
576
+ // ratchet went red — which is the census working.
577
+ process.stdout.write(`scanned: ${result.filesScanned}\n`);
578
+ if (!quiet) {
579
+ for (const f of actionable.slice(0, 40)) {
580
+ process.stderr.write(` ${f.file}:${f.line} · \`${f.variant}\` → \`${f.canonical}\`\n`);
581
+ }
582
+ if (actionable.length > 40) process.stderr.write(` … and ${actionable.length - 40} more\n`);
583
+ }
584
+ process.stderr.write(`❌ ${verdict.message}\n`);
585
+ return 1;
586
+ }
587
+
588
+ ledger?.report(quiet ? () => undefined : undefined);
589
+ if (!quiet) {
590
+ process.stdout.write(`✅ ${verdict.message}\n`);
591
+ process.stdout.write(`scanned: ${result.filesScanned}\n`);
592
+ }
593
+ return 0;
594
+ }
595
+
596
+ /* c8 ignore start */
597
+ function isCliEntry(): boolean {
598
+ if (process.argv[1] === undefined) return false;
599
+ return pathToFileURL(path.resolve(process.argv[1])).href === pathToFileURL(_HERE).href;
600
+ }
601
+ if (isCliEntry()) {
602
+ process.exit(main());
603
+ }
604
+ /* c8 ignore stop */