session-orchestrator 4.2.0 → 5.0.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 (129) hide show
  1. package/.agents/skills/session-start/SKILL.md +1 -1
  2. package/.agents/skills/ux-grill/SKILL.md +22 -0
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/.claude-plugin/plugin.json +3 -2
  5. package/.codex-plugin/plugin.json +1 -1
  6. package/.codex-plugin/skills/session-start/SKILL.md +1 -1
  7. package/.codex-plugin/skills/ux-grill/SKILL.md +21 -0
  8. package/.codex-plugin/skills/ux-grill/agents/openai.yaml +5 -0
  9. package/.cursor/commands/ux-grill.md +14 -0
  10. package/.cursor/skills/session-start/SKILL.md +1 -1
  11. package/.cursor/skills/ux-grill/SKILL.md +13 -0
  12. package/.cursor-plugin/plugin.json +1 -1
  13. package/AGENTS.md +2 -1
  14. package/CHANGELOG.md +94 -1
  15. package/README.md +98 -86
  16. package/agents/dialectic-deriver.md +11 -0
  17. package/commands/ux-grill.md +51 -0
  18. package/docs/USER-GUIDE.md +2 -2
  19. package/docs/codex-setup.md +8 -0
  20. package/docs/components.md +7 -7
  21. package/docs/events-schema.md +9 -5
  22. package/docs/github-mirror-protection.md +61 -20
  23. package/docs/migration-v5.md +62 -0
  24. package/docs/scope-collision-guard.md +14 -0
  25. package/hooks/_lib/hook-import-set.json +44 -2
  26. package/hooks/_lib/lock-bootstrap.mjs +84 -1
  27. package/hooks/_lib/vcs-create-matcher.mjs +190 -3
  28. package/hooks/enforce-scope.mjs +201 -0
  29. package/hooks/hooks-codex.json +1 -1
  30. package/hooks/hooks-cursor.json +5 -0
  31. package/hooks/hooks.json +7 -2
  32. package/hooks/on-session-start.mjs +171 -49
  33. package/hooks/post-bash-issue-budget-refund.mjs +375 -0
  34. package/hooks/pre-auq-clarity.mjs +70 -18
  35. package/hooks/pre-bash-issue-budget.mjs +51 -4
  36. package/package.json +2 -1
  37. package/pi/prompts/ux-grill.md +12 -0
  38. package/scripts/ci/assert-vitest-green.mjs +4 -2
  39. package/scripts/dialectic-deriver.mjs +32 -8
  40. package/scripts/emit-session.mjs +72 -1
  41. package/scripts/lib/agent-status.mjs +441 -9
  42. package/scripts/lib/auq/schema.mjs +10 -3
  43. package/scripts/lib/ci-status-banner.mjs +29 -6
  44. package/scripts/lib/claude-md-budget-lint.mjs +52 -2
  45. package/scripts/lib/config.mjs +12 -1
  46. package/scripts/lib/eval/engine.mjs +7 -1
  47. package/scripts/lib/file-lock.mjs +114 -13
  48. package/scripts/lib/git-porcelain.mjs +113 -0
  49. package/scripts/lib/instruction-budget-guard.mjs +415 -47
  50. package/scripts/lib/io.mjs +29 -4
  51. package/scripts/lib/issue-budget.mjs +336 -6
  52. package/scripts/lib/learnings/sizing-subject.mjs +44 -0
  53. package/scripts/lib/locks/staging-fence-lock.mjs +19 -38
  54. package/scripts/lib/locks/state-md-lock.mjs +19 -41
  55. package/scripts/lib/maintenance-due-banner.mjs +11 -1
  56. package/scripts/lib/peer-cards/merger.mjs +143 -0
  57. package/scripts/lib/pre-dispatch-check.mjs +20 -14
  58. package/scripts/lib/project-hygiene.mjs +81 -30
  59. package/scripts/lib/quality-gate.mjs +14 -65
  60. package/scripts/lib/reconcile/engine.mjs +19 -1
  61. package/scripts/lib/reconcile/writer.mjs +278 -11
  62. package/scripts/lib/scope-echo.mjs +346 -0
  63. package/scripts/lib/session-lock.mjs +62 -2
  64. package/scripts/lib/session-record-repair.mjs +91 -0
  65. package/scripts/lib/session-schema/filters.mjs +26 -1
  66. package/scripts/lib/session-start-probes.mjs +419 -53
  67. package/scripts/lib/test-runner/artifact-paths.mjs +30 -5
  68. package/scripts/lib/test-runner/issue-reconcile.mjs +45 -8
  69. package/scripts/lib/tmux-layout/layouts.mjs +62 -4
  70. package/scripts/lib/ux-grill/collect.mjs +1163 -0
  71. package/scripts/lib/ux-grill/compare.mjs +285 -0
  72. package/scripts/lib/ux-grill/manifest.mjs +618 -0
  73. package/scripts/lib/ux-grill/measures.mjs +431 -0
  74. package/scripts/lib/ux-grill/paths.mjs +224 -0
  75. package/scripts/lib/ux-grill/pencil-coverage.mjs +284 -0
  76. package/scripts/lib/ux-grill/reconcile.mjs +344 -0
  77. package/scripts/lib/ux-grill/run-record.mjs +316 -0
  78. package/scripts/lib/ux-grill/schema.mjs +321 -0
  79. package/scripts/lib/validate/check-untracked-test-deps.mjs +33 -19
  80. package/scripts/lib/validate/check-unwired-features.mjs +48 -20
  81. package/scripts/lib/vault-status/board-lock.mjs +18 -0
  82. package/scripts/lib/vault-status/board-writer.mjs +8 -0
  83. package/scripts/mcp-server.sh +16 -1
  84. package/scripts/release.mjs +7 -2
  85. package/skills/bootstrap/SKILL.md +12 -209
  86. package/skills/bootstrap/references/bootstrap-ecosystem-health-flow.md +48 -0
  87. package/skills/bootstrap/references/bootstrap-refresh-lock-flow.md +37 -0
  88. package/skills/bootstrap/references/bootstrap-retroactive-flow.md +108 -0
  89. package/skills/bootstrap/references/bootstrap-rules-fetch-bridge.md +64 -0
  90. package/skills/claude-md-drift-check/SKILL.md +9 -2
  91. package/skills/claude-md-drift-check/checker.mjs +213 -21
  92. package/skills/discovery/SKILL.md +6 -173
  93. package/skills/discovery/probes/vault-staleness.mjs +35 -5
  94. package/skills/discovery/probes-docs.md +8 -4
  95. package/skills/discovery/probes-supply-chain.md +4 -2
  96. package/skills/discovery/probes-ui.md +7 -3
  97. package/skills/discovery/probes-vault.md +12 -4
  98. package/skills/discovery/references/discovery-interactive-triage.md +139 -0
  99. package/skills/discovery/references/discovery-triage-state.md +54 -0
  100. package/skills/eval/rubric-v1.md +13 -0
  101. package/skills/evolve/SKILL.md +2 -458
  102. package/skills/evolve/references/evolve-analyze-mode.md +360 -0
  103. package/skills/evolve/references/evolve-dialectic-mode.md +139 -0
  104. package/skills/plan/mode-retro.md +4 -3
  105. package/skills/reconcile/SKILL.md +10 -0
  106. package/skills/session-end/drift-operations.md +20 -5
  107. package/skills/session-end/metrics-collection.md +1 -0
  108. package/skills/session-end/phase-3-6-tail.md +4 -2
  109. package/skills/session-end/references/phase-2-quality-gate.md +3 -3
  110. package/skills/session-end/references/phase-5-issue-cleanup.md +6 -1
  111. package/skills/session-end/session-metrics-write.md +2 -0
  112. package/skills/session-plan/SKILL.md +2 -144
  113. package/skills/session-plan/references/session-plan-task-classification.md +152 -0
  114. package/skills/session-start/SKILL.md +24 -6
  115. package/skills/session-start/references/operations-contract.md +114 -0
  116. package/skills/session-start/references/phase-4-ssot-environment-check.md +22 -20
  117. package/skills/session-start/soul.md +2 -2
  118. package/skills/test-runner/SKILL.md +1 -1
  119. package/skills/tmux-layout/SKILL.md +3 -1
  120. package/skills/ux-grill/SKILL.md +211 -0
  121. package/skills/ux-grill/rubric-v2.md +201 -0
  122. package/skills/ux-grill/soul.md +76 -0
  123. package/skills/wave-executor/SKILL.md +3 -128
  124. package/skills/wave-executor/references/wave-executor-quality-gate.md +61 -0
  125. package/skills/wave-executor/references/wave-executor-state-init.md +86 -0
  126. package/skills/wave-executor/references/wave-loop-dispatch.md +8 -0
  127. package/skills/wave-executor/references/wave-loop-review.md +18 -5
  128. package/templates/_shared/ux-manifest.template.md +149 -0
  129. package/templates/_shared/journey-manifest.md +0 -114
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: session-start
3
- description: Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations for user alignment before creating a wave plan. Triggered by /session [housekeeping|feature|deep] command.
3
+ description: Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations before development wave planning, or an operations contract for explicitly requested time-bounded operational work. Triggered by /session [housekeeping|feature|deep] or a direct session-start request.
4
4
  metadata:
