@event4u/agent-config 16.0.0 → 16.1.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 (246) 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 +297 -137
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  25. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +15 -15
  27. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  28. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  29. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  30. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  31. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  32. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  33. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  34. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  35. package/dist/agent-src/rules/delegation-policy.md +23 -45
  36. package/dist/agent-src/rules/design-fidelity.md +32 -37
  37. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  38. package/dist/agent-src/rules/test-first.md +41 -0
  39. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  40. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  41. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  42. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  43. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  44. package/dist/agent-src/skills/design-review/SKILL.md +50 -0
  45. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -4
  46. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  47. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  48. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  49. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  50. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  51. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  52. package/dist/agent-src/templates/agent-settings.md +1 -1
  53. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  54. package/dist/agent-src/templates/roadmaps.md +188 -0
  55. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  56. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  57. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  58. package/dist/cli-delegate/check_update_banner.js +40 -2
  59. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  60. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  61. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  62. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  63. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  64. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-FXYUYU4N.js} +57 -13
  65. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-GJCBB2UG.js} +168 -10
  66. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-HDVVGPQW.js} +873 -539
  67. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  68. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  69. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  70. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-UNCRJBT3.js} +31 -7
  71. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  72. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  73. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  74. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  75. package/dist/cli-delegate/cmd_conformance.js +90 -56
  76. package/dist/cli-delegate/cmd_doctor.js +7 -5
  77. package/dist/cli-delegate/cmd_explain.js +2 -2
  78. package/dist/cli-delegate/cmd_export.js +9 -7
  79. package/dist/cli-delegate/cmd_fleet.js +4 -2
  80. package/dist/cli-delegate/cmd_handoff.js +4 -4
  81. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  82. package/dist/cli-delegate/cmd_migrate.js +2 -2
  83. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  84. package/dist/cli-delegate/cmd_preflight.js +4 -2
  85. package/dist/cli-delegate/cmd_prune.js +10 -8
  86. package/dist/cli-delegate/cmd_route_audit.js +2 -2
  87. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  88. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  89. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  90. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  91. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  92. package/dist/cli-delegate/cmd_sync.js +10 -8
  93. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  94. package/dist/cli-delegate/cmd_update.js +1 -1
  95. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  96. package/dist/cli-delegate/cmd_validate.js +12 -10
  97. package/dist/cli-delegate/cmd_versions.js +1 -1
  98. package/dist/discovery/deprecation-report.md +1 -1
  99. package/dist/discovery/discovery-manifest.json +85 -38
  100. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  101. package/dist/discovery/discovery-manifest.summary.md +4 -4
  102. package/dist/discovery/dormancy-report.md +1 -1
  103. package/dist/discovery/orphan-report.md +1 -1
  104. package/dist/discovery/packs.json +10 -8
  105. package/dist/discovery/trust-report.md +3 -3
  106. package/dist/discovery/workspaces.json +8 -6
  107. package/dist/hooks/dispatch.js +1832 -1007
  108. package/dist/install/claudeRuleRewrite.js +14 -3
  109. package/dist/install/conflict.js +60 -10
  110. package/dist/install/conflictTracker.js +218 -0
  111. package/dist/install/fsPrimitives.js +54 -0
  112. package/dist/install/install.mjs +2007 -1481
  113. package/dist/install/manifestFiles.js +259 -0
  114. package/dist/install/preflight.js +14 -2
  115. package/dist/install/preserve.js +157 -0
  116. package/dist/install/recordedOwnership.js +132 -0
  117. package/dist/install/txlog.js +11 -2
  118. package/dist/mcp/registry-manifest.json +3 -3
  119. package/dist/mcp/server.json +2 -2
  120. package/dist/mcp/server.mjs +398 -39
  121. package/dist/router.json +1 -1
  122. package/dist/scripts/_lib/agent_settings.js +3 -2
  123. package/dist/scripts/_lib/fs_atomic.js +167 -0
  124. package/dist/scripts/_lib/installed_tools.js +460 -0
  125. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  126. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  127. package/dist/server/routes/install.js +8 -0
  128. package/dist/server/routes/wizard.js +9 -3
  129. package/dist/server/schemas/settings.js +17 -5
  130. package/dist/shared/missionExecution.js +97 -0
  131. package/dist/ui/assets/{index-D0jaCRPM.js → index-DH1xc62v.js} +17 -17
  132. package/dist/ui/index.html +1 -1
  133. package/docs/MIGRATION.md +103 -0
  134. package/docs/contracts/settings-classes.md +26 -7
  135. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  136. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  137. package/docs/guidelines/design-fidelity-routing.md +29 -0
  138. package/package.json +1 -1
  139. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  140. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  141. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  142. package/src/config/agent-settings.template.yml +139 -27
  143. package/src/config/auto-rule-budget.json +16 -0
  144. package/src/config/ci-local-parity.yml +18 -5
  145. package/src/config/continuity-surface.json +21 -0
  146. package/src/config/gate-coverage.yml +146 -13
  147. package/src/config/gate-violation-baselines.json +6 -6
  148. package/src/config/hook-token-budget.json +2 -0
  149. package/src/config/loop-surfaces.yaml +215 -0
  150. package/src/config/platform-anchor.json +3 -2
  151. package/src/config/release-gate-locality.yml +11 -0
  152. package/src/config/routing-coverage-seed.json +18 -4
  153. package/src/config/rule-enforcement-baseline.json +0 -1
  154. package/src/install/claudeRuleRewrite.ts +16 -2
  155. package/src/install/conflict.ts +65 -9
  156. package/src/install/conflictTracker.ts +299 -0
  157. package/src/install/fsPrimitives.ts +54 -0
  158. package/src/install/manifestFiles.ts +312 -0
  159. package/src/install/preflight.ts +16 -2
  160. package/src/install/preserve.ts +205 -0
  161. package/src/install/recordedOwnership.ts +149 -0
  162. package/src/install/txlog.ts +11 -2
  163. package/src/install/types.ts +19 -4
  164. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  165. package/src/scripts/_cli/cmd_doctor.ts +4 -3
  166. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  167. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  168. package/src/scripts/_cli/doctor_execution.ts +90 -0
  169. package/src/scripts/_lib/activation_payload.ts +247 -0
  170. package/src/scripts/_lib/agent_settings.ts +4 -2
  171. package/src/scripts/_lib/authority_path.ts +102 -0
  172. package/src/scripts/_lib/cascade_base.ts +94 -0
  173. package/src/scripts/_lib/changelog_eras.ts +7 -2
  174. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  175. package/src/scripts/_lib/council_transport.ts +129 -0
  176. package/src/scripts/_lib/delivery_ready.ts +112 -0
  177. package/src/scripts/_lib/forge_protection.ts +267 -0
  178. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  179. package/src/scripts/_lib/json_python_parity.ts +127 -0
  180. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  181. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  182. package/src/scripts/_lib/mission_record.ts +190 -0
  183. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  184. package/src/scripts/_lib/obligations.ts +402 -0
  185. package/src/scripts/_lib/orchestration_record.ts +11 -20
  186. package/src/scripts/_lib/release_highlights.ts +199 -10
  187. package/src/scripts/_lib/release_holds.ts +493 -0
  188. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  189. package/src/scripts/_lib/rides_along.ts +102 -0
  190. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  191. package/src/scripts/_lib/test_delta.ts +166 -0
  192. package/src/scripts/_lib/test_provenance.ts +171 -0
  193. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  194. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  195. package/src/scripts/ai_council/config.ts +8 -11
  196. package/src/scripts/ai_council/fallback_config.ts +5 -3
  197. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  198. package/src/scripts/ask_block_census.ts +285 -15
  199. package/src/scripts/check_enforcement_matrix.ts +624 -0
  200. package/src/scripts/check_gate_reachability.ts +173 -10
  201. package/src/scripts/check_generator_sync.ts +954 -0
  202. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  203. package/src/scripts/check_release_highlights.ts +33 -1
  204. package/src/scripts/check_release_holds.ts +567 -0
  205. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  206. package/src/scripts/check_test_delta.ts +294 -0
  207. package/src/scripts/check_test_weakening.ts +299 -0
  208. package/src/scripts/closure_scan.ts +333 -0
  209. package/src/scripts/compile_hook_manifest.ts +43 -3
  210. package/src/scripts/council_record_shape.ts +209 -0
  211. package/src/scripts/hook_manifest.json +1 -1
  212. package/src/scripts/hook_manifest.yaml +88 -9
  213. package/src/scripts/hooks/concern_registry.ts +4 -0
  214. package/src/scripts/hooks/design_pass_hook.ts +128 -3
  215. package/src/scripts/hooks/evidence_independence.ts +96 -0
  216. package/src/scripts/hooks/memory_recall_hook.ts +540 -0
  217. package/src/scripts/hooks/obligation_settle_hook.ts +182 -0
  218. package/src/scripts/hooks/rule_inject_hook.ts +49 -1
  219. package/src/scripts/hooks/run_continuation_hook.ts +17 -20
  220. package/src/scripts/hooks_doctor.ts +85 -0
  221. package/src/scripts/hooks_status.ts +19 -0
  222. package/src/scripts/install.ts +106 -206
  223. package/src/scripts/janitor.ts +7 -0
  224. package/src/scripts/lint_decision_classes.ts +464 -0
  225. package/src/scripts/lint_hook_manifest.ts +11 -0
  226. package/src/scripts/lint_major_migration_sections.ts +450 -0
  227. package/src/scripts/lint_plan_risk_register.ts +47 -3
  228. package/src/scripts/lint_roadmap_blockers.ts +61 -0
  229. package/src/scripts/lint_roadmap_producers.ts +202 -0
  230. package/src/scripts/measure_rule_budget.ts +101 -5
  231. package/src/scripts/new_roadmap.ts +37 -0
  232. package/src/scripts/orchestration_record.ts +0 -5
  233. package/src/scripts/release.ts +14 -98
  234. package/src/scripts/release_drill.ts +267 -1
  235. package/src/scripts/release_publication.ts +51 -0
  236. package/src/scripts/report_enforcement_drift.ts +566 -0
  237. package/src/scripts/report_host_injection_effect.ts +3 -11
  238. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  239. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  240. package/src/scripts/schemas/command.schema.json +4 -0
  241. package/src/scripts/scope_growth.ts +219 -0
  242. package/src/scripts/trigger_eval_grandfather.json +0 -1
  243. package/src/scripts/ui_conformance_probe.ts +622 -0
  244. package/src/server/schemas/settings.ts +31 -7
  245. package/src/shared/missionExecution.ts +126 -0
  246. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
