@event4u/agent-config 16.1.0 → 16.2.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 (217) 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 +237 -248
  5. package/dist/agent-src/commands/roadmap/process-full.md +1 -1
  6. package/dist/agent-src/commands/work.md +4 -0
  7. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  8. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  9. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  10. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  11. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  12. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  13. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +16 -1
  14. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  15. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  16. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  17. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  18. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  19. package/dist/agent-src/templates/roadmaps.md +30 -0
  20. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  21. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  22. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  23. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  24. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  25. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  26. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  27. package/dist/cli/python/knowledge_ingest.js +1 -1
  28. package/dist/cli/python/workspace_drive.js +1 -1
  29. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  30. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-7XGBX6ZH.js} +471 -387
  31. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-FP36SWJD.js} +432 -144
  32. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-IGD6ERX6.js} +31 -0
  33. package/dist/cli-delegate/{chunk-FXYUYU4N.js → chunk-JNMVLWLR.js} +42 -1
  34. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  35. package/dist/cli-delegate/cmd_conformance.js +902 -782
  36. package/dist/cli-delegate/cmd_doctor.js +2 -2
  37. package/dist/cli-delegate/cmd_export.js +2 -2
  38. package/dist/cli-delegate/cmd_handoff.js +4 -4
  39. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  40. package/dist/cli-delegate/cmd_prune.js +2 -2
  41. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  42. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  43. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  44. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  45. package/dist/cli-delegate/cmd_sync.js +2 -2
  46. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  47. package/dist/cli-delegate/cmd_validate.js +2 -2
  48. package/dist/discovery/deprecation-report.md +1 -1
  49. package/dist/discovery/discovery-manifest.json +10 -10
  50. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  51. package/dist/discovery/discovery-manifest.summary.md +1 -1
  52. package/dist/discovery/dormancy-report.md +1 -1
  53. package/dist/discovery/orphan-report.md +1 -1
  54. package/dist/discovery/packs.json +2 -2
  55. package/dist/discovery/trust-report.md +1 -1
  56. package/dist/discovery/workspaces.json +2 -2
  57. package/dist/hooks/dispatch.js +6666 -2581
  58. package/dist/hooks/dispatch.sha256 +1 -0
  59. package/dist/install/install.mjs +848 -733
  60. package/dist/mcp/registry-manifest.json +2 -2
  61. package/dist/mcp/server.json +2 -2
  62. package/dist/mcp/server.mjs +2747 -1510
  63. package/dist/server/schemas/settings.js +23 -0
  64. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  65. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  66. package/dist/ui/index.html +2 -2
  67. package/docs/contracts/settings-classes.md +6 -4
  68. package/package.json +4 -4
  69. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  70. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  71. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  72. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  73. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  74. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  75. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  76. package/src/config/agent-settings.template.yml +29 -0
  77. package/src/config/ci-local-parity.yml +18 -0
  78. package/src/config/continuity-surface.json +2 -2
  79. package/src/config/gate-coverage.yml +234 -3
  80. package/src/config/gate-violation-baselines.json +7 -3
  81. package/src/config/hook-latency-budget.json +21 -0
  82. package/src/config/hook-token-budget.json +2 -2
  83. package/src/config/metric-registry.yml +114 -0
  84. package/src/config/placeholder-drift-budget.json +10 -0
  85. package/src/config/routing-coverage-seed.json +18 -4
  86. package/src/scripts/_cli/cmd_doctor.ts +5 -19
  87. package/src/scripts/_cli/doctor_execution.ts +100 -17
  88. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  89. package/src/scripts/_lib/body_portable.ts +102 -0
  90. package/src/scripts/_lib/cc_transcript.ts +132 -1
  91. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  92. package/src/scripts/_lib/forge_protection.ts +114 -0
  93. package/src/scripts/_lib/forge_reader.ts +558 -0
  94. package/src/scripts/_lib/host_env_write.ts +111 -0
  95. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  96. package/src/scripts/_lib/obligations.ts +119 -8
  97. package/src/scripts/_lib/probe_inputs.ts +271 -0
  98. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  99. package/src/scripts/_lib/release_highlights.ts +2 -1
  100. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  101. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  102. package/src/scripts/_lib/structural_hiding.ts +622 -0
  103. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  104. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  105. package/src/scripts/_lib/verification_command.ts +324 -0
  106. package/src/scripts/_lib/verification_evidence.ts +647 -0
  107. package/src/scripts/_lib/verify_clause.ts +280 -0
  108. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  109. package/src/scripts/before_complete_hook.ts +340 -23
  110. package/src/scripts/bench_hook_latency.ts +412 -5
  111. package/src/scripts/cache_realization_report.ts +61 -1
  112. package/src/scripts/check_argument_hint.ts +287 -0
  113. package/src/scripts/check_claims.ts +125 -1
  114. package/src/scripts/check_enforcement_coverage.ts +38 -9
  115. package/src/scripts/check_enforcement_matrix.ts +245 -3
  116. package/src/scripts/check_finding_dispositions.ts +22 -3
  117. package/src/scripts/check_generator_sync.ts +71 -0
  118. package/src/scripts/check_host_docs_digest.ts +786 -0
  119. package/src/scripts/check_host_format_column.ts +559 -0
  120. package/src/scripts/check_input_references.ts +228 -0
  121. package/src/scripts/check_inputs_parity.ts +235 -0
  122. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  123. package/src/scripts/check_kill_switch_table.ts +294 -0
  124. package/src/scripts/check_placeholder_drift.ts +221 -0
  125. package/src/scripts/check_read_surface_coverage.ts +515 -0
  126. package/src/scripts/check_release_holds.ts +2 -1
  127. package/src/scripts/check_routing_coverage.ts +378 -11
  128. package/src/scripts/check_static_parity.ts +17 -0
  129. package/src/scripts/ci_settle.ts +145 -19
  130. package/src/scripts/closure_scan.ts +115 -1
  131. package/src/scripts/compile_hook_manifest.ts +60 -16
  132. package/src/scripts/external_sources_denylist.json +29 -4
  133. package/src/scripts/hook_manifest.json +1 -1
  134. package/src/scripts/hook_manifest.yaml +42 -5
  135. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  136. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  137. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  138. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  139. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  140. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  141. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  142. package/src/scripts/hooks/concern_registry.ts +2 -0
  143. package/src/scripts/hooks/concern_timings.ts +94 -0
  144. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  145. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  146. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  147. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  148. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  149. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  150. package/src/scripts/hooks/evidence_independence.ts +1 -2
  151. package/src/scripts/hooks/exit_codes.ts +128 -0
  152. package/src/scripts/hooks/host_lowering.json +1 -0
  153. package/src/scripts/hooks/host_lowering.ts +128 -3
  154. package/src/scripts/hooks/host_lowering.yaml +262 -46
  155. package/src/scripts/hooks/host_semantics.ts +30 -2
  156. package/src/scripts/hooks/injection_budget.ts +9 -2
  157. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  158. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  159. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  160. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  161. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  162. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  163. package/src/scripts/hooks/permission_gate.ts +1 -2
  164. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  165. package/src/scripts/hooks/replay_hook.ts +2 -1
  166. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  167. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  168. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  169. package/src/scripts/hooks/rule_inject_hook.ts +8 -5
  170. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  171. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  172. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  173. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  174. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  175. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  176. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  177. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  178. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  179. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  180. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  181. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  182. package/src/scripts/install.ts +3 -4
  183. package/src/scripts/lint_deny_text.ts +419 -0
  184. package/src/scripts/lint_exit_codes.ts +262 -0
  185. package/src/scripts/lint_hook_manifest.ts +55 -0
  186. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  187. package/src/scripts/lint_skill_descriptions.ts +1 -1
  188. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  189. package/src/scripts/mcp_server/prompts.ts +142 -1
  190. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  191. package/src/scripts/measure_turn_end_gate.ts +193 -0
  192. package/src/scripts/memory_lookup.ts +1 -1
  193. package/src/scripts/prepack-check.mjs +53 -0
  194. package/src/scripts/release_publication.ts +13 -1
  195. package/src/scripts/report_invocation_surface.ts +383 -0
  196. package/src/scripts/report_obligation_carriers.ts +699 -0
  197. package/src/scripts/report_obligation_writer_split.ts +236 -0
  198. package/src/scripts/report_skill_menu_census.ts +208 -0
  199. package/src/scripts/report_standing_payload_by_host.ts +1 -1
  200. package/src/scripts/roadmap_verify_share.ts +213 -0
  201. package/src/scripts/schemas/command.schema.json +44 -0
  202. package/src/scripts/schemas/review-findings.schema.json +4 -0
  203. package/src/scripts/schemas/skill.schema.json +44 -0
  204. package/src/scripts/self_review_gate.ts +359 -10
  205. package/src/scripts/semantic_parity_census.ts +314 -0
  206. package/src/scripts/session_eol_report.ts +54 -1
  207. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  208. package/src/scripts/trigger_eval_grandfather.json +0 -2
  209. package/src/scripts/trigger_eval_rotation.ts +122 -27
  210. package/src/scripts/ui_conformance_probe.ts +156 -4
  211. package/src/scripts/update_prices.ts +85 -3
  212. package/src/scripts/write_bundle_digest.mjs +74 -0
  213. package/src/server/schemas/settings.ts +31 -0
  214. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  215. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  216. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  217. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -63,8 +63,26 @@