5
5
  user-invocable: "false"
6
6
  tags: orchestration, initialization, analysis, alignment
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: ux-grill
3
+ description: Use when a running web app's UX has to be audited reproducibly rather than by feel — a deterministic mechanical pass over routes and viewports (axe, target size, horizontal overflow, page title, scripted journeys) followed by a screenshot-grounded interrogation of the operator, journey by journey, with two persona lenses from the target repo's manifest. Triggered by "grill the UX", "roast the dashboard", "UX-Audit", "/ux-grill". Bootstraps its own manifest from a loopback URL on the first run, so it never requires a hand-written file to start.
4
+ metadata:
5
+ model: inherit
6
+ color: magenta
7
+ allowed-tools:
8
+ - Read
9
+ - Grep
10
+ - Glob
11
+ - Bash
12
+ - Write
13
+ ---
14
+
15
+ # ux-grill
16
+
17
+ > **Portable mirror — generated, do not edit.** The canonical skill body lives at
18
+ > [`skills/ux-grill/SKILL.md`](../../../skills/ux-grill/SKILL.md); read that file for the full instructions.
19
+ > This mirror carries only agentskills.io-spec-legal frontmatter so harnesses that
20
+ > discover skills under `.agents/skills/` can find and route to the skill.
21
+ >
22
+ > Regenerate with `node scripts/generate-agents-skills.mjs`.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
10
- "version": "4.2.0"
10
+ "version": "5.0.0"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "session-orchestrator",
4
- "version": "4.2.0",
4
+ "version": "5.0.0",
5
5
  "description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