@@ -15,16 +15,23 @@
15
15
  * A **region** is one contiguous stretch of an authored file. Every region
16
16
  * falls into exactly one of four classes, decided in this precedence order:
17
17
  *
18
- * 1. `count-only` — a line that renders a NUMBER where a question belongs:
18
+ * 1. `bypass` — a line recording an EXPLICIT user bypass of a gate
19
+ * ("just write it", "skip closure", "einfach machen").
20
+ * Its own axis, and that is the whole point: a bypass is
21
+ * a decision the user took, and counting it as an absent
22
+ * closure would read as a surface that forgot to close
23
+ * rather than one the user told to stop. It is also never
24
+ * inferred — only an explicit phrasing matches.
25
+ * 2. `count-only` — a line that renders a NUMBER where a question belongs:
19
26
  * it mentions `question(s)` and carries a `{count}`
20
27
  * placeholder. This is the shape that prints how many
21
28
  * decisions are outstanding instead of putting one.
22
- * 2. `file-parked` — an `Open questions` SECTION heading whose body carries
29
+ * 3. `file-parked` — an `Open questions` SECTION heading whose body carries
23
30
  * no ask obligation. The section is then a parking lot:
24
31
  * a place a question is written down instead of asked.
25
- * 3. `batch` — a run of non-blank lines carrying TWO OR MORE question
32
+ * 4. `batch` — a run of non-blank lines carrying TWO OR MORE question
26
33
  * lines: one hand-back, several decisions.
