@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,197 @@
1
+ /**
2
+ * Per-judge position-consistency — order-swap over sampled pairwise judgments.
3
+ *
4
+ * `road-to-inbox-harvest-2026-08-e-council-topology-evidence` Phase 3, step 3.5:
5
+ * "repeat sampled pairwise judgments with candidate order reversed; emit a
6
+ * per-judge position-consistency metric", verified by "the metric exists per
7
+ * judge and is reported with the verdict".
8
+ *
9
+ * ── What already existed, and why this is not a second copy of it ────────────
10
+ * `check_quality_regression.evaluatePair` (`:84-108`) already judges every pair
11
+ * in both orders and resolves a flip to `inconsistent` rather than to a winner,
12
+ * and `_lib/judge_hygiene.ts:5-9` records that as stronger than the step that
13
+ * asked for it. Two things it does not do, and both are the point here:
14
+ *
15
+ * 1. It is SINGLE-JUDGE. `aggregate` returns one run-wide `inconsistency_rate`
16
+ * (`:216`), so two judges of differing reliability average into one number
17
+ * that describes neither. The step asks for the metric PER JUDGE, which is
18
+ * what makes it actionable: you can drop the flipper.
19
+ * 2. It reports presence, not DIRECTION. A judge that flips at random and a
20
+ * judge that always prefers whatever it was shown first produce the same
21
+ * inconsistency rate and need opposite remedies — the first is noise, the
22
+ * second is a systematic bias that survives averaging and quietly decides
23
+ * verdicts. `first_position_rate` separates them.
24
+ *
25
+ * That module is the thin-vs-eager token gate and is not council code; this one
26
+ * is council-side and stays a pure library — no transport, no fixture path, no
27
+ * `main`. The judge is an injected function, so the whole surface is testable
28
+ * against a scripted judge and costs nothing to exercise.
29
+ *
30
+ * ── Honest scope ────────────────────────────────────────────────────────────
31
+ * The shipped council has NO pairwise judging stage (`grep -rn pairwise
32
+ * src/scripts/ai_council` returns nothing): members deliberate, peer-review and
33
+ * are scored on findings, never compared two at a time. So this module is the
34
+ * metric and its renderer, exercised by the provider-leakage bench that Phase 3
35
+ * builds; it does not claim a live council verdict currently carries the line,
36
+ * because there is currently no live council judgment for it to describe.
37
+ */
38
+
39
+ import { _sha256_hex } from './blind_review.js';
40
+
41
+ /** A judge's preference between the two PRESENTED candidates — order-relative. */
42
+ export type PairwiseVerdict = 'first' | 'second' | 'tie';
43
+
44
+ /**
45
+ * The pluggable judge. `first`/`second` are the candidate bodies in PRESENTED
46
+ * order, so a judge implementation cannot tell which arm it is looking at
47
+ * unless the bodies themselves leak it.
48
+ */
49
+ export type PairwiseJudge = (ctx: { readonly id: string }, first: string, second: string) => PairwiseVerdict;
50
+
51
+ /** How a pair resolved once both orders were seen. */
52
+ export type PairResolution = 'a' | 'b' | 'tie' | 'inconsistent';
53
+
54
+ export interface SwapObservation {
55
+ readonly id: string;
56
+ /** Verdict with A presented first. */
57
+ readonly forward: PairwiseVerdict;
58
+ /** Verdict with B presented first. */
59
+ readonly reverse: PairwiseVerdict;
60
+ readonly resolution: PairResolution;
61
+ }
62
+
63
+ /**
64
+ * Judge one (a, b) pair in BOTH orders.
65
+ *
66
+ * A verdict that does not survive the swap resolves to `inconsistent`, never to
67
+ * a winner — a flagged tie would still be a tie in somebody's denominator,
68
+ * while `inconsistent` is its own bucket with its own reported rate.
69
+ */
70
+ export function judgeBothOrders(id: string, a: string, b: string, judge: PairwiseJudge): SwapObservation {
71
+ const ctx = { id };
72
+ const forward = judge(ctx, a, b);
73
+ const reverse = judge(ctx, b, a);
74
+ // Normalise each verdict to the CANDIDATE it names, not the position.
75
+ const wf: PairResolution = forward === 'first' ? 'a' : forward === 'second' ? 'b' : 'tie';
76
+ const wr: PairResolution = reverse === 'first' ? 'b' : reverse === 'second' ? 'a' : 'tie';
77
+ const resolution: PairResolution = wf === wr ? wf : 'inconsistent';
78
+ return { id, forward, reverse, resolution };
79
+ }
80
+
81
+ export interface JudgeConsistency {
82
+ readonly judge: string;
83
+ /** Pairs actually repeated in both orders. */
84
+ readonly sampled: number;
85
+ /** Both orders named the same candidate, or both said tie. */
86
+ readonly consistent: number;
87
+ /** The two orders disagreed. */
88
+ readonly inconsistent: number;
89
+ /**
90
+ * `consistent / sampled`. `null` at zero pairs — a rate over an empty
91
+ * denominator is not 1.0, and reporting it as 1.0 would make an unrun swap
92
+ * indistinguishable from a perfectly stable judge.
93
+ */
94
+ readonly position_consistency: number | null;
95
+ /**
96
+ * Share of DECISIVE verdicts (`first`/`second`, both orders pooled) that
97
+ * named whichever candidate was shown FIRST. ~0.5 is unbiased; near 1.0 is
98
+ * primacy, near 0.0 recency. `null` when nothing decisive was said.
99
+ *
100
+ * This is the direction an inconsistency rate cannot show, and the reason a
101
+ * judge that flips at random is a different problem from one that always
102
+ * prefers the top of the list.
103
+ */
104
+ readonly first_position_rate: number | null;
105
+ /** Decisive verdicts counted for `first_position_rate`. */
106
+ readonly decisive_verdicts: number;
107
+ }
108
+
109
+ /** Per-judge metric over that judge's own swapped observations. */
110
+ export function positionConsistency(judge: string, obs: readonly SwapObservation[]): JudgeConsistency {
111
+ let consistent = 0;
112
+ let inconsistent = 0;
113
+ let firstWins = 0;
114
+ let decisive = 0;
115
+ for (const o of obs) {
116
+ if (o.resolution === 'inconsistent') {
117
+ inconsistent += 1;
118
+ } else {
119
+ consistent += 1;
120
+ }
121
+ for (const v of [o.forward, o.reverse]) {
122
+ if (v === 'first') {
123
+ decisive += 1;
124
+ firstWins += 1;
125
+ } else if (v === 'second') {
126
+ decisive += 1;
127
+ }
128
+ }
129
+ }
130
+ const sampled = obs.length;
131
+ return {
132
+ judge,
133
+ sampled,
134
+ consistent,
135
+ inconsistent,
136
+ position_consistency: sampled > 0 ? consistent / sampled : null,
137
+ first_position_rate: decisive > 0 ? firstWins / decisive : null,
138
+ decisive_verdicts: decisive,
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Deterministic sample of `ids` at `rate`, seeded by `seed`.
144
+ *
145
+ * The step says "SAMPLED pairwise judgments", because the swap doubles judge
146
+ * calls and doubling every one of them to measure the judge is a cost the
147
+ * measurement does not need. Deterministic so a published consistency figure is
148
+ * reproducible from the report's own seed — a sample nobody can re-draw is a
149
+ * number nobody can check.
150
+ *
151
+ * `rate <= 0` samples nothing; `rate >= 1` samples everything.
152
+ */
153
+ export function sampleForSwap(ids: readonly string[], rate: number, seed: string): string[] {
154
+ if (!(rate > 0)) {
155
+ return [];
156
+ }
157
+ if (rate >= 1) {
158
+ return [...ids];
159
+ }
160
+ return ids.filter((id) => _unitHash(`${seed} ${id}`) < rate);
161
+ }
162
+
163
+ /**
164
+ * Stable [0,1) hash of a string.
165
+ *
166
+ * Reuses `blind_review._sha256_hex` rather than carrying a local one. That
167
+ * export exists for exactly this reason — `blind_review.ts:38-44` records that
168
+ * two hashes of one input are two answers to one question and the second is the
169
+ * one nobody updates. A hand-rolled FNV-1a was tried here first and its low bits
170
+ * were skewed enough that a rate of 0.25 sampled half the corpus; the test that
171
+ * caught it is kept.
172
+ */
173
+ function _unitHash(s: string): number {
174
+ return parseInt(_sha256_hex(s).slice(0, 8), 16) / 4294967296;
175
+ }
176
+
177
+ /**
178
+ * One report line per judge, for emission BESIDE a verdict.
179
+ *
180
+ * `n/a` rather than a number when nothing was sampled: the step's verify is
181
+ * that the metric is reported, and a report that silently prints nothing for an
182
+ * unrun swap satisfies it only by looking like it ran.
183
+ */
184
+ export function renderPositionConsistency(rows: readonly JudgeConsistency[]): string {
185
+ if (rows.length === 0) {
186
+ return 'position consistency: no judge sampled — order-swap NOT RUN.';
187
+ }
188
+ const pct = (v: number | null): string => (v === null ? 'n/a' : `${(v * 100).toFixed(0)}%`);
189
+ return rows
190
+ .map(
191
+ (r) =>
192
+ `position consistency · ${r.judge}: ${pct(r.position_consistency)} ` +
193
+ `(${String(r.consistent)}/${String(r.sampled)} pairs survived the swap; ` +
194
+ `first-position rate ${pct(r.first_position_rate)} over ${String(r.decisive_verdicts)} decisive verdicts)`,
195
+ )
196
+ .join('\n');
197
+ }
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Corpus assembler for the provider-recognition leakage bench.
3
+ *
4
+ * `road-to-inbox-harvest-2026-08-e-council-topology-evidence`,
5
+ * `blocker: leakage-bench-needs-assembler-and-design-forks`, todo item 1:
6
+ * `collectGuesses` and `scoreRecognition` in `provider_leakage_bench.ts` both
7
+ * take `items: readonly LeakageItem[]` as an INJECTED parameter, and nothing in
8
+ * this tree ever produced those items from real council output. That gap — not
9
+ * quota, and not the un-committability of the corpus — is what kept the bench
10
+ * at NOT RUN. This module closes it and nothing else.
11
+ *
12
+ * It reads a directory of council response records — the JSON shape
13
+ * `{ responses: [{ provider, model, text, error, ... }] }` written per council
14
+ * run — and emits one `LeakageItem` per usable response body, with the response
15
+ * author as ground truth.
16
+ *
17
+ * ── Why the directory is a parameter, not an import ─────────────────────────
18
+ * `session.ts` binds `RESPONSES_DIR` at module load against the running
19
+ * checkout's root (`session.ts:70-73`). Importing it would couple this loader
20
+ * to one root and make it untestable over a fixture tree — and the same
21
+ * root-binding is part of the retention defect this corpus is the subject of.
22
+ * So the directory arrives as `opts.responsesDir` and this module imports
23
+ * nothing from `session.ts`.
24
+ *
25
+ * ── What this module does NOT do ────────────────────────────────────────────
26
+ * It does NOT anonymise. The bodies in `LeakageCorpus.items` are RAW: whatever
27
+ * the provider wrote, including any self-identification in the prose. An
28
+ * anonymisation pass is a DECLARED OPEN DESIGN FORK in the blocker above and is
29
+ * not settled here, so `assembleLeakageCorpus` MUST NOT be fed to a live rater
30
+ * until that fork is settled — a rater shown a raw body may be reading a
31
+ * signature rather than a style, and the recognition rate would then measure
32
+ * the absence of anonymisation rather than its failure.
33
+ *
34
+ * The `anonymise` seam below exists so the settled fork has somewhere to land
35
+ * without changing this module's shape. Its default is `IDENTITY_ANONYMISE`,
36
+ * which returns its input unchanged — deliberately, and tested for it, so that
37
+ * "the default anonymiser" can never be mistaken for anonymisation having
38
+ * happened.
39
+ *
40
+ * It also does NOT decide, score, prompt, or dispatch. No CLI, no `main()`, no
41
+ * transport, no provider calls.
42
+ *
43
+ * ── Why nothing here deletes ────────────────────────────────────────────────
44
+ * This module contains no write, no `unlink` and no `rm`, and must never gain
45
+ * one. The corpus IS the measurement subject: pruning it destroys the
46
+ * instrument, and the over-retention of these bodies is itself a recorded
47
+ * finding rather than a mess to tidy. Retention is therefore REPORTED — both
48
+ * partitions, counted per family in the census — and enforced by nobody here.
49
+ * The caller chooses which partition it is willing to measure over.
50
+ */
51
+ import * as crypto from 'node:crypto';
52
+ import * as fs from 'node:fs';
53
+ import * as path from 'node:path';
54
+
55
+ import type { LeakageItem } from './provider_leakage_bench.js';
56
+
57
+ /**
58
+ * The synthetic fixture's basename. Its own body states that "a live runner
59
+ * must refuse this file"; that refusal is implemented here rather than
60
+ * documented, because a recognition rate over hand-written fixture prose
61
+ * describes the fixture author and not a model.
62
+ */
63
+ export const SYNTHETIC_FIXTURE_BASENAME = 'smoke-items.json';
64
+
65
+ /** Any path segment containing this marker is the synthetic bench fixture dir. */
66
+ export const SYNTHETIC_PATH_MARKER = 'council-provider-leakage';
67
+
68
+ /** Mirrors the declared council retention window. Reported, never applied. */
69
+ export const DEFAULT_RETENTION_DAYS = 7;
70
+
71
+ const MS_PER_DAY = 86_400_000;
72
+
73
+ /** Thrown when the assembler is pointed at data that cannot measure leakage. */
74
+ export class SyntheticCorpusRefusal extends Error {
75
+ readonly source: string;
76
+ constructor(source: string, why: string) {
77
+ super(`refusing synthetic corpus data: ${source} — ${why}`);
78
+ this.name = 'SyntheticCorpusRefusal';
79
+ this.source = source;
80
+ }
81
+ }
82
+
83
+ /** Machine-readable exclusion buckets. A dropped body always names one. */
84
+ export type ExclusionReason =
85
+ /** Not a `.json` file (the tree also holds `.md` notes and stray dirs). */
86
+ | 'not-json-file'
87
+ /** File exists but is not parseable JSON. */
88
+ | 'unparseable-json'
89
+ /** Parsed, but carries no top-level `responses` array. */
90
+ | 'no-responses-array'
91
+ /** A `responses` entry that is not an object. */
92
+ | 'not-an-object'
93
+ /** Entry has a non-falsy `error` — a failed call has no body to rate. */
94
+ | 'response-carried-error'
95
+ /** Entry has no non-empty `provider`, so there is no ground truth. */
96
+ | 'missing-provider'
97
+ /** Entry has no non-empty `text`, so there is nothing to show a rater. */
98
+ | 'empty-text';
99
+
100
+ /** One thing that did not become an item, and why. Never a silent drop. */
101
+ export interface ExcludedRecord {
102
+ /** Source path relative to `responsesDir`, POSIX-separated. */
103
+ readonly source: string;
104
+ /** Index within `responses`, or `null` for a whole-file exclusion. */
105
+ readonly index: number | null;
106
+ readonly reason: ExclusionReason;
107
+ /** One clause of detail, safe to print beside the reason. */
108
+ readonly detail: string;
109
+ }
110
+
111
+ /**
112
+ * A corpus item: a `LeakageItem` plus the provenance the SCORER may see and the
113
+ * rater may not.
114
+ */
115
+ export interface CorpusItem extends LeakageItem {
116
+ /** Source path relative to `responsesDir`, POSIX-separated. */
117
+ readonly source_file: string;
118
+ readonly response_index: number;
119
+ /** Source file mtime in epoch ms — the only retention input. */
120
+ readonly mtime_ms: number;
121
+ /** Whether `mtime_ms` falls inside `retention_days` of `now`. Reported only. */
122
+ readonly within_retention: boolean;
123
+ }
124
+
125
+ /** Per-family counts. Keys are whatever providers were actually present. */
126
+ export type FamilyCounts = Readonly<Record<string, number>>;
127
+
128
+ export interface CorpusCensus {
129
+ readonly files_scanned: number;
130
+ readonly files_excluded: number;
131
+ readonly responses_seen: number;
132
+ readonly items_kept: number;
133
+ readonly items_excluded: number;
134
+ /** Echoed so a published census is self-describing. */
135
+ readonly retention_days: number;
136
+ readonly now_ms: number;
137
+ /** Kept items whose source mtime is inside the TTL, counted per family. */
138
+ readonly within_retention: FamilyCounts;
139
+ /** Kept items whose source mtime is older than the TTL, per family. */
140
+ readonly over_retention: FamilyCounts;
141
+ readonly within_retention_total: number;
142
+ readonly over_retention_total: number;
143
+ }
144
+
145
+ export interface LeakageCorpus {
146
+ readonly items: readonly CorpusItem[];
147
+ /** Sorted distinct families actually present. Never a hardcoded list. */
148
+ readonly families: readonly string[];
149
+ readonly excluded: readonly ExcludedRecord[];
150
+ readonly census: CorpusCensus;
151
+ }
152
+
153
+ export interface AssembleOptions {
154
+ /** Directory of council response records. Walked recursively. */
155
+ readonly responsesDir: string;
156
+ /** Epoch ms treated as "now" for the retention partition. */
157
+ readonly now?: number;
158
+ readonly retentionDays?: number;
159
+ /**
160
+ * The anonymisation seam. Defaults to `IDENTITY_ANONYMISE`, i.e. NO
161
+ * anonymisation — see the header. Supplying one does not make this module
162
+ * an anonymiser; it makes the fork's decision injectable.
163
+ */
164
+ readonly anonymise?: (text: string) => string;
165
+ }
166
+
167
+ /** The default `anonymise`: returns its argument unchanged. Not anonymisation. */
168
+ export const IDENTITY_ANONYMISE = (text: string): string => text;
169
+
170
+ /**
171
+ * Assemble a leakage corpus from a directory of council response records.
172
+ *
173
+ * Throws `SyntheticCorpusRefusal` — never excludes-and-continues — when the
174
+ * directory or any file in it is synthetic bench data. The blocker's wording is
175
+ * that a live runner must REFUSE it, and an exclusion is not a refusal: it
176
+ * would let a run proceed over a silently smaller corpus.
177
+ */
178
+ export function assembleLeakageCorpus(opts: AssembleOptions): LeakageCorpus {
179
+ const now = opts.now ?? Date.now();
180
+ const retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
181
+ const anonymise = opts.anonymise ?? IDENTITY_ANONYMISE;
182
+ const root = opts.responsesDir;
183
+
184
+ if (_posix(path.resolve(root)).includes(SYNTHETIC_PATH_MARKER)) {
185
+ throw new SyntheticCorpusRefusal(
186
+ root,
187
+ `the directory path contains "${SYNTHETIC_PATH_MARKER}", the synthetic bench fixture location`,
188
+ );
189
+ }
190
+
191
+ const excluded: ExcludedRecord[] = [];
192
+ const items: CorpusItem[] = [];
193
+ let filesScanned = 0;
194
+ let filesExcluded = 0;
195
+ let responsesSeen = 0;
196
+
197
+ // Sorted relative paths: two runs over the same tree emit byte-identical
198
+ // output, which a bench whose result gets published has to be able to claim.
199
+ for (const rel of _walkSorted(root)) {
200
+ const abs = path.join(root, rel);
201
+ const base = path.basename(rel);
202
+
203
+ if (base === SYNTHETIC_FIXTURE_BASENAME) {
204
+ throw new SyntheticCorpusRefusal(
205
+ rel,
206
+ `basename is the synthetic fixture "${SYNTHETIC_FIXTURE_BASENAME}", whose own body states a live runner must refuse it`,
207
+ );
208
+ }
209
+ if (!base.endsWith('.json')) {
210
+ excluded.push({ source: rel, index: null, reason: 'not-json-file', detail: 'no .json extension' });
211
+ filesExcluded += 1;
212
+ continue;
213
+ }
214
+
215
+ filesScanned += 1;
216
+ let parsed: unknown;
217
+ try {
218
+ parsed = JSON.parse(fs.readFileSync(abs, 'utf8'));
219
+ } catch (err) {
220
+ excluded.push({
221
+ source: rel,
222
+ index: null,
223
+ reason: 'unparseable-json',
224
+ detail: err instanceof Error ? err.message : String(err),
225
+ });
226
+ filesExcluded += 1;
227
+ continue;
228
+ }
229
+
230
+ // A truthy top-level `synthetic` key is the fixture's self-declaration.
231
+ // Checked after the basename guard so a renamed fixture is still caught.
232
+ if (_isRecord(parsed) && Boolean(parsed['synthetic'])) {
233
+ throw new SyntheticCorpusRefusal(rel, 'the record carries a truthy top-level "synthetic" key');
234
+ }
235
+
236
+ const responses = _isRecord(parsed) ? parsed['responses'] : undefined;
237
+ if (!Array.isArray(responses)) {
238
+ excluded.push({
239
+ source: rel,
240
+ index: null,
241
+ reason: 'no-responses-array',
242
+ detail: 'no top-level `responses` array',
243
+ });
244
+ filesExcluded += 1;
245
+ continue;
246
+ }
247
+
248
+ const mtimeMs = fs.statSync(abs).mtimeMs;
249
+ const withinRetention = now - mtimeMs <= retentionDays * MS_PER_DAY;
250
+
251
+ for (let i = 0; i < responses.length; i += 1) {
252
+ responsesSeen += 1;
253
+ const entry: unknown = responses[i];
254
+ if (!_isRecord(entry)) {
255
+ excluded.push({ source: rel, index: i, reason: 'not-an-object', detail: `entry is ${typeof entry}` });
256
+ continue;
257
+ }
258
+ const error = entry['error'];
259
+ if (Boolean(error)) {
260
+ excluded.push({
261
+ source: rel,
262
+ index: i,
263
+ reason: 'response-carried-error',
264
+ detail: typeof error === 'string' ? error.slice(0, 120) : 'non-falsy error field',
265
+ });
266
+ continue;
267
+ }
268
+ const provider = _nonEmptyString(entry['provider']);
269
+ if (provider === null) {
270
+ excluded.push({ source: rel, index: i, reason: 'missing-provider', detail: 'no non-empty provider' });
271
+ continue;
272
+ }
273
+ const text = _nonEmptyString(entry['text']);
274
+ if (text === null) {
275
+ excluded.push({ source: rel, index: i, reason: 'empty-text', detail: 'no non-empty text' });
276
+ continue;
277
+ }
278
+ items.push({
279
+ id: leakageItemId(rel, i),
280
+ text: anonymise(text),
281
+ true_family: provider,
282
+ source_file: rel,
283
+ response_index: i,
284
+ mtime_ms: mtimeMs,
285
+ within_retention: withinRetention,
286
+ });
287
+ }
288
+ }
289
+
290
+ const within: Record<string, number> = {};
291
+ const over: Record<string, number> = {};
292
+ for (const item of items) {
293
+ const bucket = item.within_retention ? within : over;
294
+ bucket[item.true_family] = (bucket[item.true_family] ?? 0) + 1;
295
+ }
296
+
297
+ return {
298
+ items,
299
+ families: [...new Set(items.map((i) => i.true_family))].sort(),
300
+ excluded,
301
+ census: {
302
+ files_scanned: filesScanned,
303
+ files_excluded: filesExcluded,
304
+ responses_seen: responsesSeen,
305
+ items_kept: items.length,
306
+ items_excluded: excluded.filter((e) => e.index !== null).length,
307
+ retention_days: retentionDays,
308
+ now_ms: now,
309
+ within_retention: within,
310
+ over_retention: over,
311
+ within_retention_total: _sum(within),
312
+ over_retention_total: _sum(over),
313
+ },
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Stable, collision-free, OPAQUE item id.
319
+ *
320
+ * The id is a digest of the source path plus the response index — NOT the
321
+ * basename — and that is load-bearing rather than stylistic: real filenames
322
+ * under `agents/runtime/council/responses/` include
323
+ * `anthropic-design-skills-integration.json` and several `claude-*.json`, so a
324
+ * basename-derived id would hand the rater the ground truth in the item's own
325
+ * identifier and the bench would measure reading, not recognition. The id must
326
+ * never embed the provider name, or any part of a path that might.
327
+ *
328
+ * Deterministic (a digest, not a counter) so two runs over the same tree
329
+ * produce the same ids, and index-suffixed so two bodies in one file cannot
330
+ * collide.
331
+ */
332
+ export function leakageItemId(relPath: string, index: number): string {
333
+ const digest = crypto.createHash('sha1').update(_posix(relPath)).digest('hex').slice(0, 12);
334
+ return `item-${digest}-${String(index)}`;
335
+ }
336
+
337
+ /** Recursive file walk, returning `root`-relative POSIX paths in sorted order. */
338
+ function _walkSorted(root: string): string[] {
339
+ const out: string[] = [];
340
+ const visit = (dirRel: string): void => {
341
+ let entries: fs.Dirent[];
342
+ try {
343
+ entries = fs.readdirSync(path.join(root, dirRel), { withFileTypes: true });
344
+ } catch {
345
+ return;
346
+ }
347
+ const names = entries.map((e) => e.name).sort();
348
+ for (const name of names) {
349
+ const rel = dirRel === '' ? name : `${dirRel}/${name}`;
350
+ const abs = path.join(root, rel);
351
+ let stat: fs.Stats;
352
+ try {
353
+ stat = fs.statSync(abs);
354
+ } catch {
355
+ continue;
356
+ }
357
+ if (stat.isDirectory()) {
358
+ // Real records nest: `<slug>.json/` directories hold per-round
359
+ // files. Recursing keeps those bodies eligible; the relative
360
+ // path keeps their ids distinct.
361
+ visit(rel);
362
+ } else if (stat.isFile()) {
363
+ out.push(rel);
364
+ }
365
+ }
366
+ };
367
+ visit('');
368
+ return out.sort();
369
+ }
370
+
371
+ function _posix(p: string): string {
372
+ return p.split(path.sep).join('/');
373
+ }
374
+
375
+ function _isRecord(v: unknown): v is Record<string, unknown> {
376
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
377
+ }
378
+
379
+ function _nonEmptyString(v: unknown): string | null {
380
+ return typeof v === 'string' && v.trim() !== '' ? v : null;
381
+ }
382
+
383
+ function _sum(counts: Readonly<Record<string, number>>): number {
384
+ return Object.values(counts).reduce((a, b) => a + b, 0);
385
+ }