session-orchestrator 5.3.0 → 5.5.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 (260) hide show
  1. package/.agents/skills/claude-md-drift-check/SKILL.md +1 -1
  2. package/.agents/skills/test-audit/SKILL.md +21 -0
  3. package/.agents/skills/test-audit/agents/openai.yaml +5 -0
  4. package/.claude-plugin/marketplace.json +1 -1
  5. package/.claude-plugin/plugin.json +1 -1
  6. package/.codex-plugin/plugin.json +1 -1
  7. package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +1 -1
  8. package/.codex-plugin/skills/test-audit/SKILL.md +21 -0
  9. package/.codex-plugin/skills/test-audit/agents/openai.yaml +5 -0
  10. package/.cursor/commands/test-audit.md +14 -0
  11. package/.cursor/rules/060-evolve.mdc +28 -13
  12. package/.cursor/skills/claude-md-drift-check/SKILL.md +1 -1
  13. package/.cursor/skills/test-audit/SKILL.md +12 -0
  14. package/.cursor-plugin/plugin.json +1 -1
  15. package/.orchestrator/policy/blocked-commands.json +1 -1
  16. package/AGENTS.md +9 -31
  17. package/CHANGELOG.md +162 -0
  18. package/README.md +14 -10
  19. package/docs/agent-authoring.md +2 -2
  20. package/docs/baseline.md +10 -1
  21. package/docs/ci-setup.md +154 -4
  22. package/docs/components.md +11 -11
  23. package/docs/events-schema.md +25 -19
  24. package/docs/install.md +14 -2
  25. package/docs/migration-v4.md +24 -17
  26. package/docs/persona-panel.md +5 -4
  27. package/docs/plugin-architecture-v3.md +1 -1
  28. package/docs/rule-authoring.md +27 -12
  29. package/docs/scope-collision-guard.md +40 -16
  30. package/docs/session-config-reference.md +61 -12
  31. package/docs/session-config-template.md +11 -2
  32. package/docs/vault-docs-architecture.md +8 -4
  33. package/hooks/_lib/hook-import-set.json +42 -29
  34. package/hooks/_lib/lock-bootstrap.mjs +118 -26
  35. package/hooks/config-protection.mjs +1 -1
  36. package/hooks/enforce-commands.mjs +28 -13
  37. package/hooks/enforce-scope.mjs +201 -23
  38. package/hooks/hooks-codex.json +1 -1
  39. package/hooks/hooks-cursor.json +5 -0
  40. package/hooks/hooks-pi.json +5 -0
  41. package/hooks/hooks.json +6 -1
  42. package/hooks/on-session-start.mjs +57 -2
  43. package/hooks/on-stop.mjs +70 -168
  44. package/hooks/post-bash-write-verify.mjs +98 -17
  45. package/hooks/post-edit-validate.mjs +15 -5
  46. package/hooks/post-tool-batch-wave-signal.mjs +32 -152
  47. package/hooks/pre-bash-memory-propose-audit.mjs +71 -10
  48. package/hooks/pre-bash-sessions-ledger-guard.mjs +28 -2
  49. package/hooks/pre-bash-staging-fence.mjs +29 -8
  50. package/hooks/pre-edit-sessions-ledger-guard.mjs +162 -0
  51. package/hooks/pre-task-scope-disjoint.mjs +1143 -279
  52. package/hooks/subagent-telemetry.mjs +96 -19
  53. package/hooks/wave-scope-commit-guard.mjs +168 -72
  54. package/package.json +2 -6
  55. package/pi/prompts/test-audit.md +12 -0
  56. package/rules/README.md +6 -1
  57. package/rules/_index.md +3 -3
  58. package/rules/always-on/bash-harness-pitfalls.md +7 -1
  59. package/rules/opt-in-domain/prompt-caching.md +5 -1
  60. package/rules/opt-in-stack/backend-data.md +9 -1
  61. package/rules/opt-in-stack/backend.md +7 -1
  62. package/rules/opt-in-stack/frontend.md +6 -1
  63. package/rules/opt-in-stack/security-web.md +7 -1
  64. package/rules/opt-in-stack/swift.md +5 -1
  65. package/scripts/apply-session-learnings.mjs +513 -0
  66. package/scripts/archive-closed-prds.mjs +40 -12
  67. package/scripts/autopilot.mjs +57 -54
  68. package/scripts/backfill-abandoned-sessions.mjs +81 -6
  69. package/scripts/backfill-learnings-from-vault.mjs +49 -22
  70. package/scripts/baseline-archetypes.mjs +1 -1
  71. package/scripts/check-package-manager.mjs +62 -5
  72. package/scripts/codex-install.mjs +3 -0
  73. package/scripts/cursor-install.mjs +47 -6
  74. package/scripts/emit-session.mjs +298 -2
  75. package/scripts/export-hw-learnings.mjs +40 -10
  76. package/scripts/fleet-checkin.mjs +170 -0
  77. package/scripts/lib/agent-frontmatter.mjs +3 -3
  78. package/scripts/lib/auto-dream.mjs +7 -6
  79. package/scripts/lib/autopilot/flags.mjs +6 -1
  80. package/scripts/lib/autopilot/loop.mjs +32 -13
  81. package/scripts/lib/autopilot/stall-sampler.mjs +13 -9
  82. package/scripts/lib/baseline-archetypes.mjs +41 -22
  83. package/scripts/lib/blocked-commands-policy.mjs +40 -0
  84. package/scripts/lib/common.mjs +41 -0
  85. package/scripts/lib/config/host-paths.mjs +79 -0
  86. package/scripts/lib/config/persona-gate-wave.mjs +3 -3
  87. package/scripts/lib/config/reaper.mjs +7 -6
  88. package/scripts/lib/config/vault-integration.mjs +151 -16
  89. package/scripts/lib/config-schema.mjs +13 -0
  90. package/scripts/lib/config.mjs +75 -10
  91. package/scripts/lib/eval/engine.mjs +13 -0
  92. package/scripts/lib/events-retention-banner.mjs +393 -0
  93. package/scripts/lib/events-rotation.mjs +21 -6
  94. package/scripts/lib/events.mjs +72 -5
  95. package/scripts/lib/fleet-protocol.mjs +288 -0
  96. package/scripts/lib/gates/gate-baseline.mjs +13 -3
  97. package/scripts/lib/gates/gate-full.mjs +7 -2
  98. package/scripts/lib/gates/gate-helpers.mjs +93 -2
  99. package/scripts/lib/gates/gate-incremental.mjs +23 -22
  100. package/scripts/lib/gates/gate-per-file.mjs +16 -7
  101. package/scripts/lib/hardening.mjs +1 -0
  102. package/scripts/lib/harness-audit/categories/category4.mjs +8 -6
  103. package/scripts/lib/instruction-budget-guard.mjs +77 -11
  104. package/scripts/lib/learnings/expiry-sweep.mjs +420 -22
  105. package/scripts/lib/learnings/io.mjs +274 -37
  106. package/scripts/lib/learnings/schema.mjs +163 -15
  107. package/scripts/lib/learnings/sizing-subject.mjs +2 -2
  108. package/scripts/lib/lock-reaper.mjs +38 -14
  109. package/scripts/lib/maintenance-due-banner.mjs +23 -8
  110. package/scripts/lib/memory-proposals/schema.mjs +11 -5
  111. package/scripts/lib/named-baseline-resolver.mjs +53 -28
  112. package/scripts/lib/named-vault-resolver.mjs +147 -5
  113. package/scripts/lib/orphan-reaper/defaults.mjs +41 -0
  114. package/scripts/lib/orphan-reaper/no-follow-append.mjs +87 -0
  115. package/scripts/lib/orphan-reaper/ps-snapshot.mjs +181 -0
  116. package/scripts/lib/orphan-reaper/reaper-audit.mjs +323 -0
  117. package/scripts/lib/orphan-reaper/reaper-decide.mjs +395 -0
  118. package/scripts/lib/orphan-reaper/scan-throttle.mjs +144 -0
  119. package/scripts/lib/orphan-reaper/scan.mjs +510 -0
  120. package/scripts/lib/orphan-reaper/trigger.mjs +173 -0
  121. package/scripts/lib/orphan-reaper.mjs +47 -1315
  122. package/scripts/lib/owner-yaml.mjs +104 -6
  123. package/scripts/lib/persona-panel/catalog-loader.mjs +1 -1
  124. package/scripts/lib/platform.mjs +206 -2
  125. package/scripts/lib/plugin-update-banner.mjs +3 -1
  126. package/scripts/lib/process-group.mjs +134 -22
  127. package/scripts/lib/project-hygiene.mjs +21 -1
  128. package/scripts/lib/quality-gate.mjs +45 -5
  129. package/scripts/lib/resource-probe.mjs +1 -1
  130. package/scripts/lib/rule-loader.mjs +100 -62
  131. package/scripts/lib/rules-sync.mjs +27 -12
  132. package/scripts/lib/scope-baseline.mjs +119 -29
  133. package/scripts/lib/scope-echo.mjs +7 -0
  134. package/scripts/lib/scope-gate.mjs +194 -27
  135. package/scripts/lib/session-close-backfill.mjs +129 -4
  136. package/scripts/lib/session-end/tail-runner.mjs +10 -5
  137. package/scripts/lib/session-lock-shape.mjs +6 -3
  138. package/scripts/lib/session-lock.mjs +320 -67
  139. package/scripts/lib/session-record-repair.mjs +183 -26
  140. package/scripts/lib/session-registry.mjs +67 -17
  141. package/scripts/lib/session-schema/constants.mjs +30 -4
  142. package/scripts/lib/session-schema/validator.mjs +28 -1
  143. package/scripts/lib/session-start-probes.mjs +242 -68
  144. package/scripts/lib/session-token-rollup.mjs +252 -54
  145. package/scripts/lib/sessions-canonical.mjs +100 -9
  146. package/scripts/lib/sessions-staleness-banner.mjs +3 -1
  147. package/scripts/lib/sh-quote.mjs +29 -0
  148. package/scripts/lib/subagents-schema.mjs +30 -3
  149. package/scripts/lib/tail-window.mjs +27 -3
  150. package/scripts/lib/telemetry/pricing.mjs +121 -71
  151. package/scripts/lib/telemetry/sync.mjs +17 -0
  152. package/scripts/lib/tests-src-ratio.mjs +215 -23
  153. package/scripts/lib/tmux-layout/layouts.mjs +1 -11
  154. package/scripts/lib/tmux-layout/telemetry-stats.mjs +12 -0
  155. package/scripts/lib/tmux-layout/tmux-shell.mjs +2 -11
  156. package/scripts/lib/ux-grill/reconcile.mjs +1 -1
  157. package/scripts/lib/validate/check-agents.mjs +1 -1
  158. package/scripts/lib/validate/check-hooks-symmetry.mjs +5 -0
  159. package/scripts/lib/validate/check-learnings-shell-writes.mjs +214 -0
  160. package/scripts/lib/validate/check-test-value-bans.mjs +388 -27
  161. package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
  162. package/scripts/lib/validate/check-unwired-features.mjs +1 -1
  163. package/scripts/lib/validate/check-vcs-repo-flag.mjs +1 -1
  164. package/scripts/lib/validate-vendored-rules.mjs +206 -81
  165. package/scripts/lib/vault-mirror/auto-commit.mjs +5 -3
  166. package/scripts/lib/vault-mirror/namespace.mjs +47 -26
  167. package/scripts/lib/vault-mirror/process.mjs +350 -62
  168. package/scripts/lib/vault-mirror/pseudonym-map.mjs +14 -5
  169. package/scripts/lib/vault-mirror/render-learnings.mjs +66 -3
  170. package/scripts/lib/vault-mirror/render-sessions.mjs +28 -4
  171. package/scripts/lib/vault-mirror/telemetry.mjs +25 -11
  172. package/scripts/lib/vault-status/board-writer.mjs +53 -12
  173. package/scripts/lib/vault-status/narrative-mirror.mjs +40 -9
  174. package/scripts/lib/vcs-repo-spec.mjs +8 -0
  175. package/scripts/lib/wave-executor/dispatch-common.mjs +10 -18
  176. package/scripts/lib/wave-executor/foreign-dispatch.mjs +1 -1
  177. package/scripts/lib/wave-executor/remote-dispatch.mjs +1 -1
  178. package/scripts/lib/wave-resource-gate.mjs +34 -8
  179. package/scripts/materialize-wave-scope.mjs +7 -4
  180. package/scripts/memory-propose.mjs +18 -5
  181. package/scripts/release-session-lock.mjs +2 -1
  182. package/scripts/release.mjs +11 -3
  183. package/scripts/repair-invalid-sessions.mjs +37 -4
  184. package/scripts/run-migrate-v2-cross-repo.mjs +171 -31
  185. package/scripts/run-quality-gate.mjs +331 -307
  186. package/scripts/self-update.mjs +229 -0
  187. package/scripts/site-numbers.mjs +163 -5
  188. package/scripts/sweep-expired-learnings.mjs +603 -20
  189. package/scripts/sweep-expired-rules.mjs +2 -1
  190. package/scripts/validate-plugin.mjs +7 -0
  191. package/scripts/validate-wave-scope.mjs +21 -2
  192. package/scripts/vault-backfill.mjs +50 -37
  193. package/scripts/vault-integration-watcher.mjs +2 -1
  194. package/scripts/vault-mirror.mjs +238 -75
  195. package/scripts/wave-scope-binding.mjs +2 -1
  196. package/skills/_shared/config-reading.md +3 -3
  197. package/skills/_shared/fleet-protocol.md +134 -0
  198. package/skills/_shared/state-ownership.md +41 -12
  199. package/skills/autopilot/SKILL.md +25 -11
  200. package/skills/bootstrap/SKILL.md +6 -4
  201. package/skills/bootstrap/_shared-template.md +97 -2
  202. package/skills/bootstrap/fast-template.md +4 -1
  203. package/skills/bootstrap/private-contract.md +2 -1
  204. package/skills/bootstrap/public-fallback.md +6 -3
  205. package/skills/bootstrap/references/bootstrap-refresh-lock-flow.md +5 -3
  206. package/skills/bootstrap/references/bootstrap-retroactive-flow.md +2 -1
  207. package/skills/bootstrap/standard-template.md +2 -0
  208. package/skills/claude-md-drift-check/SKILL.md +10 -10
  209. package/skills/claude-md-drift-check/checker.mjs +26 -18
  210. package/skills/discovery/probes/ssot-code-diff.mjs +12 -6
  211. package/skills/discovery/probes/vault-narrative-staleness.mjs +6 -2
  212. package/skills/discovery/probes/vault-staleness.mjs +9 -3
  213. package/skills/docs-orchestrator/SKILL.md +12 -5
  214. package/skills/docs-orchestrator/audience-mapping.md +5 -3
  215. package/skills/evolve/SKILL.md +13 -4
  216. package/skills/evolve/references/evolve-analyze-mode.md +42 -18
  217. package/skills/memory-cleanup/SKILL.md +1 -1
  218. package/skills/persona-panel/SKILL.md +2 -2
  219. package/skills/persona-panel/persona-format.md +2 -2
  220. package/skills/persona-panel/presets/designer-lens.md +1 -1
  221. package/skills/persona-panel/presets/engineer-lens.md +1 -1
  222. package/skills/persona-panel/presets/pm-lens.md +1 -1
  223. package/skills/plan/mode-retro.md +19 -4
  224. package/skills/quality-gates/SKILL.md +6 -4
  225. package/skills/remote-offload/SKILL.md +4 -2
  226. package/skills/session-end/learning-patterns.md +23 -13
  227. package/skills/session-end/metrics-collection.md +6 -7
  228. package/skills/session-end/phase-3-2-docs-verification.md +7 -6
  229. package/skills/session-end/plan-verification.md +25 -9
  230. package/skills/session-end/references/phase-3-documentation-updates.md +13 -3
  231. package/skills/session-end/references/phase-4a-worktree-cleanup.md +2 -0
  232. package/skills/session-end/references/session-summary-template.md +1 -1
  233. package/skills/session-end/session-metrics-write.md +65 -68
  234. package/skills/session-plan/SKILL.md +0 -3
  235. package/skills/session-start/SKILL.md +8 -2
  236. package/skills/session-start/references/operations-contract.md +6 -0
  237. package/skills/session-start/references/phase-1-2-session-lock.md +32 -7
  238. package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
  239. package/skills/session-start/references/phase-4-ssot-environment-check.md +10 -3
  240. package/skills/test-audit/SKILL.md +152 -0
  241. package/skills/test-audit/references/campaign.md +111 -0
  242. package/skills/test-audit/references/mutate.sh +50 -0
  243. package/skills/vault-mirror/SKILL.md +21 -11
  244. package/skills/wave-executor/SKILL.md +2 -2
  245. package/skills/wave-executor/circuit-breaker.md +1 -1
  246. package/skills/wave-executor/references/wave-executor-state-init.md +3 -0
  247. package/skills/wave-executor/references/wave-loop-dispatch.md +11 -1
  248. package/skills/wave-executor/references/wave-loop-review.md +4 -6
  249. package/skills/wave-executor/references/wave-loop-scope-manifest.md +13 -5
  250. package/templates/personas/README.md +5 -5
  251. package/templates/personas/accounting-compliance.v1.md +1 -1
  252. package/templates/personas/accounting-tax-advisor.v1.md +1 -1
  253. package/templates/personas/buyer-p1-cto.v1.md +1 -1
  254. package/templates/personas/buyer-p2-kanzlei.v1.md +1 -1
  255. package/templates/personas/buyer-p3-build.v1.md +1 -1
  256. package/templates/personas/buyer-p4-tech-veto.v1.md +1 -1
  257. package/templates/personas/buyer-p5-solo.v1.md +1 -1
  258. package/templates/personas/buyer-p6-ld.v1.md +1 -1
  259. package/templates/personas/klima-ai-expert.v1.md +1 -1
  260. package/templates/personas/klima-physicist.v1.md +1 -1