27
- * 4. `single` — the same run carrying exactly one.
34
+ * 5. `single` — the same run carrying exactly one.
28
35
  *
29
36
  * A **question line** is a non-blank line that either ends in `?` (after
30
37
  * trailing backticks, emphasis and whitespace are stripped) or is a list item
@@ -77,7 +84,7 @@ const _HERE = fileURLToPath(import.meta.url);
77
84
  export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
78
85
 
79
86
  /** The census classes, in precedence order. */
80
- export const ASK_CLASSES = ['single', 'batch', 'count-only', 'file-parked'] as const;
87
+ export const ASK_CLASSES = ['single', 'batch', 'count-only', 'file-parked', 'bypass'] as const;
81
88
  export type AskClass = (typeof ASK_CLASSES)[number];
82
89
 
83
90
  /** Default scan roots. `src/domains` is narrowed to `command.md` — the ask surface. */
@@ -87,6 +94,16 @@ export const DEFAULT_ROOTS: readonly string[] = [
87
94
  'src/agent-src/contexts',
88
95
  ];
89
96
 
97
+ /**
98
+ * An EXPLICIT user bypass of a gate. Every alternative is a phrasing the user
99
+ * types; none of them is a state the agent can infer. A mission grant, an
100
+ * autonomy setting and momentum are deliberately absent from this list — an
101
+ * inferred bypass is the failure this axis exists to make visible, not a
102
+ * second way to match.
103
+ */
104
+ export const BYPASS_RE =
105
+ /\b(?:just write it|just do it|skip (?:the )?(?:closure|protocol|validation)|einfach machen|mach einfach|skip closure)\b/i;
106
+
90
107
  /** A line printing a question COUNT where a question belongs. */