6
6
  "author": {
7
7
  "name": "Bernhard Goetzendorfer",
@@ -13,6 +13,7 @@
13
13
  "license": "MIT",
14
14
  "keywords": ["session", "orchestration", "waves", "gitlab", "github", "quality-gates", "verification-gates", "multi-agent", "codex", "cursor", "pi"],
15
15
  "experimental": {
16
- "monitors": "./monitors/monitors.json"
16
+ "monitors": "./monitors/monitors.json",
17
+ "evals": "./evals"
17
18
  }
18
19
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-orchestrator",
3
- "version": "4.2.0+codex.20260909111615",
3
+ "version": "5.0.0+codex.20260913085049",
4
4
  "description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
5
5
  "keywords": [
6
6
  "session",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: session-start
3
- description: Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations for user alignment before creating a wave plan. Triggered by /session [housekeeping|feature|deep] command.
3
+ description: Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations before development wave planning, or an operations contract for explicitly requested time-bounded operational work. Triggered by /session [housekeeping|feature|deep] or a direct session-start request.
4
4
  metadata:
5
5
  user-invocable: 'false'
6
6
  tags: orchestration, initialization, analysis, alignment
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: ux-grill
3
+ description: Grill a running web app's UX — a deterministic mechanical pass (axe, target size, overflow, journeys) followed by a screenshot-grounded interrogation of the operator.
4
+ metadata:
5
+ argument-hint: '[url | manifest-path]'
6
+ ---
7
+
8
+ <!-- Generated by scripts/generate-codex-skills.mjs; do not edit. -->
9
+
10
+ # ux-grill
11
+
12
+ Read [`commands/ux-grill.md`](../../../commands/ux-grill.md) in full before taking any action, then follow its complete workflow.
13
+ The linked document is authoritative, including its prechecks, argument rules, and stop conditions.
14
+
15
+ Resolve this link relative to this SKILL.md, not the project working directory. The plugin root is three directories above this file. Resolve package paths such as `skills/` and `scripts/` from that root; resolve relative links inside the canonical document from its own directory. Keep the user’s project as the target of project operations.
16
+
17
+ `$ARGUMENTS` means the trailing user input after the selected command skill, or an empty string when absent. Preserve flags, quoted text, and Unicode as data. Do not perform global substitution in the command document, shell expansion on the argument string, or execution of that string as shell code. When the workflow needs a command, pass its arguments through structured tool parameters or safely quoted individual arguments.
18
+
19
+ Read the full command before invoking an internal skill. An instruction to invoke a skill (including `session-orchestrator:<name>` or the `Skill` tool) means read and follow the canonical `skills/<name>/SKILL.md` beneath the plugin root. Do not redispatch the public command adapter: a same-named command and internal skill are distinct documents, and redispatch would recurse.
20
+
21
+ Regenerate with `node scripts/generate-codex-skills.mjs`.
@@ -0,0 +1,5 @@
1
+ # Generated by scripts/generate-codex-skills.mjs; do not edit.
2
+ interface:
3
+ display_name: ux-grill
4
+ policy:
5
+ allow_implicit_invocation: true
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: "Grill a running web app's UX — a deterministic mechanical pass (axe, target size, overflow, journeys) followed by a screenshot-grounded interrogation of the operator."
3
+ argument-hint: "[url | manifest-path]"
4
+ ---
5
+
6
+ # /ux-grill
7
+
8
+ Use the Session Orchestrator command definition at `commands/ux-grill.md`.
9
+
10
+ Arguments: $ARGUMENTS
11
+
12
+ Read that command file and follow it exactly. When it references `$ARGUMENTS`, substitute the arguments above. Keep all Session Orchestrator platform fallbacks intact.
13
+
14
+ Cursor has no Skill tool. When the command says to invoke a skill, Read `skills/<skill-name>/SKILL.md` and follow it. Supporting files (`soul.md`, phase docs) live in that same `skills/<skill-name>/` directory.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: session-start
3
- description: "Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations for user alignment before creating a wave plan. Triggered by /session [housekeeping|feature|deep] command."
3
+ description: "Use this skill when initializing a session for any project repo. Autonomously analyzes git state, VCS issues, SSOT files, branches, environment, and cross-repo status. Then presents structured findings with recommendations before development wave planning, or an operations contract for explicitly requested time-bounded operational work. Triggered by /session [housekeeping|feature|deep] or a direct session-start request."
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: ux-grill
3
+ description: "Use when a running web app's UX has to be audited reproducibly rather than by feel — a deterministic mechanical pass over routes and viewports (axe, target size, horizontal overflow, page title, scripted journeys) followed by a screenshot-grounded interrogation of the operator, journey by journey, with two persona lenses from the target repo's manifest. Triggered by \"grill the UX\", \"roast the dashboard\", \"UX-Audit\", \"/ux-grill\". Bootstraps its own manifest from a loopback URL on the first run, so it never requires a hand-written file to start."
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # ux-grill
8
+
9
+ Canonical skill: `skills/ux-grill/SKILL.md`
10
+
11
+ Read that file and follow it exactly. Resolve relative links against `skills/ux-grill/`, not this wrapper.
12
+
13
+ Cursor has no Skill tool. Treat "invoke the ux-grill skill" as: Read `skills/ux-grill/SKILL.md`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-orchestrator",
3
- "version": "4.2.0",
3
+ "version": "5.0.0",
4
4
  "description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
5
5
  "author": {
6
6
  "name": "Bernhard Goetzendorfer"
package/AGENTS.md CHANGED
@@ -30,7 +30,8 @@ These are the non-obvious, mistake-causing facts that must load every session. E
30
30
  - **CI status is the source of truth at session-start.** Local-only test runs are insufficient evidence of CI green.
31
31
  Phase 4 of session-start invokes `scripts/lib/ci-status-banner.mjs` via `checkCiStatus({ repoRoot })` to render a 🚨 banner when CI is red on HEAD.
32
32
  Never claim CI green from `npm test` alone — the 8-pipeline silent regression (2026-05-09 → 2026-05-10, fixed in deep-2) is the cautionary tale. <!-- consistency:exempt:runtime-only -->
33
- - **Destructive-Command Guard is active in main + subagent waves.** `hooks/pre-bash-destructive-guard.mjs` blocks destructive shell commands per `.orchestrator/policy/blocked-commands.json` **10 of its 14 rules are `severity: block`, the other 4 only warn** (measured 2026-09-06: `jq '[.rules[].severity]|group_by(.)|map({(.[0]):length})|add' .orchestrator/policy/blocked-commands.json`).
33
+ - **Destructive-Command Guard is active in main + subagent waves on harnesses with a compatible command hook.** On Claude Code, and through the supported Cursor and Pi bridges, `hooks/pre-bash-destructive-guard.mjs` applies `.orchestrator/policy/blocked-commands.json`:
34
+ **10 of its 14 rules are `severity: block`, the other 4 only warn** (measured 2026-09-06: `jq '[.rules[].severity]|group_by(.)|map({(.[0]):length})|add' .orchestrator/policy/blocked-commands.json`). On Codex, the destructive-command rules are instructions only because no compatible `PreToolUse` command handler is wired.
34
35
  Rule source of truth: [`.claude/rules/parallel-sessions.md`](.claude/rules/parallel-sessions.md) (PSA-003). Per-session bypass via Session Config: `allow-destructive-ops: true` (intentional maintenance only).
35
36
  - **Session Config below is runtime-critical.** `scripts/parse-config.mjs` parses the `## Session Config` block; `claude-md-drift-check` Check 6 enforces top-level-key parity against `docs/session-config-template.md`. Edit it like code, not prose — a dropped key changes runtime behaviour.
36
37
  - **Live state is not in this file.** Stack: Node 24+, vitest, ESLint 10 (`npm ci` after clone). Test counts, backlog, version, component inventory drift fast — the SSOT is README badges + `.orchestrator/metrics/sessions.jsonl`. Per-session detail lives in the Meta-Vault decisions log (linked above), not here.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,99 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.0.0] - 2026-09-13
11
+
12
+ Migration from 4.x: [v5 migration guide](docs/migration-v5.md). The major version reflects the changed `readCurrentStatus()` return contract and the now-enabled default close-time discovery scan.
13
+
14
+ ### Added
15
+
16
+ - `orchestrator.issue_budget.refunded` event (#1353) — `hooks/post-bash-issue-budget-refund.mjs` emitted only a stderr line, which under exit 0 reaches the debug log alone, so refunds were uncountable after the fact (HR-105). The event fires once per refund DECISION including the no-op branches, so a census has a denominator; `reason` is a closed enum, and the payload carries no command text, issue title or path. Lazily imported, awaited and caught — a failing emit can never change the hook's exit code.
17
+ - `scripts/lib/git-porcelain.mjs` (#1355) — one `git status --porcelain -z` parser for the three consumers that each had their own (`quality-gate.mjs`'s private copy, `project-hygiene.mjs`'s inline loop, `pre-dispatch-check.mjs`'s hand-written unquoting). Malformed-entry drop and the `R`/`C` rename double-field rule now live in one place.
18
+ - `sizingSubject()` + `session_profile` on the session record (#1247) — see below.
19
+
20
+ - Time-bounded operations route in `session-start`: explicit deadline, scoped accounts and products, rolling queue, a single publisher, and provider read-back evidence. Uses the current harness; installs no scheduler or new receipt engine.
21
+
22
+ - `withFileLock(lockPath, fn, { onRelease })` in `scripts/lib/file-lock.mjs` (#1336): an optional, throw-safe callback receives the `releaseFileLock` result; the return shape of `withFileLock` is unchanged. `scripts/lib/vault-status/board-lock.mjs` forwards it as `onReleaseOutcome`, and `orchestrator.vault.board_written` now carries `lock.release: 'not-owner' | 'busy'` when the lease expired mid-write or the release gave up.
23
+ - `checkCiStatus({ sha })` in `scripts/lib/ci-status-banner.mjs` (#1332): an optional full 40/64-hex commit id (validated before any CLI spawns) replaces the internal `getHeadSha` for GitLab and GitHub; without it behaviour is unchanged. The `ci-status` session-start probe uses it through a new optional registry `followUp` step: on `no-pipeline-for-head-sha` / `no-check-runs-for-head`, and only behind `SO_PROBES_INCLUDE_NETWORK`, it renders the pushed commit's verdict (`last pushed: <sha8> — CI <status> (#<pipeline>)`). The follow-up runs inside the probe's own budget race; if it throws or overruns, the HEAD reading already delivered stands (`follow_up: 'threw' | 'budget-exceeded'` in `orchestrator.probes.completed`). The GitLab re-query is skipped when the pushed SHA equals HEAD.
24
+ - `isCoordinatorDirectHousekeeping(record)` in `scripts/lib/session-schema/filters.mjs` (#1321) — the one definition of the coordinator-direct housekeeping record shape (every wave `role: 'Housekeeping'` with `coordinator_direct: true`), used by the eval engine and cited by the session-end writer rule and `/evolve analyze`.
25
+ - Budget pre-flight in the reconcile writer (`scripts/lib/reconcile/writer.mjs`, #1316): before writing, `writeApprovedRules` projects the instruction budget with the guard's own `computeInstructionBudget` on a temporary copy of `.claude/rules/` plus the pending writes. If an axis would end over its ceiling and the batch grows it, nothing is written, no candidate is stamped, and the result carries `ok: false, reason: 'instruction-budget-exceeded'` (`BUDGET_REFUSAL_REASON`) with a hint to consolidate into a thematic file. `tests/rules/receiving-review.test.mjs` stays the second safety net.
26
+ - Campaign film embedded on `site/index.html` and `site/de/index.html`, inside the `#loop` section (#1306): `<figure class="fig film">` with `poster`, `preload="none"`, visible controls (the film carries sound, unlike the silent hero loop), no autoplay, the webm source before the mp4 source, and an AI-disclosure `figcaption`. `site/assets/ui.js`'s hero-loop `<video>` now reads its filename from that element's `data-video-base` attribute instead of a hardcoded `agent-production-loop` literal — needed once a second `<video>` exists on the same page.
27
+ - `claude-md-drift-check` Check 9 (`rule-scoping`) gained a sixth probe, `fleet-intent-glob`, and a third JSON output category, `notes[]`: a rule can declare a `globs:` pattern as intentionally zero-match here via its own `fleet-intent-globs:` frontmatter key, and the checker reports it in `notes[]` instead of warning on it. `.claude/rules/testing.md` now declares this for `**/*Tests*` (#445) — on 2026-09-09 the unqualified zero-match warning had led an agent to delete that pattern (reverted only because a pinning test caught it). Measured against the live rule corpus: 37 warnings → 36 warnings + 1 note (total findings unchanged).
28
+ - `scripts/lib/scope-echo.mjs` + CLI + `orchestrator.wave_dispatch.scope_echo_checked` event (#1092) — soft receive-side signal that a dispatched agent echoed its declared file-scope back in its report; a named ceiling, never blocking. Additive `applicable: false` + `reason: 'scope-empty'` payload keys fire for an EMPTY declared scope, so consumers filter that never-instructed population out of the echo rate; the CLI gained `--help` and now exits 1 with a stderr WARN on an unreadable scope file instead of failing silently.
29
+ - `native_source` + `resume_linkage` fields on `orchestrator.session.started` (#1091) — measurement only, answers whether Claude Code preserves the raw `session_id` across a native `resume` instead of guessing.
30
+ - `references/` splits of the `bootstrap`/`discovery`/`evolve`/`session-plan`/`wave-executor` SKILL.md bodies (#1246) — byte-identical extraction; line counts 641→444, 571→404, 720→264, 620→478, 541→414.
31
+ - The optional tmux agent-status pane (Pane 5, #565) renders through that reader instead of `jq`-ing the cache file (#1342): `buildStatusPaneRenderCommand()` in `scripts/lib/tmux-layout/layouts.mjs` is exported so a test can run the render once instead of the 2 s poll loop, and its header line carries `source=…`, `at=…` plus TEXT markers (`⚠`, `STALE`, `DEGRADED: <reasons>`), never colour alone. Only `source=absent` (no ledger, no cache yet — the normal fresh-repo state) is deliberately unmarked (`host-resources.md` HR-101); every `degraded` reason on any other source renders `DEGRADED:`.
32
+ - `hooks/post-bash-issue-budget-refund.mjs` (`PostToolUseFailure`, wired in `hooks/hooks.json` and `hooks/hooks-cursor.json`) plus `refundBooking()` in `scripts/lib/issue-budget.mjs` (#1347): a `gh`/`glab issue create` that fails gives its budget slot back, so the retry does not spend a second one of `issue-budget.max-per-session`. A refund requires PROOF OF CHARGE: `chargeIssueBudget` (called by `hooks/pre-bash-issue-budget.mjs` with the payload's `tool_use_id` + statement index) records every counter-incrementing charge in the persisted `charged[]` ledger (`{id, key, unit, at}`, `bookingId()` = `<tool_use_id>#<index>`, `bookingKey()` = hash of `session|command|index`, `CHARGED_LEDGER_MAX` = 64, eviction fail-closed) and `refundBooking` refunds only a matched record, removes it, and decrements the unit it names (`count` or `exempt`). A create the pre-hook PARKED (#1314) or blocked was never charged and is never refunded, however often it is retried; `overflow[]` is never touched; `count`/`exempt` never go below 0. The hook refunds only when the failure is attributable to the create itself — `statementsCoverWholeCommand()` (`hooks/_lib/vcs-create-matcher.mjs`) must hold, so `glab issue create … && false` is `chain-not-attributable` and refunds nothing (the review's cap-drain exploit). `isFailure()` reads fields only (`exit_code !== 0`, non-empty `error`, `is_error` at top level or in `tool_response`), never the bare event name; on the Cursor bridge (`scripts/lib/cursor-hook-bridge.mjs`) the only reachable failure field is top-level `is_error`, and a failure event without any field is a loud no-op (stderr WARN).
33
+ - `hooks/enforce-scope.mjs` Gate 5c (#1295): this repo's Claude Code auto-memory directory (`~/.claude/projects/<encoded-repo-path>/memory/`, encoded via the one repo encoder `encodeProjectDir()`) is always writable — for EVERY agent of the session, coordinator and dispatched subagents alike, because the hook cannot distinguish them (tracked as #1352, priority::high) — evaluated inside Gate 6's out-of-root branch. A sibling repo's memory directory and every other out-of-repo path stay denied, and each allow emits one `orchestrator.scope.memory_dir_allowed` event so the carveout is countable. Before this, every wave manifest blocked the coordinator's own memory writes until it was torn down (six sessions in a consumer repo re-documented the same workaround, bewerbungs-assistent#307).
34
+
35
+ ### Changed
36
+
37
+ - `hooks/enforce-scope.mjs` `classifyCaller` distinguishes a PRESENT-but-unusable `agent_id` (number, object, array, blank string) as `'malformed'` rather than folding it into `'absent'` (#1352, review follow-up). The fail-open posture is unchanged — fail-closed would deny the coordinator's own `/close` memory writes — but the ledger can now tell a marker that arrived broken from a genuine main-thread call, which is what the HR-105 countability claim rested on.
38
+ - The `orchestrator.issue_budget.refunded` emits now all sit behind the `mode: off` check (#1353, review follow-up). Two of the four fired before it and so contradicted the contract written into `docs/events-schema.md` in the same change: a repo with the feature off contributed records to a census it can never supply a numerator for. Three enum values (`no-signal`, `not-charged`, `unit: 'exempt'`) were emitted, reached by the suite and asserted by nothing — each is now pinned by a test proven red under mutation.
39
+ - `scripts/emit-session.mjs` adopts `session_profile` only when the STATE.md frontmatter `session` matches the record's `session_id` (#1247, review follow-up), and omits the field with a stderr WARN otherwise. Without the check, two sessions sharing one working copy cross-contaminate: a plain `deep` close reads a concurrent `ultradeep` session's STATE.md and files its wave numbers under the ultradeep sizing row — the contamination #1247 exists to remove, inverted. The same block is now fail-safe: an unreadable STATE.md (EISDIR/EACCES) previously escaped to the top-level handler and exited 2 having appended NO record at all, letting an optional enrichment read decide whether the ledger got a line.
40
+ - `claude-md-drift-check` Check 6's unknown-local-key warning states only what it measured (#1356, review follow-up): the key's absence from the template's two `## Session Config` blocks, that population named inline. It previously also claimed the key was documented nowhere and that nothing read it — both refuted by measurement for all three keys it fires on, each of which is documented in the template's own sections and read at runtime.
41
+ - `hooks/enforce-scope.mjs` Gate 5c now grants the auto-memory carve-out to the COORDINATOR only (#1352). The discriminator is the PreToolUse payload's `agent_id`, measured against the shipped Claude Code binary rather than assumed — its own schema states the field is present only inside a subagent and absent on the main thread. A dispatched wave agent (including Discovery under `allowedPaths: []`) now falls through to the normal gates, which denies it. Ambiguity stays fail-OPEN — fail-closed would deny the coordinator's own `/close` memory writes — and rides on the existing `orchestrator.scope.memory_dir_allowed` event as `discriminator`, so the fail-open case is countable.
42
+ - `scripts/lib/pre-dispatch-check.mjs` reads `git status` with `-z` (#1354). Its hand-written unquoting handled only `\"` and `\\`, so an untracked path carrying a TAB or a non-ASCII byte (git's octal C-quoting) resolved to a non-existent path and the overlap guard silently passed. Both call sites in the file were on the same defect and both are fixed.
43
+ - `claude-md-drift-check` Check 6 routes opt-in session-config gaps to `notes[]` instead of `warnings[]` (#1356). All 36 live parity warnings in this repo were unactionable "not required" entries; after the split the live run reports 3 warnings — genuine template lag for `auto-skill-dispatch`, `wave-reviewers` and `issue-budget` — and 37 notes. Mandatory-key gaps stay `errors[]`. Category separation, not a threshold change.
44
+ - `scripts/lib/locks/state-md-lock.mjs` and `staging-fence-lock.mjs` release through `releaseFileLock` (#1349) instead of each carrying its own read-check-unlink sequence (net −60 production lines). Both gain the `.acquire` sibling guard from #1285: a release that races a concurrent takeover now answers `busy` and leaves the lock intact, where the old code unlinked it and reported success. Not a security repair — both use `staleCheck: 'pid'`, under which the takeover race is unreachable; the revisit trigger is the first of them moving to a lease mode.
45
+ - `readStdin` in `scripts/lib/io.mjs` accepts an injectable timeout (#1358), replacing a permanently skipped test with an empty body that asserted nothing. The 5 s default and the public signature are unchanged.
46
+ - Probe work-time attribution is tested with synchronized invocation windows and a controlled clock (#1360). This removes scheduler-dependent duration assertions while retaining the regression check and the original 50 ms budget. Vitest worker fanout is bounded to reduce contention from integration child processes.
47
+
48
+ - **BREAKING (deep-import surface, `scripts/lib/agent-status.mjs`):** provenance on the agent-status reader (#1342) — `readCurrentStatus()` no longer returns the bare map. `agent-status.jsonl` is documented as the source of truth and `agent-status-current.json` as its rebuildable cache. `rebuildCurrentFromLedger({ repoRoot, maxBytes })` folds a bounded ledger tail (`DEFAULT_REBUILD_MAX_BYTES` = 256 KiB — named ceiling with a revisit trigger) into the same map shape, one entry per `agentId` won by the greatest parsable `ts`, marking each `binding: 'bound' | 'legacy' | 'unknown'` so an older session's record can never overwrite a newer one. `readCurrentStatus()` now returns `{ entries, source: 'live-map' | 'rebuilt-log' | 'stale-cache' | 'absent', at, degraded? }` instead of the bare map, folding PER `agentId` (cache vs ledger, newer `ts` wins; equal-ms ties retain the cache only when its ledger payload agrees) — a global newest-timestamp comparison would have let one sibling's later successful push mask another agent's lost map write, which is the #1342 case itself (review finding, fake-regression-proven). `absent` means neither ledger nor cache exists; `live-map` means every entry was verified against the ledger. Entries live in null-prototype maps and `__proto__`/`constructor`/`prototype` agent ids are rejected as unbound records. `readCurrentStatusEntries()` is the backwards-compatible bare-map accessor.
49
+ - `discovery-on-close` now defaults to `true` when the key is absent and accepts `auto` (= `true`) (#1340). **Consumer repos without the key now run the close-time discovery scan** — one extra Explore agent per `/close`, and critical/high findings count against the issue budget; set `discovery-on-close: false` to keep the previous behaviour. Before this, the parser hard-defaulted to `false` (the 2026-07-29 doc switch to `true` never reached code) and the template's recommended `auto` made the whole Session Config parse throw.
50
+ - `project-hygiene`'s ignored-ballast check runs one `du` per 500 paths instead of one per path (#1317): `checkProjectHygiene` ~773 ms → ~142 ms on this repo (298 ignored entries), findings unchanged; a partial `du` failure still parses the surviving output.
51
+ - The path-scoped instruction-budget axis now counts toward the aggregate verdict (#1316): `overBudget` ORs in `overPathScopedBudget`, and a path-scoped breach alone raises the session-start banner. The 124,000 B ceiling is unchanged — a replay over 90 recorded states of `.claude/rules/` showed it fires on 5/90 (5.6 %), while the peak + 2.5 % method used for the generated axis would have been an unfalsifiable raise (the peak was HEAD). Before folding, the 8 generated rule files were condensed prose-only (every provenance pair, heading, evidence line and frontmatter unchanged; `alreadyMaterialized` identical): `bySurface.generated` 99,774 → 75,130 B, `bySurface.pathScoped` 147,407 → 122,763 B (headroom 1,237 B). A review-panel pass restored five qualifiers the condensation had weakened.
52
+ - `/evolve analyze` and the retro read the sessions ledger by its real shape (#1321, #1296): stubs are excluded with the existing `isRealSession` predicate (`status !== 'abandoned'` — a `_backfill_source` filter would have dropped 35 real repaired records); every analyzer states its population `n` and emits a WARN below n = 5; fragile-file derives paths from git (`waves[].files_changed` stays a count); effective-sizing reads the agent count defensively and skips coordinator-direct waves; `mode-retro` reports excluded stubs as their own line and treats a null `files_changed` as missing. The session-end writer now records a coordinator-direct/housekeeping session as one `Housekeeping` wave instead of `waves: []` / 0/0/0, and `skills/eval/rubric-v1.md` clarifies that this shape counts as "no waves ran" (0 of 427 existing records carry it, so no historical verdict changes).
53
+ - `ci-status` session-start probe: when HEAD's CI state is undetermined and the pushed commit's pipeline is **red**, the probe now scores `alert` (was `warn`) and the banner line carries 🚨 like a red HEAD (#1337) — the code on origin is broken, which is exactly the case the "CI status is the source of truth at session-start" rule exists for. The line still appears only behind `SO_PROBES_INCLUDE_NETWORK=1`.
54
+ - `session_metrics` (`scripts/mcp-server.sh`) reports `abandoned stubs excluded: N`, and a stubs-only ledger now says `No real sessions (abandoned stubs excluded: N)` instead of "file is empty" (#1296).
55
+ - `RUN_ID_PATTERN` / `assertRunId` are defined once, in `scripts/lib/test-runner/artifact-paths.mjs`; `scripts/lib/ux-grill/paths.mjs` imports and re-exports them.
56
+ - `scripts/lib/instruction-budget-guard.mjs`'s "generated rule surface" (`bySurface.generated`) now counts only rules carrying a reconciliation-provenance marker (`auto-generated: true`, `learning-key`, or `expires-at`) instead of every `globs:`-scoped rule — the old, broader definition mixed in 3 hand-written rules and is kept available as `bySurface.pathScoped` (#1297). Measured at `c73c094f`: 123,747 B / 11 files (old) → 76,114 B / 8 files (corrected); headroom against the unchanged 124,000 B ceiling: 253 B → 47,886 B. The ceiling itself was deliberately left as-is in this change — correcting a population and recalibrating a threshold are two separate decisions.
57
+ - `scripts/lib/claude-md-budget-lint.mjs`'s banner hint now names an absolute, runnable path (`__filename`, plus `--repo-root <repoRoot>`) instead of a repo-root-relative `scripts/lib/claude-md-budget-lint.mjs` literal that resolves to nothing in a consumer repo's own checkout (#1302).
58
+ - `skills/session-start/SKILL.md` § Phase 4 and `scripts/lib/session-start-probes.mjs`'s own probe census corrected 18 → 19 registered probes — `bootstrap-lock-freshness` had been described separately from the registered probe family instead of counted as one of its members.
59
+ - `.gitignore` now ignores `*.pen` (native Pencil design source files) — they stay host-local because a scanner finding sits in metadata the Node API cannot edit (#1304); exports and assets remain tracked.
60
+ - `hooks/hooks.json` `SessionStart` matcher now `startup|resume|clear|compact` (was missing `resume`); on a resume whose raw `session_id` matches the prior lock, the prior semantic id is reused and wave high-water marks (`last_wave`/`last_batch`) are preserved instead of reset (#1091).
61
+ - `hooks/_lib/lock-bootstrap.mjs` force-refreshes a stale-looking lock only when the re-entry source AND the semantic label both match, AND `existingLock.session_id === predecessorSessionId` (the raw id our own previous hook run recorded in `current-session.json`) — a bare label match alone can collide across repos, and this third, predecessor-witness conjunct is the only one tied to something WE wrote about ourselves.
62
+ - 10 `/reconcile`-generated rules absorbed into the thematic rule files (surface 140,482 → 123,747 B / 11 files under the broader pre-#1297 definition — today's `bySurface.pathScoped`, not `bySurface.generated` (see the correction above); headroom 253 B against the 124,000 B ceiling; idempotency `alreadyMaterialized` 40 → 50).
63
+ - A further 10 `/reconcile`-generated learnings absorbed into 7 already-consolidated thematic rule files this session, rather than materialized as 10 new standalone files — `bySurface.generated` 95,414 B / 18 files (right after `/reconcile` wrote them as standalones alongside the 8 pre-existing thematic files) → 87,336 B / 8 files (after absorbing them into 7 of those 8, per the merge contract); `alreadyMaterialized` 50 → 60; `measurement-discipline.md`'s `expires-at` lowered 2026-10-24 → 2026-10-20 (11 → 12 absorbed dates) once the newly-absorbed `ls`-verify-declared-test-paths learning became its earliest-due content.
64
+ - `scripts/ci/assert-vitest-green.mjs` kill-artifact hint now reads `[ci] KILL ARTEFACT — … (NOT a test failure)` instead of a bare `[ci] hint: …` (#1294 b) — the false-green-vs-real-failure distinction is now visible at a glance in CI logs.
65
+ - `.claude/rules/security.md` gained a VCS-anchor caveat — the GitHub mirror's `main` is pushed directly from the operator's machine, not via CI (#1079); `docs/github-mirror-protection.md` re-measured state (2026-09-09 @ `c2e19604`) and the required remediation order; `enforce_admins` deliberately left `false` until the push-path change lands.
66
+ - Dead `globs:`/`paths:` frontmatter entries removed from 3 rules (`bash-harness-pitfalls.md`, `cli-design.md`, `testing.md`) — `rule-scoping` drift-check warnings 11 → 1; `**/*Tests*` in `testing.md` deliberately kept (fleet intent for consumer Swift repos, #445; pinned by `tests/skills/config-reading-glob-rules.test.mjs`).
67
+ - `orchestrator.reconcile.completed` renames the payload key `written` → `candidate_store_merged` (#1315) — the old name read as "rule files were written", which the reconcile engine never does: the flag reports the merge of the `reconcile-candidates.jsonl` idempotency sidecar, the engine's only disk write, emitted before the operator-approval AUQ. For "a rule reached `.claude/rules/`" read `orchestrator.reconcile.rules_written`. `written` stays as a deprecated alias, written from the same expression so the two can never disagree, until removal on 2027-03-13 (same one-generation precedent as `orchestrator.session.stopped` → `orchestrator.turn.stopped`, #1234).
68
+ - `session-end` drift-operations renders `warnings[]` in every mode that runs, also on `status: ok` (#1350) — under its own "CLAUDE.md drift warnings" heading, with ONE success-line format everywhere: `CLAUDE.md drift: OK (N files scanned, mode=<mode>) — E errors, W warnings, T notes`. Previously the success line required `errors: [] && warnings: []`, so a run with warnings printed neither the section nor a count (live: 36 warnings, all `session-config-parity`, rendered nowhere).
69
+ - `skills/session-start/references/phase-4-ssot-environment-check.md` no longer instructs a hand-invocation of any Phase 4 probe (#1351): the whole 19-probe banner family runs ONCE mechanically via `runSessionStartProbes()` from `hooks/on-session-start.mjs`, with the `PROBES` registry in `scripts/lib/session-start-probes.mjs` as the single declaration site for module path, entry function, arguments, precondition and network flag. All 19 "Additionally, …" hand-invocation passages — 16 worded "Additionally, invoke", 3 "Additionally, if" — one per registered probe (measured 2026-09-13 at `5e9506b1`: 19 before, 0 after) are now rendering references — return contract plus exact banner wording — because a hand-invocation paid the full measurement twice (`project-hygiene` alone is a `git status --ignored` plus a batched `du` over every ignored path, ~150-800 ms).
70
+
71
+ ### Fixed
72
+
73
+ - The untracked test-dependency scanner retries only the explicitly rejected symlink descendant when Git identifies it, retaining the fallback for other failures. This avoids recursively splitting healthy paths in tracked-tree gates (#1360).
74
+
75
+ - Agent-status recovery preserves a later ledger append when two real updates share a millisecond and the second cache write fails. Equal-time matching payloads still retain cache metadata; conflicting payloads recover the ledger and report `rebuilt-log`. The actual write/lock-failure regression now uses a deterministic clock.
76
+ - Discovery probe guards and imports in `skills/discovery/probes-{docs,supply-chain,vault}.md` and `skills/session-end/references/phase-2-quality-gate.md` resolve from `${PLUGIN_ROOT}` instead of a repo-relative/undefined `$REPO_ROOT` path (#1313) — in consumer repos the probes silently ended in `SKIPPED`; a skip now names the checked path.
77
+ - Issue-budget overflow parks a structured `{ title, description, repo, command, at, truncated }` record through one shared `buildOverflowRecord` instead of the raw command cut at 500 chars (#1314): `--description-file`/`-F`/`$(cat <path>)` bodies are read at block time (size-capped), `-t` and `-R/--repo` are recognised, and the `[Backlog-Sammel]` fold renders the body.
78
+ - Drift-check `notes[]` reach the operator at `/close` (#1318): `skills/session-end/drift-operations.md` reads them from the stdout JSON it already parses and renders them under their own, non-warning heading.
79
+ - `countManagedSections()` in `scripts/dialectic-deriver.mjs` counts fence-aware `## ` headings when no managed sentinels are present (#1319) — a 12-section proposal was reported as 1 delta.
80
+ - `docs/events-schema.md` documents the `orchestrator.probes.completed` element as `{ id, outcome, reason?, work_ms?, follow_up? }` and the per-probe work-time budget (#1320), and names that dry-run and apply measure different quantities for `orchestrator.dialectic.completed` deltas (#1319).
81
+ - `releaseFileLock` (`scripts/lib/file-lock.mjs`) can no longer delete a replacement lease (#1285): with the owner guard on, release now takes the same `${lockPath}.acquire` guard as acquisition and takeover (bounded synchronous retry, `DEFAULT_RELEASE_GUARD_MS = 1000`, optional `guardTimeoutMs`), so a delayed release that finds a newer owner returns `not-owner` instead of unlinking it; an exhausted budget returns `busy` and leaves the lock untouched. A missing lock returns `not-found` without taking the guard. `ownerGuard: false` is unchanged. The JSDoc now states what lease expiry does and does not guarantee for a still-running old holder.
82
+ - `runDirPath` in `scripts/lib/test-runner/artifact-paths.mjs` rejects run ids outside `[A-Za-z0-9._-]+`, plus `.` and `..` (#1330); `scripts/lib/ux-grill/paths.mjs` had the same `.`/`..` hole (`runDirPath('/repo', '..')` resolved one level above the run directory).
83
+ - `createFinding` (`scripts/lib/test-runner/issue-reconcile.mjs`) fails closed with `VALIDATION` when the body does not carry the `**Fingerprint:**` sentinel for the given fingerprint (#1331) — a caller that forgot it used to break deduplication permanently. `buildIssueBody(finding, fp)` is exported for direct callers and now neutralises sentinel literals in `description` and `locator`, so page text embedded in an axe result can no longer plant a fake fingerprint line ahead of the real one.
84
+ - `ci-status` probe: `severityOf` and `render` share one allow-failure predicate — an empty `allowFailureJobs` array scored `warn` with no banner line (#1333).
85
+ - `/ux-grill` fingerprints are injective for locators longer than 256 characters (#1334): the fingerprint input becomes `truncated + ':' + digestSha256Short(locator)`, so a decoy element under a long shared class chain no longer shadows a real violation. Locators ≤ 256 characters keep their fingerprint byte-for-byte. Deliberately limited to `skills/ux-grill/rubric-v2.md`; `/test` (rubric-v1) is unchanged because its existing fingerprints would otherwise be invalidated (#1338).
86
+ - `/ux-grill` credential substitution replaces every `${LOGIN_EMAIL}` / `${LOGIN_PASSWORD}` occurrence in one pass with a replacer function (#1335) — previously only the first occurrence was replaced, and a password containing `$&` or `$'` was mangled by string-replacement patterns.
87
+ - The eval engine's gate-health criterion scores a coordinator-direct housekeeping record (`isCoordinatorDirectHousekeeping`) `not-applicable` again under the new writer shape, keyed on the wave shape only — never on `session_type` or `coordinator_direct` alone (multi-wave housekeeping sessions stay `cannot-determine`, as rubric-v1 pre-registers).
88
+ - `scripts/lib/session-record-repair.mjs` no longer discards a field's original value when defaulting it — the pre-repair value is preserved under a `_<field>_raw` sidecar, idempotent across repeated repair passes (6 fields across 8 call sites: `waves` and `agent_summary` each defaulted from two code paths, plus `total_waves`, `total_agents`, `total_files_changed`, `completed_at`). Motivated by 7 of 40 records in a consumer ledger that had lost narrative `agent_summary` strings and `total_files_changed` path lists to `{...0}`/`0` (#1303).
89
+ - `scripts/lib/session-lock.mjs`'s `forceAcquire()` now rejects a missing or blank `sessionId` (`{ ok: false, reason: 'missing-session-id' }`) instead of writing a lock file with no `session_id` key at all — such a lock was classified as foreign by every reader and silently skipped enforcement (#1303).
90
+ - **GitHub Actions `test (ubuntu-latest)`** — the unsharded Linux run was wrapped in `timeout --preserve-status 240s`; the suite now exceeds that (killed at exactly 240 s on `c2e19604` and `d4c51bd8`, vitest never wrote its result file → `FAIL-CLOSED`). Raised to 840 s, matching the GitLab shard cap from #1294 (a); the job's `timeout-minutes: 15` already covers it.
91
+
92
+ - `check-unwired-features` root filter no longer masks two roots sharing a basename — a colliding basename now needs the qualified `dirname/base` form to downgrade out of the reportable class (#1293).
93
+ - `tests/scripts/pack-policy-floor.test.mjs` accepts npm 12's keyed-object `npm pack --dry-run --json` shape (was red on the v4.2.0 tag push).
94
+ - `project-hygiene`'s ignored-ballast check reads `git status -z` (#1348) — without it git QUOTES any path carrying a space, tab or non-ASCII byte (`"ign dir/"`, `"ign\tt.bin"`, `"ign\303\244.bin"`), and the quoted string does not exist on disk, so `duBytesBatch` dropped it and its bytes never reached the sum. Measured: a tree with `ign dir/` 3 MB, `ign<TAB>t.bin` 5 MB, `ignä.bin` 5 MB and `big.bin` 5 MB reported "5 MB (largest: big.bin 5MB)" against an actual 18 MB — low enough to fall under the report threshold entirely. `-z` is NUL-separated and never quotes, so the leading-quote strip in `isOwnRuntimeArtifact` is gone and rename/copy source fields are consumed explicitly.
95
+ - The `vault-staleness` probe skips a `01-projects/<slug>/` carrying a `_passive.md` marker (#1238) — a folder nobody syncs on purpose is no longer compared at all. The marker is checked BEFORE the `_overview.md` existence check, so a passive folder without an overview is still counted; it is excluded from `scanned_projects` and counted as `passive_skipped` (same key in `metrics` and in the JSONL summary record), so the skip is visible rather than indistinguishable from a healthy project. Same marker `scripts/lib/context-coverage-banner.mjs` already honours.
96
+
97
+ ### Notes
98
+
99
+ - `skills/discovery/probes-ui.md`'s `design-drift` probe gained a naming-collision flag: GitLab issue #1300 (`projects-baseline` design-first Pencil-token-contract epic) proposes a second, mechanical `design-drift` probe under the identical name; unresolved, left for whoever implements #1300 to settle.
100
+ - 4.2.0 published to npm 2026-09-09 20:45 UTC.
101
+ - GH#68 (Codex command entry points missing after an update) answered — fixed since 4.0.1; see `docs/codex-setup.md` § Refresh and Explicit Cache Invalidation.
102
+
10
103
  ## [4.2.0] - 2026-09-09
11
104
 
12
105
  ### Added
@@ -1611,7 +1704,7 @@ without it. Its entries are carried below under *From the 2026-08-22 → 2026-08
1611
1704
  fact sits under all of them: **a surface that said it was live, and a measurement that never
1612
1705
  had a caller.** Cursor advertised as a first-class platform and loaded only `.cursor/rules` —
1613
1706
  no slash commands, no skills, no hook bridge; `/session` was chat text. The same class shows up
1614
- in the rest of the line: 18 probes with zero mechanical callers, three vault writers that could
1707
+ in the rest of the line: 19 probes with zero mechanical callers, three vault writers that could
1615
1708
  not report their own failure, gates that wrote and never read, and `detectPlatform()` walking
1616
1709
  to `$HOME` until 63 of 84 telemetry records said `platform=pi`.
1617
1710