@@ -47,6 +47,16 @@
47
47
  * tier-gate conditionals `applyGates` already implements (the `context`
48
48
  * param below is the exact mechanism rule-loader exposes for this).
49
49
  *
50
+ * #1449: that membership now matches what Claude Code actually loads. The
51
+ * loader used to skip a leading provenance comment before `---`, so a rule
52
+ * Claude Code loads always-on (it reads frontmatter only from line 1) counted
53
+ * here as path-scoped, and every consumer's always-on total was short by the
54
+ * vendored rules carrying that header. Such a file now counts as always-on —
55
+ * its displaced frontmatter as body bytes, since that is how Claude Code
56
+ * delivers it — and is listed in `defects` and on its own banner line. The
57
+ * list is report-only: it is not an `overBudget` term, so a defect never turns
58
+ * a budget verdict red.
59
+ *
50
60
  * Cross-references:
51
61
  * - "2026-06-20 instruction-budget audit" (#668 / #687; archived in the private Meta-Vault)
52
62
  * - scripts/lib/rule-loader.mjs (always-on classification + tier-gate SSOT)
@@ -79,7 +89,11 @@
79
89
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
80
90
  import { basename, join } from 'node:path';
81
91
  import { scanEventsBackwards } from './events.mjs';
82
- import { loadApplicableRules, parseGlobsFrontmatter } from './rule-loader.mjs';
92
+ import {
93
+ FRONTMATTER_NOT_AT_TOP,
94
+ loadApplicableRules,
95
+ parseGlobsFrontmatter,
96
+ } from './rule-loader.mjs';
83
97
 
84
98
  /** Default directive ceiling (operator-chosen growth ratchet just above the ~457 baseline). */
85
99
  export const DEFAULT_CEILING = 480;
@@ -1055,12 +1069,18 @@ function measureRuleCorpora(rulesDir) {
1055
1069
  * bySurface: { coordinator: number, wave: number, always: number,
1056
1070
  * generated: {bytes: number, files: number},
1057
1071
  * pathScoped: {bytes: number, files: number} },
1072
+ * defects: string[],
1058
1073
  * }}