91
108
  export const COUNT_ONLY_RE = /\bquestions?\b/i;
92
109
  export const COUNT_PLACEHOLDER_RE = /\{count\}/;
@@ -124,10 +141,149 @@ export function isQuestionLine(line: string): boolean {
124
141
  return line.replace(TRAILING_NOISE_RE, '').endsWith('?');
125
142
  }
126
143
 
144
+ /**
145
+ * THE FOUR AXES
146
+ *
147
+ * The class above says what SHAPE an ask has. It does not say whether the ask
148
+ * should have existed, and that is the question the targets are about. Four
149
+ * axes, each decidable from the region and its file — never from intent:
150
+ *
151
+ * `phase` planning · execution · delivery. Read from the file's
152
+ * role, because "an owner ask in execution" and the same
153
+ * ask during planning are different defects: planning is
154
+ * where a decision is SUPPOSED to be closed.
155
+ * `ownership` which class the ask routes to, from the same mapping
156
+ * `closure_scan` uses. `unknown` when the region carries
157
+ * no signal — reported as unknown, never as technical.
158
+ * `avoidable` the ask is one the surface could have closed itself: a
159
+ * commit / push / CI / conflict ask, a continuation, a
160
+ * count, or a TECHNICAL question routed to the owner.
161
+ * `resolver_attempted` the region names a rung it tried before asking —
162
+ * evidence, convention, the agent, an independent
163
+ * session, the council, the team. An ask that names none
164
+ * went to the person first.
165
+ *
166
+ * All four are properties of an AUTHORED surface. The third target — zero
167
+ * repeats of an already-answered question — is a property of a TRANSCRIPT and
168
+ * is reported NOT MEASURED rather than as zero, on the same ground the
169
+ * native-ask rate is carried in rather than computed: a number this script
170
+ * cannot see must not be published as a number it measured.
171
+ */
172
+ export const PHASES = ['planning', 'execution', 'delivery'] as const;
173
+ export type Phase = (typeof PHASES)[number];
174
+
175
+ /** Path fragments that put a file in a phase. First match wins, in order. */
176
+ const PHASE_MARKERS: ReadonlyArray<readonly [Phase, RegExp]> = [
177
+ ['delivery', /(commit|\/pr\/|pull-request|release|push|merge)/],
178
+ [
179
+ 'planning',
180
+ /(roadmap\/create|roadmap\/materialize|feature\/plan|feature\/roadmap|challenge-me|analyze\/|refine-ticket|estimate-ticket|plan-confidence)/,
181
+ ],
182
+ ['execution', /(process-full|process-phase|process-step|implement-ticket|work|roadmap-process-loop|jira-ticket)/],
183
+ ];
184
+
185
+ /** Ask shapes the surface could have closed itself. */
186
+ export const AVOIDABLE_RE =
187
+ /\b(?:shall i (?:continue|go on|proceed)|weiter\?|should i commit|commit this|one commit or multiple|push (?:this|it)\?|re-?run ci|resolve the conflicts?\?|which branch|new pr\?)\b/i;
188
+
189
+ /** A rung named before the ask — the region tried something first. */
190
+ export const RESOLVER_RE =
191
+ /\b(?:evidence|convention|ADR-\d+|contract|independent session|the council|council:status|the team|ai_team)\b/i;
192
+
193
+ /** Ownership signals, the same mapping the closure detector uses. */
194
+ const OWNERSHIP_MARKERS: ReadonlyArray<readonly [string, RegExp]> = [
195
+ ['destructive-owned', /\b(?:merge|force-push|deploy|publish|purchase|delete the branch)\b/i],
196
+ ['product-owned', /\b(?:user-visible|what the user sees|product semantics|UX)\b/i],
197
+ ['business-owned', /\b(?:deadline|budget|policy|pricing)\b/i],
198
+ ['spend-exhaustion', /\b(?:ceiling|quota|spend)\b/i],
199
+ ['critical-technical', /\b(?:security|auth|tenant|authority)\b/i],
200
+ ['contested-technical', /\b(?:architecture|two valid|trade-?off|migration design)\b/i],
201
+ ['reversible-technical', /\b(?:refactor|test organisation|pattern)\b/i],
202
+ ['deterministic', /\b(?:naming|file placement|commit split)\b/i],
203
+ ];
204
+
205
+ /** The three owner-routed classes — the ones an ask legitimately reaches. */
206
+ const OWNER_OWNED: ReadonlySet<string> = new Set([
207
+ 'product-owned',
208
+ 'business-owned',
209
+ 'destructive-owned',
210
+ ]);
211
+
212
+ export function phaseOf(file: string): Phase {
213
+ for (const [phase, re] of PHASE_MARKERS) {
214
+ if (re.test(file)) return phase;
215
+ }
216
+ return 'planning';
217
+ }
218
+
219
+ export function ownershipOf(text: string): string {
220
+ for (const [cls, re] of OWNERSHIP_MARKERS) {
221
+ if (re.test(text)) return cls;
222
+ }
223
+ return 'unknown';
224
+ }
225
+
127
226
  export interface Region {
128
227
  readonly cls: AskClass;
129
228
  readonly line: number;
130
229
  readonly questions: number;
230
+ /** The region's own text, for the axis classifiers. */
231
+ readonly text: string;
232
+ }
233
+
234
+ /** One region with its four axes resolved. */
235
+ export interface AxisRow {
236
+ readonly file: string;
237
+ readonly line: number;
238
+ readonly cls: AskClass;
239
+ readonly phase: Phase;
240
+ readonly ownership: string;
241
+ /** A commit / push / CI / conflict / continuation ask — target 2's subject. */
242
+ readonly workflow: boolean;
243
+ readonly avoidable: boolean;
244
+ readonly resolver_attempted: boolean;
245
+ }
246
+
247
+ export function axesFor(file: string, region: Region): AxisRow {
248
+ const ownership = ownershipOf(region.text);
249
+ const technical = ownership !== 'unknown' && !OWNER_OWNED.has(ownership);
250
+ const phase = phaseOf(file);
251
+ const workflow = AVOIDABLE_RE.test(region.text);
252
+ return {
253
+ file,
254
+ line: region.line,
255
+ cls: region.cls,
256
+ phase,
257
+ ownership,
258
+ workflow,
259
+ // A technical question put to a person in EXECUTION is avoidable by
260
+ // construction: planning owned it, and the ownership ladder closes it
261
+ // without a person at all.
262
+ avoidable: workflow || (technical && phase === 'execution'),
263
+ resolver_attempted: RESOLVER_RE.test(region.text),
264
+ };
265
+ }
266
+
267
+ /** The three targets, and whether the corpus meets them. */
268
+ export interface Targets {
269
+ readonly technical_owner_asks_in_execution: number;
270
+ readonly workflow_asks: number;
271
+ /** Transcript-only. `null` is NOT MEASURED, never zero. */
272
+ readonly repeat_asks: number | null;
273
+ }
274
+
275
+ export function targets(rows: readonly AxisRow[]): Targets {
276
+ const asks = rows.filter((r) => r.cls === 'single' || r.cls === 'batch');
277
+ return {
278
+ technical_owner_asks_in_execution: asks.filter(
279
+ (r) =>
280
+ r.phase === 'execution' &&
281
+ r.ownership !== 'unknown' &&
282
+ !OWNER_OWNED.has(r.ownership),
283
+ ).length,
284
+ workflow_asks: asks.filter((r) => r.workflow).length,
285
+ repeat_asks: null,
286
+ };
131
287
  }
