@event4u/agent-config 16.1.0 → 16.3.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 (467) 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 +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -0,0 +1,657 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Classify every tracked file in the evidence tree as hot, warm or cold —
4
+ * by reference, never by age.
5
+ *
6
+ * `road-to-an-evidence-tree-with-a-cold-half` Phase 1.1 and 3.2. The tree is
7
+ * 32 MB over 1,300-odd files and its retention is `permanent`, so the question
8
+ * "which of these does anything still read" has to be answerable by a rule a
9
+ * reader can check, not by a judgement someone made once.
10
+ *
11
+ * THE RULE, stated before the code so it can be argued with:
12
+ *
13
+ * cold — no tracked file anywhere in the repository references its path or,
14
+ * when the filename is unique repo-wide, its bare filename.
15
+ * warm — referenced only by archived or skipped roadmaps, or only by other
16
+ * files that are themselves inside the evidence tree.
17
+ * hot — referenced by at least one live tracked file outside the evidence
18
+ * tree, OR sitting under a directory a gate enumerates as its corpus.
19
+ *
20
+ * Two properties of that rule carry the whole instrument.
21
+ *
22
+ * FIRST, cold means zero inbound references from ANYWHERE, including from
23
+ * inside the evidence tree. The weaker reading — "nothing outside evidence
24
+ * cites it" — would class a file that a hot analysis document links to as cold,
25
+ * and moving it would break `check_references`, which scans all of `agents/`.
26
+ * Zero-inbound is the only definition under which the cold set is safe to move
27
+ * without rewriting a single link.
28
+ *
29
+ * SECOND, matching is deliberately PERMISSIVE and the error direction is
30
+ * deliberately one-way. A citation form this misses classes a live file cold,
31
+ * which is the expensive mistake; a loose match classes a dead file hot, which
32
+ * only means it stays where it is. So tokens are harvested from prose, from
33
+ * JSON values and from code alike — not only from backticks or markdown links —
34
+ * and a bare filename counts whenever no other tracked file in the repository
35
+ * shares it.
36
+ *
37
+ * WHAT IT DELIBERATELY DOES NOT DO. It never deletes, never moves, never
38
+ * refuses. Exit is 0 even when the cold set grows, because a census that can
39
+ * fail is a gate, and a gate over a corpus nobody has pruned yet would be a
40
+ * refusal with no remedy behind it. `--since` names what became cold since a
41
+ * previous report; it still exits 0.
42
+ *
43
+ * Usage:
44
+ * ./scripts-run src/scripts/report_evidence_temperature
45
+ * ./scripts-run src/scripts/report_evidence_temperature --write
46
+ * ./scripts-run src/scripts/report_evidence_temperature --json
47
+ * ./scripts-run src/scripts/report_evidence_temperature --since <report.md>
48
+ * ./scripts-run src/scripts/report_evidence_temperature --write --since latest
49
+ *
50
+ * Exit codes: 0 always, except 2 for a usage error.
51
+ */
52
+
53
+ import { execFileSync } from 'node:child_process';
54
+ import * as fs from 'node:fs';
55
+ import * as path from 'node:path';
56
+ import { fileURLToPath, pathToFileURL } from 'node:url';
57
+
58
+ const _HERE = fileURLToPath(import.meta.url);
59
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
60
+
61
+ /** The tree this census is about, repo-relative and POSIX-separated. */
62
+ export const EVIDENCE_ROOT = 'agents/evidence';
63
+
64
+ /** Where `--write` puts the report, relative to the repository root. */
65
+ export const REPORT_DIR = 'agents/evidence/analysis';
66
+
67
+ /** Reports of this family are excluded from the reference index — see `isSelfReport`. */
68
+ const REPORT_PREFIX = 'evidence-temperature-';
69
+
70
+ /** Extension of a census report. Shared by the writer and by `--since latest`. */
71
+ const REPORT_EXT = '.md';
72
+
73
+ /**
74
+ * The day a census is stamped with. UTC, so a run either side of local midnight
75
+ * names the same report.
76
+ */
77
+ export function censusDateStamp(now: Date = new Date()): string {
78
+ return now.toISOString().slice(0, 10);
79
+ }
80
+
81
+ /**
82
+ * The path `--write` composes for a census taken on `generatedAt`. Every part of
83
+ * the name comes from a constant this module shares — `REPORT_DIR`,
84
+ * `REPORT_PREFIX`, `REPORT_EXT` — or from `censusDateStamp`, and
85
+ * `latestReportBefore` filters on the same two, so discovery and writing cannot
86
+ * drift apart. The release-PR shape gate's test goes through this builder too.
87
+ * Writer and gate holding independent spellings of this name is what broke
88
+ * `task release` for 16.3.0.
89
+ */
90
+ export function defaultReportPath(generatedAt: string): string {
91
+ return path.posix.join(REPORT_DIR, `${REPORT_PREFIX}${generatedAt}${REPORT_EXT}`);
92
+ }
93
+
94
+ /** File extensions a path token may end in. Anything else is not a path to us. */
95
+ const PATH_EXTENSIONS = [
96
+ 'md',
97
+ 'json',
98
+ 'jsonl',
99
+ 'yaml',
100
+ 'yml',
101
+ 'csv',
102
+ 'txt',
103
+ 'patch',
104
+ 'sh',
105
+ 'mjs',
106
+ 'ts',
107
+ 'tsx',
108
+ 'py',
109
+ 'png',
110
+ 'svg',
111
+ 'html',
112
+ ] as const;
113
+
114
+ const TOKEN_RE = new RegExp(
115
+ String.raw`(?:[A-Za-z0-9_.@+-]+\/)*[A-Za-z0-9_.@+-]+\.(?:${PATH_EXTENSIONS.join('|')})(?![A-Za-z0-9])`,
116
+ 'g',
117
+ );
118
+
119
+ /** Largest file this will read looking for references. Bigger ones are skipped. */
120
+ const MAX_READ_BYTES = 8 * 1024 * 1024;
121
+
122
+ /**
123
+ * Directories inside the evidence tree that a gate enumerates as its corpus.
124
+ *
125
+ * Membership is the point: these gates do not merely happen to read a file they
126
+ * were handed, they walk the directory and derive a verdict from what is in it.
127
+ * A file that left one of these roots would leave the gate scanning fewer files
128
+ * and still exiting green, which is the gate-that-scans-nothing shape this
129
+ * repository refuses elsewhere. So every file beneath them is hot by that fact
130
+ * alone, whatever cites it — this is Phase 1.2's clause that a gate relying on a
131
+ * file reclassifies it hot, applied mechanically instead of by hand.
132
+ *
133
+ * `agents/` as a whole is NOT in this list even though `check_references` scans
134
+ * it, because that gate validates the links a file contains rather than
135
+ * requiring the file to exist: moving a file to another directory under
136
+ * `agents/` keeps it inside that scan root and changes nothing for it.
137
+ */
138
+ export const GATE_SCAN_ROOTS: ReadonlyArray<{
139
+ readonly dir: string;
140
+ readonly gates: readonly string[];
141
+ readonly loses: string;
142
+ }> = [
143
+ {
144
+ dir: 'reviews',
145
+ gates: [
146
+ 'check_review_dispositions',
147
+ 'check_review_schema',
148
+ 'check_completion_review',
149
+ 'probe_review_binding_drift',
150
+ 'lint_judge_prompt_expectation',
151
+ ],
152
+ loses:
153
+ 'Archived round records are validated in place — a finding left open after it was fixed ' +
154
+ 'is caught only by walking this directory. Five gates read it, one of them specifically ' +
155
+ 'for the archived rounds, so a file leaving the root is coverage leaving with it.',
156
+ },
157
+ {
158
+ dir: 'release-findings',
159
+ gates: ['check_review_schema', 'check_release_pr_shape', 'release_publication'],
160
+ loses:
161
+ 'Per-release ledgers are read by version, and the release PR shape check asserts one is ' +
162
+ 'present. A missing ledger is indistinguishable from a release that produced no findings.',
163
+ },
164
+ {
165
+ dir: 'archived-skills',
166
+ gates: ['lint_archived_skills'],
167
+ loses: 'The archive note for a retired skill is the record that it was retired rather than lost.',
168
+ },
169
+ {
170
+ dir: 'ratifications',
171
+ gates: ['_lib/ratification_artifact'],
172
+ loses: 'A governance diff is pushed only against a ratification artefact found in this directory.',
173
+ },
174
+ ];
175
+
176
+ /** Paths that are structural rather than evidential — an index or a directory keeper. */
177
+ function isStructural(rel: string): boolean {
178
+ const base = path.posix.basename(rel);
179
+ return base === '.gitkeep' || base === 'README.md';
180
+ }
181
+
182
+ /** True for the census reports themselves, which name every cold file and would otherwise warm them all. */
183
+ export function isSelfReport(rel: string): boolean {
184
+ return path.posix.basename(rel).startsWith(REPORT_PREFIX);
185
+ }
186
+
187
+ export type Temperature = 'hot' | 'warm' | 'cold';
188
+ export type ReferrerClass = 'live' | 'archived' | 'evidence';
189
+ export type Reason = 'scan-root' | 'structural' | 'live-citation' | 'historical-citation' | 'uncited';
190
+
191
+ /**
192
+ * Which kind of thing a referring file is.
193
+ *
194
+ * `archived` is the class that makes warm a useful word: an archived or skipped
195
+ * roadmap is a record of work that finished, so a citation from one says the
196
+ * file mattered once, not that anything reads it now.
197
+ */
198
+ export function referrerClass(rel: string): ReferrerClass {
199
+ if (rel.startsWith(`${EVIDENCE_ROOT}/`)) return 'evidence';
200
+ if (rel.startsWith('agents/roadmaps/archive/')) return 'archived';
201
+ if (rel.startsWith('agents/roadmaps/skipped/')) return 'archived';
202
+ if (rel.startsWith('agents/tmp.old/')) return 'archived';
203
+ return 'live';
204
+ }
205
+
206
+ /** Strip leading `./` and `../` segments so a relative link and an absolute path share one key. */
207
+ export function normaliseToken(token: string): string {
208
+ const parts = token.split('/').filter((p) => p !== '' && p !== '.' && p !== '..');
209
+ return parts.join('/');
210
+ }
211
+
212
+ /** Every path-shaped token in a blob of text, normalised, deduplicated. */
213
+ export function extractPathTokens(text: string): string[] {
214
+ const out = new Set<string>();
215
+ for (const m of text.matchAll(TOKEN_RE)) {
216
+ const norm = normaliseToken(m[0]);
217
+ if (norm !== '') out.add(norm);
218
+ }
219
+ return [...out];
220
+ }
221
+
222
+ /** The keys under which a tracked path may be cited: the full path and each of its suffixes. */
223
+ export function citationKeys(rel: string): string[] {
224
+ const parts = rel.split('/');
225
+ const keys: string[] = [];
226
+ for (let i = 0; i < parts.length; i += 1) keys.push(parts.slice(i).join('/'));
227
+ return keys;
228
+ }
229
+
230
+ export interface Reference {
231
+ readonly from: string;
232
+ readonly cls: ReferrerClass;
233
+ }
234
+
235
+ export function classify(
236
+ refs: readonly Reference[],
237
+ scanRoot: string | null,
238
+ structural: boolean,
239
+ ): { temperature: Temperature; reason: Reason } {
240
+ if (scanRoot !== null) return { temperature: 'hot', reason: 'scan-root' };
241
+ if (structural) return { temperature: 'hot', reason: 'structural' };
242
+ if (refs.some((r) => r.cls === 'live')) return { temperature: 'hot', reason: 'live-citation' };
243
+ if (refs.length > 0) return { temperature: 'warm', reason: 'historical-citation' };
244
+ return { temperature: 'cold', reason: 'uncited' };
245
+ }
246
+
247
+ /** The gate scan root a path sits under, or null. */
248
+ export function scanRootFor(rel: string): string | null {
249
+ for (const root of GATE_SCAN_ROOTS) {
250
+ if (rel.startsWith(`${EVIDENCE_ROOT}/${root.dir}/`)) return root.dir;
251
+ }
252
+ return null;
253
+ }
254
+
255
+ export interface EvidenceFile {
256
+ readonly path: string;
257
+ readonly bytes: number;
258
+ readonly addedOn: string | null;
259
+ readonly temperature: Temperature;
260
+ readonly reason: Reason;
261
+ readonly referrers: readonly string[];
262
+ }
263
+
264
+ export interface Census {
265
+ readonly generatedAt: string;
266
+ readonly commit: string;
267
+ readonly files: readonly EvidenceFile[];
268
+ }
269
+
270
+ function git(args: readonly string[], root: string): string {
271
+ return execFileSync('git', [...args], { cwd: root, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
272
+ }
273
+
274
+ /** Earliest add date per path, from one walk of the history. */
275
+ export function parseAddDates(log: string): Map<string, string> {
276
+ const dates = new Map<string, string>();
277
+ let current: string | null = null;
278
+ for (const line of log.split('\n')) {
279
+ if (line.startsWith('@')) {
280
+ current = line.slice(1).trim();
281
+ continue;
282
+ }
283
+ const file = line.trim();
284
+ if (file === '' || current === null) continue;
285
+ // git log walks newest-first, so the LAST sighting is the earliest add.
286
+ dates.set(file, current);
287
+ }
288
+ return dates;
289
+ }
290
+
291
+ function readTextOrNull(abs: string): string | null {
292
+ let stat: fs.Stats;
293
+ try {
294
+ stat = fs.statSync(abs);
295
+ } catch {
296
+ return null;
297
+ }
298
+ if (!stat.isFile() || stat.size > MAX_READ_BYTES) return null;
299
+ try {
300
+ return fs.readFileSync(abs, 'utf8');
301
+ } catch {
302
+ return null;
303
+ }
304
+ }
305
+
306
+ export function buildCensus(root: string): Census {
307
+ const tracked = git(['ls-files', '-z'], root)
308
+ .split('\0')
309
+ .filter((p) => p !== '');
310
+ const evidence = tracked.filter((p) => p.startsWith(`${EVIDENCE_ROOT}/`));
311
+
312
+ const basenameCount = new Map<string, number>();
313
+ for (const p of tracked) {
314
+ const b = path.posix.basename(p);
315
+ basenameCount.set(b, (basenameCount.get(b) ?? 0) + 1);
316
+ }
317
+
318
+ // token -> referring files. Self-reports are excluded at index time, not at
319
+ // lookup time, so a report can never warm the set it was written about.
320
+ const index = new Map<string, Set<string>>();
321
+ for (const rel of tracked) {
322
+ if (isSelfReport(rel)) continue;
323
+ const text = readTextOrNull(path.join(root, rel));
324
+ if (text === null) continue;
325
+ for (const token of extractPathTokens(text)) {
326
+ let bucket = index.get(token);
327
+ if (bucket === undefined) {
328
+ bucket = new Set<string>();
329
+ index.set(token, bucket);
330
+ }
331
+ bucket.add(rel);
332
+ }
333
+ }
334
+
335
+ const addDates = parseAddDates(
336
+ git(['log', '--diff-filter=A', '--name-only', '--date=short', '--format=@%ad', '--', EVIDENCE_ROOT], root),
337
+ );
338
+
339
+ const files: EvidenceFile[] = [];
340
+ for (const rel of evidence) {
341
+ const keys = citationKeys(rel);
342
+ const bare = keys[keys.length - 1];
343
+ const seen = new Map<string, ReferrerClass>();
344
+ for (const key of keys) {
345
+ // A bare filename counts only when nothing else in the tree shares it.
346
+ if (key === bare && (basenameCount.get(bare) ?? 0) > 1) continue;
347
+ for (const from of index.get(key) ?? []) {
348
+ if (from === rel) continue;
349
+ seen.set(from, referrerClass(from));
350
+ }
351
+ }
352
+ const refs: Reference[] = [...seen].map(([from, cls]) => ({ from, cls }));
353
+ const { temperature, reason } = classify(refs, scanRootFor(rel), isStructural(rel));
354
+ let bytes = 0;
355
+ try {
356
+ bytes = fs.statSync(path.join(root, rel)).size;
357
+ } catch {
358
+ bytes = 0;
359
+ }
360
+ files.push({
361
+ path: rel,
362
+ bytes,
363
+ addedOn: addDates.get(rel) ?? null,
364
+ temperature,
365
+ reason,
366
+ referrers: refs.map((r) => r.from).sort(),
367
+ });
368
+ }
369
+ files.sort((a, b) => a.path.localeCompare(b.path));
370
+
371
+ let commit = 'unknown';
372
+ try {
373
+ commit = git(['rev-parse', '--short', 'HEAD'], root).trim();
374
+ } catch {
375
+ commit = 'unknown';
376
+ }
377
+ return { generatedAt: censusDateStamp(), commit, files };
378
+ }
379
+
380
+ export interface Totals {
381
+ readonly files: number;
382
+ readonly bytes: number;
383
+ }
384
+
385
+ export function totalsBy(files: readonly EvidenceFile[], key: (f: EvidenceFile) => string): Map<string, Totals> {
386
+ const out = new Map<string, Totals>();
387
+ for (const f of files) {
388
+ const k = key(f);
389
+ const prev = out.get(k) ?? { files: 0, bytes: 0 };
390
+ out.set(k, { files: prev.files + 1, bytes: prev.bytes + f.bytes });
391
+ }
392
+ return out;
393
+ }
394
+
395
+ /** Top-level directory under the evidence root, or `(root)` for a file directly in it. */
396
+ export function subtreeOf(rel: string): string {
397
+ const rest = rel.slice(EVIDENCE_ROOT.length + 1);
398
+ const slash = rest.indexOf('/');
399
+ return slash === -1 ? '(root)' : rest.slice(0, slash);
400
+ }
401
+
402
+ function mib(bytes: number): string {
403
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MiB`;
404
+ }
405
+
406
+ /**
407
+ * The newest existing report other than `exclude`, or null when there is none.
408
+ *
409
+ * `--since latest` resolves through this so the release pipeline needs no date
410
+ * arithmetic: the names sort lexicographically because they carry an ISO date,
411
+ * and the file about to be written is excluded so a same-day re-run compares
412
+ * against an earlier report rather than against itself.
413
+ *
414
+ * It picks the newest OTHER report, not the newest report of an earlier
415
+ * release. A release resumed across UTC midnight writes a second census, and
416
+ * that one's baseline is the first census of the same release — so its delta
417
+ * covers minutes, not a release. The heading it renders under says "since the
418
+ * previous report", which is exactly that, so nothing misreports; a reader
419
+ * wanting a release-wide delta compares against the earlier release by hand.
420
+ */
421
+ export function latestReportBefore(names: readonly string[], exclude: string): string | null {
422
+ const candidates = names
423
+ .filter((n) => n.startsWith(REPORT_PREFIX) && n.endsWith(REPORT_EXT) && n !== exclude)
424
+ .sort();
425
+ return candidates.at(-1) ?? null;
426
+ }
427
+
428
+ /** The cold paths a previous report listed, read back out of its own fenced block. */
429
+ export function coldPathsIn(reportText: string): Set<string> {
430
+ const out = new Set<string>();
431
+ for (const line of reportText.split('\n')) {
432
+ const t = line.trim();
433
+ if (t.startsWith(`${EVIDENCE_ROOT}/`)) out.add(t);
434
+ }
435
+ return out;
436
+ }
437
+
438
+ export function renderReport(census: Census, previousCold: Set<string> | null): string {
439
+ const byTemp = totalsBy(census.files, (f) => f.temperature);
440
+ const cold = census.files.filter((f) => f.temperature === 'cold');
441
+ const warm = census.files.filter((f) => f.temperature === 'warm');
442
+ const all: Totals = { files: census.files.length, bytes: census.files.reduce((s, f) => s + f.bytes, 0) };
443
+
444
+ const L: string[] = [];
445
+ L.push('<!-- evidence-type: analysis -->');
446
+ L.push('');
447
+ L.push(`# Evidence temperature census — ${census.generatedAt}`);
448
+ L.push('');
449
+ L.push(
450
+ `Generated by \`./scripts-run src/scripts/report_evidence_temperature --write\` at \`${census.commit}\`. ` +
451
+ 'Report-only: it classifies, and refuses nothing.',
452
+ );
453
+ L.push('');
454
+ L.push('## The rule');
455
+ L.push('');
456
+ L.push(
457
+ 'Classification is by reference, never by age. An old file a contract cites is load-bearing; ' +
458
+ 'a file added last week that nothing reads is not.',
459
+ );
460
+ L.push('');
461
+ L.push('| Class | Definition |');
462
+ L.push('|---|---|');
463
+ L.push(
464
+ '| cold | No tracked file anywhere in the repository references its path, or — when the filename is unique repo-wide — its bare filename. |',
465
+ );
466
+ L.push(
467
+ '| warm | Referenced only by archived or skipped roadmaps, or only by other files that are themselves under the evidence tree. |',
468
+ );
469
+ L.push(
470
+ '| hot | Referenced by at least one live tracked file outside the evidence tree, or sitting under a directory a gate enumerates as its corpus. |',
471
+ );
472
+ L.push('');
473
+ L.push(
474
+ 'Cold means zero inbound references from anywhere, the evidence tree included. The weaker reading — ' +
475
+ 'nothing outside evidence cites it — would class a file that a live analysis document links to as cold, ' +
476
+ 'and moving it would break the reference checker, which scans the whole of `agents/`. Zero-inbound is the ' +
477
+ 'only definition under which the cold set moves without rewriting one link.',
478
+ );
479
+ L.push('');
480
+ L.push(
481
+ 'Matching is permissive on purpose and the error direction is one-way: a citation form the resolver misses ' +
482
+ 'would class a live file cold, which is the expensive mistake, while a loose match only leaves a dead file ' +
483
+ 'where it already is. Tokens are therefore read from prose, from JSON values and from code alike, not only ' +
484
+ 'from backticks or links.',
485
+ );
486
+ L.push('');
487
+ L.push('## Totals');
488
+ L.push('');
489
+ L.push('| Class | Files | Bytes |');
490
+ L.push('|---|---:|---:|');
491
+ for (const t of ['hot', 'warm', 'cold'] as const) {
492
+ const v = byTemp.get(t) ?? { files: 0, bytes: 0 };
493
+ L.push(`| ${t} | ${v.files} | ${mib(v.bytes)} |`);
494
+ }
495
+ L.push(`| **all** | **${all.files}** | **${mib(all.bytes)}** |`);
496
+ L.push('');
497
+ L.push('## Why each file is where it is');
498
+ L.push('');
499
+ L.push('| Reason | Files | Bytes |');
500
+ L.push('|---|---:|---:|');
501
+ const byReason = totalsBy(census.files, (f) => f.reason);
502
+ for (const [reason, v] of [...byReason].sort((a, b) => b[1].files - a[1].files)) {
503
+ L.push(`| ${reason} | ${v.files} | ${mib(v.bytes)} |`);
504
+ }
505
+ L.push('');
506
+ L.push('## By subtree');
507
+ L.push('');
508
+ L.push('| Subtree | Files | Bytes | hot | warm | cold |');
509
+ L.push('|---|---:|---:|---:|---:|---:|');
510
+ const subtrees = [...new Set(census.files.map((f) => subtreeOf(f.path)))].sort();
511
+ for (const sub of subtrees) {
512
+ const inSub = census.files.filter((f) => subtreeOf(f.path) === sub);
513
+ const bytes = inSub.reduce((s, f) => s + f.bytes, 0);
514
+ const h = inSub.filter((f) => f.temperature === 'hot').length;
515
+ const w = inSub.filter((f) => f.temperature === 'warm').length;
516
+ const c = inSub.filter((f) => f.temperature === 'cold').length;
517
+ L.push(`| ${sub} | ${inSub.length} | ${mib(bytes)} | ${h} | ${w} | ${c} |`);
518
+ }
519
+ L.push('');
520
+ L.push('## Gates that scan the tree, and what each would lose');
521
+ L.push('');
522
+ L.push(
523
+ 'A gate below enumerates its directory and derives a verdict from what is in it. Membership is the point, ' +
524
+ 'so every file beneath one of these roots is hot by that fact alone — a gate that relies on a file ' +
525
+ 'reclassifies it hot, applied mechanically rather than by hand. A file leaving one of these roots would ' +
526
+ 'leave its gate scanning fewer files and still exiting green.',
527
+ );
528
+ L.push('');
529
+ L.push('| Scan root | Files | Gates reading it | What leaving the scan root would cost |');
530
+ L.push('|---|---:|---|---|');
531
+ for (const root of GATE_SCAN_ROOTS) {
532
+ const n = census.files.filter((f) => f.path.startsWith(`${EVIDENCE_ROOT}/${root.dir}/`)).length;
533
+ L.push(`| \`${root.dir}/\` | ${n} | ${root.gates.map((g) => `\`${g}\``).join(', ')} | ${root.loses} |`);
534
+ }
535
+ L.push('');
536
+ L.push(
537
+ 'Four readers of the tree are deliberately absent from that table. `check_references` scans the whole of ' +
538
+ '`agents/` but validates the links a file contains rather than requiring the file to exist, so a file ' +
539
+ 'moved to another directory under `agents/` stays inside its scan root and nothing changes for it. ' +
540
+ '`lint_evidence_artifacts` is diff-scoped against a base ref and fires on files added, not on files ' +
541
+ 'present, so it gains and loses nothing from a move either. `check_council_references` and ' +
542
+ '`lint_hidden_unicode` name the analysis directory as an allowed prefix rather than as a corpus; a cold ' +
543
+ 'location added beside it would need the same one-line entry in each, and that is the only edit either ' +
544
+ 'of them needs.',
545
+ );
546
+ L.push('');
547
+ L.push('## The cold set');
548
+ L.push('');
549
+ L.push(
550
+ `${cold.length} tracked files, ${mib(cold.reduce((s, f) => s + f.bytes, 0))}, referenced by nothing. ` +
551
+ 'Listed in full because the move that follows has to be checkable against a list, not against a count.',
552
+ );
553
+ L.push('');
554
+ L.push('```text');
555
+ for (const f of cold) L.push(f.path);
556
+ L.push('```');
557
+ L.push('');
558
+ L.push('## The warm set');
559
+ L.push('');
560
+ L.push(
561
+ `${warm.length} tracked files, ${mib(warm.reduce((s, f) => s + f.bytes, 0))}, cited only by archived work ` +
562
+ 'or by other evidence. These are not candidates for a move: a citation inside the evidence tree is a link ' +
563
+ 'that would break.',
564
+ );
565
+ L.push('');
566
+ if (previousCold !== null) {
567
+ const newly = cold.filter((f) => !previousCold.has(f.path));
568
+ L.push('## Newly cold since the previous report');
569
+ L.push('');
570
+ L.push(`${newly.length} file(s).`);
571
+ L.push('');
572
+ if (newly.length > 0) {
573
+ L.push('```text');
574
+ for (const f of newly) L.push(f.path);
575
+ L.push('```');
576
+ L.push('');
577
+ }
578
+ }
579
+ return L.join('\n');
580
+ }
581
+
582
+ function usage(): never {
583
+ process.stderr.write(
584
+ 'usage: report_evidence_temperature [--write] [--out PATH] [--json] [--since REPORT.md] [--quiet]\n',
585
+ );
586
+ process.exit(2);
587
+ }
588
+
589
+ export function main(argv: readonly string[]): number {
590
+ let write = false;
591
+ let json = false;
592
+ let quiet = false;
593
+ let out: string | null = null;
594
+ let since: string | null = null;
595
+ for (let i = 0; i < argv.length; i += 1) {
596
+ const a = argv[i];
597
+ if (a === '--write') write = true;
598
+ else if (a === '--json') json = true;
599
+ else if (a === '--quiet') quiet = true;
600
+ else if (a === '--out') {
601
+ out = argv[i + 1] ?? null;
602
+ i += 1;
603
+ if (out === null) usage();
604
+ } else if (a === '--since') {
605
+ since = argv[i + 1] ?? null;
606
+ i += 1;
607
+ if (since === null) usage();
608
+ } else usage();
609
+ }
610
+
611
+ const census = buildCensus(REPO_ROOT);
612
+ if (json) {
613
+ process.stdout.write(`${JSON.stringify(census, null, 2)}\n`);
614
+ return 0;
615
+ }
616
+
617
+ const dest = out ?? defaultReportPath(census.generatedAt);
618
+
619
+ let previousCold: Set<string> | null = null;
620
+ if (since === 'latest') {
621
+ // No previous report is a legitimate first run, never an error.
622
+ const dir = path.join(REPO_ROOT, REPORT_DIR);
623
+ const names = fs.existsSync(dir) ? fs.readdirSync(dir) : [];
624
+ const prev = latestReportBefore(names, path.posix.basename(dest));
625
+ previousCold = prev === null ? null : coldPathsIn(fs.readFileSync(path.join(dir, prev), 'utf8'));
626
+ } else if (since !== null) {
627
+ const abs = path.isAbsolute(since) ? since : path.join(REPO_ROOT, since);
628
+ if (!fs.existsSync(abs)) {
629
+ process.stderr.write(`--since: no such report: ${since}\n`);
630
+ return 2;
631
+ }
632
+ previousCold = coldPathsIn(fs.readFileSync(abs, 'utf8'));
633
+ }
634
+
635
+ const body = renderReport(census, previousCold);
636
+ if (write) {
637
+ const abs = path.isAbsolute(dest) ? dest : path.join(REPO_ROOT, dest);
638
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
639
+ fs.writeFileSync(abs, `${body}\n`, 'utf8');
640
+ if (!quiet) process.stdout.write(`wrote ${path.relative(REPO_ROOT, abs)}\n`);
641
+ } else if (!quiet) {
642
+ process.stdout.write(`${body}\n`);
643
+ }
644
+
645
+ const byTemp = totalsBy(census.files, (f) => f.temperature);
646
+ const line = (['hot', 'warm', 'cold'] as const)
647
+ .map((t) => `${t}=${(byTemp.get(t) ?? { files: 0, bytes: 0 }).files}`)
648
+ .join(' · ');
649
+ process.stdout.write(`evidence temperature · ${census.files.length} tracked files · ${line}\n`);
650
+ return 0;
651
+ }
652
+
653
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
654
+ // `process.exitCode`, never `process.exit`: the JSON mode writes megabytes
655
+ // and an immediate exit truncates a pipe mid-string.
656
+ process.exitCode = main(process.argv.slice(2));
657
+ }