1059
- * perFile is sorted DESC by count. On missing/unreadable dir →
1074
+ * perFile is sorted DESC by count. `defects` (#1449) lists, sorted by name,
1075
+ * the rule files whose frontmatter does not start on line 1 — Claude Code
1076
+ * ignores it, so they load always-on and are counted as such. It is
1077
+ * independent of `context` (such a file has no tier to gate on) and never
1078
+ * feeds `overBudget`. On missing/unreadable dir →
1060
1079
  * { totalDirectives: 0, totalBytes: 0, perFile: [], ceiling, byteCeiling,
1061
1080
  * overDirectiveBudget: false, overByteBudget: false, overBudget: false,
1062
1081
  * severity: 'ok', bySurface: { coordinator: 0, wave: 0, always: 0,
1063
- * generated: {bytes:0,files:0}, pathScoped: {bytes:0,files:0} } }.
1082
+ * generated: {bytes:0,files:0}, pathScoped: {bytes:0,files:0} },
1083
+ * defects: [] }.
1064
1084
  *
1065
1085
  * #931a verdict rule — `overBudget` is the OR of the two axes
1066
1086
  * (`overDirectiveBudget || overByteBudget`), NOT a per-axis severity split:
@@ -1165,6 +1185,7 @@ export function computeInstructionBudget(opts = {}) {
1165
1185
  generated: { bytes: 0, files: 0 },
1166
1186
  pathScoped: { bytes: 0, files: 0 },
1167
1187
  },