132
288
 
133
289
  /**
@@ -143,8 +299,13 @@ export function scanFile(text: string): Region[] {
143
299
 
144
300
  for (let i = 0; i < lines.length; i += 1) {
145
301
  const line = lines[i] as string;
302
+ if (BYPASS_RE.test(line)) {
303
+ out.push({ cls: 'bypass', line: i + 1, questions: 0, text: line });
304
+ consumed.add(i);
305
+ continue;
306
+ }
146
307
  if (COUNT_ONLY_RE.test(line) && COUNT_PLACEHOLDER_RE.test(line)) {
147
- out.push({ cls: 'count-only', line: i + 1, questions: 0 });
308
+ out.push({ cls: 'count-only', line: i + 1, questions: 0, text: line });
148
309
  consumed.add(i);
149
310
  }
150
311
  }
@@ -159,22 +320,25 @@ export function scanFile(text: string): Region[] {
159
320
  }
160
321
  const body = lines.slice(i, end);
161
322
  if (!body.some((l) => ASK_OBLIGATION_RE.test(l))) {
162
- out.push({ cls: 'file-parked', line: i + 1, questions: 0 });
323
+ out.push({ cls: 'file-parked', line: i + 1, questions: 0, text: body.join(' ') });
163
324
  }
164
325
  }
165
326
 
166
327
  let start = -1;
167
328
  let questions = 0;
329
+ let end = -1;
168
330
  const flush = (): void => {
169
331
  if (start >= 0 && questions > 0) {
170
332
  out.push({
171
333
  cls: questions >= 2 ? 'batch' : 'single',
172
334
  line: start + 1,
173
335
  questions,
336
+ text: lines.slice(start, end + 1).join(' '),
174
337
  });
175
338
  }
176
339
  start = -1;
177
340
  questions = 0;
341
+ end = -1;
178
342
  };
179
343
  let inFence = false;
180
344
  for (let i = 0; i < lines.length; i += 1) {
@@ -194,6 +358,7 @@ export function scanFile(text: string): Region[] {
194
358
  if (start < 0) {
195
359
  start = i;
196
360
  }
361
+ end = i;
197
362
  if (!consumed.has(i) && isQuestionLine(line)) {
198
363
  questions += 1;
199
364
  }
@@ -246,19 +411,33 @@ export interface Census {
246
411
  readonly totals: Record<AskClass, number>;
247
412
  readonly files: readonly FileCensus[];
248
413
  readonly scanned: number;
414
+ readonly axes: readonly AxisRow[];
415
+ }
416
+
417
+ /** One line per target, for the terminal summary. */
418
+ export function renderTargets(t: Targets): string[] {
419
+ const verdict = (n: number): string => (n === 0 ? 'MET' : `MISSED (${String(n)})`);
420
+ return [
421
+ ` target: zero technical owner asks in execution ${verdict(t.technical_owner_asks_in_execution)}`,
422
+ ` target: zero commit/push/CI/conflict asks ${verdict(t.workflow_asks)}`,
423
+ ` target: zero repeats of an answered question NOT MEASURED (transcript axis)`,
424
+ ];
249
425
  }