63
63
  * means reusing `nextState`, which is a larger change than a measurement
64
64
  * correction and would itself need re-measuring.
65
65
  *
66
+ * · THE RETRY-CONDITIONED RE-REFUSAL SHARE, which every bullet above is
67
+ * silent about because until 2026-09-30 nothing produced it. It does NOT
68
+ * come from the corpus: it is read from the gate's own shadow records under
69
+ * `agents/runtime/state/turn-end-gate/*.shadow.json`, written live on an
70
+ * allow path by the instrument `turn-end-detector-demotion.md` registers as
71
+ * instrument 1. Reported per LAYER and then per detector, never pooled, and
72
+ * never as `0.0%` where it has no denominator — see `renderQ1`.
73
+ * · It does NOT print that contract's **Q1**, and the distinction is in the
74
+ * report's own header rather than only here. Q1 conditions BOTH its halves
75
+ * on the detector; the shadow record cannot, because it never stores which
76
+ * refusal produced a retry. The two disagree in direction and neither
77
+ * bounds the other, so no number this prints may be read against a bar.
78
+ * · It does not report promissory, completion or untested at all. Those three
79
+ * are skipped by the gate when a dispatch is open and the shadow record
80
+ * carries no dispatch flag, so their denominators would absorb silences
81
+ * nobody observed. The contract's attribution clause already forbids it.
82
+ *
66
83
  * Usage:
