session-orchestrator 3.24.0 → 4.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.
- package/.agents/skills/architecture/SKILL.md +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +1 -1
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1125 -2
- package/NOTICE +11 -6
- package/README.md +127 -94
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +108 -62
- package/docs/codex-setup.md +65 -21
- package/docs/components.md +36 -15
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +9 -6
- package/docs/instruction-delivery.md +62 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +341 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +5 -5
- package/docs/session-config-reference.md +57 -56
- package/docs/session-config-template.md +6 -29
- package/docs/telemetry.md +157 -3
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/hook-import-set.json +1487 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +2 -2
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +2 -2
- package/hooks/on-session-start.mjs +103 -2
- package/hooks/on-stop.mjs +36 -11
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +85 -0
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +187 -431
- package/hooks/post-tool-batch-wave-signal.mjs +118 -4
- package/hooks/post-tool-failure-corrective-context.mjs +2 -2
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +13 -4
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/plugin.json +27 -0
- package/scripts/backfill-abandoned-sessions.mjs +50 -4
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +249 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +82 -6
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +8 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +2 -1
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +7 -2
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +2 -1
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events.mjs +22 -6
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +26 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +43 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +207 -14
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +406 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +13 -4
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/session-close-backfill.mjs +67 -9
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +125 -10
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +36 -2
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +18 -1
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +181 -9
- package/scripts/lib/telemetry/sync.mjs +368 -12
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
- package/scripts/lib/validate/check-skill-links.mjs +163 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
- package/scripts/lib/validate/check-unwired-features.mjs +0 -2
- package/scripts/lib/validate/check-validator-registration.mjs +10 -4
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
- package/scripts/lib/wave-resource-gate.mjs +8 -2
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +118 -4
- package/scripts/materialize-wave-scope.mjs +12 -5
- package/scripts/memory-propose.mjs +19 -5
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/release.mjs +337 -29
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +161 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-preamble.md +1 -0
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/evolve/SKILL.md +65 -26
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +33 -0
- package/skills/remote-offload/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +10 -3
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +22 -904
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +15 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1309
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -270
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,1063 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.0.0] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
> **A MAJOR, because public surfaces are REMOVED** (see *Removed (BREAKING)* below).
|
|
13
|
+
> This entry was folded out of `[Unreleased]` by hand at cut time. That is a manual
|
|
14
|
+
> editorial act and not an automated one: `scripts/release.mjs` carries no writer for it —
|
|
15
|
+
> `checkChangelogEntry()` (`release.mjs:283`) only REQUIRES a dated `## [4.0.0]` heading as
|
|
16
|
+
> the topmost release with `[Unreleased]` emptied (`release.mjs:296`), and `--check`
|
|
17
|
+
> separately requires HEAD to be present on BOTH remotes (`evaluateRemoteHeadParity`,
|
|
18
|
+
> `release.mjs:556`) before it will tag. An earlier draft of this note asserted that the
|
|
19
|
+
> script renames the heading; it does not, and a release trusting that would have failed
|
|
20
|
+
> its own preflight. The reason the heading is written no earlier than the cut is the
|
|
21
|
+
> `3.22.1` failure this repo already paid for once — a release section for a release that
|
|
22
|
+
> never completed, unnoticed for 12 days (`e4674109`).
|
|
23
|
+
|
|
24
|
+
**Predecessor commits (v3.24.0 → `e4674109`).** Three commits (`ee8ea425` Wave 2,
|
|
25
|
+
`e22a702e` Wave 3, `37169158` Wave 4; 71 files, +3,505/−678, measured
|
|
26
|
+
`git diff --stat 2cb8708b..37169158`), no `BREAKING CHANGE:` footer and no `!` subject.
|
|
27
|
+
Twelve Wave-2 follow-up issues land as mechanism: identity resolution folds onto the
|
|
28
|
+
process-local reader on a third surface, seven ledger readers move onto one canonical
|
|
29
|
+
`sessions.jsonl` collapse, and a `secret_masker.applied` guard closes its last two gaps.
|
|
30
|
+
Wave 3 arms the schema-drift CI gate; Wave 4 is a 3-Opus-reviewer panel plus fixpass that
|
|
31
|
+
closed 2 HIGH findings inside the panel's own Wave-2/3 diff.
|
|
32
|
+
|
|
33
|
+
**The 360° audit session (`main-2026-09-06-deep-1`) — what makes this a major.** Everything
|
|
34
|
+
below marked *4.0.0 — audit session* is that session's own change set, which lands as this
|
|
35
|
+
release's commit: 277 working-tree entries at fold time (`git status --short | wc -l`,
|
|
36
|
+
2026-09-06), on top of 13 already-committed predecessors measuring 256 files, +13,537/−1,669
|
|
37
|
+
(`git diff --stat v3.24.0..HEAD | tail -1`, same day). Fourteen read-only Discovery agents measured the plugin against its own
|
|
38
|
+
telemetry, the 90-day fleet ledger and the vendor docs; the synthesis is
|
|
39
|
+
[`docs/audits/2026-09-06-360-audit.md`](docs/audits/2026-09-06-360-audit.md), per-agent
|
|
40
|
+
evidence under `docs/audits/2026-09-06-360-audit/w1/`. Nothing was removed on judgement: the
|
|
41
|
+
rule was 0 telemetry ∧ 0 fleet invocation in 90 days ∧ no runtime consumer, and
|
|
42
|
+
prose-invoked skills (0 by construction) were exempt. Upgrade guide:
|
|
43
|
+
[`docs/migration-v4.md`](docs/migration-v4.md).
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
#### 4.0.0 — audit session
|
|
48
|
+
|
|
49
|
+
- **Root `AGENTS.md`, root `plugin.json`, and a portable `.agents/skills/` mirror — this repo
|
|
50
|
+
was previously invisible to Copilot CLI and to agent-plugins.org clients.** The root
|
|
51
|
+
`AGENTS.md` is byte-identical to `CLAUDE.md` (`diff -q AGENTS.md CLAUDE.md` → no
|
|
52
|
+
difference), so a harness that resolves project instructions from `AGENTS.md` now finds
|
|
53
|
+
this repo's own Session Config instead of nothing — before this there was no root
|
|
54
|
+
`AGENTS.md` at all, meaning the plugin's own repository could not read its own config on a
|
|
55
|
+
harness that looks for that filename. `plugin.json` declares
|
|
56
|
+
`$schema: https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`, the cross-vendor
|
|
57
|
+
manifest shape Cursor, Amazon, Microsoft, OpenAI and Vercel converged on.
|
|
58
|
+
`.agents/skills/<name>/SKILL.md` mirrors all 43 skills (`ls .agents/skills | wc -l` → 43)
|
|
59
|
+
for harnesses that read that convention rather than a plugin manifest.
|
|
60
|
+
- **`ultradeep` — a session PROFILE over `deep`, deliberately NOT a fourth session type.**
|
|
61
|
+
`/session ultradeep` resolves to `session-type: deep` + `session-profile: ultradeep` in
|
|
62
|
+
STATE.md frontmatter (`commands/session.md` § Argument alias). `session-type` never becomes
|
|
63
|
+
`ultradeep`: `VALID_SESSION_TYPES` is a closed set in
|
|
64
|
+
`scripts/lib/session-schema/constants.mjs`, and a fourth member would degrade silently in
|
|
65
|
+
two places (`telemetry/schema.mjs` maps an unknown type to `"other"`,
|
|
66
|
+
`session-close-backfill.mjs` labels it `housekeeping`). The profile changes only the wave
|
|
67
|
+
SHAPE — 7 waves with a coordinator-direct Synthesis-Gate at W2 (`agents: 0`, the one wave
|
|
68
|
+
whose whole job is to stop and ask before any code is written), per the new row at
|
|
69
|
+
`skills/session-plan/SKILL.md:336`. `SESSION_PROFILE_FIELD` in `scripts/lib/state-md.mjs`
|
|
70
|
+
is the reader; absent is not empty. Budgets (`ultradeep.max-*`) are deliberately NOT
|
|
71
|
+
implemented — the PRD defers thresholds until three runs have been measured. Spec:
|
|
72
|
+
[`docs/prd/2026-09-06-ultradeep-session-profile.md`](docs/prd/2026-09-06-ultradeep-session-profile.md).
|
|
73
|
+
- **A plugin-update banner at session-start — the first code in this repo that compares
|
|
74
|
+
INSTALLED against AVAILABLE.** `scripts/lib/plugin-update-banner.mjs` (`checkPluginUpdate`,
|
|
75
|
+
imported lazily from `hooks/on-session-start.mjs:976`) reads the RUNNING package's own
|
|
76
|
+
`package.json` — resolved as `../..` from `scripts/lib/`, i.e. the loaded bytes, never
|
|
77
|
+
`$CLAUDE_PLUGIN_ROOT`, which is precisely what resolved to the checkout while a stale cache
|
|
78
|
+
copy was executing — and compares it against the npm `dist-tags.latest`. The measured gap:
|
|
79
|
+
this host ran the marketplace cache at **3.19.0** (installed 2026-08-09) while repo and npm
|
|
80
|
+
were at **3.24.0** — five minors, four weeks, no warning, because the freshness probe
|
|
81
|
+
shells out to `git -C <plugin-dir> log -1` against a FILE COPY and
|
|
82
|
+
`classifyVersionMismatch()` downgrades anything below a major jump to `info`. Contract:
|
|
83
|
+
fail SILENT, never optimistic — offline, non-2xx, malformed JSON, timeout and unusable
|
|
84
|
+
cache each return `null` ("no statement"), never "you are up to date" (the #1031 collapse
|
|
85
|
+
class).
|
|
86
|
+
- **A telemetry sandbox guard — `detectSandbox()` in `scripts/lib/telemetry/sync.mjs`.**
|
|
87
|
+
This session's own read-only Wave-1 benches sent 6 production pings from agent sandboxes:
|
|
88
|
+
`hooks/on-session-end.mjs` ran from the repo checkout with `SO_CONFIG_HOME` redirected
|
|
89
|
+
while `owner.yaml` was unreachable inside the sandbox. The sender now refuses when
|
|
90
|
+
`SO_TELEMETRY_DISABLED=1` (`sandbox:telemetry-disabled`), `DO_NOT_TRACK` is set
|
|
91
|
+
(`sandbox:do-not-track`), the config home is split from the state dir
|
|
92
|
+
(`sandbox:config-home-split`), or the state dir resolves under a temp root
|
|
93
|
+
(`sandbox:temp-root` — matched after realpath, because macOS's `/var` → `/private/var`
|
|
94
|
+
symlink defeats a raw string prefix). Every refusal is reported with its `sandbox:*` reason
|
|
95
|
+
rather than as a silent no-op.
|
|
96
|
+
- **[`docs/baseline.md`](docs/baseline.md) — the `projects-baseline` relationship written
|
|
97
|
+
down.** One line: the baseline is a **private, optional** companion repository, read when
|
|
98
|
+
present, degraded to a documented fallback when absent, required by nothing in this plugin
|
|
99
|
+
and needed by no public consumer. The measurement behind it: of 49 baseline-touching files,
|
|
100
|
+
17 are docs-only, 15 test-only, 13 soft, and only 4 hard-runtime — **2 of those 4 pointed
|
|
101
|
+
at a hardcoded `$HOME/Projects/projects-baseline` that does not exist**.
|
|
102
|
+
|
|
103
|
+
#### Predecessor commits (v3.24.0 → `e4674109`)
|
|
104
|
+
|
|
105
|
+
- **`scripts/lib/wave-executor/dispatch-common.mjs` (#1204).** Six symbols
|
|
106
|
+
(`NEVER_FOREIGN_ROLES`, `DEFAULT_TIMEOUT_SEC`, `DEFAULT_KILL_GRACE_MS`, `isSafeRunId`,
|
|
107
|
+
`isNeverForeignRole`, `runChild`) extracted out of `foreign-dispatch.mjs` into one shared
|
|
108
|
+
module, imported by both dispatch call sites (`foreign-dispatch.mjs`, `remote-dispatch.mjs`)
|
|
109
|
+
and their two test files. A redundant `isNeverForeignRole` check that had never lived in
|
|
110
|
+
`wave-resource-gate.mjs`'s own invariant is now a documented non-check there instead of a
|
|
111
|
+
silent gap.
|
|
112
|
+
- **`scripts/lib/learnings/evolve-telemetry.mjs` (#1206).** Mechanical
|
|
113
|
+
`orchestrator.evolve.completed` / `orchestrator.dialectic.completed` emitters replace
|
|
114
|
+
hand-written prose emit sites in `skills/evolve/SKILL.md`: `recordEvolveCompletion()` is
|
|
115
|
+
called from `scripts/sweep-expired-learnings.mjs`'s `--prune` exit path (the one store
|
|
116
|
+
write `/evolve analyze` already performs), and `recordDialecticRun()` from
|
|
117
|
+
`scripts/dialectic-deriver.mjs`. Both skip the emit with a stderr note when no `repoRoot`
|
|
118
|
+
is given, so test runs never write the fleet ledger. `docs/events-schema.md` gained both
|
|
119
|
+
event's producer lines.
|
|
120
|
+
- **`hooks/post-edit-import-probe.mjs` — a PostToolUse hook that catches a broken
|
|
121
|
+
hook-reachable module at save time, not at the next tool call (#1224).** On every
|
|
122
|
+
Edit/Write/MultiEdit to a `.mjs`/`.js`/`.cjs` file that is a member of the new committed
|
|
123
|
+
allowlist `hooks/_lib/hook-import-set.json` (149 modules at generation time — every module
|
|
124
|
+
transitively reachable from the four hook manifests' entry files), it runs ESLint's
|
|
125
|
+
`no-undef` on that single file (primary check) plus an `import()` probe scoped to
|
|
126
|
+
`scripts/lib/**` only (secondary — `hooks/*.mjs` is excluded because half of them run
|
|
127
|
+
`main()` at module bottom). Reports via `additionalContext` + the new
|
|
128
|
+
`orchestrator.hook.import_probe_failed` event (payload: `file`, `check`, `error`,
|
|
129
|
+
`reachable_from`, `duration_ms` — documented in `docs/events-schema.md`); never blocks
|
|
130
|
+
(exit 0 always); kill switch `SO_DISABLED_HOOKS=post-edit-import-probe`. The allowlist is
|
|
131
|
+
built by the new `scripts/generate-hook-import-set.mjs` (`--check` re-crawls and diffs) and
|
|
132
|
+
is guarded three ways: a new blocking `validate-plugin.mjs` check
|
|
133
|
+
(`runHookImportSetCheck`), a new `.husky/pre-commit` stage scoped to commits touching
|
|
134
|
+
`hooks/` or `scripts/lib/`, and — deliberately absent — no Codex wiring
|
|
135
|
+
(`hooks-codex.json`), documented as an intentional asymmetry in
|
|
136
|
+
`check-hooks-symmetry.mjs`'s `DOCUMENTED_ASYMMETRIES` (same missing Edit/Write-payload
|
|
137
|
+
adapter gap as `post-edit-validate.mjs`). Wired into `hooks.json`, `hooks-cursor.json`, and
|
|
138
|
+
`hooks-pi.json`. Rationale: the 2026-09-04 host-wide hook-block incident documented
|
|
139
|
+
elsewhere in this section (a `SyntaxError`/`ReferenceError` in an intermediate save of
|
|
140
|
+
`own-session.mjs` blocked every Bash/Edit call on the host for ~8 minutes) was a
|
|
141
|
+
**call-time** defect neither `node --check` (syntactically valid) nor a bare `await
|
|
142
|
+
import()` (module-level evaluation succeeds) catches — ESLint's `no-undef` does. The
|
|
143
|
+
allowlist has since been regenerated to include `scripts/lib/config/private-config-dir.mjs`
|
|
144
|
+
(new, #1223 below), which became hook-reachable via `host-identity.mjs`: measured
|
|
145
|
+
2026-09-05, `node scripts/generate-hook-import-set.mjs --check` → `✓ hook-import-set: 150
|
|
146
|
+
modules, in sync`.
|
|
147
|
+
|
|
148
|
+
### Changed
|
|
149
|
+
|
|
150
|
+
#### 4.0.0 — audit session
|
|
151
|
+
|
|
152
|
+
- **43 generated rule files consolidated into 8 thematic ones; `.claude/rules/` goes 61 → 26
|
|
153
|
+
files.** Measured now: `ls .claude/rules/*.md | wc -l` → 26, of which **zero** still match
|
|
154
|
+
the generated-rule naming (`anti-pattern-*`, `proven-pattern-*`, `recurring-issue-*`,
|
|
155
|
+
`convention-*`, `fragile-*`); at `HEAD` (`e4674109`) the same directory held 61 files
|
|
156
|
+
(`git ls-tree -r --name-only HEAD .claude/rules/ | grep -c '\.md$'`). The eight
|
|
157
|
+
replacements are `identity-and-locks`, `guard-design`, `measurement-discipline`,
|
|
158
|
+
`test-hygiene`, `process-contracts`, `toolchain-and-build`, `git-and-worktrees`,
|
|
159
|
+
`review-and-adapter-contracts`. Grounds: the 43 files were delivered TWICE per dispatched
|
|
160
|
+
agent (natively plus via rule injection) — 112 kB, 46 % overhead — and 10 of them restated
|
|
161
|
+
an always-on rule's insight verbatim.
|
|
162
|
+
- **The three largest instruction files are SPLIT into `references/`, not shortened.**
|
|
163
|
+
`skills/session-start/SKILL.md` 1,275 → 388 lines (9 reference files);
|
|
164
|
+
`skills/session-end/SKILL.md` 1,203 → 316 (6 reference files);
|
|
165
|
+
`skills/wave-executor/wave-loop.md` 1,337 → a 39-line index over 3 references
|
|
166
|
+
(`wave-loop-dispatch.md`, `wave-loop-review.md`, `wave-loop-scope-manifest.md`). Baselines
|
|
167
|
+
measured `git show HEAD:<path> | wc -l`, current `wc -l`. Content moved rather than
|
|
168
|
+
vanished: a phase you need is one hop away, a phase you do not need is no longer in every
|
|
169
|
+
agent's context.
|
|
170
|
+
- **`orchestrator.session.stopped` → `orchestrator.turn.stopped` (GitLab #1234).** The
|
|
171
|
+
emitter fires per assistant TURN, not per session: measured over the 90-day fleet window,
|
|
172
|
+
**15,538 records against 2,016 distinct `orchestrator.session.started` ids = 7.7 per
|
|
173
|
+
session**, with 184 for one id — so six consumers reading it as a session-lifecycle signal
|
|
174
|
+
were wrong by that factor. `hooks/on-stop.mjs:489`/`:493` emits BOTH names from the same
|
|
175
|
+
payload object for one deprecation generation (so they cannot disagree); the legacy name
|
|
176
|
+
additionally carries `deprecated: true`, letting a reader tell at a glance that it matched
|
|
177
|
+
the old name. **Removal of `orchestrator.session.stopped`: 2027-03-06.** Readers switch the
|
|
178
|
+
name they match on and change nothing else. NOT affected: the SubagentStop branch keeps
|
|
179
|
+
emitting `orchestrator.agent.stopped`, whose per-agent cardinality is correct. Contract:
|
|
180
|
+
`docs/events-schema.md:92-93`.
|
|
181
|
+
- **The telemetry `fleet` field becomes `fleet_self_declared`, and attribution moves
|
|
182
|
+
server-side.** The old name asserted something the client cannot know: 394 of 490 pings
|
|
183
|
+
were the operator's own second Mac, mis-attributed as "external", because the flag hung off
|
|
184
|
+
an `owner.yaml` line rather than off a person. The client field now says what it is —
|
|
185
|
+
self-declared, derived from the RESOLVED consent state (`enabled-fleet` / `enabled-env`)
|
|
186
|
+
rather than from a raw `owner.yaml` read (`scripts/lib/telemetry/schema.mjs:552-555`) — and
|
|
187
|
+
the authoritative classification is an anon-id allowlist in the ingest server
|
|
188
|
+
(`server/ingest/config.mjs`, `server/ingest/validate.mjs:190-196`). `fleet` remains in the
|
|
189
|
+
payload, identical in value for the whole generation so the server's existing column stays
|
|
190
|
+
comparable: **DEPRECATED 2026-09-06, removal 2027-03-06** (`docs/telemetry.md:42-43`).
|
|
191
|
+
|
|
192
|
+
- **`zod` and `yaml` hoisted from a nested skill's own `package.json` to this repo's
|
|
193
|
+
root `dependencies`.** `skills/vault-sync/package.json` resolved them as its own
|
|
194
|
+
transitive install; a fresh top-level `npm ci` without that nested install step
|
|
195
|
+
left `vault-sync`'s schema validation unable to resolve either package. Root
|
|
196
|
+
`package.json` now pins `yaml@^2.9.0` and `zod@^3.25.76` directly (the nested
|
|
197
|
+
`package.json` still carries its own copies — redundant, harmless, left as a
|
|
198
|
+
cleanup candidate).
|
|
199
|
+
- **`scripts/release.mjs`'s preflight gains a GitHub-mirror CI row and two
|
|
200
|
+
publish-time hardenings.** `evaluateGithubCiRow()` adds `ci-green-on-head-github`
|
|
201
|
+
as its own preflight check (26 rows total) via `checkCiStatus({ vcs: 'github' })`
|
|
202
|
+
— `--skip-ci` marks it skipped, and a repo with no configured GitHub remote also
|
|
203
|
+
skips it rather than failing; `publishInvocation()` now pins
|
|
204
|
+
`npm_config_loglevel=notice` on the publish spawn (an inherited silent level from
|
|
205
|
+
an outer gate run suppressed output the packlist gate depends on); and
|
|
206
|
+
`describeTagProgress()` is printed on a post-receipt tag/push failure, so a
|
|
207
|
+
publish that succeeds but fails to tag no longer leaves the operator guessing
|
|
208
|
+
what state the release is in. `commands/release.md` step 7 documents the ≥600s
|
|
209
|
+
Bash wrapper this needs; `skills/npm-publish/SKILL.md` is updated to match.
|
|
210
|
+
- **The release drift sweep moves off `git grep` onto `enumerateRepoFiles()`
|
|
211
|
+
(#1248) — the same gitignore/untracked blind spot the two checkers below were
|
|
212
|
+
fixed for, closed once.** `collectDriftHits()` in `scripts/release.mjs` now
|
|
213
|
+
enumerates via `git ls-files --cached --others --exclude-standard` instead of
|
|
214
|
+
shelling out to `git grep` for the previous release tag, so an untracked file is
|
|
215
|
+
swept for a leftover version literal exactly as a tracked one is.
|
|
216
|
+
- **The Full Gate now names which test FILES failed, not just the pass/fail
|
|
217
|
+
count.** `extractFailedTestFiles()` (`scripts/lib/gates/gate-helpers.mjs`) parses
|
|
218
|
+
vitest's own failure output; `gate-full.mjs` carries the result as a new
|
|
219
|
+
`failed_files[]` field alongside the existing counts, on both the returned
|
|
220
|
+
object and the `orchestrator.quality_gate.*` event payload. Separately,
|
|
221
|
+
`.husky/pre-push` now passes the real repo root to the gate as an **argv flag**,
|
|
222
|
+
`--ledger-root <path>` (`npm run --silent quality-gate -- --ledger-root
|
|
223
|
+
"$repo_root"`), so a gate run against a materialised temp worktree writes its
|
|
224
|
+
event into the REAL repo ledger instead of a tree an `EXIT` trap deletes seconds
|
|
225
|
+
later — the root cause of several `quality_gate.passed` records the test suite
|
|
226
|
+
itself had polluted into `.orchestrator/metrics/events.jsonl`.
|
|
227
|
+
`resolveLedgerRoot()` (`scripts/run-quality-gate.mjs`) validates the flag as an
|
|
228
|
+
existing directory that itself contains `.orchestrator/`; anything else is one
|
|
229
|
+
stderr WARN and a fallback to the pre-existing resolution, never a crash. **This
|
|
230
|
+
shipped first as an exported `SO_GATE_LEDGER_ROOT` env var and was replaced
|
|
231
|
+
before release, inside the same session**: the Wave-4 architect-panel review
|
|
232
|
+
measured that an exported env var is inherited by every descendant process,
|
|
233
|
+
including the gate's OWN vitest children three levels down (`npm run
|
|
234
|
+
quality-gate` → `gate-full.mjs` → `npm test`), where `run-quality-gate.test.mjs`'s
|
|
235
|
+
telemetry tests spread `...process.env` and so wrote their fixture events to the
|
|
236
|
+
pinned root instead of their own tmp fixture — measured **8 of 9 telemetry tests
|
|
237
|
+
red** under the hook's own environment. An argv flag reaches only the one
|
|
238
|
+
process meant to read it. `run-quality-gate.test.mjs` still sandboxes
|
|
239
|
+
`CLAUDE_PROJECT_DIR` per test and additionally scrubs any ambient
|
|
240
|
+
`SO_GATE_LEDGER_ROOT` before each run (belt-and-braces against a leftover from
|
|
241
|
+
the removed env-var form); a new `tests/husky/pre-push-gate.test.mjs` case
|
|
242
|
+
("hands the real repo root to the gate as --ledger-root") pins the hook's own
|
|
243
|
+
argv, and 0 production readers of `SO_GATE_LEDGER_ROOT` remain repo-wide
|
|
244
|
+
(`grep -rn SO_GATE_LEDGER_ROOT` outside test files and history comments → no
|
|
245
|
+
match, measured 2026-09-06).
|
|
246
|
+
- **The session-start plugin-update banner is now English and platform-aware.**
|
|
247
|
+
`checkPluginUpdate()` (`scripts/lib/plugin-update-banner.mjs`) accepts an
|
|
248
|
+
optional `platform` and picks the matching upgrade command from
|
|
249
|
+
`PLATFORM_UPDATE_INSTRUCTIONS` (`claude`/`codex`/`cursor`/`pi`), falling back to
|
|
250
|
+
a generic `npm update`-shaped instruction when the platform is unset or
|
|
251
|
+
unknown — previously every consumer saw the same `claude plugin update` line
|
|
252
|
+
regardless of harness.
|
|
253
|
+
- **Six documentation corrections following the 4.0.0 removals above.** The
|
|
254
|
+
`autopilot` SKILL's Configuration section is now a 4-line pointer to
|
|
255
|
+
`docs/migration-v4.md` instead of describing the removed `bg-isolation` field in
|
|
256
|
+
full; `NOTICE`'s attribution table is repointed at
|
|
257
|
+
`skills/architecture/references/{domain-model,ADR-FORMAT,CONTEXT-FORMAT}.md` and
|
|
258
|
+
notes `ubiquitous-language`'s removal while keeping its MIT attribution history;
|
|
259
|
+
`check-agents.mjs`'s warn-string citations of `agents/AGENTS.md` now cite
|
|
260
|
+
`docs/agent-authoring.md`; `templates/_shared/journey-manifest.md` gained a
|
|
261
|
+
retirement notice (kept as an optional per-repo template — no command consumes
|
|
262
|
+
it anymore); `skills/architecture/SKILL.md` documents when to load
|
|
263
|
+
`references/domain-model.md`; and `README.md` picked up matching corrections
|
|
264
|
+
(Windows is untested rather than "runs natively"; the update banner is
|
|
265
|
+
patch-silent; nothing is sent without consent EXCEPT the update check's own
|
|
266
|
+
anonymous `GET`, opt-out via `SO_DISABLE_UPDATE_CHECK`/`DO_NOT_TRACK`).
|
|
267
|
+
|
|
268
|
+
#### Predecessor commits (v3.24.0 → `e4674109`)
|
|
269
|
+
|
|
270
|
+
- **Quality-gate ownership check now folds onto the process-local reader (#1205).**
|
|
271
|
+
`quality-gate.mjs`'s private copy of `readOwnSessionIds()` is gone; the ownership check now
|
|
272
|
+
calls `readProcessLocalSessionIds()` directly (removing the STATE.md-lock fallback), with
|
|
273
|
+
the first test of the ownership path itself.
|
|
274
|
+
- **Wave-scope manifest binding writes `attributionForRecord()`, not a hand-rolled call
|
|
275
|
+
(#1207).** `skills/wave-executor/wave-loop.md`'s manifest-binding step now sources
|
|
276
|
+
`session`/`wave` from the same `attributionForRecord()` used by `emitEvent()`'s own
|
|
277
|
+
correlation fill, instead of a raw `sessionAttribution()` call; the STATE.md prose
|
|
278
|
+
comparison the previous step asked for is removed as a redundant, sometimes-wrong check
|
|
279
|
+
(a peer-owned lock and STATE.md agree with each other, not with this process).
|
|
280
|
+
- **Seven readers now go through `readCanonicalSessions()` instead of their own duplicate
|
|
281
|
+
parse (#1209).** `session-id.mjs`, `auto-dream.mjs`, `dispatcher/rank.mjs`,
|
|
282
|
+
`eval/engine.mjs` (+ new `eval/session-resolve.mjs`), `harness-audit/categories/category4.mjs`
|
|
283
|
+
(via `canonicalizeSessions({ keepUnidentified: true })`, since a bare wrapper broke 3
|
|
284
|
+
id-less fixtures), `skill-health/join.mjs`, `sessions-staleness-banner.mjs`, and
|
|
285
|
+
`dialectic-deriver.mjs` each had a red-on-HEAD fake-regression proof before the fix
|
|
286
|
+
(auto-dream 3→2 sessions, rank 50→<6 candidates, eval 2→1 peers). `category9` is
|
|
287
|
+
deliberately untouched.
|
|
288
|
+
- **Four modules now distinguish silent `ENOENT` from a loud read failure (#1210).**
|
|
289
|
+
`session-close-backfill.mjs`, `reconcile/engine.mjs`, `memory-proposals/store.mjs` (via a
|
|
290
|
+
new shared helper — a decorative `err.code` branch there is now real), and
|
|
291
|
+
`agent-status.mjs` all warn on stderr for `EACCES`/`EISDIR`/other, and stay silent only on
|
|
292
|
+
a genuinely missing file.
|
|
293
|
+
- **German distributional-claim patterns join the English set (#1211).** Six German
|
|
294
|
+
quantifier-triggered patterns (`N von M`, `100% von/der/aller`, `alle N`, `keine
|
|
295
|
+
verbleibenden`, `jeder/jede/jedes`, `keiner/keine/keins von/der`) plus a narrow German
|
|
296
|
+
`CARDINAL_NOUN` extension (`zeilen?|dateien?|datei|aufrufer|eintr(?:ag|(?:ä|ae)ge)`) land
|
|
297
|
+
in `hooks/post-subagent-discovery-validator.mjs`. Measured against a 41-line German claim
|
|
298
|
+
corpus pulled from this repo's own `events.jsonl`: the quantifier patterns flag 7/41
|
|
299
|
+
(same order of magnitude as the English baseline); a wider bare-cardinal noun set
|
|
300
|
+
(`Sessions`/`Repos`/`Treffer`/`Stellen`/`Module`/`Tests`) was tried and rejected after it
|
|
301
|
+
flagged 39/41 (~57% false-positive rate) — those six nouns stay reachable only through the
|
|
302
|
+
lexically-anchored quantifier patterns. German `GATE_SUMMARY` recap forms are also
|
|
303
|
+
recognised and skipped, matching the existing English recap-skip.
|
|
304
|
+
- **`skills/vault-sync/validator.mjs`'s vendored schema catches up with upstream (#531).**
|
|
305
|
+
`peer-card` (#503), `board` (#738), and the optional `source-repo` field (#725) were
|
|
306
|
+
vendor-ahead of `projects-baseline` until this session; upstream-lifted in
|
|
307
|
+
`projects-baseline@cb9ec97`. `sync-vault-schema.mjs` now strips a bare `//` line (with a
|
|
308
|
+
regression test), and the provenance notes documenting the vendoring history moved outside
|
|
309
|
+
the generated-block sentinels so a `--write` regeneration can no longer drop them (measured:
|
|
310
|
+
it did, once).
|
|
311
|
+
- **The schema-drift CI gate is armed (#1175, follow-up to #531).** `SCHEMA_DRIFT_OPTIONAL`
|
|
312
|
+
is `"false"` in both `.gitlab-ci.yml` jobs that read it — `schema-drift-check` (line 636)
|
|
313
|
+
and `pipeline-gate` (line 723) — so a missing `SCHEMA_DRIFT_TOKEN` now hard-fails (exit 4)
|
|
314
|
+
instead of degrading to an amber "not verified" pass. A test pins the armed state (a half
|
|
315
|
+
revert of either flag turns 2 tests red). Proof: pipelines 8355–8357 @ `bca78dae` (bogus
|
|
316
|
+
enum) failed with `schema-drift-check` reporting "drift detected"; pipeline 8358 @
|
|
317
|
+
`dc9522dd` (branch `proof/1175-schema-drift-green`) passed with the check running WITH the
|
|
318
|
+
token, in sync. `docs/ci-setup.md` documents the Project-Access-Token recipe and the
|
|
319
|
+
now-current state (was previously the follow-up gap this same doc named).
|
|
320
|
+
- **`#1203` — the `enforce-scope.mjs` peer-manifest fix confirmed already SHIPPED via
|
|
321
|
+
#1194.** `tests/hooks/enforce-scope.test.mjs:1219/:1245/:1302` (56/56) prove the behaviour
|
|
322
|
+
landed with #1194; no further code change was needed.
|
|
323
|
+
- **`scripts/lib/platform.mjs`'s five module-constant exports become lazy, memoized
|
|
324
|
+
accessors; the five constants are REMOVED (#1153 P5).** They were internal accessor
|
|
325
|
+
constants of this plugin, never a documented public API, and every in-repo importer is
|
|
326
|
+
migrated — an external importer of these five names (none known) switches to the
|
|
327
|
+
getters; no `BREAKING CHANGE:` footer is carried for that reason. `SO_PLATFORM`, `SO_PLUGIN_ROOT`,
|
|
328
|
+
`SO_PROJECT_DIR`, `SO_STATE_DIR`, `SO_CONFIG_FILE` used to be `export const … =
|
|
329
|
+
detect…()` evaluated at module load, so every one of ~31 static importers — including
|
|
330
|
+
the hottest deny-capable hooks, which run on every tool call — paid a filesystem
|
|
331
|
+
walk-up (`statSync`/`existsSync` per ancestor directory) merely for importing the
|
|
332
|
+
module, whether or not the value was ever read. They are replaced by
|
|
333
|
+
`getPlatform()`/`getPluginRoot()`/`getProjectDir()`/`getStateDir()`/`getConfigFile()`,
|
|
334
|
+
computed on first call and memoized for the process (plus a test-only
|
|
335
|
+
`_resetPlatformCache()`); the deprecated names are fully REMOVED, not kept as
|
|
336
|
+
deprecated live bindings — a re-introduction is caught by the named-export assertion in
|
|
337
|
+
`tests/lib/platform.test.mjs`. 23 non-test call sites across `scripts/` and `hooks/` now
|
|
338
|
+
call a getter (re-measured 2026-09-05: `grep -rlE "getPlatform\(\)|getPluginRoot\(\)|getProjectDir\(\)|getStateDir\(\)|getConfigFile\(\)" scripts/ hooks/ --include="*.mjs" | grep -v /tests/ | grep -v platform.mjs | wc -l` → 23; it was 22 on 2026-09-04, before this session's own new modules landed).
|
|
339
|
+
- **Wave-scope manifest session keys renamed to `session_id`/`semantic_session_id`,
|
|
340
|
+
canonical since #1153 P2.** The pre-#1153 spellings `session`/`semantic_session` are
|
|
341
|
+
still ACCEPTED on the read side for one release (`MANIFEST_SESSION_KEYS` in
|
|
342
|
+
`scripts/lib/session-identity/own-session.mjs` is the shared SSOT list every writer and
|
|
343
|
+
reader now imports); `scripts/validate-wave-scope.mjs` flags a manifest that carries
|
|
344
|
+
BOTH spellings with conflicting values as an error rather than silently preferring one
|
|
345
|
+
and dropping the other. `scripts/wave-scope-binding.mjs` (new — #1153 P4) replaces the
|
|
346
|
+
inline `node --input-type=module -e` block `skills/wave-executor/wave-loop.md` § Scope
|
|
347
|
+
Manifest previously asked the coordinator to retype once per wave; an unbound `{}`
|
|
348
|
+
manifest now emits `orchestrator.scope.unbound_manifest` (0 hits repo-wide before this
|
|
349
|
+
file existed), making the previously-silent fail-closed case countable. `--merge` folds
|
|
350
|
+
the binding into an existing manifest in place.
|
|
351
|
+
- **`_privateDir()` (host-identity.mjs) now honours `SO_CONFIG_HOME` (#1153 P6).** Two
|
|
352
|
+
overrides, most specific first: `SO_CONFIG_HOME` names the private config directory
|
|
353
|
+
itself; `XDG_CONFIG_HOME` (same variable `owner-config-loader.mjs` already reads) names
|
|
354
|
+
its parent. Both read via `.trim() || fallback`, not a bare `||` (the whitespace-only
|
|
355
|
+
env-var trap). Without either, the homedir default `~/.config/session-orchestrator` is
|
|
356
|
+
unchanged.
|
|
357
|
+
- **`scripts/lib/session-identity/own-session.mjs`'s static import closure cut from 3,567
|
|
358
|
+
to 269 lines (#1153 P7).** It no longer imports `../session-lock.mjs` — which drags
|
|
359
|
+
`session-lock` → `exclusivity-matrix` → `file-lock` → `io` → `host-identity` behind it —
|
|
360
|
+
and instead shares the shape predicate `isLockShape()` via a new zero-import module,
|
|
361
|
+
`scripts/lib/session-lock-shape.mjs`, with `session-lock.mjs`'s own `parseLock()`. This
|
|
362
|
+
matters because `hooks/enforce-scope.mjs` loads `own-session.mjs` on every Edit/Write;
|
|
363
|
+
anything it imports joins that hook's static closure.
|
|
364
|
+
- **codex-cli 0.144.4 fixes the `plugin add` failure documented against 0.141.0
|
|
365
|
+
(#1163, FIXED-UPSTREAM — no code change here).** Re-verified end-to-end 2026-09-04
|
|
366
|
+
against this repo's unchanged flat layout: `codex plugin add session-orchestrator@kanevry
|
|
367
|
+
--json` now exits 0 where it previously failed with `plugin session-orchestrator was
|
|
368
|
+
not found in marketplace kanevry`. `docs/codex-setup.md`'s "Short-Form Marketplace Add"
|
|
369
|
+
section is promoted to the recommended install path and the prior failure kept as a
|
|
370
|
+
historical note; a new "Switching Marketplace Sources" section documents that
|
|
371
|
+
`marketplace add` silently REPLACES an already-registered marketplace of the same
|
|
372
|
+
declared name.
|
|
373
|
+
- **`#1152` closed — the reported defect was a false premise (two separate writers, not
|
|
374
|
+
one drifting one).** No code change; investigation confirmed the two paths write
|
|
375
|
+
independently and neither needed reconciling.
|
|
376
|
+
- **`#1214` — the suspected `maskerWouldChange` gap in the vault narrative-mirror sink was
|
|
377
|
+
a false premise, refuted by measurement.** Unlike the two `vault-mirror/process.mjs`
|
|
378
|
+
sinks fixed under #1028 (five-field comparisons that can match while a raw needle
|
|
379
|
+
survives on disk), `scripts/lib/vault-status/narrative-mirror.mjs` compares the WHOLE
|
|
380
|
+
rendered document, and the candidate always passes through the CURRENT `maskNarrative` —
|
|
381
|
+
so a value that masker would redact can never appear in it, and a false
|
|
382
|
+
`matchesModuloRedaction` match cannot occur. Measured 2026-09-04 at HEAD `cd785003` in
|
|
383
|
+
both directions (no marker on disk; marker on disk plus a second needle entering the
|
|
384
|
+
env): both returned `written` with the raw value gone. No probe added; two invariant
|
|
385
|
+
tests in `tests/lib/vault-status/narrative-mirror.test.mjs` pin it. Named ceiling: the
|
|
386
|
+
invariant depends on `maskNarrative` walking every rendered string, and `repo` is fed to
|
|
387
|
+
`renderNarrative` OUTSIDE that walk (a directory basename, not STATE.md content) — a
|
|
388
|
+
future rendered field added the same way would reopen this.
|
|
389
|
+
- **Three host-private-config-dir resolvers collapse into one (#1223).** New zero-import
|
|
390
|
+
leaf `scripts/lib/config/private-config-dir.mjs` (`node:os` + `node:path` only) exports
|
|
391
|
+
`resolvePrivateConfigDir({env}?)` — precedence `SO_CONFIG_HOME` (the dir itself) >
|
|
392
|
+
`XDG_CONFIG_HOME` (its parent) > `~/.config/session-orchestrator`, each `.trim()`ed. Before
|
|
393
|
+
this, `owner-yaml.mjs`'s import-time `OWNER_YAML_PATH` (homedir-only, no overrides at all),
|
|
394
|
+
`owner-config-loader.mjs`'s `resolveOwnerConfigPath()` (XDG-only, untrimmed), and
|
|
395
|
+
`host-identity.mjs`'s `_privateDir()` each had a different precedence — so
|
|
396
|
+
`SO_CONFIG_HOME=<sandbox>` moved the self-alias ledger but not `owner.yaml`, which kept
|
|
397
|
+
reading the operator's real home (the CLAUDE.md "vault-dir resolves HOST-LOCALLY" hazard
|
|
398
|
+
class). `host-identity.mjs` now delegates `_privateDir()` to the leaf directly (it is
|
|
399
|
+
reachable from live hooks via `session-lock.mjs`, so the leaf must stay import-free);
|
|
400
|
+
`owner-yaml.mjs` re-exports `resolvePrivateConfigDir` and adds a call-time
|
|
401
|
+
`resolveOwnerYamlPath(env?)`; the old `OWNER_YAML_PATH` constant is fully REMOVED (verified
|
|
402
|
+
0 remaining consumers repo-wide), not kept as a deprecated back-compat export.
|
|
403
|
+
`owner-config-loader.mjs` and `owner-interview.mjs` now call through
|
|
404
|
+
`resolveOwnerYamlPath()` instead of their own copies. `tests/husky/pre-commit-owner-leakage.test.mjs`'s
|
|
405
|
+
file-by-file copied import chain (the CP11 scanner's dynamic-import fixture) gained the new
|
|
406
|
+
leaf as a required copy.
|
|
407
|
+
- **`skills/wave-executor/wave-loop.md` gains a "Shell variables used in this section"
|
|
408
|
+
preamble (#1225).** `$PLUGIN_ROOT`, `$WAVE`, `$ROLE`, `$STATE_DIR` are defined once ahead
|
|
409
|
+
of the Scope Manifest steps instead of being re-explained inline (the `$PLUGIN_ROOT`
|
|
410
|
+
per-platform expansion parenthetical is now a single cross-reference instead of a repeated
|
|
411
|
+
clause).
|
|
412
|
+
- **`scripts/lib/ci-status-banner.mjs` gains a three-state contract instead of two (#1031).**
|
|
413
|
+
A frozen `DEGRADED_REASONS` enum (`cli-missing`, `timeout`, `parse-error`, `query-failed`,
|
|
414
|
+
`git-error`) now distinguishes "state could not be read" from "no CI here" — before this,
|
|
415
|
+
4 return sites collapsed every CLI-missing / timeout / unparseable-output / VCS-probe-error
|
|
416
|
+
case onto the same `null` an absent remote returns, which the banner reads as all-clear
|
|
417
|
+
(the same collapse class documented for `mirror-issues-banner.mjs` in
|
|
418
|
+
`skills/session-start/SKILL.md`, now fixed on this probe too). Consumers updated to the new
|
|
419
|
+
shape: the probe registry in `scripts/lib/session-start-probes.mjs` (`ci-status` entry's
|
|
420
|
+
`render`/`severityOf` now treat a `degraded` result as `warn`, not `ok`);
|
|
421
|
+
`scripts/lib/dispatcher/rank.mjs` (new exported `normalizeCiSignal()` reduces every shape —
|
|
422
|
+
bare string, `{status}`, `{degraded}`, `null` — to `{ciStatus, ciDegraded}`, mapping
|
|
423
|
+
`degraded` to `'unknown'` for scoring parity with the old behaviour while surfacing the
|
|
424
|
+
reason into `signals.readiness.ciDegraded` and a `warnings` entry); `scripts/release.mjs`'s
|
|
425
|
+
`evaluateCiRow()` treats `ci.degraded` as "CI status unknown (\<reason\>)", never as green.
|
|
426
|
+
`skills/session-start/SKILL.md`'s CI-status paragraph documents the new degraded banner
|
|
427
|
+
line. `scripts/lib/qg-command-drift-banner.mjs` is converted too (W3-P1): it mints its own
|
|
428
|
+
`degradedBanner(reason)` and passes a nested `detailed.degraded` straight through
|
|
429
|
+
(`:59`/`:99`/`:102-103`), so a config that could not be READ is reported as `warn` rather
|
|
430
|
+
than read as "no drift".
|
|
431
|
+
- **`hooks/post-tool-batch-wave-signal.mjs` starts emitting the `files_changed` measurement
|
|
432
|
+
`scripts/lib/convergence-monitor.mjs`'s `shrinking_diff` signal has always read but no
|
|
433
|
+
producer ever wrote (#980).** At an N→N+1 wave-open transition, the `orchestrator.wave.completed`
|
|
434
|
+
emitted for wave N now carries `files_changed` (the deduped union of
|
|
435
|
+
`git diff --name-only <wave_start_sha>` and `git ls-files --others --exclude-standard`,
|
|
436
|
+
measured against the working tree because the coordinator commits at session close, not per
|
|
437
|
+
wave) and `files_changed_source: 'worktree-vs-wave-start-sha'`; `wave_start_sha` (`git
|
|
438
|
+
rev-parse HEAD`) is persisted into `.orchestrator/current-session.json` under the same
|
|
439
|
+
ownership gate as the other wave keys when a wave opens. Both keys are optional and
|
|
440
|
+
absent-is-not-zero — any git failure, a 1.5s timeout, or a missing `wave_start_sha` omits
|
|
441
|
+
them, and the monitor reads the absence as `null` rather than firing on a fabricated 0.
|
|
442
|
+
`skills/convergence-monitoring/SIGNALS.md` gained a "Live monitor input" subsection under
|
|
443
|
+
all three signals (shrinking_diff, pass_rate_plateau, velocity_drop), each quoting a fresh
|
|
444
|
+
measurement of `.orchestrator/metrics/events.jsonl` (2026-09-05): 144 pre-existing
|
|
445
|
+
`wave.completed` records carried no `files_changed` key at all before this fix; 33
|
|
446
|
+
`orchestrator.quality_gate.*` records carry both `wave_number` and a well-formed `counts`
|
|
447
|
+
object (making `pass_rate_plateau` fireable, `_evaluateSignals` now a test-only export);
|
|
448
|
+
11,754 `orchestrator.agent.stopped` records make `velocity_drop` fireable, versus 0 for the
|
|
449
|
+
`agent.dispatched` type the reader also accepts. `monitors/monitors.json`'s
|
|
450
|
+
`convergence-monitor` description corrected — it never opened `sessions.jsonl` (a stale
|
|
451
|
+
claim from an earlier draft of the monitor).
|
|
452
|
+
- **`scripts/validate-wave-scope.mjs`'s internal `validateSession()` renamed to
|
|
453
|
+
`validateSessionBinding()` (#1153 P11).** Rename only, no behaviour change — the old name
|
|
454
|
+
read as "validate the session" when it validates the wave-scope manifest's session-BINDING
|
|
455
|
+
fields specifically; a previously-considered `skipped[]` return addition was dropped as a
|
|
456
|
+
false premise (the function already reports via `errors`/`warnings`).
|
|
457
|
+
- **`docs/rule-authoring.md`'s `LEARNING_TYPE_REGISTRY` table gains its fourth axis,
|
|
458
|
+
`hostScoped` (#1153 P12, follow-up to #1090).** The table previously transcribed only
|
|
459
|
+
`ttlDays`/`agentProposable`/`ruleConvertible`, so a drifted `host_class`-gating cell was
|
|
460
|
+
structurally invisible to `tests/docs/rule-authoring-registry-parity.test.mjs`; the parser
|
|
461
|
+
regex and parity-diff logic there now check all four axes bijectively.
|
|
462
|
+
`hardware-pattern` is the only `hostScoped: true` type today.
|
|
463
|
+
- **`docs/README.md` gains a "Superseded design notes" section (#1153 P14).** Because
|
|
464
|
+
`docs/specs/` is gitignored, a correction written INTO a spec can never be committed — the
|
|
465
|
+
correction for `docs/specs/2026-05-26-parallel-aware-sessions-design.md` (which still
|
|
466
|
+
specifies PID-based lock liveness) lives here instead: liveness has been heartbeat-age
|
|
467
|
+
based since #1137, and the recorded PID has been consulted nowhere since #1151.
|
|
468
|
+
`skills/_shared/parallel-aware-preamble.md` cross-references it.
|
|
469
|
+
|
|
470
|
+
### Removed (BREAKING)
|
|
471
|
+
|
|
472
|
+
Every entry under *4.0.0 — audit session* below is a REMOVED PUBLIC SURFACE and the reason
|
|
473
|
+
this release is a major. Each names its replacement or states explicitly that there is none.
|
|
474
|
+
Upgrade guide: [`docs/migration-v4.md`](docs/migration-v4.md).
|
|
475
|
+
|
|
476
|
+
**Deviation from the deprecate-then-remove norm** (`.claude/rules/development.md` § Package
|
|
477
|
+
Lifecycle: stub + first-call `console.warn` for ≥1 minor cycle). The 5 skills and 3 commands
|
|
478
|
+
below are HARD-deleted, not stubbed — an explicit operator decision made 2026-09-06. The
|
|
479
|
+
removal test was **0 telemetry ∧ 0 fleet invocation over 90 days ∧ no runtime consumer**, and
|
|
480
|
+
that 90-day window is honestly a *search* window, not 90 days of shipped exposure: `journey-audit`
|
|
481
|
+
itself had existed for only 9 days (added 2026-08-28) before this cut. Full rationale and the
|
|
482
|
+
manual cleanup steps: [`docs/migration-v4.md`](docs/migration-v4.md).
|
|
483
|
+
|
|
484
|
+
#### 4.0.0 — audit session
|
|
485
|
+
|
|
486
|
+
- **5 skills removed** — `find skills -name SKILL.md | wc -l` → 43, against
|
|
487
|
+
`git ls-tree -r --name-only HEAD skills/ | grep -c 'SKILL.md$'` → 49 (the sixth delta is
|
|
488
|
+
the `domain-model` merge below):
|
|
489
|
+
- **`skills/daily/`** (plus `generate.sh`, its template and 2 test files) — **no
|
|
490
|
+
replacement.** 0 telemetry, 0 fleet invocations in 90 days, no runtime consumer; only its
|
|
491
|
+
own test and documentation referenced it. Recurring daily notes are a Routine / scheduled
|
|
492
|
+
task, not a session skill (`.claude/rules/loop-and-monitor.md` § LM-004).
|
|
493
|
+
- **`skills/skill-creator/`** — **replaced by Anthropic's official `skill-creator` plugin**
|
|
494
|
+
plus `claude plugin init`. 0/0/0 with 0 in-repo references.
|
|
495
|
+
- **`skills/ubiquitous-language/`** — **no replacement.** 0/0/0.
|
|
496
|
+
- **`skills/contract-version-bump/`** — **no replacement.** 0/0/0; one comment referenced
|
|
497
|
+
it.
|
|
498
|
+
- **`skills/journey-audit/`** — **no replacement.** 0 journey manifests exist fleet-wide,
|
|
499
|
+
so the skill was never runnable in the first place.
|
|
500
|
+
- **3 commands removed** — `commands/*.md` 28 → 25, mirrored in `.cursor/commands/` and
|
|
501
|
+
`pi/prompts/` (both also 25): **`/contract-version-bump`**, **`/journey-audit`** (with
|
|
502
|
+
their skills above) and **`/autopilot-multi`**. **No replacement** for any of the three.
|
|
503
|
+
`/autopilot` is a different command, unaffected, and stays.
|
|
504
|
+
- **Session Config `autopilot.bg-isolation` removed** — the `autopilot:` block's only field,
|
|
505
|
+
retired together with `/autopilot-multi` above (`scripts/parse-config.mjs` never parsed an
|
|
506
|
+
`autopilot` key at all, so removing it changes no runtime behaviour). **No replacement** —
|
|
507
|
+
delete the block from your `CLAUDE.md`/`AGENTS.md`; single-story `/autopilot` takes no
|
|
508
|
+
Session Config block and is unaffected.
|
|
509
|
+
- **8 top-level scripts removed** (`scripts/*.mjs`, 0 runtime callers each, with their test
|
|
510
|
+
files): `autopilot-multi.mjs`, `backfill-learnings.mjs`, `backfill-learnings-expires.mjs`,
|
|
511
|
+
`fleet-instruction-scan.mjs`, `lifecycle-sim-v6.mjs`, `migrate-learnings-jsonl.mjs`,
|
|
512
|
+
`migrate-subagents-jsonl.mjs`, `upload-social-preview.mjs` — **no replacement.** The two
|
|
513
|
+
`migrate-*` scripts were one-shot format migrations whose target format has been canonical
|
|
514
|
+
for several releases; the two `backfill-learnings*` scripts were one-shot ledger repairs.
|
|
515
|
+
- **`skills/_shared/model-selection.md` removed — no replacement, 0 consumers.**
|
|
516
|
+
- **`skills/domain-model/` removed and MERGED into `skills/architecture/references/`** — its
|
|
517
|
+
three files now live as `references/domain-model.md`, `references/ADR-FORMAT.md` and
|
|
518
|
+
`references/CONTEXT-FORMAT.md`. It was reference-only material with 4 inbound links, never
|
|
519
|
+
a dispatchable skill; `/architecture` is the entry point.
|
|
520
|
+
- **Two non-agents moved OUT of `agents/`** — `agents/*.md` 16 → 14. The agent loader
|
|
521
|
+
registers every `.md` file in that directory as a dispatchable agent, and
|
|
522
|
+
`agents/AGENTS.md` — the authoring SPEC — was measurably dispatched as an agent 5 times. It
|
|
523
|
+
is now [`docs/agent-authoring.md`](docs/agent-authoring.md), and
|
|
524
|
+
`agents/memory-proposal-collector.md` is now
|
|
525
|
+
[`docs/memory-proposal-flow.md`](docs/memory-proposal-flow.md). Same content, no longer a
|
|
526
|
+
dispatch target. **Anything dispatching either by name must stop** — they are
|
|
527
|
+
documentation, and were only ever documentation.
|
|
528
|
+
|
|
529
|
+
- **`scripts/lib/autopilot/multi-killswitch.mjs` and
|
|
530
|
+
`scripts/lib/autopilot/dep-graph.mjs` deleted, with their test files** —
|
|
531
|
+
orphaned by the `/autopilot-multi` removal above, with only their own tests as
|
|
532
|
+
consumers. **No replacement**; single-story `/autopilot` never imported either
|
|
533
|
+
module.
|
|
534
|
+
|
|
535
|
+
#### Predecessor commits (v3.24.0 → `e4674109`)
|
|
536
|
+
|
|
537
|
+
- **`scripts/lib/webhook-url.mjs` deleted — zero callers repo-wide (#1168).** `resolveWebhookUrl`/
|
|
538
|
+
`WebhookConfigError` and their test file (`tests/lib/webhook-url.test.mjs`, 192 lines) are gone,
|
|
539
|
+
−364 lines total; the `## Webhooks (#228)` section in `docs/session-config-reference.md`
|
|
540
|
+
(the `webhooks.<kind>.url` Session Config surface it backed) is removed with it. The still-live
|
|
541
|
+
Clank Event Bus webhook (`scripts/lib/events.mjs`, `CLANK_EVENT_SECRET`/`CLANK_EVENT_URL`) is
|
|
542
|
+
unrelated and unaffected — its doc section is promoted from `### Clank Event Bus` to
|
|
543
|
+
`## Clank Event Bus` in the same edit, since it is no longer a subsection of the now-removed one.
|
|
544
|
+
|
|
545
|
+
### Fixed
|
|
546
|
+
|
|
547
|
+
#### 4.0.0 — audit session
|
|
548
|
+
- **`tests/telemetry/sync.test.mjs`: the two `detectSandbox` "real operator shape" cases no longer use `process.cwd()`** — the husky pre-push gate runs the suite in a tracked-tree copy under `$TMPDIR`, where cwd IS a temp root and the guard correctly answers `sandbox:temp-root`; both cases were red on every push attempt while green in the checkout (measured 2026-09-06 — the first push of the 4.0.0 review session failed on exactly this file, unnamed until the gate learned to name `failed_files`). They now probe a path under `os.homedir()`.
|
|
549
|
+
|
|
550
|
+
- **`js-yaml` out of the eager hook import graph — 4 of 27 hooks crashed on every turn
|
|
551
|
+
without `node_modules`; now 0 (GH#62 / GH#63 / GitLab #1230).**
|
|
552
|
+
`scripts/lib/owner-yaml.mjs` carried a static `import yaml from 'js-yaml'` and sits on the
|
|
553
|
+
import graph of `on-session-start`, `on-session-end`, `post-edit-validate` and
|
|
554
|
+
`skill-invocation-telemetry` — so a skipped or interrupted `npm install`, a half-synced
|
|
555
|
+
plugin cache, or an EPERM sandbox killed all four at MODULE-LOAD time with
|
|
556
|
+
`ERR_MODULE_NOT_FOUND` and exit 1, on every single turn. `js-yaml` now resolves lazily
|
|
557
|
+
inside `loadOwnerConfig`/`writeOwnerConfig`. Measured 2026-09-06 @ `e4674109` with `hooks/`
|
|
558
|
+
and `scripts/` copied to a tmp dir carrying no `node_modules` anywhere up the tree:
|
|
559
|
+
**23/27 hooks exited 0 before the fix, 27/27 after.** Pinned by the new
|
|
560
|
+
`tests/hooks/hooks-run-without-node-modules.test.mjs`, which EXECUTES every hook instead of
|
|
561
|
+
reading source text — the pre-existing static-import guard would have accepted a lazy
|
|
562
|
+
`require()` of a package that does not exist. The `zx` half of #1230 is stale and now says
|
|
563
|
+
so: `scripts/lib/worktree/listing.mjs`'s `zx` imports are already lazy and caught, verified
|
|
564
|
+
by installing a `js-yaml` stub alone and watching all four hooks reach rc=0 with `zx` still
|
|
565
|
+
absent.
|
|
566
|
+
- **`picomatch` declared in `package.json` dependencies.** It is imported by four runtime
|
|
567
|
+
modules (`rule-loader.mjs`, `command-blocker.mjs`, `validate-vendored-rules.mjs`,
|
|
568
|
+
`reconcile/emitter.mjs`) and was absent from the manifest at `HEAD`
|
|
569
|
+
(`git show HEAD:package.json | grep picomatch` → no match) — reachable only as somebody
|
|
570
|
+
else's transitive dependency, the GH#62 class exactly.
|
|
571
|
+
- **The Cursor adapter generator wrote a malformed `argument-hint` into 24 of 28 command
|
|
572
|
+
files (GH#54).** `scripts/generate-cursor-adapter.mjs` now renders every frontmatter value
|
|
573
|
+
through `yamlQuote()`, which JSON-quotes anything failing `YAML_PLAIN_SAFE` (and anything
|
|
574
|
+
that looks like a YAML scalar, or starts with a digit). Measured after the fix:
|
|
575
|
+
`grep -c "argument-hint: \[" .cursor/commands/*.md` reports zero files carrying the array
|
|
576
|
+
shape. The regression survived for as long as it did because the generator was tested
|
|
577
|
+
against itself; it is now pinned against the spec.
|
|
578
|
+
- **`generateFrontmatterSnippet()` THREW when the baseline was absent.**
|
|
579
|
+
`scripts/lib/frontmatter-guard.mjs` resolves the schema through
|
|
580
|
+
`resolveHostPath('baseline-path')` and treats a missing schema as `null` — the honest
|
|
581
|
+
outcome, with the documented fallback enum set — instead of raising inside a pre-dispatch
|
|
582
|
+
guard. `scripts/lib/vault-backfill/template.mjs` uses the same resolver.
|
|
583
|
+
- **The session registry wrote into the operator's REAL host-private config from a
|
|
584
|
+
sandbox.** `scripts/lib/session-registry.mjs` now derives its `sessions/` directory from
|
|
585
|
+
`resolvePrivateConfigDir()` (`SO_CONFIG_HOME` > `XDG_CONFIG_HOME` >
|
|
586
|
+
`~/.config/session-orchestrator`) instead of its own homedir-only path, so a redirected
|
|
587
|
+
config home actually redirects it.
|
|
588
|
+
- **`node scripts/parse-config.mjs --json` failed with `File not found: --json`.** The flag
|
|
589
|
+
is now an explicit NO-OP alias for the default (stdout has always been JSON), because the
|
|
590
|
+
bootstrap documentation names that exact command as THE validation command and
|
|
591
|
+
`cli-design.md` § JSON-First Output requires every CLI to accept `--json`.
|
|
592
|
+
- **The vault-staleness probe measured against the clock instead of against the repo
|
|
593
|
+
(#1238).** `skills/discovery/probes/vault-staleness.mjs` now asks whether the upstream repo
|
|
594
|
+
advanced PAST the last sync, not what `now - lastSync` is — a mirror of a repo nobody has
|
|
595
|
+
committed to in three weeks is CURRENT, not three weeks stale. Both sides of the comparison
|
|
596
|
+
(`lastSync`, `lastCommit`) come from one read of the same `_overview.md` frontmatter.
|
|
597
|
+
Measured against the live vault before the fix (2026-09-05): **33 of 48 overviews reported
|
|
598
|
+
"stale", 26 of them >7d, with a demonstrably healthy sync chain.** An overview without
|
|
599
|
+
`lastCommit` still falls back to the wall-clock comparison, marked
|
|
600
|
+
`basis: 'probe-runtime'` and carried at lower confidence so a consumer can tell a measured
|
|
601
|
+
delta from a guessed one.
|
|
602
|
+
- **The discovery-validator scanned the WRONG transcript, so effectively every violation it
|
|
603
|
+
recorded was the coordinator's own prose (#1191).** `input.transcript_path` on
|
|
604
|
+
`SubagentStop` is the PARENT/main session transcript, not the subagent's — measured
|
|
605
|
+
2026-09-02 on a seeded random sample of 60 violations: **100 % coordinator text,
|
|
606
|
+
scope-adjusted precision 0 %**, with `agent` reported as `"unknown"` in 90.8 % of 1,541
|
|
607
|
+
events. The hook now reads `<transcriptDir>/<session>/subagents/agent-<agent_id>.jsonl`
|
|
608
|
+
(the layout `subagent-telemetry.mjs` and `wave-transcript-tail.mjs` already use) and NEVER
|
|
609
|
+
falls back to the parent path: a scan of the wrong transcript is worse than no scan. Claims
|
|
610
|
+
are additionally collapsed per distinct normalised claim with an `occurrences` count
|
|
611
|
+
(`normalizeClaim()` / `dedupeViolations()` in `hooks/_lib/subagent-transcript.mjs`, #1198)
|
|
612
|
+
— the worst repo in the fleet held 3,360 records over 205 distinct claim texts, a ×16.4
|
|
613
|
+
duplication factor.
|
|
614
|
+
- **The `ecosystem-health` watcher could never start.** Its trigger in
|
|
615
|
+
`monitors/monitors.json` was `on-skill-invoke:ecosystem-health` — self-referential: the
|
|
616
|
+
watcher could only start when the `ecosystem-health` skill ran, and that skill has 0
|
|
617
|
+
recorded invocations fleet-wide, so it never started once. The trigger is now
|
|
618
|
+
`on-skill-invoke:session-start`.
|
|
619
|
+
|
|
620
|
+
- **CP11 (the confidential-names owner-leakage check) could fail OPEN and still
|
|
621
|
+
print `PASS` (#1244).** `getConfidentialNamePatterns()`
|
|
622
|
+
(`scripts/lib/validate/check-owner-leakage.mjs`) collapsed three different
|
|
623
|
+
outcomes onto the same `[]` degrade: (a) the standalone single-file copy, where
|
|
624
|
+
the helpers are genuinely unresolvable — the only case meant to go inert; (b)
|
|
625
|
+
CP11 simply unconfigured; and (c) CP11 configured but unreadable (missing
|
|
626
|
+
`js-yaml`, an unparseable `owner.yaml`, or a wholesale-discarded partial
|
|
627
|
+
config), which used to read identically to (b) and print the clean verdict it
|
|
628
|
+
never earned. Case (c) now returns a `disabledReason` and the scanner prints
|
|
629
|
+
`CP11 DISABLED: <reason>` plus a counted FAIL — with no path in the reason
|
|
630
|
+
string, since this scanner's output is mirrored to a public CI log.
|
|
631
|
+
`owner-yaml.mjs`'s whole-file discard on an invalid REQUIRED section (#820) now
|
|
632
|
+
merges a still-VALID optional object section (e.g. `paths:`) back onto its
|
|
633
|
+
default instead of discarding it too, so a correctly declared
|
|
634
|
+
`confidential-names-file` no longer vanishes because an unrelated section was
|
|
635
|
+
malformed; a genuinely invalid optional section is reported via
|
|
636
|
+
`droppedSections`, and an unparseable file gets the new `reason: 'unparseable'`.
|
|
637
|
+
A third, unrelated fail-open closed in the same file: `isMain` now compares
|
|
638
|
+
`realpathSync()` of both sides, so a symlinked invocation path (`/tmp` →
|
|
639
|
+
`/private/tmp` on macOS) can no longer make the scanner exit 0 having scanned
|
|
640
|
+
nothing.
|
|
641
|
+
- **CP11's own DISABLED path (above) was itself overbroad, and its degrade
|
|
642
|
+
path had two further defects — all three closed together in
|
|
643
|
+
`scripts/lib/validate/check-owner-leakage.mjs` and `.husky/pre-commit`
|
|
644
|
+
(Wave-4 Codex second-look).** First, DISABLED fired even when NO
|
|
645
|
+
confidential-names file was ever configured: a healthy `owner.yaml` with
|
|
646
|
+
only an unrelated invalid optional section (e.g. `paths: { vault-dir: 42
|
|
647
|
+
}`) and no `confidential-names-file` key reported `CP11 DISABLED` and a
|
|
648
|
+
counted FAIL, when the correct verdict is "inactive." A new
|
|
649
|
+
`rawConfidentialNamesKeyState()` re-reads the RAW (pre-validation) key
|
|
650
|
+
before classifying, so DISABLED now fires only when a names path was
|
|
651
|
+
actually configured; a healthy owner.yaml with `paths:` present but no
|
|
652
|
+
names-file key now stays `PASS` with no CP11 line at all, pinned by a new
|
|
653
|
+
case in `tests/lib/validate/check-owner-leakage.test.mjs`. Second, the
|
|
654
|
+
standalone-copy inert degrade (the ONE case meant to go silently
|
|
655
|
+
patterns-`[]`) was scoped by a bare `ERR_MODULE_NOT_FOUND` check, so a
|
|
656
|
+
missing TRANSITIVE module — not just the scanner's own three vendored
|
|
657
|
+
sibling imports — also went inert instead of failing closed.
|
|
658
|
+
`isMissingDirectSibling()` now inspects `err.url` (the URL Node's own
|
|
659
|
+
`ERR_MODULE_NOT_FOUND` carries) against the scanner's three known
|
|
660
|
+
direct-sibling import URLs; a miss anywhere deeper now THROWS. Third,
|
|
661
|
+
`.husky/pre-commit` was capturing but discarding the scanner's own stderr
|
|
662
|
+
on failure and printing the same "privacy leak detected" line regardless
|
|
663
|
+
of cause; it now prints the scanner's (redacted) diagnostic tail and
|
|
664
|
+
distinguishes "the scanner could not run" from "the scanner found a
|
|
665
|
+
leak." 259/259 tests pass; 4 fake-regression probes (each reverting one
|
|
666
|
+
of the three fixes above) still turn red; a live repo scan still PASSes.
|
|
667
|
+
- **`checkOwnerConfig()`'s whole-file-discard message claimed "the entire
|
|
668
|
+
file was discarded" even when a validly-configured OPTIONAL section
|
|
669
|
+
survived the discard onto its default (#1244 merge rule, Fixed above).**
|
|
670
|
+
`scripts/lib/owner-config-banner.mjs` now diffs each optional object
|
|
671
|
+
section (`paths`, `dispatcher`) in the merged config against
|
|
672
|
+
`getDefaults()`: a section that differs was real and survived, and the
|
|
673
|
+
banner names it — `"paths" kept, everything else defaulted` — instead of
|
|
674
|
+
claiming total loss; `droppedSections` renders on this branch too.
|
|
675
|
+
Reproduced with a tmp owner.yaml carrying an invalid `owner.name`
|
|
676
|
+
(required, triggers the whole-file path) alongside a valid
|
|
677
|
+
`paths.confidential-names-file` (a Wave-4 architect-panel finding); a new
|
|
678
|
+
case in `tests/lib/owner-config-banner.test.mjs` pins the "kept" wording.
|
|
679
|
+
168/168 tests pass; 2 fake-regression probes still turn red; a real,
|
|
680
|
+
healthy host's banner still returns null.
|
|
681
|
+
- **`check-skill-script-paths` and `check-validator-registration` answered an
|
|
682
|
+
existence question from the git INDEX, so a brand-new untracked file's own
|
|
683
|
+
citation was invisible to them until `git add` (#1248).** Measured in a clone
|
|
684
|
+
(Wave-1 discovery): an untracked skill citing a non-existent script reported
|
|
685
|
+
`1 passed, 0 failed` before staging and `0 passed, 1 failed` after — same tree,
|
|
686
|
+
same defect. New shared `scripts/lib/validate/enumerate-repo-files.mjs`
|
|
687
|
+
(`enumerateRepoFiles()`) answers "does this exist under these roots right now"
|
|
688
|
+
via `git ls-files --cached --others --exclude-standard`, honouring
|
|
689
|
+
`.gitignore` without reopening #1143 (a bare `readdirSync` walk was tried and
|
|
690
|
+
REFUTED: it pulls a gitignored peer worktree under `.claude/worktrees/` into
|
|
691
|
+
the census — measured 287 vs 290 `.md` files with one peer worktree present).
|
|
692
|
+
Both checkers, and the release drift sweep (Changed, above), now share this
|
|
693
|
+
one enumerator instead of three different partial views.
|
|
694
|
+
- **`enumerateRepoFiles()` (above) silently dropped `stat` failures other than
|
|
695
|
+
`ENOENT`/`ENOTDIR`, so a permission-denied file read as "does not exist"
|
|
696
|
+
instead of "unknown" — the release drift sweep (Changed, above) could
|
|
697
|
+
report a clean sweep on 0 actual reads (Wave-4 Codex second-look).** It
|
|
698
|
+
now throws a typed `RepoEnumerationError`
|
|
699
|
+
(`scripts/lib/validate/enumerate-repo-files.mjs`) for
|
|
700
|
+
`EACCES`/`EPERM`/`ELOOP`/`EIO`/`ENAMETOOLONG`/anything else it cannot
|
|
701
|
+
classify as "provably absent," carrying the path and the underlying error
|
|
702
|
+
code; only `ENOENT` and `ENOTDIR` are still treated as absent.
|
|
703
|
+
`collectDriftHits()` (`scripts/release.mjs`) reports this as exit 128,
|
|
704
|
+
which the release preflight already reads as "inconclusive" — the same
|
|
705
|
+
reading a crashed `git grep` got, never a silent clean. 195/195 tests
|
|
706
|
+
pass across the 5 touched files; the Codex reproduction now shows status
|
|
707
|
+
128 / `ok:false` instead of a clean sweep; `validate-plugin.mjs` still
|
|
708
|
+
234/0. Two known sibling sites of the same swallow-class are filed as
|
|
709
|
+
follow-up issues rather than fixed here: `listRepoFiles()`
|
|
710
|
+
(`scripts/lib/repo-files.mjs`) has the identical catch-all, and 3 further
|
|
711
|
+
test files still hand-roll `git init` + a bare recursive `rmSync` in
|
|
712
|
+
template-string form (invisible to a literal-string census) rather than
|
|
713
|
+
routing through `tests/_helpers/tmp-fixture.mjs`.
|
|
714
|
+
- **15 more fixture-shaped test files are routed onto the
|
|
715
|
+
`tests/_helpers/tmp-fixture.mjs` helper (the detached-`git-maintenance` fix,
|
|
716
|
+
below) this session — 3 of them deleting a weaker inline copy of the same
|
|
717
|
+
helper that `enumerate-repo-files.test.mjs`, `check-validator-registration.test.mjs`
|
|
718
|
+
and `check-skill-script-paths.test.mjs` had each written for themselves
|
|
719
|
+
(above), plus 11 legacy files and a `release.test.mjs` git-grep-to-fixture
|
|
720
|
+
probe.** `tests/unit/quality-gate-session-config.test.mjs` — the one file
|
|
721
|
+
this pass left out of scope, needed here because it was also touching
|
|
722
|
+
`enumerate-repo-files.test.mjs` — is itself routed in this same session (23
|
|
723
|
+
insertions / 30 deletions: `execSync('git init', ...)` →
|
|
724
|
+
`fixtureGit(['init', ...])`, `rmSync(repoRoot, ...)` → `removeTree(repoRoot)`,
|
|
725
|
+
`mkdtempSync(...)` → `makeTmpDir(...)`). Net −28 LOC across the routed
|
|
726
|
+
files; 526/526 of their own tests unchanged per file; eslint clean. The
|
|
727
|
+
self-census this whole effort is measured against
|
|
728
|
+
(`tests/lib/validate/check-test-git-config-target.test.mjs`) cannot resolve
|
|
729
|
+
a fixture helper's own `...spread` token to a git subcommand, so its floor
|
|
730
|
+
was lowered from `>50` to `>20` with a Revisit-Trigger comment naming the
|
|
731
|
+
exact condition ("the census learning to resolve the helper's spread")
|
|
732
|
+
rather than silently loosened — the remaining git-fixture files are reduced
|
|
733
|
+
this session, not proven at zero: 3 further sites in template-string form
|
|
734
|
+
are filed as follow-up issues (above).
|
|
735
|
+
- **The full test suite failed 1–2 tests per run, never the same one twice — a
|
|
736
|
+
detached `git maintenance` child racing the fixture's own cleanup.**
|
|
737
|
+
`GIT_TRACE=1` on a bare fixture commit showed `git maintenance run --auto
|
|
738
|
+
--quiet --detach` still writing into a temp `.git` directory after
|
|
739
|
+
`execFileSync` returned, so the `afterEach` `rmSync` raced a live process and
|
|
740
|
+
failed with `ENOTEMPTY`. New `tests/_helpers/tmp-fixture.mjs` (`fixtureGit`,
|
|
741
|
+
`fixtureGitSpawn`, `removeTree`, `makeTmpDir`) sets `maintenance.auto=false`,
|
|
742
|
+
`gc.auto=0` and `core.fsmonitor=false` on every fixture repo, and retries
|
|
743
|
+
removal (`maxRetries: 5`) for what config alone cannot reach. 24 fixture files
|
|
744
|
+
(118 git call sites, 35 `rmSync` sites, 39 `mkdtemp` sites) now route through
|
|
745
|
+
it; the detached-maintenance trace line is gone from the routed files, and the
|
|
746
|
+
494 tests across those 24 files still pass.
|
|
747
|
+
- **10 of the 43 learning-provenance markers the 43→8 generated-rule
|
|
748
|
+
consolidation (Changed, above) dropped are restored, across
|
|
749
|
+
`git-and-worktrees`, `guard-design`, `measurement-discipline`,
|
|
750
|
+
`process-contracts`, `review-and-adapter-contracts`, `test-hygiene` and
|
|
751
|
+
`toolchain-and-build.md` (+121/−19 across the 7 files).** That consolidation's
|
|
752
|
+
own claim — "each keeping its provenance markers" — was false for 10 of them
|
|
753
|
+
when written; a fresh census now counts 43 of 43 keys and 43 of 43 ids (a
|
|
754
|
+
previously reported "42 ids" was itself a UUID-filter miss, not a real 42nd
|
|
755
|
+
gap). One of the 10 was a markers-only restoration against an
|
|
756
|
+
already-present duplicate prose block; the other 9 restore both the prose and
|
|
757
|
+
its evidence line.
|
|
758
|
+
- **`docs/migration-v4.md` §6 Rollback and its "Cursor still shows the removed
|
|
759
|
+
commands" recipe both had defects that would have re-broken what they were
|
|
760
|
+
trying to fix (Wave-4 Codex second-look).** §6's `cursor-install.mjs` /
|
|
761
|
+
`pi-install.mjs` rollback lines were missing the target-project argument
|
|
762
|
+
both scripts require — they default to `process.cwd()`
|
|
763
|
+
(`scripts/cursor-install.mjs:24-51`), so an operator following the doc
|
|
764
|
+
literally would link the installer into the session-orchestrator checkout
|
|
765
|
+
itself, not their own project. Both lines now carry
|
|
766
|
+
`/path/to/your-project` explicitly, with a note that both installers are
|
|
767
|
+
ADD-ONLY: they skip any destination that already exists, so re-running the
|
|
768
|
+
3.24.0-checkout installer never resyncs a `hooks.json` or Pi settings file
|
|
769
|
+
written under 4.0.0 — only adds what your project is missing. The
|
|
770
|
+
"Cursor still shows the removed commands" recipe used `rm -f` to delete
|
|
771
|
+
three named files, which deletes a REGULAR file with the same name exactly
|
|
772
|
+
as readily as a symlink — including a command an operator wrote themselves
|
|
773
|
+
under one of those three retired names; it is now a `[ -L "$p" ] && rm
|
|
774
|
+
"$p"` loop, symlink-only. Separately, the session-start plugin-update
|
|
775
|
+
banner's Pi instruction (Changed, above) led with the dev-fallback checkout
|
|
776
|
+
recipe though the documented PRIMARY Pi install path is `pi install
|
|
777
|
+
npm:session-orchestrator` (`docs/pi-setup.md:16`, §"Option 1") —
|
|
778
|
+
`PLATFORM_UPDATE_INSTRUCTIONS.pi` (`scripts/lib/plugin-update-banner.mjs`)
|
|
779
|
+
now leads with re-running that exact command, naming the checkout +
|
|
780
|
+
`--settings-only` recipe only as the fallback for a dev-fallback
|
|
781
|
+
registration. 25/25 tests pass; `check-skill-links.mjs` reports 86;
|
|
782
|
+
`validate-plugin.mjs` 234/0.
|
|
783
|
+
- **`#1229` closed — the session-lock heartbeat already refreshes on `Stop`,
|
|
784
|
+
`PostToolBatch` and `SessionStart`, not only once per wave; the reported gap
|
|
785
|
+
was a false premise, refuted by measurement.** Live call sites confirmed at
|
|
786
|
+
`hooks/on-stop.mjs:438` (fires every turn), `hooks/post-tool-batch-wave-signal.mjs:377`,
|
|
787
|
+
and `hooks/on-session-start.mjs:1078`; a heartbeat mid-wave measured 3 seconds
|
|
788
|
+
old. No `UserPromptSubmit` hook exists to add a fourth site.
|
|
789
|
+
- **`#1230` closed — `js-yaml` is already lazily required in `owner-yaml.mjs`
|
|
790
|
+
(`:120-127`, memoised, one WARN on failure) and all 27 hooks already exit 0
|
|
791
|
+
with both `zx` and `js-yaml` blocked; the reported gap was a false premise,
|
|
792
|
+
refuted by measurement.** The eager-import class the Predecessor-commits Fixed
|
|
793
|
+
entry above already closed was re-verified still holding at this session's
|
|
794
|
+
base commit — no regression, no further code change needed.
|
|
795
|
+
|
|
796
|
+
#### Predecessor commits (v3.24.0 → `e4674109`)
|
|
797
|
+
|
|
798
|
+
- **Two silent Session Config parsing bugs fixed across 39 block-shaped parsers
|
|
799
|
+
(#1162).** New shared module `scripts/lib/config/block-preprocess.mjs` fixes both: (a) a
|
|
800
|
+
block commented out with a multi-line `<!-- … -->` was read as LIVE config —
|
|
801
|
+
`stripHtmlCommentBlocks()` now strips it via the existing `htmlCommentSkipper()` state
|
|
802
|
+
machine; (b) the bold-bullet sub-key rendering (`- **enabled:** true`) matched no sub-key
|
|
803
|
+
regex and silently fell back to its default — `normalizeBoldSubkeys()` now normalises it
|
|
804
|
+
to `enabled: true` before parsing. `preprocessBlockLines()` (35 standard parsers) and
|
|
805
|
+
`preprocessBlockLinesNoDash()` (4 dash-RECORD parsers — `custom-phases`, `remote-hosts`,
|
|
806
|
+
`evolve`'s `_parseEvolve`, `health-endpoints`) are the two drop-ins for
|
|
807
|
+
`content.split(/\r?\n/)`; NoDash skips bold-normalisation because de-dashing a record's
|
|
808
|
+
first key would silently merge it into the previous record.
|
|
809
|
+
`config-protection.mjs`'s `_isConfigWeakeningAllowed()` and
|
|
810
|
+
`hooks/pre-bash-destructive-guard.mjs`'s bypass scan deliberately use HTML-comment
|
|
811
|
+
stripping ONLY, never bold-normalisation — the bold form
|
|
812
|
+
(`- **allow-config-weakening:** true` / `- **allow-destructive-ops:** true`) must not arm
|
|
813
|
+
a bypass. An UNTERMINATED `<!--` fails closed in both directions: a block parser gets its
|
|
814
|
+
lines back UNFILTERED, while both bypass scanners treat it as NOT ARMED (said out loud on
|
|
815
|
+
stderr, since a silently-ignored bypass would look identical to an operator typo);
|
|
816
|
+
`scripts/parse-config.mjs` prints one stderr WARN per session, not one per parser. Two
|
|
817
|
+
new parity test files pin the contract per parser: `tests/lib/config/preprocess-parity-a-l.test.mjs`
|
|
818
|
+
and `-m-z.test.mjs`.
|
|
819
|
+
- **PSA-007's git-write detector is argument-aware, closing a false-positive AND a
|
|
820
|
+
false-negative class (#1172, #1215).** `isGitWrite()` in
|
|
821
|
+
`scripts/lib/wave-transcript-tail.mjs` used to match the subcommand literal alone; it now
|
|
822
|
+
parses the arguments too. `git stash list`/`show`, `--version`, `--help`/`-h`,
|
|
823
|
+
`--dry-run`, and `-n` on every subcommand except `commit` (where `-n` means
|
|
824
|
+
`--no-verify`, still a real write — measured 2026-09-04 that `git commit -h` prints
|
|
825
|
+
`-n, --no-verify` while `add`/`rm`/`push -h` all print `-n, --[no-]dry-run`) now read as
|
|
826
|
+
reads, not writes. The value-taking global flags `-C`/`-c`/`--git-dir`/`--work-tree`/
|
|
827
|
+
`--namespace`/`--exec-path`/`--config-env`, and `command`/`env VAR=x` prefixes, are now
|
|
828
|
+
absorbed so `git -C /tmp stash` and `env FOO=x git commit` are still recognised as git
|
|
829
|
+
writes at all (measured false negative before the fix: `isGitWrite('git -C /tmp stash')`
|
|
830
|
+
returned `false`). `FIXTURE_CONTEXT_RE`'s `cd /tmp` match now also fires on the BARE path
|
|
831
|
+
with no trailing `/` — the reported false alarm was
|
|
832
|
+
`cd /tmp && git init && git commit -m x`. 32 table rows added (70 cases in the file
|
|
833
|
+
after the change) in `tests/lib/wave-transcript-tail.test.mjs`.
|
|
834
|
+
- **`buildLiveSignals()`'s injectable `_scanBacklog` test seam was unused at 5 call sites,
|
|
835
|
+
so the affected test suite shelled out to a live `glab issue list` on every run
|
|
836
|
+
(#1169).** `tests/lib/autopilot.test.mjs` now passes a null-returning `_scanBacklog` stub
|
|
837
|
+
at every call site that does not assert on `signals.backlog` (`selectMode`/`computeDelta`
|
|
838
|
+
never read that field, so the stub is behaviour-preserving). `scripts/lib/autopilot/loop.mjs`
|
|
839
|
+
also migrated off the removed `SO_STATE_DIR` constant onto `getStateDir()` (#1153 P5) in
|
|
840
|
+
the same pass. Test-phase wall time for the affected suite dropped from ~750ms to ~25ms,
|
|
841
|
+
with no live network calls remaining.
|
|
842
|
+
- **One flaky spawn-timeout test pinned with margin instead of disabled (#1217).**
|
|
843
|
+
`tests/unit/plugin-manifests-exit-codes.test.mjs`'s "exits 1 (not 2) when plugin.json
|
|
844
|
+
contains broken JSON" case now passes `{ timeout: 30_000 }` explicitly — measured
|
|
845
|
+
887–965ms unloaded (W1-D4, 2026-09-04), with one observed 10,095ms outlier attributed to
|
|
846
|
+
host contention, not spawn cost. Same discriminating-margin pattern as the three tests
|
|
847
|
+
hardened earlier in this file; no repo-wide pinned-slow list exists (#976 never
|
|
848
|
+
implemented).
|
|
849
|
+
- **Legacy `.bak.`-delimited backup files were invisible to both rotation and restore
|
|
850
|
+
(#1173).** `scripts/lib/learnings/io.mjs` exports `backupSuffixOf()`/`isBackupOf()`,
|
|
851
|
+
accepting both the canonical `.bak-<ISO>` delimiter this module writes and the legacy
|
|
852
|
+
`.bak.<label>-<ts>` delimiter pre-#721 writers left behind (e.g.
|
|
853
|
+
`learnings.jsonl.bak.evolve-<ts>`), shared with `backfill-learnings-from-vault`'s restore
|
|
854
|
+
sweep so the two predicates cannot drift apart again. `rotateBackups()` now sorts on the
|
|
855
|
+
SUFFIX (a leading non-digit label stripped first) rather than the whole filename —
|
|
856
|
+
sorting on the whole name grouped every `.`-delimited legacy file after every
|
|
857
|
+
`-`-delimited one regardless of age (`-` is 0x2D, `.` is 0x2E), so rotation pruned only
|
|
858
|
+
hyphen-form backups. `scripts/backfill-learnings-expires.mjs` itself was still emitting
|
|
859
|
+
the dot form on `--apply` — its own backups were the ones going unrotated and unrestored
|
|
860
|
+
— and now emits the canonical hyphen form (`tests/scripts/backfill-learnings-expires.test.mjs`
|
|
861
|
+
pins both the new emission and the absence of the old one).
|
|
862
|
+
- **A peer session's `wave-scope.json` drove this session's Bash gates on two hooks, and a
|
|
863
|
+
rebind of the manifest could suppress its own tamper notice (#1153 P1).**
|
|
864
|
+
`hooks/enforce-commands.mjs` and `hooks/post-bash-write-verify.mjs` both gain a Gate 3b
|
|
865
|
+
ownership check — `classifyManifestSession()` against `readProcessLocalSessionIds()` (hook
|
|
866
|
+
payload + `CLAUDE_CODE_SESSION_ID`, deliberately not the lock-file tier that made a
|
|
867
|
+
peer's manifest classify as `'own'` under #1194) — and stand down silently when the
|
|
868
|
+
manifest provably names another session, emitting `orchestrator.scope.foreign_session_ignored`
|
|
869
|
+
for observability. In `post-bash-write-verify.mjs` the ordering is the fix itself: the
|
|
870
|
+
control-file hash/enforcement snapshot is now computed BEFORE the Gate 3b stand-down, so
|
|
871
|
+
a `cat >` rebind of `wave-scope.json` to a fabricated `session_id` can no longer disarm
|
|
872
|
+
this session's gates AND suppress the #938 control-file notice in the same stroke — the
|
|
873
|
+
notice now fires once, on the call where the SESSION BINDING itself changed.
|
|
874
|
+
- **`processSession`'s two skipped-noop returns had no `maskerWouldChange` re-probe
|
|
875
|
+
(#1028, found by the Wave-4 security-reviewer as MED, confirmed HIGH by qa-strategist).**
|
|
876
|
+
The session-note generator's skip paths (`process.mjs:953`/`:991`) now carry the same
|
|
877
|
+
`maskerWouldChange` guard the learning-note generator already had at 3 sites
|
|
878
|
+
(`:703`/`:773`/`:797`); a missing `!force` check in the disambiguation-collision branch is
|
|
879
|
+
also fixed. 4 new tests, red on HEAD before the fix.
|
|
880
|
+
- **The legacy-flat heal wrote the masked copy to the namespaced path while the plaintext
|
|
881
|
+
legacy original stayed in place and became unreachable (#1028, qa-strategist HIGH-2).**
|
|
882
|
+
Both generators (learning-note and session-note) now re-render the leaking legacy note
|
|
883
|
+
masked IN PLACE (same fix shape at both `#660 IDEMPOTENCY DUAL-PROBE` sites), marking the
|
|
884
|
+
result `action: 'created'` + `meta.healed_legacy_flat: true` instead of leaving a second,
|
|
885
|
+
unreachable plaintext copy on disk.
|
|
886
|
+
- **`orchestrator.secret_masker.applied` now emits from all three channels that mask vault
|
|
887
|
+
content, not two.** `scripts/lib/vault-mirror/process.mjs`,
|
|
888
|
+
`scripts/lib/vault-status/narrative-mirror.mjs`, and `scripts/export-hw-learnings.mjs`
|
|
889
|
+
(documented in its own comment as "the third") each carry the emit at the end of their run.
|
|
890
|
+
- **`quality-gate.mjs`'s fail-open corrective-context path was silent (#1205, Wave-4
|
|
891
|
+
security-reviewer LOW).** Without `CLAUDE_CODE_SESSION_ID` (Codex/Cursor with no
|
|
892
|
+
process-local witness), a peer's `corrective_context` was kept without any signal. It now
|
|
893
|
+
prints a stderr WARN naming the UNVERIFIED session id before keeping the context; behaviour
|
|
894
|
+
is unchanged, only the visibility.
|
|
895
|
+
- **`check-skill-script-paths` had 24 dead script citations sitting unannotated inside
|
|
896
|
+
`docs/` (#1208).** `docs/` is now in `SCAN_DIRS`; 24 dead citations were annotated with the
|
|
897
|
+
existing `<!-- path-check: planned #<iid> | historical | example -->` marker convention
|
|
898
|
+
(measured: 1,087 citations across 259 files, 53 total annotated after this pass). A new
|
|
899
|
+
`--strict-sh` mode extends the same dead-path scan to `.sh` citations, reported as an
|
|
900
|
+
advisory count line in `validate-plugin` output (0 findings currently).
|
|
901
|
+
- **A block commented out with `<!-- … -->` still armed the dispatcher-autonomy one-time
|
|
902
|
+
capture AUQ (#1222).** `isDispatcherAutonomyBlockPresent()` called the shared
|
|
903
|
+
`hasBlockHeader()` matcher directly on raw content, while `scripts/parse-config.mjs`
|
|
904
|
+
preprocesses with comment-stripping first — so a `dispatcher-autonomy:` header sitting
|
|
905
|
+
inside a comment parsed to defaults (correctly) but was reported PRESENT (incorrectly),
|
|
906
|
+
meaning the operator was never asked. The guard now preprocesses with the same
|
|
907
|
+
`preprocessBlockLinesNoDash()` the parser uses before testing for the header, so an
|
|
908
|
+
unterminated `<!--` still fails toward PRESENT (one un-asked question, not a silently
|
|
909
|
+
disarmed bypass) matching the module's own documented one-time-capture contract.
|
|
910
|
+
`block-header.mjs`'s docblock now states explicitly that comment-stripping is the caller's
|
|
911
|
+
job. The three further planned call sites in `skill-evolution.mjs`, `persona-gate-wave.mjs`,
|
|
912
|
+
and `wave-reviewers.mjs` are NOT part of this change — none of the three currently calls
|
|
913
|
+
`hasBlockHeader()`/`isDispatcherAutonomyBlockPresent()` at all, so there is nothing to sweep
|
|
914
|
+
there yet.
|
|
915
|
+
- **`reapStaleBudgetFiles()`'s exact-boundary comparison had no falsifying test (#1153
|
|
916
|
+
P8).** A file aged exactly `maxAgeDays` is meant to be KEPT (`mtimeMs >= now -
|
|
917
|
+
maxAgeDays*86400000`); the existing 30-vs-1-day test stayed green even if `>=` were
|
|
918
|
+
weakened to `>`, which would silently reap a peer session's issue-budget file the instant
|
|
919
|
+
it turned `maxAgeDays` old. A new test in `tests/unit/hook-issue-budget.test.mjs` seeds one
|
|
920
|
+
file exactly on the cutoff and one a second past it and asserts the former survives and the
|
|
921
|
+
latter does not.
|
|
922
|
+
- **`sessionFromLock()`'s `host_id` fallback for a pre-#1072 lock had no test (#1153 P9).**
|
|
923
|
+
Two new cases in `tests/lib/session-discovery.test.mjs` pin both branches of
|
|
924
|
+
`lock.host_id ?? stableHostname(...)`: a lock that already carries `host_id` passes it
|
|
925
|
+
through unchanged, and a lock written before #1072 (no `host_id` field) derives it from the
|
|
926
|
+
raw `host` — without the fallback pin, a lock-sourced session could silently regress to the
|
|
927
|
+
raw, non-normalised host for comparison against the registry path's normalised twin.
|
|
928
|
+
- **`markCandidateProcessed()`'s mint branch stamped a self-contradictory terminal record
|
|
929
|
+
(#1153 P10, follow-up to #1042).** A freshly-minted candidate (no prior sidecar record) was
|
|
930
|
+
always given `status: 'proposed'` even while being stamped `processed_at` +
|
|
931
|
+
`outcome: 'rejected'` in the same write — a terminal, declined candidate that still read as
|
|
932
|
+
a live proposal to anything rendering `status`. `status` now agrees with `outcome`
|
|
933
|
+
(`'rejected'` when the outcome is `'rejected'`, `'proposed'` otherwise, the only two
|
|
934
|
+
ReconcileCandidate status values). Separately, the function used to return the
|
|
935
|
+
freshly-built stamp even when `mergeCandidates`'s dedupe rule kept an OLDER terminal record
|
|
936
|
+
on disk instead — caller and store then disagreed about the verdict, silently. It now reads
|
|
937
|
+
the record back out of the merge result and returns that, plus a new `alreadyProcessed`
|
|
938
|
+
flag (`written` stays `true` in that case: the store IS in the intended terminal state, and
|
|
939
|
+
`written: false` is reserved for a genuine write failure). Four new tests in
|
|
940
|
+
`tests/lib/reconcile/idempotency.test.mjs`, red on HEAD before the fix.
|
|
941
|
+
- **A learning whose `file_paths` were entirely unusable produced the same generic
|
|
942
|
+
"no activation axis" rejection as a learning with no `file_paths` at all (#1153 P13).**
|
|
943
|
+
`globsFromFilePaths()` now records each skipped entry (glob-metachar / control-char / quote)
|
|
944
|
+
into an out-parameter sink, rendered by the new `formatDroppedFilePaths()` into a
|
|
945
|
+
`— dropped file_paths: N (…)` suffix on the never-always-on rejection reason — the message
|
|
946
|
+
engine.mjs forwards verbatim into the operator-visible candidates ledger. Each dropped value
|
|
947
|
+
is `JSON.stringify`-escaped (so a raw newline or ANSI control char in a hostile `file_paths`
|
|
948
|
+
entry cannot inject into the reason string) and hard-truncated to 60 chars with no
|
|
949
|
+
mid-escape cut. Two new tests in `tests/lib/reconcile/emitter.test.mjs` pin both the naming
|
|
950
|
+
and the escaping.
|
|
951
|
+
- **`writer.mjs`'s `isOperatorRejection()` inferred an operator rejection from rendered
|
|
952
|
+
`content` being non-empty, which an operator-declined proposal with an empty rendered body
|
|
953
|
+
could fail (#1153 P15, closes the inference gap left open by #1042's own documented
|
|
954
|
+
ceiling).** `skills/session-end/phase-3-6-tail.md` step 6 now stamps every proposal the
|
|
955
|
+
operator left unselected with an explicit `operatorRejected: true` flag before it joins the
|
|
956
|
+
engine's `rejected` array; `isOperatorRejection()` keys on that flag first, falling back to
|
|
957
|
+
the old content-presence heuristic (marked `@deprecated`, kept only for a consumer repo
|
|
958
|
+
pinning a pre-P15 skill body) when the flag is absent. Two new tests in
|
|
959
|
+
`tests/lib/reconcile/writer.test.mjs` cover the flag-wins-over-empty-content case and the
|
|
960
|
+
legacy flagless fallback.
|
|
961
|
+
|
|
962
|
+
### Security
|
|
963
|
+
|
|
964
|
+
#### 4.0.0 — audit session
|
|
965
|
+
|
|
966
|
+
- **The published npm tarball shipped 0 of the 6 files under
|
|
967
|
+
`.orchestrator/policy/` — including the destructive-command guard's own floor
|
|
968
|
+
policy (`blocked-commands.json`) — so an npm-installed consumer without a host
|
|
969
|
+
overlay ran `hooks/pre-bash-destructive-guard.mjs` against `rules: null` and it
|
|
970
|
+
silently ALLOWED every command the floor exists to block (found by the Wave-1
|
|
971
|
+
Codex external review, P1: `npm pack --dry-run | grep -c orchestrator/policy`
|
|
972
|
+
→ 0).** `package.json`'s `files[]` now lists `.orchestrator/policy/`;
|
|
973
|
+
`npm pack --dry-run --json | grep -c "orchestrator/policy"` reports **6**
|
|
974
|
+
policy files today (`blocked-commands.json`, `ecosystem.schema.json`,
|
|
975
|
+
`quality-gates.example.json`, `quality-gates.schema.json`,
|
|
976
|
+
`templates-policy.json`, `test-profiles.json`) where it reported 0 before,
|
|
977
|
+
pinned by the new `tests/scripts/pack-policy-floor.test.mjs` (a real
|
|
978
|
+
`npm pack --dry-run`, not a mock). The release leakage gate's `.orchestrator/`
|
|
979
|
+
exclusion is narrowed to carve THIS directory back IN (`scripts/release.mjs`
|
|
980
|
+
around line 319) — the operator's own `metrics/`, `debug/` artefacts and live
|
|
981
|
+
`*.lock` files stay excluded; only the tracked policy floor ships. **The
|
|
982
|
+
Wave-4 review panel found this test asserted only a FLOOR (≥ 6 files) while
|
|
983
|
+
`package.json`'s `files[]` admits the WHOLE `.orchestrator/policy/` directory
|
|
984
|
+
and the carve-out above matched the path anywhere in the string, not just at
|
|
985
|
+
its start** — an untracked private file dropped into that directory would
|
|
986
|
+
ship, invisible to a floor check. `pack-policy-floor.test.mjs` now asserts SET
|
|
987
|
+
EQUALITY between the packed policy entries and `git ls-files
|
|
988
|
+
.orchestrator/policy` (tracked-only, sorted); the carve-out regex is anchored
|
|
989
|
+
to the path start (`/^\.orchestrator\/policy\//`, previously unanchored),
|
|
990
|
+
with the VCS-trust rationale — everything under this one directory ships
|
|
991
|
+
because it is tracked, not because of a per-file allowlist — recorded in a
|
|
992
|
+
comment beside it.
|
|
993
|
+
- **`session_profile` was the one free-form string field on the telemetry
|
|
994
|
+
wire — both an internal Wave-1 review and the Codex review reproduced sending
|
|
995
|
+
an arbitrary string through it.** `VALID_SESSION_PROFILES` (`['ultradeep']`
|
|
996
|
+
today) is now enforced on BOTH ends: the client
|
|
997
|
+
(`scripts/lib/telemetry/schema.mjs`) omits the field when it is not a known
|
|
998
|
+
profile rather than passing it through, and the ingest server
|
|
999
|
+
(`server/ingest/validate.mjs`) rejects a record whose `session_profile` fails
|
|
1000
|
+
the same enum plus a 32-char length ceiling. Reproduced before the fix: a
|
|
1001
|
+
crafted `session_profile` string reached the wire; after, the client sends
|
|
1002
|
+
nothing and the server rejects a forged one directly. `detectSandbox()`
|
|
1003
|
+
(`scripts/lib/telemetry/sync.mjs`) now also fails CLOSED — a probe that
|
|
1004
|
+
itself throws returns `{ sandbox: true, reason: 'sandbox:probe-failed' }`
|
|
1005
|
+
instead of falling through to "not a sandbox". `docs/telemetry.md` gained the
|
|
1006
|
+
field table, the no-free-text-fields claim, and a new section documenting the
|
|
1007
|
+
npm update-check's own egress (`SO_DISABLE_UPDATE_CHECK`, `DO_NOT_TRACK`,
|
|
1008
|
+
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`).
|
|
1009
|
+
- **The `session_profile` whitelist above covered only the LIVE send path — a
|
|
1010
|
+
record already sitting in the offline queue when that whitelist landed still
|
|
1011
|
+
carried whatever it was enqueued with, and `flush()`
|
|
1012
|
+
(`scripts/lib/telemetry/sync.mjs`) forwarded every queued batch to the sender
|
|
1013
|
+
UNCHANGED.** Reproduced by the Wave-4 Codex second-look: a record the ingest
|
|
1014
|
+
server rejects is retried, still rejected, still requeued — the offline queue
|
|
1015
|
+
grew 1 → 2 → 3 and never drained. `sanitizeQueuedRecord()` now re-projects
|
|
1016
|
+
(`projectUsagePing`) and re-whitelists `session_profile` on every queued
|
|
1017
|
+
record before it joins a send batch, closing the same gap the live path was
|
|
1018
|
+
already closed for; the Codex reproduction now shows the queue back at 0
|
|
1019
|
+
after 3 consecutive rejections. A batch the server rejects with HTTP 400/422
|
|
1020
|
+
is now EVICTED (`reason: 'rejected-evicted'`) instead of re-queued: the
|
|
1021
|
+
ingest API validates a batch all-or-nothing with no per-record index, so the
|
|
1022
|
+
only bounded response to "the server will never accept this payload" is to
|
|
1023
|
+
drop the whole batch (named ceiling, BV-004: revisit if the server ever
|
|
1024
|
+
reports which record failed). `defaultSender` now attaches the HTTP status to
|
|
1025
|
+
the thrown error (`err.status`) so `flush()` can tell a schema rejection
|
|
1026
|
+
(evict) from a transport failure (re-queue, unchanged behaviour) — an
|
|
1027
|
+
injected sender that throws a bare `Error` still falls through to the
|
|
1028
|
+
pre-existing re-queue path. `docs/telemetry.md` documents `rejected-evicted`
|
|
1029
|
+
under "When a ping is sent".
|
|
1030
|
+
|
|
1031
|
+
### Notes
|
|
1032
|
+
|
|
1033
|
+
- **Full Gate (Wave 4, `37169158`):** typecheck 432 OK, lint 0, 642 files / 15,926 passed /
|
|
1034
|
+
0 failed / 11 skipped, `validate-plugin` 249/0, owner-leakage PASS (macOS). Quoted from
|
|
1035
|
+
the Wave-4 commit trailer, not re-run for this entry.
|
|
1036
|
+
- **The Wave-4 review panel (3 Opus reviewers: security-reviewer, session-reviewer,
|
|
1037
|
+
qa-strategist) found both HIGH findings above inside its own Wave-2/Wave-3 diff** — the
|
|
1038
|
+
`#1028` masker-guard residue the panel itself was reviewing. Both were closed same-wave by
|
|
1039
|
+
a dedicated fixpass (F-A/F-B/F-C) with fresh red-on-HEAD proofs before the green fix,
|
|
1040
|
+
plus test-hygiene cleanup (`dispatch-common.test.mjs` lost 12 structure-only assertions;
|
|
1041
|
+
`evolve-telemetry.test.mjs`'s regression tests no longer stat the real repo ledger) and
|
|
1042
|
+
three LOW fixes (`evolve-telemetry.mjs`'s `catch {}` now WARNs on stderr;
|
|
1043
|
+
`post-subagent-discovery-validator.mjs`'s German pattern now matches singular `Eintrag`;
|
|
1044
|
+
`.gitlab-ci.yml`/`docs/ci-setup.md` prose brought in line with the now-armed drift gate).
|
|
1045
|
+
- **A use-before-define in an intermediate save of `own-session.mjs` blocked every
|
|
1046
|
+
Bash/Edit call of every session on this host for roughly 8 minutes.** `hooks/enforce-commands.mjs`
|
|
1047
|
+
and `hooks/enforce-scope.mjs` both import the module live on every tool call (the #1153
|
|
1048
|
+
P1 Gate 3b wiring), so a `SyntaxError`/`ReferenceError` mid-refactor link-crashed both
|
|
1049
|
+
hooks host-wide rather than degrading to one repo's GUARD INACTIVE banner. Follow-up
|
|
1050
|
+
issue pending to make an intermediate, uncommitted save of a hot-path hook dependency
|
|
1051
|
+
fail more locally.
|
|
1052
|
+
- **This session's `session.lock` heartbeat was only refreshed once per wave, not
|
|
1053
|
+
continuously.** A long session-start/plan phase between heartbeats let a foreign
|
|
1054
|
+
`SessionEnd` reap the lock as stale before this session's own wave loop had a chance to
|
|
1055
|
+
refresh it. Deviation logged in this session's narrative; a follow-up issue for a
|
|
1056
|
+
time-based (not wave-based) heartbeat refresh is to be filed at close, not yet opened as of
|
|
1057
|
+
this diff.
|
|
1058
|
+
- **Full Gate (session close, uncommitted vs `4b451303`):** 651 files / 16,261 passed /
|
|
1059
|
+
0 failed / 16 skipped, typecheck 435 OK, lint 0, `validate-plugin` 250/0, Semgrep 0 new
|
|
1060
|
+
findings, hook-import-set-check in sync (150 modules) — supersedes the Wave-4 note above.
|
|
1061
|
+
|
|
10
1062
|
## [3.24.0] - 2026-09-02
|
|
11
1063
|
|
|
12
|
-
|
|
13
|
-
`BREAKING CHANGE:` footer and no
|
|
1064
|
+
Six commits since v3.23.0 (4 `feat`, 1 `fix`, 1 `docs`; 160 files,
|
|
1065
|
+
+15,586/−981 before the release commit), no `BREAKING CHANGE:` footer and no
|
|
1066
|
+
`!` subject.
|
|
14
1067
|
|
|
15
1068
|
One `docs` commit (`c3ab4801`) adds ADR-0013, naming the ownership-check-before-deregister
|
|
16
1069
|
ordering that closed Wave 4 of the previous session. One `feat` commit (`a019d5a4`, deep
|
|
@@ -303,6 +1356,19 @@ mint (UUIDv7, semantic session ids) were rejected or dropped by readers built fo
|
|
|
303
1356
|
only — every resume looked foreign — and call sites that needed a repo root passed
|
|
304
1357
|
`undefined` instead, so fail-open paths ran on every invocation.
|
|
305
1358
|
|
|
1359
|
+
This tag is also the first to carry the thirty-three commits of the 2026-08-22 → 2026-08-25
|
|
1360
|
+
line (`141d418..0626387`: 20 `fix`, 11 `feat`, 2 `docs`; 297 files, +24,535/−1,318 — measured
|
|
1361
|
+
2026-09-06 @ `befdda47`, `git log --format=%s v3.22.0..0626387` → 33 subjects). A `3.22.1` cut
|
|
1362
|
+
dated 2026-08-25 had changelogged that line, but the release commit never left its working
|
|
1363
|
+
copy — no push, no tag, no registry tarball — and the next day's work resumed from `0626387`
|
|
1364
|
+
without it. Its entries are carried below under *From the 2026-08-22 → 2026-08-25 line*. One
|
|
1365
|
+
fact sits under all of them: **a surface that said it was live, and a measurement that never
|
|
1366
|
+
had a caller.** Cursor advertised as a first-class platform and loaded only `.cursor/rules` —
|
|
1367
|
+
no slash commands, no skills, no hook bridge; `/session` was chat text. The same class shows up
|
|
1368
|
+
in the rest of the line: 18 probes with zero mechanical callers, three vault writers that could
|
|
1369
|
+
not report their own failure, gates that wrote and never read, and `detectPlatform()` walking
|
|
1370
|
+
to `$HOME` until 63 of 84 telemetry records said `platform=pi`.
|
|
1371
|
+
|
|
306
1372
|
### Added
|
|
307
1373
|
|
|
308
1374
|
- **Eight core rules ship as sanitized copies, with a report-only leak scanner (#1098).**
|
|
@@ -330,6 +1396,27 @@ only — every resume looked foreign — and call sites that needed a repo root
|
|
|
330
1396
|
session-start Phase 0.5, Phase 1.2.1, and both `parallel-aware-*` docs. A real
|
|
331
1397
|
`git worktree` two-root integration test goes phantom-peer red → green.
|
|
332
1398
|
|
|
1399
|
+
#### From the 2026-08-22 → 2026-08-25 line
|
|
1400
|
+
|
|
1401
|
+
- **Cursor is a native surface, not a rules overlay.** `.cursor/commands/*.md`
|
|
1402
|
+
(27 slash-command wrappers), `.cursor/skills/<name>/SKILL.md` (47 wrappers),
|
|
1403
|
+
`.cursor/hooks.json` → `scripts/lib/cursor-hook-bridge.mjs`. Same pattern as
|
|
1404
|
+
Pi: one maintained body in `commands/` and `skills/`, generated adapters,
|
|
1405
|
+
`validate-plugin` check (`check-cursor-adapter.mjs --check`). `cursor-install.mjs`
|
|
1406
|
+
now links commands and skills and writes `hooks.json` for other projects.
|
|
1407
|
+
Cursor has no Skill tool — commands Read `skills/<name>/SKILL.md`.
|
|
1408
|
+
- **The coordinator can see its agents, and a foreign model can build (#1113, #1150).**
|
|
1409
|
+
Wave supervision plus `cursor-agent` CLI foreign dispatch (detached worktree,
|
|
1410
|
+
filesystem-measured result, mandatory Claude review, `NEVER_FOREIGN_ROLES`).
|
|
1411
|
+
- **`/reconcile` ran for the first time in seven weeks** — 10 learnings became
|
|
1412
|
+
conditional rules (`feat(rules)`).
|
|
1413
|
+
- **Express-path** gained a mechanical caller and a single documented reading.
|
|
1414
|
+
- **18 measurement probes** now have a mechanical caller (`feat(probes)`);
|
|
1415
|
+
session-start no longer depends on prose to run them.
|
|
1416
|
+
- **Vault writers report their own failure**; the vault mirror reports that it
|
|
1417
|
+
is healthy (`feat(vault)`, `feat(telemetry)`).
|
|
1418
|
+
- **Ingest server** counts answers; the weekly digest has a caller.
|
|
1419
|
+
|
|
333
1420
|
### Fixed
|
|
334
1421
|
|
|
335
1422
|
- **Six onboarding contradictions + a `cursor-install` TARGET guard (#1078).** First
|
|
@@ -424,6 +1511,36 @@ only — every resume looked foreign — and call sites that needed a repo root
|
|
|
424
1511
|
one) plus a matching `hookTimeout` bump, and two further spawn-timeout assertions gained
|
|
425
1512
|
the same discriminating margin between contention and a genuine failure.
|
|
426
1513
|
|
|
1514
|
+
#### From the 2026-08-22 → 2026-08-25 line
|
|
1515
|
+
|
|
1516
|
+
- **Cursor hooks were a reference mapping that silent-no-op'd (#919).**
|
|
1517
|
+
`hooks/hooks-cursor.json` is the live handler map. The bridge projects Cursor
|
|
1518
|
+
stdin onto `tool_name` / `tool_input` and maps deny envelopes onto Cursor
|
|
1519
|
+
`{ permission }`. `afterFileEdit` remains post-hoc (cannot unwrite).
|
|
1520
|
+
- **`detectPlatform()` walked to `$HOME` and found `~/.pi`** — 63 of 84
|
|
1521
|
+
telemetry records carried `platform=pi` (`fix(platform)`).
|
|
1522
|
+
- **Identity compared the machine name, then the working copy, then DHCP**
|
|
1523
|
+
(#1072, #1085 follow-through). Budget and lock judge the session. One machine,
|
|
1524
|
+
one name.
|
|
1525
|
+
- **A vault probe wrote into the operator's real vault**; the mirror now writes
|
|
1526
|
+
on the second run too (#1033).
|
|
1527
|
+
- **Guards that saw the wrong thing or discarded the right one** (#1029, #1130,
|
|
1528
|
+
#1077, #1125, #979, and a second trio that measured nothing).
|
|
1529
|
+
- **Reconcile: a No stays a No**; three paths that turned a rule into silent
|
|
1530
|
+
reject; gates that wrote and never read (EventDrop #484, #386, #385, #429);
|
|
1531
|
+
a type registry is enough (#1042, #1151).
|
|
1532
|
+
- **STATE.md: a broken list item costs the item, never the document (#1111).**
|
|
1533
|
+
- **Telemetry: noop-silent mirror, recorded aborts, budget reaper (#1151, #1127).**
|
|
1534
|
+
Consent question and flush no longer hang on prose.
|
|
1535
|
+
- **Privacy: the operator's machine name leaves the examples (#1151 Review).**
|
|
1536
|
+
- **Archive: a citation is not a declaration; foreign files are not mine
|
|
1537
|
+
(#1112, #1123).**
|
|
1538
|
+
- **Scope manifest knows whom it belongs to, and the hook asks (#1123, #1082).**
|
|
1539
|
+
- **Security copy claimed SAST/Semgrep levels that never existed**; the private
|
|
1540
|
+
npm scope no longer rides along with a copied baseline.
|
|
1541
|
+
- **A dying suite at import is counted as what it is**; a blocking guard now
|
|
1542
|
+
says why.
|
|
1543
|
+
|
|
427
1544
|
### Changed
|
|
428
1545
|
|
|
429
1546
|
- **Host-registry census counts `semantic_session_id` (#1066).** `hooks/on-session-start.mjs`
|
|
@@ -453,6 +1570,12 @@ Two of this session's diffs were authored by foreign models under the #1150 adap
|
|
|
453
1570
|
#1078 by composer-2.5, #1093 by grok-4.6 — each passing mandatory Claude review before
|
|
454
1571
|
landing. First production use of the foreign-dispatch path.
|
|
455
1572
|
|
|
1573
|
+
npm 3.22.0 never contained the 2026-08-22 → 2026-08-25 line; 3.23.0 is the first registry
|
|
1574
|
+
tarball that does (`npm view session-orchestrator versions` @ 2026-09-06: 3.22.0, 3.23.0,
|
|
1575
|
+
3.24.0 — no 3.22.1). The 3.22.1 cut that first documented the line was a release commit that
|
|
1576
|
+
was never pushed, which is why those entries surface here, one release late, re-measured at
|
|
1577
|
+
`befdda47` rather than quoted.
|
|
1578
|
+
|
|
456
1579
|
## [3.22.0] - 2026-08-22
|
|
457
1580
|
|
|
458
1581
|
Twenty commits (12 `fix`, 5 `docs`, 2 `feat`, 1 `chore`; 150 files, +17,312/−2,575), no
|