250
426
 
251
427
  function zero(): Record<AskClass, number> {
252
- return { single: 0, batch: 0, 'count-only': 0, 'file-parked': 0 };
428
+ return { single: 0, batch: 0, 'count-only': 0, 'file-parked': 0, bypass: 0 };
253
429
  }
254
430
 
255
431
  export function census(roots: readonly string[], only: string | null): Census {
256
432
  const totals = zero();
433
+ const axes: AxisRow[] = [];
257
434
  const files: FileCensus[] = [];
258
- const targets = only
435
+ // Renamed off `targets` — that name is the exported target-summary
436
+ // function now, and a local shadowing it reads as a call site that works.
437
+ const targetFiles = only
259
438
  ? [path.join(REPO_ROOT, only)]
260
439
  : roots.flatMap((r) => filesUnder(r));
261
- for (const abs of targets) {
440
+ for (const abs of targetFiles) {
262
441
  let text: string;
263
442
  try {
264
443
  text = fs.readFileSync(abs, 'utf8');
@@ -266,15 +445,17 @@ export function census(roots: readonly string[], only: string | null): Census {
266
445
  continue;
267
446
  }
268
447
  const counts = zero();
448
+ const rel = path.relative(REPO_ROOT, abs).split(path.sep).join('/');
269
449
  for (const r of scanFile(text)) {
270
450
  counts[r.cls] += 1;
271
451
  totals[r.cls] += 1;
452
+ axes.push(axesFor(rel, r));
272
453
  }
273
454
  if (ASK_CLASSES.some((c) => counts[c] > 0)) {
274
455
  files.push({ file: path.relative(REPO_ROOT, abs).split(path.sep).join('/'), counts });
275
456
  }
276
457
  }
277
- return { roots, totals, files, scanned: targets.length };
458
+ return { roots, totals, files, scanned: targetFiles.length, axes };
278
459
  }
279
460
 
280
461
  /** `<sha> <ISO commit date>` — the pin. Deterministic at a given commit. */
@@ -345,14 +526,76 @@ export function render(c: Census, native: NativeRate | null): string {
345
526
  lines.push('MEASURED zero, not an uninstrumented one.');
346
527
  }
347
528
  lines.push('');
529
+ lines.push('## Axes');
530
+ lines.push('');
531
+ lines.push(
532
+ 'Four axes per ask region — `phase`, `ownership`, `avoidable`,',
533
+ );
534
+ lines.push(
535
+ '`resolver_attempted`. Every one is decidable from the region and its file;',
536
+ );
537
+ lines.push('none is read from intent.');
538
+ lines.push('');
539
+ lines.push('| phase | regions | avoidable | resolver named |');
540
+ lines.push('|---|---|---|---|');
541
+ for (const phase of PHASES) {
542
+ const rows = c.axes.filter((r) => r.phase === phase);
543
+ lines.push(
544
+ `| \`${phase}\` | ${String(rows.length)} | ` +
545
+ `${String(rows.filter((r) => r.avoidable).length)} | ` +
546
+ `${String(rows.filter((r) => r.resolver_attempted).length)} |`,
547
+ );
548
+ }
549
+ lines.push('');
550
+ lines.push('| ownership | regions |');
551
+ lines.push('|---|---|');
552
+ const byOwnership = new Map<string, number>();
553
+ for (const r of c.axes) byOwnership.set(r.ownership, (byOwnership.get(r.ownership) ?? 0) + 1);
554
+ for (const key of [...byOwnership.keys()].sort()) {
555
+ lines.push(`| \`${key}\` | ${String(byOwnership.get(key) ?? 0)} |`);
556
+ }
557
+ lines.push('');
558
+ lines.push('### Targets');
559
+ lines.push('');
560
+ const t = targets(c.axes);
561
+ const verdict = (n: number): string => (n === 0 ? 'MET' : `MISSED (${String(n)})`);
562
+ lines.push(
563
+ `- **Zero technical owner asks in execution:** ${verdict(t.technical_owner_asks_in_execution)}`,
564
+ );
565
+ lines.push(`- **Zero commit / push / CI / conflict asks:** ${verdict(t.workflow_asks)}`);
566
+ lines.push(
567
+ '- **Zero repeats of an already-answered question:** NOT MEASURED — a repeat is a',
568
+ );
569
+ lines.push(
570
+ ' property of a TRANSCRIPT, not of an authored surface. Reported absent rather than',
571
+ );
572
+ lines.push(' as zero, on the same ground the native-ask rate is carried in.');
573
+ if (t.technical_owner_asks_in_execution > 0 || t.workflow_asks > 0) {
574
+ lines.push('');
575
+ lines.push('Rows that miss a target:');
576
+ lines.push('');
577
+ for (const r of c.axes) {
578
+ if (r.cls !== 'single' && r.cls !== 'batch') continue;
579
+ const missesOne =
580
+ r.phase === 'execution' && r.ownership !== 'unknown' && !OWNER_OWNED.has(r.ownership);
581
+ if (!missesOne && !r.workflow) continue;
582
+ lines.push(
583
+ `- \`${r.file}:${String(r.line)}\` — ${r.cls}, ${r.phase}, ${r.ownership}` +
584
+ (r.workflow ? ', workflow ask' : ''),
585
+ );
586
+ }
587
+ }
588
+ lines.push('');
348
589
  lines.push('## Per file');
349
590
  lines.push('');
350
- lines.push('| file | single | batch | count-only | file-parked |');
351
- lines.push('|---|---|---|---|---|');
591
+ // Derived from ASK_CLASSES rather than written out: a hand-written header
592
+ // and a derived Totals block drift the moment a class is added, and the
593
+ // drift is silent because both tables still render.
594
+ lines.push(`| file | ${ASK_CLASSES.join(' | ')} |`);
595
+ lines.push(`|${'---|'.repeat(ASK_CLASSES.length + 1)}`);
352
596
  for (const f of c.files) {
353
597
  lines.push(
354
- `| \`${f.file}\` | ${String(f.counts.single)} | ${String(f.counts.batch)} | ` +
355
- `${String(f.counts['count-only'])} | ${String(f.counts['file-parked'])} |`,
598
+ `| \`${f.file}\` | ${ASK_CLASSES.map((cls) => String(f.counts[cls])).join(' | ')} |`,
356
599
  );
357
600
  }
358
601
  lines.push('');
@@ -367,6 +610,30 @@ export function selfTest(): number {
367
610
  'count-only',
368
611
  1,
369
612
  ],
613
+ [
614
+ 'an explicit user bypass is its own axis',
615
+ 'An explicit *just write it* drops the closure pass.\n',
616
+ 'bypass',
617
+ 1,
618
+ ],
619
+ [
620
+ 'a bypass line is not also counted as a question',
621
+ 'Did the user say just write it?\n',
622
+ 'single',
623
+ 0,
624
+ ],
625
+ [
626
+ 'a mission grant is NOT a bypass — an inferred one never matches',
627
+ 'The mission grant authorises the run end to end.\n',
628
+ 'bypass',
629
+ 0,
630
+ ],
631
+ [
632
+ 'an autonomy setting is NOT a bypass either',
633
+ 'With personal.autonomy on, trivial questions are suppressed.\n',
634
+ 'bypass',
635
+ 0,
636
+ ],
370
637
  [
371
638
  'a bare Open questions section with no obligation is file-parked',
372
639
  '## Open questions\n\n- something\n',
@@ -491,6 +758,9 @@ export function main(argv: string[]): number {
491
758
  for (const cls of ASK_CLASSES) {
492
759
  process.stdout.write(` ${cls.padEnd(12)} ${String(c.totals[cls])}\n`);
493
760
  }
761
+ for (const line of renderTargets(targets(c.axes))) {
762
+ process.stdout.write(`${line}\n`);
763
+ }
494
764
  return 0;
495
765
  }
496
766