67
84
  * ./scripts-run src/scripts/measure_turn_end_gate --store <dir> [--limit N] [--show-fires]
85
+ * ./scripts-run src/scripts/measure_turn_end_gate --q1 [--workspace <dir>]
68
86
  */
69
87
  import * as fs from 'node:fs';
70
88
  import * as path from 'node:path';
@@ -82,6 +100,14 @@ import {
82
100
  extractToolCalls,
83
101
  type ToolCall,
84
102
  } from './hooks/turn_end_gate_hook.js';
103
+ import {
104
+ DETECTOR_IDS,
105
+ DISPATCH_CENSORED_DETECTORS,
106
+ SHADOW_LAYERS,
107
+ collectShadowStats,
108
+ retryConditionedShare,
109
+ type ShadowStats,
110
+ } from './_lib/turn_end_refusals.js';
85
111
 
86
112
  interface Counts {
87
113
  /**
@@ -495,16 +521,169 @@ export function render(c: Counts): string {
495
521
  ].join('\n');
496
522
  }
497
523
 
524
+ /** `n` with a unit that agrees with it. One place, so no line has to remember. */
525
+ function plural(n: number, one: string, many = `${one}s`): string {
526
+ return `${String(n)} ${n === 1 ? one : many}`;
527
+ }
528
+
529
+ /**
530
+ * The retry-conditioned re-refusal share, read from the shadow records rather
531
+ * than from a transcript. `road-to-a-stop-that-holds` step 2.2; the producer is
532
+ * step 2.1.
533
+ *
534
+ * ```
535
+ * THIS IS NOT THE Q1 `turn-end-detector-demotion.md` REGISTERS BARS AGAINST,
536
+ * AND THE REPORT SAYS SO IN ITS OWN HEADER.
537
+ * ```
538
+ *
539
+ * The first version of this renderer called the number Q1 outright. An
540
+ * independent review (2026-10-01) showed the two are different quantities that
541
+ * disagree in direction, not one quantity with a looser denominator — see
542
+ * `retryConditionedShare` for the worked ten-retry example. The rename and this
543
+ * header are that finding, acted on rather than noted.
544
+ *
545
+ * A DIFFERENT SOURCE FROM EVERY OTHER NUMBER THIS SCRIPT PRINTS, which is why
546
+ * it is a separate renderer rather than another line in `render`. The fire
547
+ * rates above are replayed out of a transcript corpus; this comes out of
548
+ * `agents/runtime/state/turn-end-gate/*.shadow.json`, written live by the gate
549
+ * on an allow path. The two cannot be joined — a shadow record carries no
550
+ * transcript and a transcript carries no shadow record — so printing them from
551
+ * one `Counts` object would invite exactly the cross-instrument arithmetic R2
552
+ * finding 7 removed from the language totals.
553
+ *
554
+ * NULL IS PRINTED AS `—`, NEVER AS `0.0%`. The two mean opposite things and
555
+ * `retryConditionedShare` keeps them apart for that reason; collapsing them at
556
+ * the render layer would undo the distinction one line before a human reads it.
557
+ *
558
+ * THE RESOLVED WORKSPACE IS PRINTED, because the one failure mode here is quiet:
559
+ * `--workspace` takes any path, a worktree carries no `agents/runtime/`, and the
560
+ * report for a wrong directory is a confident "no shadow record". A reading
561
+ * pasted into an evidence file must carry the directory that produced it.
562
+ */
563
+ export function renderQ1(stats: ShadowStats, today: string, workspace?: string): string {
564
+ const lines: string[] = [
565
+ `measure-turn-end-gate · retry-conditioned re-refusal share · ${plural(stats.files, 'shadow record')} · read ${today}`,
566
+ ' NOT the Q1 `turn-end-detector-demotion.md` registers bars against — see',
567
+ ' the note below the table before comparing any number here to a bar.',
568
+ ];
569
+ if (workspace !== undefined) {
570
+ lines.push(` workspace: ${path.resolve(workspace)}`);
571
+ }
572
+ lines.push('');
573
+ if (stats.files === 0) {
574
+ lines.push(
575
+ ' No readable shadow record under this workspace. The share has no',
576
+ ' denominator and is not reported as zero — a gate that has never been',
577
+ ' retried is not a gate whose retries all came back clean.',
578
+ );
579
+ if (stats.unreadable > 0) {
580
+ lines.push(
581
+ '',
582
+ ` ${plural(stats.unreadable, 'shadow file')} present but UNREADABLE, so "none" above`,
583
+ ' means none could be parsed rather than none exists. That is a defect to',
584
+ ' investigate, not an empty sample.',
585
+ );
586
+ }
587
+ return lines.join('\n');
588
+ }
589
+ for (const layer of SHADOW_LAYERS) {
590
+ const bucket = stats.byLayer.find((b) => b.layer === layer);
591
+ if (bucket === undefined) continue;
592
+ lines.push(
593
+ ` layer ${layer} — ${plural(bucket.retries, 'retry', 'retries')} observed, ${plural(bucket.rows, 'shadow row')}`,
594
+ );
595
+ if (bucket.retries === 0) {
596
+ lines.push(' (no retry on this layer; every share below would have no denominator)');
597
+ }
598
+ for (const id of DETECTOR_IDS) {
599
+ if (DISPATCH_CENSORED_DETECTORS.has(id)) {
600
+ lines.push(` ${id.padEnd(17)} censored (dispatch-suppressed retries are indistinguishable)`);
601
+ continue;
602
+ }
603
+ const share = retryConditionedShare(stats, layer, id);
604
+ // A dash denies a measurement; a count beside it reads like one. The
605
+ // `(0 / 0)` the first version printed there was the denial and the
606
+ // measurement on the same line.
607
+ if (share === null) {
608
+ lines.push(` ${id.padEnd(17)} —`);
609
+ continue;
610
+ }
611
+ const shown = `${(100 * share).toFixed(1)}%`.padStart(6);
612
+ lines.push(` ${id.padEnd(17)} ${shown} (${String(bucket.byDetector[id])} / ${String(bucket.retries)})`);
613
+ }
614
+ lines.push('');
615
+ }
616
+ lines.push(
617
+ ' WHAT THIS IS, precisely, because the contract defines a DIFFERENT number',
618
+ ' under a similar name. Printed here: of the retries observed on a layer,',
619
+ " the share a detector would have refused. The contract's Q1: of a",
620
+ " DETECTOR'S OWN initial refusals, the share whose retry that same detector",
621
+ ' refuses again. Both halves of the contract definition are conditioned on',
622
+ ' the detector; neither half here is, because `ShadowRecord` does not record',
623
+ ' which refusal produced a retry. The two disagree in direction and neither',
624
+ ' bounds the other, so no number above may be read against a bar in',
625
+ " `turn-end-detector-demotion.md` § The bars. Closing that needs the",
626
+ ' PRODUCER to record the originating detector.',
627
+ '',
628
+ ' THREE DETECTORS ARE CENSORED, and the contract requires it rather than',
629
+ ' this renderer choosing it. `runDetectors` skips promissory, completion and',
630
+ " untested when a dispatch is open, and the shadow record carries no dispatch",
631
+ ' flag — so those retries would enter their denominators as observations of',
632
+ ' silence that nobody made. The attribution clause already forbids reporting',
633
+ ' a detector a rollup cannot separate that way.',
634
+ '',
635
+ ' GROUPED BY LAYER FIRST, and the grouping is load-bearing rather than',
636
+ ' cosmetic. A `stop_hook_active` retry follows ANY stop concern\'s block, not',
637
+ ' only this gate\'s, so a pooled share divides another concern\'s retries into',
638
+ ' this gate\'s refusals. `retries_observed` is keyed by layer so the',
639
+ ' denominator splits with the numerator.',
640
+ '',
641
+ ' TWO FURTHER BOUNDS ON EVERY NUMBER ABOVE, both properties of the instrument:',
642
+ ' 1. A retry whose transcript is absent, oversized or textless records',
643
+ ' NOTHING — no row and no retry. Numerator and denominator shrink',
644
+ ' together, so each share is an UPPER bound, never a point estimate.',
645
+ ' 2. On a host that sends no `session_id` every session shares one record.',
646
+ ' The record count above is FILES; it equals sessions only where the',
647
+ ' host sends an id.',
648
+ '',
649
+ ' A share above may be well-formed and still unreadable as a rate if the',
650
+ ' record count is small. The count is printed first for that reason.',
651
+ );
652
+ if (stats.dropped > 0) {
653
+ lines.push(
654
+ '',
655
+ ` ${plural(stats.dropped, 'row')} dropped to the per-session cap: every numerator above is a FLOOR.`,
656
+ );
657
+ }
658
+ if (stats.unreadable > 0) {
659
+ lines.push(
660
+ '',
661
+ ` ${plural(stats.unreadable, 'shadow file')} present but unreadable — same reading, same direction.`,
662
+ );
663
+ }
664
+ if (stats.earliest !== null && stats.latest !== null) {
665
+ lines.push('', ` Window observed: ${stats.earliest} … ${stats.latest}`);
666
+ }
667
+ return lines.join('\n');
668
+ }
669
+
498
670
  export function main(argv: string[] = process.argv.slice(2)): number {
499
671
  let store = '';
500
672
  let limit = 30;
501
673
  let showFires = false;
674
+ let q1Only = false;
675
+ let workspace = process.cwd();
502
676
  for (let i = 0; i < argv.length; i += 1) {
503
677
  if (argv[i] === '--store' && argv[i + 1] !== undefined) {
504
678
  store = argv[i + 1]!;
505
679
  i += 1;
506
680
  } else if (argv[i] === '--show-fires') {
507
681
  showFires = true;
682
+ } else if (argv[i] === '--q1') {
683
+ q1Only = true;
684
+ } else if (argv[i] === '--workspace' && argv[i + 1] !== undefined) {
685
+ workspace = argv[i + 1]!;
686
+ i += 1;
508
687
  } else if (argv[i] === '--limit' && argv[i + 1] !== undefined) {
509
688
  // R2 finding 15: an unvalidated parseInt made `--limit abc` yield
510
689
  // NaN, `slice(0, NaN)` return nothing, and the script exit 2 with
@@ -521,6 +700,16 @@ export function main(argv: string[] = process.argv.slice(2)): number {
521
700
  i += 1;
522
701
  }
523
702
  }
703
+ const today = new Date().toISOString().slice(0, 10);
704
+ // `--q1` needs no transcript corpus: the shadow records are the source, and
705
+ // requiring a `--store` for them would make the one reading this roadmap
706
+ // asks for contingent on an unrelated input. Deliberately NOT a silent
707
+ // default of the store path — a reading over the wrong workspace is the
708
+ // failure mode here, and it is quiet.
709
+ if (q1Only) {
710
+ process.stdout.write(`${renderQ1(collectShadowStats(workspace), today, workspace)}\n`);
711
+ return 0;
712
+ }
524
713
  if (!store) {
525
714
  process.stderr.write('measure-turn-end-gate: --store <transcript dir> is required\n');
526
715
  return 2;
@@ -533,6 +722,10 @@ export function main(argv: string[] = process.argv.slice(2)): number {
533
722
  return 2;
534
723
  }
535
724
  process.stdout.write(`${render(counts)}\n`);
725
+ // Q1 rides on the default report because step 2.2 asks that
726
+ // `measure_turn_end_gate` PRINT it, not that it offer to. A reader who
727
+ // never learns the flag exists still sees the number.
728
+ process.stdout.write(`\n${renderQ1(collectShadowStats(workspace), today, workspace)}\n`);
536
729
  if (showFires) {
537
730
  process.stdout.write(`\n detector F fired here — read these turns before\n calling any of them a false positive:\n${renderFires(counts)}\n`);
538
731
  process.stdout.write(`\n detector E fired here — same reading, same reason:\n${renderDroppedFires(counts)}\n`);
@@ -35,7 +35,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
35
35
  import { createRequire } from 'node:module';
36
36
  import YAML, { parseDocument } from 'yaml';
37
37
 
38
- import { sanitize_entry } from './_lib/retrieval_sanitize.js';
38
+ import { sanitize_entry } from './_lib/retrieval_sanitize.js'; // read-surface: ingested-corpus
39
39
  import { LexicalIndex, tokenize as _lexTokenize } from './_lib/lexical_index.js';
40
40
  import {
41
41
  ensureIndex as _ftsEnsureIndex,
@@ -23,6 +23,7 @@
23
23
  * Skip with PREPACK_SKIP_BUILD_CHECK=1 only for local `npm pack` dry-runs
24
24
  * that intentionally test the failure mode.
25
25
  */
26
+ import { createHash } from 'node:crypto';
26
27
  import { readFileSync, statSync } from 'node:fs';
27
28
  import { resolve } from 'node:path';
28
29
  import { argv, env, exit } from 'node:process';
@@ -66,6 +67,58 @@ if (head !== SHEBANG) {
66
67
 
67
68
  process.stderr.write(`prepack-check: ${BIN} OK\n`);
68
69
 
70
+ // ---------------------------------------------------------------------------
71
+ // Bundle-digest sidecar: `dist/hooks/dispatch.js` must ship with the
72
+ // `dist/hooks/dispatch.sha256` its build wrote, and the digest must still
73
+ // describe the bundle beside it.
74
+ //
75
+ // Risk 3 of road-to-a-kernel-that-guards-its-plumbing: the runtime integrity
76
+ // check treats a MISSING sidecar as `unverifiable` and allows, deliberately,
77
+ // so that a consumer install predating the sidecar is not wedged. That safety
78
+ // property has a cost — a bundle shipped without its sidecar silently disables
79
+ // the check for every consumer who installs it, and nothing at runtime can
80
+ // tell that apart from the legitimate old-package case. The place to catch it
81
+ // is here, in the one step that knows a bundle is being packaged NOW and
82
+ // therefore had no excuse for the sidecar's absence.
83
+ //
84
+ // A stale digest is treated exactly like a missing one: both mean the two
85
+ // files were not produced by the same `npm run build:hooks`.
86
+ const HOOK_BUNDLE = resolve('dist/hooks/dispatch.js');
87
+ const HOOK_SIDECAR = resolve('dist/hooks/dispatch.sha256');
88
+ let hookBundleBytes = null;
89
+ try {
90
+ hookBundleBytes = readFileSync(HOOK_BUNDLE);
91
+ } catch {
92
+ // No hook bundle in this tree at all — `npm run build` builds it, and gate
93
+ // 1 above has already failed if the build did not run. Nothing to check.
94
+ }
95
+ if (hookBundleBytes !== null) {
96
+ let sidecarText;
97
+ try {
98
+ sidecarText = readFileSync(HOOK_SIDECAR, 'utf8');
99
+ } catch {
100
+ die(
101
+ `dist/hooks/dispatch.js ships without dist/hooks/dispatch.sha256. The runtime ` +
102
+ `integrity check reads a missing sidecar as "unverifiable" and ALLOWS, so ` +
103
+ `publishing this tarball would silently disable it for every consumer. Run ` +
104
+ `\`npm run build:hooks\`, which writes both.`,
105
+ );
106
+ }
107
+ const claimed = /^([0-9a-f]{64})\s/u.exec(sidecarText.trim())?.[1] ?? null;
108
+ if (claimed === null) {
109
+ die(`dist/hooks/dispatch.sha256 is malformed — expected "<64 hex> dispatch.js".`);
110
+ }
111
+ const actual = createHash('sha256').update(hookBundleBytes).digest('hex');
112
+ if (actual !== claimed) {
113
+ die(
114
+ `dist/hooks/dispatch.sha256 does not describe dist/hooks/dispatch.js ` +
115
+ `(sidecar ${claimed.slice(0, 12)}…, bundle ${actual.slice(0, 12)}…). The two were ` +
116
+ `not produced by the same build. Run \`npm run build:hooks\`.`,
117
+ );
118
+ }
119
+ process.stderr.write(`prepack-check: dist/hooks/dispatch.sha256 OK (${actual.slice(0, 12)}…)\n`);
120
+ }
121
+
69
122
  // ---------------------------------------------------------------------------
70
123
  // Import-completeness guard: every relative import reachable from the
71
124
  // SHIPPED src/ trees must itself resolve to a shipped file. Catches the
@@ -1248,6 +1248,18 @@ export function _count_from_list_result(
1248
1248
  return lines.length;
1249
1249
  }
1250
1250
 
1251
+ /**
1252
+ * `npx` argv for the test-count probe.
1253
+ *
1254
+ * `--staticParse=false` is load-bearing: since vitest 4, `vitest list` parses
1255
+ * files statically by default, which cannot see a `describe` registered from
1256
+ * an imported helper or an `it.each` over a runtime array. The golden replay
1257
+ * shards (`defineReplayShard`) are exactly that, so after the vitest 5 upgrade
1258
+ * every shard reported "No test suite found", the probe exited 1, and 16.2.0
1259
+ * was cut without its `Tests:` footer.
1260
+ */
1261
+ export const _TEST_LIST_ARGV = ['vitest', 'list', '--staticParse=false'] as const;
1262
+
1251
1263
  /**
1252
1264
  * Return the collected vitest test-case count on the current tree
1253
1265
  * (`npx vitest list`, one line per case; ~14s wall). Returns null when
@@ -1267,7 +1279,7 @@ export function _count_tests_current(): number | null {
1267
1279
  if (process.env['VITEST'] !== undefined) {
1268
1280
  return null;
1269
1281
  }
1270
- const res = spawnSync('npx', ['vitest', 'list'], {
1282
+ const res = spawnSync('npx', [..._TEST_LIST_ARGV], {
1271
1283
  cwd: REPO_ROOT,
1272
1284
  encoding: 'utf-8',
1273
1285
  stdio: ['ignore', 'pipe', 'pipe'],