@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
@@ -36,6 +36,12 @@ import * as os from 'node:os';
36
36
 
37
37
  import { reportScanned } from './_lib/scan_scope.js';
38
38
  import { runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
39
+ import {
40
+ INVENTORY_REL,
41
+ inventoryVerdict,
42
+ resetSourceCache,
43
+ type InventoryVerdict,
44
+ } from './_lib/loop_surfaces.js';
39
45
 
40
46
  const REPO_ROOT = process.cwd();
41
47
  const TASKFILES_DIR = 'taskfiles';
@@ -238,6 +244,23 @@ export interface GateVerdict {
238
244
  unreasoned: string[];
239
245
  /** Recorded exemptions that are no longer unreachable — stale rows. */
240
246
  stale: string[];
247
+ /**
248
+ * The `_lib-export-reach` axis: a declared loop instrument nothing calls.
249
+ *
250
+ * A second axis in this one script rather than a second gate, because it
251
+ * asks the same question one layer down. The first axis asks whether CI
252
+ * reaches a gate TARGET; this asks whether production code reaches a
253
+ * declared INSTRUMENT. A separate gate would need its own registration,
254
+ * its own coverage row and its own wiring — and would then be the thing
255
+ * this family exists to detect if any of that were missed.
256
+ *
257
+ * Deliberately scoped to the instruments named in `loop-surfaces.yaml`
258
+ * rather than to every `_lib` export. Measured on `loop_guards.ts`, the
259
+ * unscoped version reports nine of fourteen exports dead; a gate whose
260
+ * signal is a minority of its own output gets allowlisted rather than
261
+ * answered.
262
+ */
263
+ inventory: InventoryVerdict;
241
264
  }
242
265
 
243
266
  /**
@@ -255,25 +278,51 @@ export function gateVerdict(root = REPO_ROOT): GateVerdict {
255
278
  return {
256
279
  unreasoned: r.unreachable.filter((n) => !exempt.has(n)).sort(),
257
280
  stale: [...exempt.keys()].filter((n) => !unreachable.has(n)).sort(),
281
+ inventory: inventoryVerdict(root),
258
282
  };
259
283
  }
260
284
 
285
+ /** The `_lib-export-reach` half of the verdict, written to stderr. */
286
+ function reportInventory(inv: InventoryVerdict): void {
287
+ for (const f of inv.shape) {
288
+ process.stderr.write(
289
+ `❌ ${INVENTORY_REL}: surface \`${f.id}\` — ${f.detail} (${f.reason}). Every surface ` +
290
+ 'carries every field, and a bound resolves to something that exists.\n',
291
+ );
292
+ }
293
+ for (const f of inv.reach) {
294
+ const fix =
295
+ f.reason === 'dead'
296
+ ? 'Give it a production consumer, declare it `status: experimental` with a future ' +
297
+ '`expires:`, or delete it. A built instrument nothing calls is not built.'
298
+ : 'An exemption is a dated deferral, not a label. Decide it, or move the date and ' +
299
+ 'say why in `note:`.';
300
+ process.stderr.write(`❌ ${f.id} (${f.file}) — ${f.detail}. ${fix}\n`);
301
+ }
302
+ }
303
+
261
304
  function runGate(root: string): number {
262
305
  const v = gateVerdict(root);
263
306
  const exempt = readExemptions(root);
307
+ const inv = v.inventory;
308
+ const declared = inv.live.length + inv.exempt.length + inv.reach.length;
264
309
  reportScanned({
265
310
  gate: 'check_gate_reachability',
266
- scanned: exempt.size + analyse(root).unreachable.length,
267
- units: 'exemption row(s) + unreachable target(s)',
268
- roots: [EXEMPTIONS_REL, ROOT_TASKFILE, TASKFILES_DIR],
311
+ scanned: exempt.size + analyse(root).unreachable.length + declared,
312
+ units: 'exemption row(s) + unreachable target(s) + declared loop instrument(s)',
313
+ roots: [EXEMPTIONS_REL, INVENTORY_REL, ROOT_TASKFILE, TASKFILES_DIR],
269
314
  });
270
- if (v.unreasoned.length === 0 && v.stale.length === 0) {
315
+ const inventoryClean = inv.shape.length === 0 && inv.reach.length === 0;
316
+ if (v.unreasoned.length === 0 && v.stale.length === 0 && inventoryClean) {
271
317
  process.stdout.write(
272
318
  `✅ check_gate_reachability: every unreachable gate target carries a recorded reason ` +
273
- `(${String(exempt.size)} exempt).\n`,
319
+ `(${String(exempt.size)} exempt); every declared loop instrument is called or ` +
320
+ `dated (${String(inv.live.length)} live, ${String(inv.exempt.length)} experimental` +
321
+ `${inv.proseBound.length > 0 ? `, ${String(inv.proseBound.length)} prose-bound surface(s)` : ''}).\n`,
274
322
  );
275
323
  return 0;
276
324
  }
325
+ reportInventory(inv);
277
326
  for (const n of v.unreasoned) {
278
327
  process.stderr.write(
279
328
  `❌ ${n} is gate-shaped, unreachable from \`task ci\` and every workflow, and carries ` +
@@ -320,6 +369,58 @@ function selfTest(): number {
320
369
  return v.unreasoned.length === 0 && v.stale.length === 0 ? 0 : 1;
321
370
  };
322
371
 
372
+ /**
373
+ * A throwaway tree for the `_lib-export-reach` axis.
374
+ *
375
+ * `surface` and `instrument` are written verbatim into the inventory, so a
376
+ * case can plant a malformed row without the builder silently repairing
377
+ * it — which is the whole point of a planted failure.
378
+ */
379
+ const inventoryFixture = (opts: { surface: string; instrument: string; calls: boolean }): number => {
380
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gr-inv-'));
381
+ roots.push(dir);
382
+ fs.mkdirSync(path.join(dir, 'src', 'config'), { recursive: true });
383
+ fs.mkdirSync(path.join(dir, 'src', 'lib'), { recursive: true });
384
+ fs.writeFileSync(
385
+ path.join(dir, 'src', 'lib', 'thing.ts'),
386
+ [
387
+ 'export const CEILING = 3;',
388
+ 'export function stalled(): boolean { return false; }',
389
+ 'export function helper(): number { return CEILING; }',
390
+ ...(opts.calls ? ['export const used = helper();'] : []),
391
+ ].join('\n'),
392
+ 'utf-8',
393
+ );
394
+ fs.writeFileSync(
395
+ path.join(dir, 'src', 'config', 'loop-surfaces.yaml'),
396
+ `surfaces:\n${opts.surface}\ninstruments:\n${opts.instrument}\n`,
397
+ 'utf-8',
398
+ );
399
+ resetSourceCache();
400
+ const v = inventoryVerdict(dir);
401
+ resetSourceCache();
402
+ return v.shape.length === 0 && v.reach.length === 0 ? 0 : 1;
403
+ };
404
+
405
+ const wholeSurface = [
406
+ ' s:',
407
+ ' kind: code',
408
+ ' bound_kind: code',
409
+ ' cap: src/lib/thing.ts:CEILING',
410
+ ' no_progress: src/lib/thing.ts:stalled',
411
+ ' success_stop: a stated stop',
412
+ ' checker: a stated checker',
413
+ ' human_gate: none',
414
+ ' terminal_vocabulary: none',
415
+ ' production_consumers: [src/lib/thing.ts]',
416
+ ].join('\n');
417
+ // Identical but for the one missing field — so a red can only be the cap.
418
+ const cappedSurface = wholeSurface
419
+ .split('\n')
420
+ .filter((l) => !l.includes('cap:'))
421
+ .join('\n');
422
+ const plainInstrument = [' helper:', ' file: src/lib/thing.ts', ' role: a stated role'].join('\n');
423
+
323
424
  const cases: SelfTestCase[] = [
324
425
  {
325
426
  name: 'a wired gate target → accept',
@@ -343,9 +444,63 @@ function selfTest(): number {
343
444
  expect: 'reject',
344
445
  run: () => fixture({ target: 'check-x', wired: true, exempt: { 'check-x': 'stale' } }),
345
446
  },
447
+ // The `_lib-export-reach` axis. The accept case comes first so a
448
+ // builder that reds on everything is caught before the three planted
449
+ // failures can be read as the axis working.
450
+ {
451
+ name: 'a whole surface and a called instrument → accept',
452
+ expect: 'accept',
453
+ run: () =>
454
+ inventoryFixture({ surface: wholeSurface, instrument: plainInstrument, calls: true }),
455
+ },
456
+ {
457
+ name: 'a surface with no `cap` → reject',
458
+ expect: 'reject',
459
+ run: () =>
460
+ inventoryFixture({ surface: cappedSurface, instrument: plainInstrument, calls: true }),
461
+ },
462
+ {
463
+ name: 'an instrument with no consumer → reject',
464
+ expect: 'reject',
465
+ run: () =>
466
+ inventoryFixture({ surface: wholeSurface, instrument: plainInstrument, calls: false }),
467
+ },
468
+ {
469
+ // The reason `expires:` is a required field rather than a nicety:
470
+ // a declared-inactive instrument with no deadline is the same dead
471
+ // code wearing a label.
472
+ name: 'an exception whose `expires:` has passed → reject',
473
+ expect: 'reject',
474
+ run: () =>
475
+ inventoryFixture({
476
+ surface: wholeSurface,
477
+ instrument: `${plainInstrument}\n status: experimental\n expires: '2020-01-01'`,
478
+ calls: false,
479
+ }),
480
+ },
481
+ {
482
+ name: 'an `experimental` exception with no `expires:` → reject',
483
+ expect: 'reject',
484
+ run: () =>
485
+ inventoryFixture({
486
+ surface: wholeSurface,
487
+ instrument: `${plainInstrument}\n status: experimental`,
488
+ calls: false,
489
+ }),
490
+ },
491
+ {
492
+ name: 'an unexpired `experimental` exception on a dead instrument → accept',
493
+ expect: 'accept',
494
+ run: () =>
495
+ inventoryFixture({
496
+ surface: wholeSurface,
497
+ instrument: `${plainInstrument}\n status: experimental\n expires: '2099-01-01'`,
498
+ calls: false,
499
+ }),
500
+ },
346
501
  ];
347
502
  try {
348
- return runSelfTest({ gate: 'check_gate_reachability', cases, minCases: 4, minRejectCases: 2 });
503
+ return runSelfTest({ gate: 'check_gate_reachability', cases, minCases: 10, minRejectCases: 6 });
349
504
  } finally {
350
505
  for (const d of roots) fs.rmSync(d, { recursive: true, force: true });
351
506
  }
@@ -355,18 +510,20 @@ export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT):
355
510
  if (argv.includes('--self-test')) return selfTest();
356
511
  if (argv.includes('--gate')) return runGate(root);
357
512
  const r = analyse(root);
513
+ const inv = inventoryVerdict(root);
358
514
  if (argv.includes('--json')) {
359
- process.stdout.write(`${JSON.stringify(r, null, 2)}\n`);
515
+ process.stdout.write(`${JSON.stringify({ ...r, inventory: inv }, null, 2)}\n`);
360
516
  return 0;
361
517
  }
362
518
  // Emitted on the DEFAULT invocation too, not only under `--gate`: the
363
519
  // coverage gate reads a bare run, and a gate that reports what it inspected
364
520
  // only in one mode is a gate that reports nothing in the other.
521
+ const declared = inv.live.length + inv.exempt.length + inv.reach.length;
365
522
  reportScanned({
366
523
  gate: 'check_gate_reachability',
367
- scanned: readExemptions(root).size + r.unreachable.length,
368
- units: 'exemption row(s) + unreachable target(s)',
369
- roots: [EXEMPTIONS_REL, ROOT_TASKFILE, TASKFILES_DIR],
524
+ scanned: readExemptions(root).size + r.unreachable.length + declared,
525
+ units: 'exemption row(s) + unreachable target(s) + declared loop instrument(s)',
526
+ roots: [EXEMPTIONS_REL, INVENTORY_REL, ROOT_TASKFILE, TASKFILES_DIR],
370
527
  });
371
528
  const total = r.reachable.length + r.scriptInWorkflow.length + r.unreachable.length;
372
529
  process.stdout.write(
@@ -375,6 +532,12 @@ export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT):
375
532
  `UNREACHABLE ${String(r.unreachable.length)}\n`,
376
533
  );
377
534
  for (const n of r.unreachable) process.stdout.write(` · ${n}\n`);
535
+ process.stdout.write(
536
+ `declared loop instruments: ${String(declared)} · live ${String(inv.live.length)} · ` +
537
+ `experimental ${String(inv.exempt.length)} · DEAD ${String(inv.reach.length)} · ` +
538
+ `prose-bound surfaces ${String(inv.proseBound.length)}\n`,
539
+ );
540
+ for (const f of inv.reach) process.stdout.write(` · ${f.id} — ${f.detail}\n`);
378
541
  return 0;
379
542
  }
380
543