@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,384 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Turnaround instrument — round-trips, batch size, blocking waits, context floor.
4
+ *
5
+ * WHY IT EXISTS. `agents/evidence/analysis/agent-turnaround-2026-08-30.md`
6
+ * measured that a user request in this package costs 42.6 API round-trips and a
7
+ * file change costs 58 tool calls, and its fifth finding is that NONE of it was
8
+ * measurable before: no instrument in the tree reported round-trips per request,
9
+ * tool-call batch size, or the blocking-wait tail, so no gate could have caught
10
+ * any of them growing. That analysis was a throwaway script. This is the
11
+ * committed form of it — `road-to-agent-turnaround` Phase 1.1.
12
+ *
13
+ * THE UNIT IS `requestId`, NOT THE ROW. The host writes `thinking`, `text` and
14
+ * `tool_use` as SEPARATE JSONL rows that share one `requestId`, so counting rows
15
+ * inflates every per-call figure. That was the first wrong answer the original
16
+ * measurement produced, and it is why this file groups before it counts.
17
+ *
18
+ * WHAT IT REPORTS, per corpus:
19
+ * - API calls per real user request (F1)
20
+ * - mean tool-call batch size (F1 — measured 1.00, i.e. fully serial)
21
+ * - the >60 s blocking tail and its share of tool time (F2)
22
+ * - the first-call context floor (F3 cross-check)
23
+ *
24
+ * A MEASUREMENT, NOT A GATE — by default. `--against-baseline` compares against
25
+ * `src/config/turnaround-budget.json` and is the only mode that can exit
26
+ * non-zero. Whether that mode may run in CI is answered in the config's own
27
+ * `_comment`, because a transcript store is machine-local and a fresh clone has
28
+ * none: a CI gate over it would be green-because-empty, the exact
29
+ * gate-that-scans-nothing-exits-green failure this repository names elsewhere.
30
+ *
31
+ * Usage:
32
+ * ./scripts-run src/scripts/probe_turnaround [--store PATH] [--limit N]
33
+ * ./scripts-run src/scripts/probe_turnaround --json
34
+ * ./scripts-run src/scripts/probe_turnaround --against-baseline
35
+ *
36
+ * The MEASURING session is excluded by default — see `recentSessions`.
37
+ *
38
+ * Exit codes: 0 = reported (or within baseline) · 1 = baseline regression, or an
39
+ * empty corpus under `--against-baseline` · 2 = usage error.
40
+ */
41
+ import * as fs from 'node:fs';
42
+ import * as path from 'node:path';
43
+ import { fileURLToPath } from 'node:url';
44
+
45
+ import { defaultStore, isInjectedBody, userText } from './conformance_scan.js';
46
+
47
+ const _FILE = fileURLToPath(import.meta.url);
48
+ const REPO_ROOT = path.resolve(path.dirname(_FILE), '..', '..');
49
+ export const BUDGET_REL = 'src/config/turnaround-budget.json';
50
+
51
+ /** The blocking-tail threshold, in seconds. Fixed by the source measurement. */
52
+ export const BLOCKING_SECONDS = 60;
53
+
54
+ type Entry = Record<string, unknown>;
55
+
56
+ function isObj(v: unknown): v is Record<string, unknown> {
57
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
58
+ }
59
+
60
+ export interface Turnaround {
61
+ sessions: number;
62
+ userRequests: number;
63
+ apiCalls: number;
64
+ callsPerRequest: number;
65
+ toolCalls: number;
66
+ /** Requests that carried at least one tool_use block. */
67
+ toolUsingRequests: number;
68
+ meanBatchSize: number;
69
+ toolSeconds: number;
70
+ blockingCalls: number;
71
+ blockingSeconds: number;
72
+ blockingShare: number;
73
+ contextFloorMin: number;
74
+ contextFloorMax: number;
75
+ }
76
+
77
+ const ZERO: Turnaround = {
78
+ sessions: 0,
79
+ userRequests: 0,
80
+ apiCalls: 0,
81
+ callsPerRequest: 0,
82
+ toolCalls: 0,
83
+ toolUsingRequests: 0,
84
+ meanBatchSize: 0,
85
+ toolSeconds: 0,
86
+ blockingCalls: 0,
87
+ blockingSeconds: 0,
88
+ blockingShare: 0,
89
+ contextFloorMin: 0,
90
+ contextFloorMax: 0,
91
+ };
92
+
93
+ function ms(iso: unknown): number | null {
94
+ if (typeof iso !== 'string') return null;
95
+ const t = Date.parse(iso);
96
+ return Number.isNaN(t) ? null : t;
97
+ }
98
+
99
+ /**
100
+ * The N most recently modified transcript files under `store`, excluding the
101
+ * MEASURING session.
102
+ *
103
+ * The exclusion is not hygiene, it is correctness, and it was found by the
104
+ * instrument failing its own baseline. Two consecutive runs minutes apart
105
+ * reported `calls_per_request` 81.42 then 81.61: the measuring session was
106
+ * adding API calls to the newest transcript while its user-request denominator
107
+ * stayed at one, so the probe was reading its own execution as a regression.
108
+ * The source analysis excluded the measuring session for the same reason and
109
+ * said so; this reproduces that rather than rediscovering it a third time.
110
+ *
111
+ * Identified by `CLAUDE_CODE_SESSION_ID` where the host exports it — the file
112
+ * is `<sessionId>.jsonl` — and otherwise by dropping the single
113
+ * most-recently-modified file, which is the same session by construction on any
114
+ * machine actually running this. `--include-current` opts back in for an
115
+ * offline corpus where nothing is being written.
116
+ */
117
+ export function recentSessions(store: string, limit: number, includeCurrent = false): string[] {
118
+ if (!fs.existsSync(store)) return [];
119
+ const all = fs
120
+ .readdirSync(store)
121
+ .filter((f) => f.endsWith('.jsonl'))
122
+ .map((f) => ({ f, m: fs.statSync(path.join(store, f)).mtimeMs }))
123
+ .sort((a, b) => b.m - a.m);
124
+ if (!includeCurrent && all.length > 0) {
125
+ const sid = process.env['CLAUDE_CODE_SESSION_ID'];
126
+ const idx =
127
+ sid !== undefined && sid !== ''
128
+ ? all.findIndex((x) => x.f === `${sid}.jsonl`)
129
+ : 0;
130
+ if (idx >= 0) all.splice(idx, 1);
131
+ }
132
+ return all.slice(0, limit).map(({ f }) => path.join(store, f));
133
+ }
134
+
135
+ export function measure(files: readonly string[]): Turnaround {
136
+ const requestIds = new Set<string>();
137
+ /** requestId → tool_use blocks in it. */
138
+ const batch = new Map<string, number>();
139
+ /** tool_use id → the timestamp the assistant emitted it. */
140
+ const emittedAt = new Map<string, number>();
141
+ const durations: number[] = [];
142
+ const contextFloors: number[] = [];
143
+ let userRequests = 0;
144
+ let sessions = 0;
145
+
146
+ for (const file of files) {
147
+ let sawFloor = false;
148
+ let sawAny = false;
149
+ for (const line of fs.readFileSync(file, 'utf8').split('\n')) {
150
+ if (!line.trim()) continue;
151
+ let e: Entry;
152
+ try {
153
+ e = JSON.parse(line) as Entry;
154
+ } catch {
155
+ continue;
156
+ }
157
+ if (e['isSidechain'] === true) continue;
158
+ sawAny = true;
159
+ const msg = e['message'];
160
+
161
+ if (e['type'] === 'user') {
162
+ // The SAME predicate the conformance scan uses, imported rather
163
+ // than re-typed: a skill body and a slash-command header arrive
164
+ // in the user role and are not requests. Counting them would
165
+ // deflate calls-per-request by inventing a denominator.
166
+ const text = userText(e);
167
+ if (text !== null && !isInjectedBody(text)) userRequests += 1;
168
+ if (isObj(msg) && Array.isArray(msg['content'])) {
169
+ const at = ms(e['timestamp']);
170
+ for (const b of msg['content']) {
171
+ if (!isObj(b) || b['type'] !== 'tool_result') continue;
172
+ const id = String(b['tool_use_id'] ?? '');
173
+ const started = emittedAt.get(id);
174
+ if (started === undefined || at === null) continue;
175
+ durations.push((at - started) / 1000);
176
+ emittedAt.delete(id);
177
+ }
178
+ }
179
+ continue;
180
+ }
181
+
182
+ if (e['type'] !== 'assistant') continue;
183
+ const rid = e['requestId'];
184
+ if (typeof rid === 'string' && rid !== '') requestIds.add(rid);
185
+
186
+ if (!sawFloor && isObj(msg) && isObj(msg['usage'])) {
187
+ const u = msg['usage'] as Record<string, unknown>;
188
+ const floor =
189
+ Number(u['input_tokens'] ?? 0) +
190
+ Number(u['cache_read_input_tokens'] ?? 0) +
191
+ Number(u['cache_creation_input_tokens'] ?? 0);
192
+ if (floor > 0) {
193
+ contextFloors.push(floor);
194
+ sawFloor = true;
195
+ }
196
+ }
197
+
198
+ if (!isObj(msg) || !Array.isArray(msg['content'])) continue;
199
+ const at = ms(e['timestamp']);
200
+ let n = 0;
201
+ for (const b of msg['content']) {
202
+ if (!isObj(b) || b['type'] !== 'tool_use') continue;
203
+ n += 1;
204
+ const id = String(b['id'] ?? '');
205
+ if (id !== '' && at !== null) emittedAt.set(id, at);
206
+ }
207
+ if (n > 0 && typeof rid === 'string' && rid !== '') {
208
+ batch.set(rid, (batch.get(rid) ?? 0) + n);
209
+ }
210
+ }
211
+ if (sawAny) sessions += 1;
212
+ }
213
+
214
+ const toolCalls = [...batch.values()].reduce((a, b) => a + b, 0);
215
+ const toolSeconds = durations.reduce((a, b) => a + b, 0);
216
+ const blocking = durations.filter((d) => d > BLOCKING_SECONDS);
217
+ const blockingSeconds = blocking.reduce((a, b) => a + b, 0);
218
+ const round = (x: number, p = 2): number => Math.round(x * 10 ** p) / 10 ** p;
219
+
220
+ return {
221
+ sessions,
222
+ userRequests,
223
+ apiCalls: requestIds.size,
224
+ callsPerRequest: userRequests === 0 ? 0 : round(requestIds.size / userRequests),
225
+ toolCalls,
226
+ toolUsingRequests: batch.size,
227
+ meanBatchSize: batch.size === 0 ? 0 : round(toolCalls / batch.size),
228
+ toolSeconds: round(toolSeconds, 1),
229
+ blockingCalls: blocking.length,
230
+ blockingSeconds: round(blockingSeconds, 1),
231
+ blockingShare: toolSeconds === 0 ? 0 : round(blockingSeconds / toolSeconds, 4),
232
+ contextFloorMin: contextFloors.length === 0 ? 0 : Math.min(...contextFloors),
233
+ contextFloorMax: contextFloors.length === 0 ? 0 : Math.max(...contextFloors),
234
+ };
235
+ }
236
+
237
+ export interface Budget {
238
+ baseline: {
239
+ calls_per_request: number;
240
+ mean_batch_size: number;
241
+ blocking_share: number;
242
+ context_floor_max: number;
243
+ };
244
+ empty_corpus: 'fail' | 'report';
245
+ }
246
+
247
+ export function readBudget(root = REPO_ROOT): Budget | null {
248
+ const p = path.join(root, BUDGET_REL);
249
+ if (!fs.existsSync(p)) return null;
250
+ return JSON.parse(fs.readFileSync(p, 'utf8')) as Budget;
251
+ }
252
+
253
+ /**
254
+ * Baseline comparison.
255
+ *
256
+ * `calls_per_request` is deliberately ABSENT — see the comment in the body.
257
+ * Direction is DOWN for `blocking_share`, and UP for
258
+ * `mean_batch_size` — a batch of 1.00 is the floor being fixed, so a higher
259
+ * number is the improvement and the ratchet must not read it as a regression.
260
+ * `context_floor_max` is DOWN. Getting one of these directions backwards would
261
+ * make the gate reward the defect, which is why they are listed rather than
262
+ * inferred from a shared helper.
263
+ */
264
+ export function compare(t: Turnaround, b: Budget): string[] {
265
+ const out: string[] = [];
266
+ // `calls_per_request` is REPORTED, never compared. Twice measured unstable
267
+ // for reasons that are not regressions: it fell 81.42 -> 72.67 when the
268
+ // measuring session was excluded, then drifted 72.67 -> 73.73 within the
269
+ // same afternoon because the corpus is an MTIME WINDOW and other sessions
270
+ // in the same project store keep sliding into it. A ratchet on a number
271
+ // that moves when nothing changed teaches the reader to ignore the gate,
272
+ // which is the failure this repository records for gates that can only
273
+ // block. The three metrics below are stable across the same runs.
274
+ if (t.meanBatchSize < b.baseline.mean_batch_size) {
275
+ out.push(
276
+ `mean batch size ${String(t.meanBatchSize)} < baseline ${String(b.baseline.mean_batch_size)} (direction: UP — 1.00 is fully serial)`,
277
+ );
278
+ }
279
+ if (t.blockingShare > b.baseline.blocking_share) {
280
+ out.push(
281
+ `blocking share ${String(t.blockingShare)} > baseline ${String(b.baseline.blocking_share)} (direction: DOWN)`,
282
+ );
283
+ }
284
+ if (t.contextFloorMax > b.baseline.context_floor_max) {
285
+ out.push(
286
+ `context floor max ${String(t.contextFloorMax)} > baseline ${String(b.baseline.context_floor_max)} (direction: DOWN)`,
287
+ );
288
+ }
289
+ return out;
290
+ }
291
+
292
+ function render(t: Turnaround, store: string): string {
293
+ const pct = (x: number): string => `${String(Math.round(x * 1000) / 10)} %`;
294
+ return [
295
+ `probe:turnaround · ${String(t.sessions)} session(s) · ${store}`,
296
+ ` API calls per user request ${String(t.callsPerRequest)} (${String(t.apiCalls)} calls / ${String(t.userRequests)} requests)`,
297
+ ` mean tool-call batch size ${String(t.meanBatchSize)} (${String(t.toolCalls)} tool calls / ${String(t.toolUsingRequests)} tool-using requests)`,
298
+ ` blocking tail (>${String(BLOCKING_SECONDS)}s) ${String(t.blockingCalls)} call(s), ${String(Math.round(t.blockingSeconds / 60))} min = ${pct(t.blockingShare)} of ${String(Math.round(t.toolSeconds / 60))} min tool time`,
299
+ ` first-call context floor ${String(t.contextFloorMin)}–${String(t.contextFloorMax)} tokens`,
300
+ '',
301
+ ].join('\n');
302
+ }
303
+
304
+ export function main(argv: string[] = process.argv.slice(2)): number {
305
+ const arg = (flag: string, dflt: string): string => {
306
+ const i = argv.indexOf(flag);
307
+ return i >= 0 && argv[i + 1] !== undefined ? (argv[i + 1] as string) : dflt;
308
+ };
309
+ for (const a of argv) {
310
+ if (a.startsWith('-') && !['--store', '--limit', '--json', '--against-baseline', '--include-current', '-h', '--help'].includes(a)) {
311
+ if (!argv.includes('--store') && !argv.includes('--limit')) {
312
+ process.stderr.write(`probe_turnaround: unrecognized argument: ${a}\n`);
313
+ return 2;
314
+ }
315
+ }
316
+ }
317
+ if (argv.includes('-h') || argv.includes('--help')) {
318
+ process.stdout.write(
319
+ 'usage: probe_turnaround [--store PATH] [--limit N] [--json] [--against-baseline] [--include-current]\n',
320
+ );
321
+ return 0;
322
+ }
323
+ const store = arg('--store', defaultStore(process.cwd()));
324
+ const limit = Number(arg('--limit', '10'));
325
+ if (!Number.isFinite(limit) || limit < 1) {
326
+ process.stderr.write('probe_turnaround: --limit needs a positive integer\n');
327
+ return 2;
328
+ }
329
+ const files = recentSessions(store, limit, argv.includes('--include-current'));
330
+ const t = files.length === 0 ? ZERO : measure(files);
331
+ const gating = argv.includes('--against-baseline');
332
+
333
+ if (argv.includes('--json')) {
334
+ process.stdout.write(`${JSON.stringify({ store, ...t }, null, 2)}\n`);
335
+ } else {
336
+ process.stdout.write(render(t, store));
337
+ }
338
+
339
+ if (!gating) {
340
+ if (files.length === 0) {
341
+ // A REPORT over an empty store is honest and exits 0; only the
342
+ // gating mode treats emptiness as a failure. The distinction is the
343
+ // whole of step 1.3.
344
+ process.stdout.write(
345
+ `probe:turnaround · no transcript store at ${store} — nothing measured (report mode; ` +
346
+ 'use --against-baseline for the fail-closed form)\n',
347
+ );
348
+ }
349
+ return 0;
350
+ }
351
+
352
+ const budget = readBudget();
353
+ if (budget === null) {
354
+ process.stderr.write(`❌ probe_turnaround: ${BUDGET_REL} is missing — nothing to compare against.\n`);
355
+ return 1;
356
+ }
357
+ if (files.length === 0) {
358
+ if (budget.empty_corpus === 'fail') {
359
+ process.stderr.write(
360
+ `❌ probe_turnaround: empty corpus at ${store}. A baseline comparison over zero ` +
361
+ 'sessions certifies nothing, and the config records `empty_corpus: "fail"` so it ' +
362
+ 'refuses rather than reporting green.\n',
363
+ );
364
+ return 1;
365
+ }
366
+ process.stdout.write('probe:turnaround · empty corpus, `empty_corpus: "report"` — no verdict.\n');
367
+ return 0;
368
+ }
369
+ const regressions = compare(t, budget);
370
+ if (regressions.length > 0) {
371
+ process.stderr.write('❌ probe_turnaround: regression against the recorded baseline:\n');
372
+ for (const r of regressions) process.stderr.write(` ${r}\n`);
373
+ process.stderr.write(
374
+ ` → raising a baseline in ${BUDGET_REL} needs the reason as a sentence in the same commit.\n`,
375
+ );
376
+ return 1;
377
+ }
378
+ process.stdout.write('✅ probe_turnaround: within the recorded baseline on all four metrics.\n');
379
+ return 0;
380
+ }
381
+
382
+ if (process.argv[1] !== undefined && process.argv[1].includes('probe_turnaround')) {
383
+ process.exit(main());
384
+ }
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * The unblocked-ask rate — the class `user-interaction` measured and then had
4
+ * no instrument for.
5
+ *
6
+ * ## Why this one, and not one of the other 55
7
+ *
8
+ * `road-to-undeclared-obligation-disposition` Phase 2.1 ranks the
9
+ * `cheaply-probeable` bucket by MEASURED failure rather than by feel, and the
10
+ * ranking is lopsided: of 56 rules in that bucket, exactly **one** carries a
11
+ * cited measurement of its own failure. `user-interaction.md:75` records a
12
+ * 30-session conformance audit in which **every** malformed ask was a one-line
13
+ * parenthetical or a trailing free-text offer, and states that **no gate ships**
14
+ * for that class because `check_reply_consistency` inspects numbered-option
15
+ * blocks — "exactly the surface that did not fail".
16
+ *
17
+ * That is the whole case for building this and not a fourth probe. Phase 2.3
18
+ * caps the roadmap at one.
19
+ *
20
+ * ## What is counted
21
+ *
22
+ * A **hand-back** assistant turn — one immediately followed by a genuine user
23
+ * turn, i.e. where the agent actually stopped — that HANDS A DECISION and
24
+ * carries **no** numbered-options block. The rule's own § "The trigger is a
25
+ * decision, not a format" says the obligation fires on handing a decision in any
26
+ * form, so a trailing offer is an ask and owes a recommendation line.
27
+ *
28
+ * Of those, an ask is **malformed** when no recommendation line follows it.
29
+ *
30
+ * ## Honest bounds, and they are wide
31
+ *
32
+ * 1. **"Hands a decision" is a heuristic.** It matches offer shapes — *"soll ich
33
+ * …?"*, *"shall I …?"*, *"sag Bescheid, wenn …"*, *"willst du …?"* — anchored
34
+ * in the closing lines. A genuine clarifying question is deliberately NOT
35
+ * excluded, because `user-interaction` treats a bare yes/no as an ask too.
36
+ * Some rhetorical questions will be counted. The rate is therefore a
37
+ * CEILING for malformed asks of this class, not a point estimate.
38
+ * 2. **Turns WITH a numbered block are excluded**, not judged. That surface
39
+ * already has `check_reply_consistency`; measuring it here would double-count
40
+ * a covered class and hide the uncovered one.
41
+ * 3. **A recommendation line is matched on the label**, `Recommendation:` or
42
+ * `Empfehlung:` (with or without emphasis). A recommendation phrased without
43
+ * the label reads as malformed here. The rule requires the label — a
44
+ * wrong-language label is explicitly "no recommendation" — so this is the
45
+ * rule's own bar, but it is a bar on FORM.
46
+ *
47
+ * Both edges are printed. A reader who cannot see the bracket cannot judge the
48
+ * number.
49
+ *
50
+ * Usage:
51
+ * ./scripts-run src/scripts/probe_unblocked_ask [--limit N] [--store PATH] [--json]
52
+ * ./scripts-run src/scripts/probe_unblocked_ask --self-test
53
+ *
54
+ * Exit codes: 0 always — a measurement, not a gate. `--self-test` exits 1 when
55
+ * the detector fails to fire on its fixtures.
56
+ */
57
+ import fs from 'node:fs';
58
+ import path from 'node:path';
59
+ import { pathToFileURL } from 'node:url';
60
+
61
+ import { defaultStore, HARNESS_TEXT, isInjectedBody } from './conformance_scan.js';
62
+ import { isSyntheticPrompt } from './_lib/prompt_shape.js';
63
+
64
+ /** A numbered-options block — the surface `check_reply_consistency` already reads. */
65
+ export const NUMBERED_BLOCK = /^\s{0,3}(\d+)[.)]\s+\S/m;
66
+
67
+ /** The recommendation line, matched on its label. Either language. */
68
+ export const RECOMMENDATION = /^\s{0,3}\**\s*(Recommendation|Empfehlung)\s*:?\**/im;
69
+
70
+ /**
71
+ * Offer shapes that hand a decision without a block. Every one of these is a
72
+ * shape the 2026-08-06 audit found in a REAL malformed ask.
73
+ */
74
+ export const OFFER_SHAPES: readonly RegExp[] = [
75
+ /\bsoll(en)? ich\b[^.!?]{0,120}\?/i,
76
+ /\bwillst du\b[^.!?]{0,120}\?/i,
77
+ /\bm(ö|oe)chtest du\b[^.!?]{0,120}\?/i,
78
+ /\bsag (mir )?bescheid,? (wenn|ob|falls)\b/i,
79
+ /\bwenn du (willst|m(ö|oe)chtest),? (dann )?(mache|kann|nehme|ziehe)\b/i,
80
+ /\bshall i\b[^.!?]{0,120}\?/i,
81
+ /\bshould i\b[^.!?]{0,120}\?/i,
82
+ /\bdo you want me to\b[^.!?]{0,120}\?/i,
83
+ /\bwould you like me to\b[^.!?]{0,120}\?/i,
84
+ /\blet me know (if|whether)\b/i,
85
+ ];
86
+
87
+ interface Turn {
88
+ role: 'user' | 'assistant';
89
+ text: string;
90
+ at: string;
91
+ }
92
+
93
+ function turns(lines: string[]): Turn[] {
94
+ const out: Turn[] = [];
95
+ for (const line of lines) {
96
+ if (!line.trim()) continue;
97
+ let e: Record<string, unknown>;
98
+ try {
99
+ e = JSON.parse(line) as Record<string, unknown>;
100
+ } catch {
101
+ continue;
102
+ }
103
+ if (e['isSidechain'] === true) continue;
104
+ if (e['type'] === 'user' && e['toolUseResult'] !== undefined) continue;
105
+ if (e['isCompactSummary'] === true) continue;
106
+ const msg = e['message'] as { role?: string; content?: unknown } | undefined;
107
+ const role = msg?.role;
108
+ if (role !== 'user' && role !== 'assistant') continue;
109
+ const c = msg?.content;
110
+ const text =
111
+ typeof c === 'string'
112
+ ? c
113
+ : Array.isArray(c)
114
+ ? c
115
+ .filter((b: { type?: string }) => b?.type === 'text')
116
+ .map((b: { text?: string }) => b.text ?? '')
117
+ .join('\n')
118
+ : '';
119
+ if (!text.trim()) continue;
120
+ if (role === 'assistant' && HARNESS_TEXT.test(text.trim())) continue;
121
+ if (role === 'user' && (isSyntheticPrompt(text) || isInjectedBody(text))) continue;
122
+ out.push({ role, text, at: String(e['timestamp'] ?? '') });
123
+ }
124
+ return out;
125
+ }
126
+
127
+ export interface AskVerdict {
128
+ /** The turn hands a decision with no numbered block. */
129
+ unblockedAsk: boolean;
130
+ /** …and carries no recommendation label. */
131
+ malformed: boolean;
132
+ }
133
+
134
+ /** Classify one assistant hand-back's closing lines. */
135
+ export function classifyTail(tail: string): AskVerdict {
136
+ if (NUMBERED_BLOCK.test(tail)) return { unblockedAsk: false, malformed: false };
137
+ const offers = OFFER_SHAPES.some((re) => re.test(tail));
138
+ if (!offers) return { unblockedAsk: false, malformed: false };
139
+ return { unblockedAsk: true, malformed: !RECOMMENDATION.test(tail) };
140
+ }
141
+
142
+ export interface Result {
143
+ handbacks: number;
144
+ withBlock: number;
145
+ unblockedAsks: number;
146
+ malformed: number;
147
+ samples: { session: string; at: string; span: string }[];
148
+ }
149
+
150
+ export function measure(store: string, limit: number): Result {
151
+ const r: Result = { handbacks: 0, withBlock: 0, unblockedAsks: 0, malformed: 0, samples: [] };
152
+ let files: string[];
153
+ try {
154
+ files = fs.readdirSync(store).filter((f) => f.endsWith('.jsonl'));
155
+ } catch {
156
+ return r;
157
+ }
158
+ const ordered = files
159
+ .map((f) => ({ f, m: fs.statSync(path.join(store, f)).mtimeMs }))
160
+ .sort((a, b) => b.m - a.m)
161
+ .slice(0, limit)
162
+ .map((x) => x.f);
163
+
164
+ for (const f of ordered) {
165
+ const session = f.replace(/\.jsonl$/, '').slice(0, 8);
166
+ const ts = turns(fs.readFileSync(path.join(store, f), 'utf8').split('\n'));
167
+ for (let i = 0; i < ts.length; i += 1) {
168
+ const t = ts[i] as Turn;
169
+ if (t.role !== 'assistant') continue;
170
+ const next = ts[i + 1];
171
+ if (next !== undefined && next.role !== 'user') continue;
172
+ r.handbacks += 1;
173
+ // Closing lines only: an offer buried mid-reply is not the hand-back.
174
+ const tail = t.text.trimEnd().split('\n').slice(-8).join('\n');
175
+ if (NUMBERED_BLOCK.test(tail)) {
176
+ r.withBlock += 1;
177
+ continue;
178
+ }
179
+ const v = classifyTail(tail);
180
+ if (!v.unblockedAsk) continue;
181
+ r.unblockedAsks += 1;
182
+ if (v.malformed) {
183
+ r.malformed += 1;
184
+ if (r.samples.length < 5) {
185
+ const hit = OFFER_SHAPES.find((re) => re.test(tail));
186
+ r.samples.push({
187
+ session,
188
+ at: t.at,
189
+ span: (tail.match(hit as RegExp)?.[0] ?? '').slice(0, 110),
190
+ });
191
+ }
192
+ }
193
+ }
194
+ }
195
+ return r;
196
+ }
197
+
198
+ /**
199
+ * Prove the detector fires, and prove it stays silent where it must.
200
+ *
201
+ * An instrument never seen fire has unknown sensitivity — and one that fires on
202
+ * everything measures nothing, which is why the negative cases are here too.
203
+ */
204
+ export function selfTest(): number {
205
+ const cases: Array<[string, string, boolean, boolean]> = [
206
+ ['a trailing German offer with no recommendation', 'Fertig.\n\nSag Bescheid, wenn ich die drei Zeilen mitnehmen soll.', true, true],
207
+ ['a bare yes/no offer', 'Der Branch ist grün.\n\nSoll ich das so umsetzen?', true, true],
208
+ ['an English trailing offer', 'Done.\n\nLet me know if you want the second half too.', true, true],
209
+ ['an offer WITH a recommendation label', 'Soll ich das so umsetzen?\n\n**Empfehlung:** ja, es ist reversibel.', true, false],
210
+ ['a numbered block is excluded — another gate reads it', '1. do it\n2. skip it\n\nRecommendation: 1', false, false],
211
+ ['an ordinary statement hands nothing', 'Fixed the schema lint; force-pushed b17c4ef8.', false, false],
212
+ ['a question about the code is not an offer to act', 'Why does this function take a callback?', false, false],
213
+ ];
214
+ let failed = 0;
215
+ for (const [name, tail, wantAsk, wantMalformed] of cases) {
216
+ const v = classifyTail(tail);
217
+ const ok = v.unblockedAsk === wantAsk && v.malformed === wantMalformed;
218
+ process.stdout.write(
219
+ `${ok ? '✅' : '❌'} ${name} — ask=${String(v.unblockedAsk)} malformed=${String(v.malformed)}` +
220
+ (ok ? '\n' : ` (wanted ask=${String(wantAsk)} malformed=${String(wantMalformed)})\n`),
221
+ );
222
+ if (!ok) failed += 1;
223
+ }
224
+ const positives = cases.filter((c) => c[3]).length;
225
+ if (positives < 3) {
226
+ process.stderr.write('❌ probe_unblocked_ask --self-test: fewer than 3 positive cases — a truncated suite must fail.\n');
227
+ return 1;
228
+ }
229
+ process.stdout.write(
230
+ `\nprobe_unblocked_ask --self-test: ${String(cases.length - failed)}/${String(cases.length)} case(s) behaved ` +
231
+ `(${String(positives)} positive, ${String(cases.length - positives)} negative)\n`,
232
+ );
233
+ return failed > 0 ? 1 : 0;
234
+ }
235
+
236
+ export function main(argv: string[]): number {
237
+ let limit = 60;
238
+ let storeArg: string | null = null;
239
+ let json = false;
240
+ for (let i = 0; i < argv.length; i += 1) {
241
+ const a = argv[i] as string;
242
+ if (a === '--limit') limit = Number(argv[++i]);
243
+ else if (a === '--store') storeArg = String(argv[++i]);
244
+ else if (a === '--json') json = true;
245
+ // --self-test runs BEFORE the store is resolved: the fixtures are
246
+ // in-process, and resolving a transcript store that may not exist here
247
+ // would make the sensitivity proof depend on the host having one.
248
+ else if (a === '--self-test') return selfTest();
249
+ else {
250
+ process.stderr.write(`probe_unblocked_ask: unrecognized argument: ${a}\n`);
251
+ return 0;
252
+ }
253
+ }
254
+ const store = storeArg ?? defaultStore(process.cwd());
255
+ const r = measure(store, limit);
256
+ if (json) {
257
+ process.stdout.write(`${JSON.stringify(r, null, 2)}\n`);
258
+ return 0;
259
+ }
260
+ const pct = (n: number, d: number): string => (d === 0 ? 'n/a' : `${((100 * n) / d).toFixed(1)}%`);
261
+ process.stdout.write(
262
+ `probe_unblocked_ask · ${String(limit)} most recent session(s) under ${store}\n` +
263
+ ` hand-back turns ${String(r.handbacks)}\n` +
264
+ ` …with a numbered block ${String(r.withBlock)} (excluded — check_reply_consistency reads that surface)\n` +
265
+ ` …unblocked asks ${String(r.unblockedAsks)} ${pct(r.unblockedAsks, r.handbacks)} of hand-backs\n` +
266
+ ` …of those, malformed ${String(r.malformed)} ${pct(r.malformed, r.unblockedAsks)} of unblocked asks\n` +
267
+ '\n CEILING, not a point estimate: "hands a decision" is a heuristic and a\n' +
268
+ ' rhetorical question can match it. A recommendation is matched on its LABEL,\n' +
269
+ ' which is the rule\'s own bar but a bar on form.\n',
270
+ );
271
+ for (const s of r.samples) {
272
+ process.stdout.write(` · ${s.session} ${s.at}: ${s.span}\n`);
273
+ }
274
+ return 0;
275
+ }
276
+
277
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
278
+ process.exit(main(process.argv.slice(2)));
279
+ }