1188
+ defects: [],
1168
1189
  };
1169
1190
 
1170
1191
  let allEntries;
@@ -1256,6 +1277,13 @@ export function computeInstructionBudget(opts = {}) {
1256
1277
  const overPathScopedBudget = bySurface.pathScoped.bytes > pathScopedByteCeiling;
1257
1278
  const overBudget = overDirectiveBudget || overByteBudget || overGeneratedBudget || overPathScopedBudget;
1258
1279
 
1280
+ // #1449: read off the untiered list — a defective file carries no meta, so
1281
+ // no tier gate drops it and `alwaysOnAll` holds every one of them.
1282
+ const defects = alwaysOnAll
1283
+ .filter((e) => e.defect === FRONTMATTER_NOT_AT_TOP)
1284
+ .map((e) => basename(e.path))
1285
+ .sort((a, b) => a.localeCompare(b));
1286
+
1259
1287
  return {
1260
1288
  totalDirectives,
1261
1289
  totalBytes,
@@ -1271,12 +1299,21 @@ export function computeInstructionBudget(opts = {}) {
1271
1299
  overBudget,
1272
1300
  severity: overBudget ? 'warn' : 'ok',
1273
1301
  bySurface,
1302
+ defects,
1274
1303
  };
1275
1304
  }
1276
1305
 
1277
1306
  /** Ledger event the backlog number below is read from (`reconcile/engine.mjs`). */
1278
1307
  const RECONCILE_EVENT = 'orchestrator.reconcile.completed';
1279
1308
 
1309
+ /**
1310
+ * Events-ledger window for the #1401 census (`events-retention-banner.mjs`):
1311
+ * `null` — {@link readLastReconcileCapped} wants the NEWEST reconcile record,
1312
+ * whose age no constant bounds; a pruned one reads as not measured (`null`,
1313
+ * rendered as absence), never as a measured `0`.
1314
+ */
1315
+ export const REQUIRED_EVENTS_WINDOW_DAYS = null;
1316
+
1280
1317
  /**
1281
1318
  * Wall-clock budget for the backwards ledger walk. Deliberately small: this
1282
1319
  * runs inside a session-start probe whose own budget is 2 s, and the number it
@@ -1351,6 +1388,21 @@ function formatNearPathScopedLine(budget, repoRoot) {
1351
1388
  );
1352
1389
  }
1353
1390
 
1391
+ /**
1392
+ * The #1449 defect line: rule files whose frontmatter Claude Code never reads.
1393
+ * Names every file — the fix is per file (move the text above `---` below the
1394
+ * closing fence), so a count alone would send the operator hunting.
1395
+ *
1396
+ * @param {string[]} defects - basenames from `computeInstructionBudget().defects`
1397
+ * @returns {string}
1398
+ */
1399
+ function formatFrontmatterDefectLine(defects) {
1400
+ return (
1401
+ `⚠ ${defects.length} rule(s) carry frontmatter that Claude Code ignores ` +
1402
+ `(text before the opening ---): ${defects.join(', ')} — they load always-on.`
1403
+ );
1404
+ }
1405
+
1354
1406
  /**
1355
1407
  * Banner wrapper — session-start Phase 4 convention.
1356
1408
  *
@@ -1367,7 +1419,9 @@ function formatNearPathScopedLine(budget, repoRoot) {
1367
1419
  *
1368
1420
  * The message names WHICH axis breached (#931a) — a banner that only said
1369
1421
  * "over budget" would leave the operator guessing whether to prune bullets or
1370
- * prose. It stays at three lines because it renders at every session start.
1422
+ * prose. It stays short because it renders at every session start: the breach
1423
+ * block is three lines, plus one line per extra finding (near-threshold,
1424
+ * frontmatter defect).
1371
1425
  *
1372
1426
  * @param {object} [opts] forwarded to computeInstructionBudget.
1373
1427
  * @param {string} [opts.repoRoot] project root for the config read.
@@ -1382,8 +1436,10 @@ function formatNearPathScopedLine(budget, repoRoot) {
1382
1436
  * raises it at {@link PATH_SCOPED_NEAR_THRESHOLD} of its ceiling, before the
1383
1437
  * breach: that band used to be silent, and the ceiling then surfaced only
1384
1438
  * inside `writeApprovedRules` — after the operator had approved rules it
1385
- * refused to write. The RETURN SHAPE is unchanged (`{severity:'warn',
1386
- * message}` | null), so `session-start-probes.mjs` needs no new vocabulary.
1439
+ * refused to write. Since #1449 a rule whose frontmatter is not on line 1
1440
+ * raises it too (one line naming the files), whether or not any axis is
1441
+ * close. The RETURN SHAPE is unchanged (`{severity:'warn', message}` |
1442
+ * null), so `session-start-probes.mjs` needs no new vocabulary.
1387
1443
  */
1388
1444
  export function checkInstructionBudget(opts = {}) {
1389
1445
  let cfg;
@@ -1455,18 +1511,27 @@ export function checkInstructionBudget(opts = {}) {
1455
1511
  budget.pathScopedByteCeiling > 0 &&
1456
1512
  budget.bySurface.pathScoped.bytes >= budget.pathScopedByteCeiling * PATH_SCOPED_NEAR_THRESHOLD;
1457
1513
 
1458
- if (!budget.overBudget && !nearPathScoped) return null;
1514
+ // #1449 — reported on its own line, never folded into `overBudget`.
1515
+ const defectLine =
1516
+ budget.defects.length > 0 ? formatFrontmatterDefectLine(budget.defects) : null;
1517
+
1518
+ if (!budget.overBudget && !nearPathScoped && !defectLine) return null;
1459
1519
 
1460
1520
  const nearLine = nearPathScoped ? formatNearPathScopedLine(budget, opts.repoRoot) : null;
1461
1521
 
1462
- // Near-threshold and nothing breached: a one-finding banner, carrying the
1463
- // same remedy as the breach clause below (consolidate — never raise).
1522
+ // Nothing breached: one block per finding — the near-threshold line carries
1523
+ // the same remedy as the breach clause below (consolidate — never raise).
1464
1524
  if (!budget.overBudget) {
1465
1525
  return {
1466
1526
  severity: 'warn',
1467
1527
  message: [
1468
- `⚠ ${nearLine}.`,
1469
- ' Consolidate generated rules per docs/rule-authoring.md § Consolidated rules; never raise the ceiling.',
1528
+ ...(nearLine
1529
+ ? [
1530
+ `⚠ ${nearLine}.`,
1531
+ ' Consolidate generated rules per docs/rule-authoring.md § Consolidated rules; never raise the ceiling.',
1532
+ ]
1533
+ : []),
1534
+ ...(defectLine ? [defectLine] : []),
1470
1535
  ].join('\n'),
1471
1536
  };
1472
1537
  }
@@ -1523,6 +1588,7 @@ export function checkInstructionBudget(opts = {}) {
1523
1588
  ...(nearLine ? [` ${nearLine}.`] : []),
1524
1589
  ` Top files: ${top}`,
1525
1590
  ' See the instruction-budget audit (#687; archived in the private Meta-Vault) for the prune/demote list.',
1591
+ ...(defectLine ? [defectLine] : []),
1526
1592
  ].join('\n');
1527
1593
 
1528
1594
  return { severity: 'warn', message };