session-orchestrator 3.20.0 → 3.22.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/000-session-orchestrator.mdc +3 -2
- package/.cursor/rules/030-wave-execution.mdc +10 -8
- package/.cursor/rules/040-discovery.mdc +6 -6
- package/.cursor/rules/050-plan.mdc +8 -8
- package/CHANGELOG.md +515 -0
- package/README.md +16 -11
- package/agents/analyst.md +1 -1
- package/agents/architect-reviewer.md +1 -1
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +1 -1
- package/agents/dialectic-deriver.md +1 -1
- package/agents/docs-writer.md +1 -1
- package/agents/memory-proposal-collector.md +7 -5
- package/agents/qa-strategist.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/session-reviewer.md +42 -1
- package/agents/skill-applied-judge.md +1 -1
- package/agents/test-writer.md +1 -1
- package/agents/ui-developer.md +1 -1
- package/agents/ux-evaluator.md +1 -1
- package/commands/eli5.md +33 -0
- package/commands/release.md +62 -0
- package/commands/test.md +2 -2
- package/docs/components.md +6 -5
- package/docs/migration-v3.md +9 -6
- package/docs/persona-panel.md +3 -1
- package/docs/scope-collision-guard.md +167 -0
- package/docs/session-config-reference.md +31 -8
- package/hooks/_lib/lock-bootstrap.mjs +19 -13
- package/hooks/enforce-scope.mjs +103 -3
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +21 -1
- package/hooks/on-session-end.mjs +76 -97
- package/hooks/on-session-start.mjs +195 -104
- package/hooks/on-stop.mjs +127 -12
- package/hooks/post-bash-write-verify.mjs +8 -32
- package/hooks/pre-auq-clarity.mjs +787 -0
- package/hooks/pre-bash-issue-budget.mjs +17 -18
- package/hooks/pre-task-scope-disjoint.mjs +1042 -0
- package/package.json +3 -1
- package/pi/prompts/eli5.md +12 -0
- package/pi/prompts/release.md +12 -0
- package/scripts/auq-audit.mjs +825 -0
- package/scripts/autopilot.mjs +10 -9
- package/scripts/emit-session.mjs +42 -0
- package/scripts/export-hw-learnings.mjs +61 -2
- package/scripts/lib/auq/clarity.mjs +1314 -0
- package/scripts/lib/auq/parse.mjs +1006 -0
- package/scripts/lib/auq/schema.mjs +1457 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/backlog-scan.mjs +106 -15
- package/scripts/lib/build-live-signals.mjs +7 -3
- package/scripts/lib/ci-status-banner.mjs +267 -77
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +32 -9
- package/scripts/lib/config/vault-integration.mjs +12 -1
- package/scripts/lib/dispatcher/rank.mjs +4 -7
- package/scripts/lib/gates/gate-full.mjs +3 -3
- package/scripts/lib/gates/gate-helpers.mjs +17 -6
- package/scripts/lib/git-config-drift.mjs +471 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
- package/scripts/lib/io.mjs +432 -7
- package/scripts/lib/issue-budget.mjs +63 -9
- package/scripts/lib/learnings/select.mjs +157 -3
- package/scripts/lib/memory-cleanup-stamp.mjs +132 -8
- package/scripts/lib/mirror-issues-banner.mjs +266 -0
- package/scripts/lib/named-vault-resolver.mjs +105 -16
- package/scripts/lib/owner-interview.mjs +78 -32
- package/scripts/lib/peer-cards/schema.mjs +6 -2
- package/scripts/lib/peer-discovery.mjs +73 -22
- package/scripts/lib/project-hygiene.mjs +64 -4
- package/scripts/lib/reconcile/renderer.mjs +17 -4
- package/scripts/lib/reconcile/writer.mjs +69 -30
- package/scripts/lib/redact-spans.mjs +89 -0
- package/scripts/lib/resource-probe/evaluate.mjs +330 -149
- package/scripts/lib/resource-probe/probe-platform.mjs +35 -0
- package/scripts/lib/resource-probe.mjs +18 -2
- package/scripts/lib/scope-baseline.mjs +77 -17
- package/scripts/lib/scope-gate.mjs +658 -0
- package/scripts/lib/secret-masker.mjs +262 -0
- package/scripts/lib/session-lock.mjs +34 -10
- package/scripts/lib/session-registry.mjs +9 -1
- package/scripts/lib/spiral-carryover.mjs +23 -2
- package/scripts/lib/state-md/mission-status.mjs +164 -58
- package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
- package/scripts/lib/validate/check-agents.mjs +77 -5
- package/scripts/lib/validate/check-auq-clarity.mjs +274 -0
- package/scripts/lib/validate/check-commands.mjs +2 -20
- package/scripts/lib/validate/check-doc-cli-commands.mjs +514 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +48 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +185 -17
- package/scripts/lib/validate/check-rules.mjs +153 -9
- package/scripts/lib/validate/check-skills.mjs +191 -0
- package/scripts/lib/validate/check-test-git-config-target.mjs +665 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +22 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +925 -0
- package/scripts/lib/validate/check-unwired-features.mjs +219 -11
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +965 -0
- package/scripts/lib/validate/frontmatter-block.mjs +61 -0
- package/scripts/lib/validate/tier-inference.mjs +46 -8
- package/scripts/lib/vault-backfill/glab.mjs +91 -58
- package/scripts/lib/vault-backfill/manifest.mjs +28 -8
- package/scripts/lib/vault-mirror/namespace.mjs +146 -1
- package/scripts/lib/vault-mirror/process.mjs +264 -31
- package/scripts/lib/vault-mirror/render-sessions.mjs +115 -4
- package/scripts/lib/vault-status/board-writer.mjs +300 -56
- package/scripts/lib/vault-status/narrative-mirror.mjs +119 -5
- package/scripts/lib/vcs-repo-spec.mjs +680 -30
- package/scripts/lib/wave-resource-gate.mjs +67 -73
- package/scripts/materialize-wave-scope.mjs +281 -0
- package/scripts/print-learnings-index.mjs +30 -3
- package/scripts/release.mjs +983 -107
- package/scripts/run-quality-gate.mjs +14 -0
- package/scripts/site-numbers.mjs +1049 -0
- package/scripts/validate-plugin.mjs +64 -0
- package/scripts/validate-wave-scope.mjs +286 -12
- package/scripts/vault-backfill.mjs +32 -5
- package/scripts/vault-mirror.mjs +26 -1
- package/skills/_shared/monitor-patterns.md +24 -4
- package/skills/_shared/parallel-aware-auq.md +30 -24
- package/skills/_shared/parallel-aware-preamble.md +31 -2
- package/skills/_shared/state-ownership.md +49 -6
- package/skills/bootstrap/SKILL.md +2 -1
- package/skills/brainstorm/SKILL.md +18 -18
- package/skills/brainstorm/soul.md +12 -0
- package/skills/claude-md-drift-check/SKILL.md +9 -1
- package/skills/debug/SKILL.md +4 -1
- package/skills/discovery/SKILL.md +28 -24
- package/skills/discovery/issue-templates.md +4 -4
- package/skills/discovery/probes-code.md +2 -2
- package/skills/discovery/probes-feature.md +6 -6
- package/skills/discovery/probes-infra.md +2 -2
- package/skills/discovery/probes-session.md +5 -5
- package/skills/dispatcher/SKILL.md +10 -1
- package/skills/eli5/SKILL.md +43 -0
- package/skills/evolve/SKILL.md +8 -9
- package/skills/frontmatter-guard/SKILL.md +9 -1
- package/skills/gitlab-ops/SKILL.md +73 -59
- package/skills/gitlab-portfolio/SKILL.md +10 -1
- package/skills/grill/SKILL.md +6 -6
- package/skills/grill/soul.md +16 -0
- package/skills/memory-cleanup/SKILL.md +20 -7
- package/skills/npm-publish/SKILL.md +23 -51
- package/skills/peekaboo-driver/SKILL.md +3 -3
- package/skills/persona-panel/SKILL.md +3 -1
- package/skills/plan/SKILL.md +18 -16
- package/skills/plan/mode-feature.md +1 -1
- package/skills/plan/mode-new.md +42 -12
- package/skills/plan/soul.md +12 -0
- package/skills/reconcile/SKILL.md +3 -3
- package/skills/repo-audit/SKILL.md +10 -1
- package/skills/session-end/SKILL.md +97 -22
- package/skills/session-end/metrics-collection.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +37 -2
- package/skills/session-end/session-metrics-write.md +4 -10
- package/skills/session-plan/SKILL.md +2 -2
- package/skills/session-plan/wave-template.md +1 -1
- package/skills/session-start/SKILL.md +82 -36
- package/skills/session-start/phase-2-5-docs-planning.md +8 -8
- package/skills/session-start/phase-4-5-resource-health.md +82 -19
- package/skills/session-start/soul.md +110 -0
- package/skills/spinout/SKILL.md +5 -1
- package/skills/sunset-review/SKILL.md +11 -1
- package/skills/test-runner/SKILL.md +2 -2
- package/skills/tmux-layout/SKILL.md +7 -2
- package/skills/using-orchestrator/SKILL.md +1 -1
- package/skills/vault-mirror/SKILL.md +10 -1
- package/skills/vault-sync/SKILL.md +10 -1
- package/skills/vault-sync/validator.mjs +55 -6
- package/skills/wave-executor/wave-loop.md +64 -12
- package/skills/write-executable-plan/SKILL.md +6 -6
- package/scripts/lib/mission-status-schema.mjs +0 -114
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +0 -8
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +0 -8
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +0 -8
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +0 -8
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +0 -10
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +0 -8
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +0 -3
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +0 -11
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +0 -9
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +0 -8
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +0 -7
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +0 -9
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +0 -11
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +0 -3
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +0 -3
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +0 -11
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +0 -11
- package/skills/vault-sync/tests/schema-drift.test.mjs +0 -133
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,521 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.22.0] - 2026-08-22
|
|
11
|
+
|
|
12
|
+
Twenty commits (12 `fix`, 5 `docs`, 2 `feat`, 1 `chore`; 150 files, +17,312/−2,575), no
|
|
13
|
+
`BREAKING CHANGE:` footer and no `!` subject. One strand runs through all of it:
|
|
14
|
+
**an instrument that reported confidently and measured the wrong quantity.** Not a
|
|
15
|
+
threshold set too tight, not a rule too strict — the wrong quantity, reported with the
|
|
16
|
+
same certainty as the right one. The host-resource warning fired on **99.0% of 1,477
|
|
17
|
+
measured session starts** and nobody could falsify it, because the two fields it was
|
|
18
|
+
computed from were never persisted. A hygiene probe announced "37 files (11 MB)" while
|
|
19
|
+
sizing the entire directory instead of the 37 — factor ~18 — and proposed deleting
|
|
20
|
+
version-controlled source files, because it never asked git. And the questions this tool
|
|
21
|
+
puts to its operator had never been measured at all: **21 of 72 passed** on first
|
|
22
|
+
measurement, and the single dominant cause was a 12-codepoint header limit that silently
|
|
23
|
+
truncated 30 of them.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **The operator's questions are now measured before he reads them (#1107).** `scripts/auq-audit.mjs`
|
|
28
|
+
scores every `AskUserQuestion` block and every prose fallback list against eight criteria
|
|
29
|
+
(K1–K8) plus two hard hurdles, all thresholds sourced from one registry — no hard-coded
|
|
30
|
+
number in the scorer. The census found **three populations, not one**: 40 Claude Code blocks,
|
|
31
|
+
10 Codex prose lists, and 17 that every prior count had missed (9 `.cursor/rules/*.mdc` with
|
|
32
|
+
10 choice blocks, plus 6 runtime questions inside `.mjs`). Baseline **21 of 72 (29%)**, after
|
|
33
|
+
the pass **72 of 72**. The dominant cause was structural: `header` caps at 12 codepoints and
|
|
34
|
+
the tool truncates silently — **26 of 42 headers exceeded it**, and one NFD-composed header
|
|
35
|
+
measured 12 visible characters as 14, destroying the question. Population A — the operator's
|
|
36
|
+
own path — ran 62% failing against 0% for the Codex and Cursor forms, which carry no header
|
|
37
|
+
at all.
|
|
38
|
+
- **A guard on the guard (#1122 groundwork).** The `AskUserQuestion` PreToolUse hook denies only
|
|
39
|
+
the two hard hurdles and reports the rest to stderr; `check-auq-clarity` is wired into
|
|
40
|
+
`validate-plugin` and blocks on H1/H2 with a measured 0% false-positive rate. Its own wiring is
|
|
41
|
+
proven by fake regression, not asserted.
|
|
42
|
+
- **`/eli5`** — say the last answer again in plain words, same facts, in the order the operator
|
|
43
|
+
needs them.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- **The warning that fired on 99.0% of all session starts (#1089).** Measured over **1,477
|
|
48
|
+
`orchestrator.session.started` events across 18 repos**: `ram_free_gb < 2` fired on 84.0%,
|
|
49
|
+
`claude_processes >= 5` on 93.6%, any of the three on **99.0%** — against 4,884 stop events
|
|
50
|
+
with zero OOM markers. Three independent measurement errors, no threshold among them.
|
|
51
|
+
(a) `os.freemem()` on Darwin reports only `Pages free` — median **0.4 GB** on hosts with
|
|
52
|
+
24–128 GB — so memory is now judged on `memory_pressure_pct_free` and a better signal
|
|
53
|
+
*replaces* a worse one rather than suppressing it. (b) `concurrent-sessions-warn` is
|
|
54
|
+
denominated in sessions and was compared against a process count; measured ratio **6.0:1**.
|
|
55
|
+
Same threshold, right denominator: 93.6% → **4.2%**. (c) One noisy axis could cap a wave;
|
|
56
|
+
now two independent soft signals must agree. The verdict had reached `sessions.jsonl` for
|
|
57
|
+
**15 of 1,734 sessions (0.9%)**, which is why the false alarm survived four months while six
|
|
58
|
+
repos independently wrote it into their learnings store — one at confidence **1.0**.
|
|
59
|
+
Codified as `.claude/rules/host-resources.md` (HR-101..106).
|
|
60
|
+
- **A standing condition is not a second opinion (#1089 follow-up).** The first live run of the
|
|
61
|
+
rebuilt verdict returned `warn | cap 2 | soft: ["cpu","zombies"]`. Zombie processes are idle
|
|
62
|
+
by definition — they cannot cause the load they are paired with — and are always present
|
|
63
|
+
(6, 13 and 9 in three readings minutes apart). They report; they never count toward the
|
|
64
|
+
two-signal rule.
|
|
65
|
+
- **A probe that sized the wrong set and never asked git.** `checkStaleArtifacts` computed its
|
|
66
|
+
byte figure over the whole of `.orchestrator/` instead of the aged subset it names —
|
|
67
|
+
the 37 files weighed **0.68 MB against 11 MB reported**. Independently, it consulted git
|
|
68
|
+
nowhere and therefore proposed pruning seven tracked, runtime-read source files, among them
|
|
69
|
+
the policy file `pre-bash-templates-first.mjs` reads and the two steering documents
|
|
70
|
+
session-start loads. The git exclusion is fail-**silent**, not fail-open: falling back to
|
|
71
|
+
"nothing is tracked" *is* the defect it closes.
|
|
72
|
+
- **Only the raw session ID releases a lock (#1085).** One session carried three identities —
|
|
73
|
+
STATE.md said `session-2`, the lock said `session-3`, issue-budget counted against a third
|
|
74
|
+
UUID — and `findPeers` reported the session's own presence as a foreign peer. The semantic
|
|
75
|
+
ID is now attribution metadata only; a release requires exact raw-ID equality, and a
|
|
76
|
+
semantic hint resolves to a raw ID only against a verified local binding. The companion fix
|
|
77
|
+
to issue-budget removed a data-loss path in which an identity-less invocation wrote its
|
|
78
|
+
fresh state into the shared counter and erased a live session's parked overflow.
|
|
79
|
+
- **The npm receipt is the boundary — and a `throw` lay behind it (#1088).** `publish()` threw
|
|
80
|
+
*after* a successful `npm publish`, during a 12-second registry-propagation poll: published,
|
|
81
|
+
not tagged, not pushed, reported as "failed, retryable". Propagation now returns a typed
|
|
82
|
+
outcome, each post-receipt step gets exactly one attempt, and everything else is collected as
|
|
83
|
+
`post-publish-reconciliation`. Leak detection decides on an extracted path rather than a regex
|
|
84
|
+
over `npm notice` prose (`contest` ≠ `tests`), and `ensureGithubRelease` became tri-state so
|
|
85
|
+
an auth or network answer no longer reads as "no release exists".
|
|
86
|
+
- **`--silent` leaked into the children, and the gate discarded its own diagnosis (a4f93cf).**
|
|
87
|
+
- **The empty sidecar path skipped the collision gate with exit 0 (#1083).** A coordinator that
|
|
88
|
+
wrote only the aggregate form of the two-shape scope declaration ran **six waves and ~27
|
|
89
|
+
dispatches with zero `FILE-SCOPE` injection** — and the absence looked identical to a clean
|
|
90
|
+
run. `scripts/materialize-wave-scope.mjs` is now the canonical writer of both shapes.
|
|
91
|
+
- **The GitLab target comes from the explicit path (#1065).** `glab repo view --output json`
|
|
92
|
+
materialised a full project response to read one field; on 2026-08-17 that response carried a
|
|
93
|
+
runner registration credential into a tool transcript. Project identity is now
|
|
94
|
+
`(host, URL-encoded namespace/project)` with traversal and double-encoding rejection, and the
|
|
95
|
+
same minimisation reached the vault-backfill path.
|
|
96
|
+
- **The restore froze after its first run (#state-md).** Also: a `gitleaks` allowlist that grew
|
|
97
|
+
by one proper name per incident now matches on a word boundary instead.
|
|
98
|
+
- **Four count claims where the checker could only see two (#docs-parity).** `docs/components.md`
|
|
99
|
+
claimed 46 skills and 26 commands against 47 and 27 on disk. The drift checker reported two —
|
|
100
|
+
its patterns are `^##`-anchored — while a census of the same two surfaces in the same file
|
|
101
|
+
found four; the two inside the Mermaid diagram were already self-contradictory before the fix.
|
|
102
|
+
|
|
103
|
+
### Notes
|
|
104
|
+
|
|
105
|
+
`.claude/rules/host-resources.md` is new and always-on. `.claude/rules/bash-harness-pitfalls.md`
|
|
106
|
+
gained no rule text this line, but two measurement traps were recorded against it in the
|
|
107
|
+
learnings store: `rg -rn` is not `grep -rn` (`-r` is `--replace` and silently substitutes every
|
|
108
|
+
match), and zsh does not word-split an unquoted variable, so a multi-path `git log -- $paths`
|
|
109
|
+
reports zero matches with exit 0 — both produce a plausible wrong number without erroring.
|
|
110
|
+
|
|
111
|
+
## [3.21.0] - 2026-08-19
|
|
112
|
+
|
|
113
|
+
Twenty-one commits (12 `fix`, 4 `feat`, 4 `docs`, 1 `chore`; 209 files, +28,906/−2,893), no
|
|
114
|
+
`BREAKING CHANGE:` footer and no `!` subject. Three strands — guard mechanics, the public
|
|
115
|
+
site, cross-session messaging — that turned out to be one strand: **a fact maintained in two
|
|
116
|
+
places, and a guard that is green without biting.** The sharpest instances are always the
|
|
117
|
+
quiet ones. **12 of 46 `SKILL.md`** and **14 of 16 `agents/*.md`** carried frontmatter that is
|
|
118
|
+
not YAML while every frontmatter checker in the tree had reported them clean for as long as
|
|
119
|
+
they existed. The v2 vault renderer's branch condition was **structurally false from its first
|
|
120
|
+
commit** and matched **0 of 253** session records. And the scope guard shipped, in the agent's
|
|
121
|
+
own channel, the command that disarms it.
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
|
|
125
|
+
- **Cross-session messaging is wired (#1049, #1050, #1047 — Epic #1048, waves 1–2 of 5).**
|
|
126
|
+
New always-on rule `.claude/rules/cross-session-messaging.md` with CSM-001..005, and the PSA
|
|
127
|
+
decision tree gained a peer-inform branch that hangs **below** the sibling check on the
|
|
128
|
+
foreign-scope leg — so it can mask neither the sibling branch nor the PSA-002 pause. That is
|
|
129
|
+
structural, not prose. The two-axis rationale is re-founded rather than restated: the moat
|
|
130
|
+
was never "peer sessions cannot talk to each other" — two independent sessions in one working
|
|
131
|
+
copy exchanged full round-trips on 2026-08-16 — it is that they share **one working copy**,
|
|
132
|
+
and what crosses the channel is information, never isolation. `READ_ONLY_TOOLS` gained
|
|
133
|
+
`SendMessage` + `ListAgents`, the mechanical precondition for upward agent escalation;
|
|
134
|
+
`validateTierConsistency` filtered on `WRITE_TOOLS` and therefore named **no culprit at all**
|
|
135
|
+
for an unknown tool.
|
|
136
|
+
- **The public site is rebuilt, and it now proves its own numbers (#1043–#1046).** The live
|
|
137
|
+
page had served v3.19.0 against v3.20.0 in the repo for six days, all five legal paths were
|
|
138
|
+
404, and the `Measured` block — the honesty argument of the whole page — carried five wrong
|
|
139
|
+
numbers.
|
|
140
|
+
- The load-bearing element is a band whose station height **is** the file count a wave may
|
|
141
|
+
write: `--unit: 15px` = one file. Discovery is therefore not a box of height 0 but a stroke
|
|
142
|
+
on the baseline — the moment the limit becomes visibly geometric rather than requested.
|
|
143
|
+
- Mobile had **no** navigation at all (`display:none`, no hamburger, 20 viewports of scroll);
|
|
144
|
+
6 links are now visible at 390px. Twelve sections in identical scoring and 27 equally loud
|
|
145
|
+
cards became 10 sections with three loud blocks, cutting rendered height to **52.4%**
|
|
146
|
+
(10,350 → 5,428px — the factor is the durable figure, the viewport count is not: 5.98
|
|
147
|
+
viewports holds at 908px window height and becomes 6.03 at 900px). State no longer rides on
|
|
148
|
+
red/green alone but on shape **and** glyph. Cold-load acceptance without `<script>`: 115
|
|
149
|
+
content-bearing elements, 0 invisible, identical height — the page hangs on no condition.
|
|
150
|
+
- **`vercel.json`** carries the deploy configuration **versioned** instead of clicked into a
|
|
151
|
+
dashboard: `outputDirectory: "site"`, CSP/HSTS/`frame-ancestors` headers, www→apex redirect.
|
|
152
|
+
The Vercel Git integration is connected, so a push to the GitHub mirror publishes the site.
|
|
153
|
+
In `scripts/release.mjs` the checklist line `cd site && vercel --prod` is replaced by
|
|
154
|
+
`verifyLiveSite()` with four **distinguished** outcomes (version mismatch / success /
|
|
155
|
+
network error / surface moved), not collapsed onto a flat "not ok".
|
|
156
|
+
- **`scripts/site-numbers.mjs`** — 13 metrics read from the repo and written into
|
|
157
|
+
`data-metric` spans, ending the hand-maintained number block. Its first run against the new
|
|
158
|
+
page caught three real errors, one of them the coordinator's (`skills: page says 47, repo
|
|
159
|
+
says 46` — `skills/_shared/` has no `SKILL.md` and is not a skill) and one a contract defect
|
|
160
|
+
that would have produced `vv3.20.0` at the next release. Wired into `--set-version`,
|
|
161
|
+
deliberately **not** into CI: `sessions` and `learnings` grow every session, so a pipeline
|
|
162
|
+
gate on them would be permanently red.
|
|
163
|
+
- **`/guide`** — the four install paths, the first session with the honest number (7–10
|
|
164
|
+
questions, 2–4 minutes, not the claimed "one question"), and a section on recognising that
|
|
165
|
+
a session failed silently. The reason is a census, not a hunch: **all four issues opened by
|
|
166
|
+
people outside this repo are install or environment failures, not one a feature request**
|
|
167
|
+
(`gh issue list -R Kanevry/session-orchestrator --state all`; authors ≠ owner: #53, #54,
|
|
168
|
+
#62, #63).
|
|
169
|
+
- **`/impressum` and `/datenschutz`.** The privacy policy is not copied but written from the
|
|
170
|
+
measured processing: 0 external requests, no analytics, self-hosted fonts (3 files,
|
|
171
|
+
55,052 B), Vercel server logs only. The sibling site's Vercel-Analytics sections are absent
|
|
172
|
+
on purpose — `grep` shows they are not active here. Sitemap 1 → 4 URLs.
|
|
173
|
+
- **Two catchers for the untracked-test-dependency class (#1081), because one was structurally
|
|
174
|
+
blind.** A differential run compares test *status*, and a test that swallows a missing file
|
|
175
|
+
and passes in **both** trees produces no difference at all. Statically:
|
|
176
|
+
`scripts/lib/validate/check-untracked-test-deps.mjs`, rules R2 (a statically resolved repo
|
|
177
|
+
root passed into an import closure that names an untracked path) and R4 (cwd-relative read),
|
|
178
|
+
measured 2/2 recall at 0 false positives where the obvious variants sat at 98–99% FP.
|
|
179
|
+
Structurally: `.husky/pre-push` materialises the sha being pushed via `git clone
|
|
180
|
+
--no-hardlinks` (1.04 s; `git archive` is unusable — without `.git`, 21 tests fail with "not a
|
|
181
|
+
git repository") and runs the gate **there**. It came within one function of being worthless:
|
|
182
|
+
`findProjectRoot()` checks `CLAUDE_PROJECT_DIR` and five siblings **before** cwd, so the gate
|
|
183
|
+
would have started in the tmp tree and read the working-tree files anyway.
|
|
184
|
+
- **A tracked census snapshot, `site/_census.json`, with ledger precedence** — per-metric opt-in
|
|
185
|
+
for exactly `sessions`, `learnings`, `counted-sha`, never a blanket fallback, which would have
|
|
186
|
+
made `collect()` blind to "wrong root". `optional: true` was considered and rejected by name:
|
|
187
|
+
it would have let the two tiles fall silently back into the hand-maintained state while
|
|
188
|
+
`--check` still reported the page as current — verbatim the silent-failure class the script
|
|
189
|
+
was written against. A known metric id without a value is now a third contract violation
|
|
190
|
+
(`unresolved`).
|
|
191
|
+
- **`tests/setup/scrub-git-env.mjs`, `scripts/lib/git-config-drift.mjs`, and
|
|
192
|
+
`check-test-git-config-target.mjs`** — the mechanism against this session's own accident (see
|
|
193
|
+
Notes). The setup file removes nine redirect-capable git variables once per worker; that is
|
|
194
|
+
the root, because the two lines that caused the damage passed a **correct** cwd, and `GIT_DIR`
|
|
195
|
+
beats cwd, `-C`, and even the positional of `git init <dir>`. The drift probe covers the
|
|
196
|
+
surface `git status` does not show, and runs with a filtered environment — a set `GIT_DIR`
|
|
197
|
+
would otherwise redirect the probe itself into a foreign repo and call this one clean. The
|
|
198
|
+
lint rule is WARN-only out of measurement, not caution: its first formulation had 11 hits,
|
|
199
|
+
**all** false positives (each a `git init <dir>` where the positional **is** the target); it
|
|
200
|
+
ends at 150/150 with a target.
|
|
201
|
+
- **`scripts/lib/validate/check-skills.mjs` — a real parser over all 46 `SKILL.md`**,
|
|
202
|
+
registered in `validate-plugin.mjs`. The sibling `check-agents.mjs` validated frontmatter
|
|
203
|
+
with line-oriented regexes, which is precisely how the 12 broken blocks stayed invisible:
|
|
204
|
+
an unquoted `description:` containing a `: ` (`"Iron Law: NO FIXES"`) is not YAML, but a
|
|
205
|
+
regex looking for `^description:` sees nothing wrong, and Claude Code's own loader is
|
|
206
|
+
lenient enough that the defect never surfaced at runtime. A `js-yaml` `CORE_SCHEMA` parse
|
|
207
|
+
is now rule R8 and the five field rules hang off it — none of them can be evaluated on a
|
|
208
|
+
block that does not parse. Deliberately **not** implemented: a block-scalar ban (see
|
|
209
|
+
below), and length ceilings on `name`/`description`, because no spec vendored in this repo
|
|
210
|
+
states one and an invented requirement is worse than none.
|
|
211
|
+
- **`scripts/lib/validate/frontmatter-block.mjs` — shared extraction, deliberately unshared
|
|
212
|
+
rules.** The extractor stood verbatim in two checkers, so the next change to the block
|
|
213
|
+
format would have landed in one copy and one gate would have started accepting what the
|
|
214
|
+
other rejects — with no test able to see it, because each gate tested its own copy. The
|
|
215
|
+
module owns the byte range and nothing else, and its header records why the three checkers
|
|
216
|
+
must stay **contradictory**: `check-agents.mjs` bans `description: >` because the agent
|
|
217
|
+
loader cannot read a folded scalar, while `check-skills.mjs` must tolerate it — for
|
|
218
|
+
`SKILL.md` the folded form is the only shape that makes the `: ` collision structurally
|
|
219
|
+
impossible, 35 of 46 files now use it, and porting the agent ban here would red 35 of 46
|
|
220
|
+
and forbid the very fix that made the gate green. The divergence is the requirement, not
|
|
221
|
+
drift.
|
|
222
|
+
- **`scripts/lib/mirror-issues-banner.mjs` — the GitHub-mirror blind spot**, wired into
|
|
223
|
+
`skills/session-start/SKILL.md` Phase 4. VCS auto-detection picks exactly one platform, so
|
|
224
|
+
in a GitLab-origin repo with a public GitHub mirror no code path ever read the mirror's
|
|
225
|
+
issues: everything filed by an external reporter was structurally invisible to every
|
|
226
|
+
session. The probe asks the other side, hard-pinned to `github` (auto-detecting here would
|
|
227
|
+
reproduce the defect it exists to compensate for) and **self-disabling** — no `github`
|
|
228
|
+
remote resolves to `undefined` → `null` → no spawn, no network call. No new Session Config
|
|
229
|
+
key by design: the spec comes from `git remote`, and a key would be a second SSOT drifting
|
|
230
|
+
against it. The return is three-valued, not two — `null` means *never asked* or *asked and
|
|
231
|
+
clean*; a `degraded` field with a closed enum means *the query failed and the state is
|
|
232
|
+
unknown*. Collapsing that third state into `null` is what makes a dead probe
|
|
233
|
+
indistinguishable from a healthy repo, which is the next section's recurring theme.
|
|
234
|
+
- **`atomicWriteWithBackup()` in `scripts/lib/io.mjs`** — the tmp-write/rename/`.bak-<ISO>`
|
|
235
|
+
primitive that `writeJsonAtomicSync` now delegates to, ending a second copy of the same
|
|
236
|
+
sequence (#734).
|
|
237
|
+
- **`check-unwired-features` signal S3 `orphaned-prose-module`** — a config key is not the
|
|
238
|
+
only thing prose can promise; a document can also assert that a module does a job nothing
|
|
239
|
+
calls. S3 fires only where the claim is in the **passive voice with a bare filename and no
|
|
240
|
+
exported symbol**: "…*are validated* against `foo.mjs`" asserts that something happens by
|
|
241
|
+
itself, whereas "dispatch via `runWavePool()`" addresses a reader who will do it. The
|
|
242
|
+
broader check — every export with no non-test importer — was measured and rejected: 1366
|
|
243
|
+
exports, 779 unimported, 93.2% false positives naive and still 81.2% after four exclusion
|
|
244
|
+
rules. A gate that prints 282 lines is switched off in week two, which is this file's own
|
|
245
|
+
disease one level up. Measured 2026-08-14, the cascade narrowed 452 production modules to
|
|
246
|
+
**2**; one of those is deleted below, so the live report now stands at 1. Read a near-empty
|
|
247
|
+
report as designed, not broken — S3 is a relapse guard, not a cleanup tool.
|
|
248
|
+
- **[ADR-0012](docs/adr/0012-pseudonym-map-privacy.md)** — host-local pseudonym map for
|
|
249
|
+
owner-leaky repo namespaces. An ADR rather than a code comment precisely because every
|
|
250
|
+
element reads like removable defensiveness at its call site while being load-bearing for a
|
|
251
|
+
property no test can observe directly: a green suite is fully compatible with the leak.
|
|
252
|
+
|
|
253
|
+
### Fixed
|
|
254
|
+
|
|
255
|
+
- **The documented Claude Code install path was guaranteed broken.** `claude plugin dir` is not
|
|
256
|
+
a subcommand: `claude plugin dir session-orchestrator` answers `error: unknown command 'dir'`
|
|
257
|
+
and exits 1 (measured on Claude Code 2.1.235; `claude plugin --help` lists 14 subcommands and
|
|
258
|
+
`dir` is none of them). The `||` fallback therefore fired **every** time and pointed at
|
|
259
|
+
`~/.claude/plugins/session-orchestrator`, a directory that does not exist — so `cd` failed,
|
|
260
|
+
`&&` aborted, `npm install` never ran, and the hooks could not find `zx`. That is the cause
|
|
261
|
+
behind external issues #62/#63, whose reporters described the symptom. Replaced by a
|
|
262
|
+
layout-independent resolution through the plugin cache, extracted from the **shipped**
|
|
263
|
+
`site/index.html`, entity-decoded and executed verbatim: exit 0, correct path. Second defect
|
|
264
|
+
in `migration-v3.md` fixed alongside — a marketplace-installed plugin sits in a managed cache,
|
|
265
|
+
not a git checkout, so `git pull` there does nothing; replaced by `/plugin update`. Five sites
|
|
266
|
+
in five files; the two remaining matches for `claude plugin dir` are deliberate quotes on the
|
|
267
|
+
site explaining the breakage.
|
|
268
|
+
- **The v2 vault renderer was dead at birth (#1074).** `total_agents` stands in
|
|
269
|
+
`REQUIRED_FIELDS`, so the branch condition `total_agents === undefined` is structurally false
|
|
270
|
+
— written 4h23m before the first record that could have matched it, and matching **0 of 253**
|
|
271
|
+
records since. The obvious fix would have been **worse than the bug**: `agents` is polymorphic
|
|
272
|
+
— measured over 599 wave objects, **210× a number and 14× an array** — so a naive `??` would
|
|
273
|
+
have written `[object Object]` into the vault. A type-aware alias chain repairs 239 agent, 5
|
|
274
|
+
files and 280 quality cells at changed-existing = 0. The fixtures are now harvested golden
|
|
275
|
+
records; the old `makeV2Entry` invented a record no production writer can produce.
|
|
276
|
+
- **Four blind probes and one active fail-open (#1039).** `backlog-scan` folded "no remote" and
|
|
277
|
+
"query failed" into a silent `null`, from which the mode selector read "contributes 0 delta" —
|
|
278
|
+
a 40-issue backlog with critical labels looked like an empty one. `ci-status-banner` forbade in
|
|
279
|
+
its own comment at ~line 490 the very form it still used. `named-vault-resolver` had one
|
|
280
|
+
`source: 'fallback'` covering two causes (honestly relativised by the agent:
|
|
281
|
+
`resolveNamedVault` has zero production callers, so the defect was real but not yet reachable).
|
|
282
|
+
`scope-baseline` hardcoded `'origin/main...HEAD'` twice, and the damage sat elsewhere than in
|
|
283
|
+
the name: `wave-loop.md:874` renders `skipped: 'unresolvable-ref'` **silently**, so the
|
|
284
|
+
scope-drift tripwire was permanently inert in every non-origin repo and never said so.
|
|
285
|
+
`vault-mirror/namespace` `deriveRepo()` degraded silently to `basename(cwd)` and wrote that as
|
|
286
|
+
repo **identity** into the vault — which then happened for real this session. And
|
|
287
|
+
`harness-audit` category 6 was an active fail-open: outside a git repository it awarded **2/2
|
|
288
|
+
with "no github mirror remote configured — skipped"**; a non-measurable query now fails 0/2,
|
|
289
|
+
full marks only for genuine absence. Census correction: **14** executable origin hardcodings,
|
|
290
|
+
not the 4 the issue named.
|
|
291
|
+
- **The scope guard shipped the instructions for its own disarmament (#1057).** The
|
|
292
|
+
stale-manifest branch ended on ``remove it with `rm -f <path>` and let the coordinator write a
|
|
293
|
+
fresh manifest`` — and that string travels through `emitDeny()` in `permissionDecisionReason`,
|
|
294
|
+
i.e. into the context of the very agent whose edit was just denied. Reproduced end to end from
|
|
295
|
+
the coordinator: `rm -f` is **allowed** by `pre-bash-destructive-guard.mjs` (14 rules, only
|
|
296
|
+
`rm-rf-destructive` bites); with the manifest removed, an in-project `Edit` is ALLOW **and** a
|
|
297
|
+
`Write` to `/etc/` is ALLOW — not only gate 7 (allowedPaths) falls but gate 6 (containment),
|
|
298
|
+
because under the exit-0 protocol the hook then emits no decision at all and exit 0 is not a
|
|
299
|
+
veto. The guard was off for the rest of the session. Not theoretical: a **parallel** session in
|
|
300
|
+
the same working copy received this suggestion for the **live** `wave-scope.json` of this one,
|
|
301
|
+
and declined only on an indicator (mtime 7 minutes old), not on knowledge — at one hour of age
|
|
302
|
+
the same indicator points the other way. The new text names both cases instead of guessing one
|
|
303
|
+
and routes to `blocked` per PSA-001/003/007. `systemMessage` was examined and deliberately not
|
|
304
|
+
used: `io.mjs:329` says the operator sees that field, `hook-development/SKILL.md:108` says it
|
|
305
|
+
is "shown to Claude" — a session-wide guard disarmament does not belong on a channel whose
|
|
306
|
+
reader the repo cannot name consistently.
|
|
307
|
+
- **The owner-leakage scanner was blind to `.html` (#1076) — and its first sighted run found a
|
|
308
|
+
real defect.** The naive fix would have excluded **nothing**, because the sanctioned form
|
|
309
|
+
carries no `www.` while the site uses `www.` throughout; the result would have been a
|
|
310
|
+
permanently red gate including pre-commit. Then the eighth planted defect: seven forms had been
|
|
311
|
+
driven against the new coverage and all seven bit, but all seven sat in the CP1 corridor, and
|
|
312
|
+
CP1 is the **only** one of the eleven rules that runs through `canonicalizeLine()` — revived,
|
|
313
|
+
in other words, for 1 of 11. The eighth is an entity-encoded dot in the **domain**, which a
|
|
314
|
+
browser resolves and the scanner did not. CP2/CP3/CP7/CP8 now additionally check the canonical
|
|
315
|
+
form, and the discriminator is measured rather than guessed: dot-anchored rules gain
|
|
316
|
+
(canonicalisation can never fabricate a dot from a separator) while slash- and slug-anchored
|
|
317
|
+
rules stay raw (canon folds `-` onto `/`, which flips CP4/CP10 false→true and shreds CP6).
|
|
318
|
+
`isAllowlisted()` on the canonical form would have been **wrong** — canon breaks its own
|
|
319
|
+
allowlist — so the design uses an occurrence counter instead: if the canonical form carries
|
|
320
|
+
more domain tokens than the raw one, the surplus came from a decoding and can never be the
|
|
321
|
+
sanctioned publication. Cost repo-wide: 0 new findings.
|
|
322
|
+
- **Admitting `.html` to `check-unicode-safety.mjs` immediately found a live legal defect.**
|
|
323
|
+
`site/datenschutz/index.html:277` carried a `U+00AD` at end of line. A soft hyphen does **not**
|
|
324
|
+
suppress the break — HTML collapses newline plus indentation into a space — so the page
|
|
325
|
+
rendered "Auftragsverarbeitungs vereinbarung", visibly broken, on a legally required page.
|
|
326
|
+
Deleting the `U+00AD` alone would not have fixed it; the space would have remained.
|
|
327
|
+
- **Six findings from the review panel against the site, the most important a false promise
|
|
328
|
+
(#1043/#1044/#1046).** The privacy policy described a network request that does not exist — a
|
|
329
|
+
reload of `leaderboard.json` including `credentials: 'omit'` — while a grep for
|
|
330
|
+
`fetch(|XMLHttpRequest|sendBeacon|WebSocket|EventSource|import(|new Image` across all four
|
|
331
|
+
pages returns 0 hits. The direction is harmless (over- rather than under-declared); the place
|
|
332
|
+
is not, because §10 expressly invites the reader to open the network tab and check. It was the
|
|
333
|
+
single statement on the page a reader could falsify in two minutes — on a page whose thesis is
|
|
334
|
+
checkability. Struck without replacement. The other five: a `TODO` placeholder shipped live in
|
|
335
|
+
public source (`index.html:665`), a `/guide` canonical that contradicted itself three ways,
|
|
336
|
+
missing og/twitter tags on all three subpages (a shared guide link rendered as a bare URL in
|
|
337
|
+
Slack, X and Discord — for a product distributed through shared links, the one classically
|
|
338
|
+
SEO-adjacent item with real return), `offers` struck from the JSON-LD, and an unquantified
|
|
339
|
+
absolute ("Every issue an outside user has ever opened" → "All four issues opened by people
|
|
340
|
+
outside it") two paragraphs above where the number already stood correctly.
|
|
341
|
+
- **A character allowlist in the census generator, and a `ReferenceError` on the path that
|
|
342
|
+
enforces it.** `rewrite()` checked `/[<>]/` against the **old** cell content, never the new
|
|
343
|
+
value. Eleven of the thirteen metrics are digits or hex by construction, but `version` is
|
|
344
|
+
whatever `package.json` says and `readPackageVersion` only checks "non-empty string" — a
|
|
345
|
+
prepared version literal could have closed the `<span>` and opened a tag. The precondition is
|
|
346
|
+
write access to `package.json`, which in this repo's trust model already means full access, so
|
|
347
|
+
this is defence in depth rather than an open hole — but it is the one place where `script-src
|
|
348
|
+
'unsafe-inline'` would stop being theoretical. A rejection now refuses the **whole** file, not
|
|
349
|
+
just the value, and the allowlist hangs on the write rather than on HTML-span presence (before,
|
|
350
|
+
`"rules": "../../etc/passwd"` landed unchecked in the shipped file). Building it caught a defect
|
|
351
|
+
of its own: the error path called a `writeStderrLine` that never existed in that file — a
|
|
352
|
+
`ReferenceError` exactly where the guard bites. Error paths have to be executed, not read.
|
|
353
|
+
- **Accessibility: one real AA violation, and controls at 1.71:1.** `/guide` had no live region —
|
|
354
|
+
the copy button only changed its visible text, so a screen-reader user got **nothing** on
|
|
355
|
+
success and nothing on the error path, where "Press Ctrl+C" is the only way forward (WCAG
|
|
356
|
+
4.1.3). Seven copy buttons shared one name and were indistinguishable in a screen reader's
|
|
357
|
+
element list, the one place they are read outside their context; names are now derived from the
|
|
358
|
+
nearest label in **document order** plus the first non-comment line, measured 7 of 7 unique
|
|
359
|
+
against 1 of 7 before. `button.copy` and `a.btn-2` sat at 1.71:1, under the 3:1 a control
|
|
360
|
+
boundary needs (WCAG 1.4.11); raising `--line-2` globally would have been a design change, since
|
|
361
|
+
the same token draws decorative hairlines where 3:1 is loud, so a separate `--line-ui` token
|
|
362
|
+
carries the purpose: measured 3.40:1 / 3.57:1 / 3.57:1 on the rendered element. Two comments
|
|
363
|
+
that claimed too much were corrected — the print comment said "only the two moving 1px lines are
|
|
364
|
+
dropped" while the rule below hides five selectors including the whole six-link navigation.
|
|
365
|
+
- **Page and receipt came from two different measurements.** `site/index.html` said
|
|
366
|
+
252/135/`6fa214d`, `site/_census.json` said 253/140/`6f6bf58` — both tracked, both publicly
|
|
367
|
+
shipped, `--check` reporting 3 drifted. The earlier instance had the same shape and a
|
|
368
|
+
coordinator cause: a `--write` step run against a file still inside a live agent's file scope —
|
|
369
|
+
the PSA-002 scope collision this repo built `--assert-disjoint` against, one level above the
|
|
370
|
+
wave mechanics. The new coupling test compares the two **committed** artefacts against each
|
|
371
|
+
other rather than against the repo, so "both stale together" is green and only a divergence is
|
|
372
|
+
red; it asserts on drift **or** stale, because `counted-sha` is `provenance: true` and a
|
|
373
|
+
drift-only assertion would have been green against exactly the state it forbids.
|
|
374
|
+
- **The mirror push had a fourth state, and it was fail-open.** Today's own fix distinguished
|
|
375
|
+
three (no remote / push ok / push failed). Outside a git repository, `git remote get-url github`
|
|
376
|
+
fails with `fatal: not a git repository` — by exit code indistinguishable from "no such remote" —
|
|
377
|
+
so the block reported `no 'github' remote configured — skipping (not an error)` on stdout and
|
|
378
|
+
exited **0**: a broken environment reported as a healthy one, inside the fix that was closing
|
|
379
|
+
fail-open. Found by a reviewer explicitly briefed to **refute** the measurements; not by the
|
|
380
|
+
author, not by the test, not by the gate. Fixed with a `git rev-parse --git-dir` guard, all four
|
|
381
|
+
states executed individually in throwaway repos, `bash -n` **and** `sh -n` green (the bash-3.2
|
|
382
|
+
trap from this repo's own rule file).
|
|
383
|
+
- **CI had been red for six consecutive pipelines (#1081).**
|
|
384
|
+
`tests/scripts/site-numbers.test.mjs` calls `collect(REPO_ROOT)` and requires `missing === []`;
|
|
385
|
+
two of the 13 metrics read `.orchestrator/metrics/*.jsonl`, untracked per `.gitignore:40`.
|
|
386
|
+
Locally 23/23, on CI `23 tests | 23 skipped`, because a throwing top-level `beforeAll` takes the
|
|
387
|
+
whole file with it in Vitest — the blast radius was 23, not 9. Measured now: #7276, #7277,
|
|
388
|
+
#7280, #7281, #7286 and #7288 red, green again from #7334 (`glab ci list --per-page 20`).
|
|
389
|
+
- **The new pre-push gate produced three defects of its own before it worked**, each measured
|
|
390
|
+
rather than reasoned. (1) `git clone <path>` points the clone's origin at a **filesystem path**
|
|
391
|
+
while CI's checkout points at the real remote URL, so any code that *parses* that URL behaves
|
|
392
|
+
differently in the two trees — the opposite of the hook's purpose: `deriveRepo()` returned
|
|
393
|
+
`unknown-repo` where working tree and CI both return `session-orchestrator`, and
|
|
394
|
+
`namespace.test.mjs:199` asserts exactly against that. The real origin URL is now carried into
|
|
395
|
+
the clone. (2) A repo-pointing git environment beats both `-C <path>` and cwd, so
|
|
396
|
+
`git -C "$tree" checkout --detach` ran against the **original** repository. (3)
|
|
397
|
+
`GIT_CONFIG_PARAMETERS` propagates into every child once any `-c` was passed anywhere: pushing
|
|
398
|
+
with `git -c core.hooksPath=.husky push` carried that override into the materialised clone,
|
|
399
|
+
where every throwaway repo the gate's own test suite creates then fired the repository's
|
|
400
|
+
**real** pre-commit hooks — `test: fail, total: 0` three times in a row, green the moment the
|
|
401
|
+
`-c` was dropped. Both scrub sites are now namespace sweeps with a justified keep-set instead
|
|
402
|
+
of name lists, because the denylist has shipped with a hole **twice in a row**, each time at
|
|
403
|
+
the sibling form of the name just closed (`GIT_CONFIG_COUNT` without `GIT_CONFIG_PARAMETERS`,
|
|
404
|
+
then the exact mirror image). The source-derived census found **six of nine** unscrubbed
|
|
405
|
+
platform variables where the panel had reported three of four — including `PLUGIN_ROOT` (rung
|
|
406
|
+
**one** of `resolvePluginRoot`, above every `CLAUDE_`/`CODEX_`/`PI_` name the old list already
|
|
407
|
+
unset) and the Cursor plugin root, which is not `CURSOR_PLUGIN_ROOT` but `CURSOR_RULES_DIR`;
|
|
408
|
+
adding the guessed name would have read in review as a complete fix and closed nothing.
|
|
409
|
+
`GIT_EDITOR` and `GIT_EXEC_PATH` are kept by measurement, not hygiene: Claude Code sets
|
|
410
|
+
`GIT_EDITOR=true`, and a blind sweep drops git back to `vi` and hangs the worker.
|
|
411
|
+
- **Two silent instrument errors (#979, #1062).** `scanBacklog` read 50 of 89 open issues and
|
|
412
|
+
reported the window nowhere: `limit 50 => total 50, critical 0, high 10, stale 0` against
|
|
413
|
+
`limit 100 => total 89, critical 1, high 18, stale 5`. All three numbers wrong, and the mode
|
|
414
|
+
selector at Phase 7.5 computed with them — `staleCount 0` meant not one stale issue was visible
|
|
415
|
+
to it. The cause was the window alone, not the `priority::`/`priority:` split. Separately,
|
|
416
|
+
`memory_cleanup_at` hung on a prose instruction that failed once: `stampMemoryCleanup()` had
|
|
417
|
+
zero production callers, every reference was text asking the coordinator LLM to remember. On
|
|
418
|
+
2026-08-14 a `/memory-cleanup` ran with documented yield, the step was skipped, all three
|
|
419
|
+
session records of that day carried `memory_cleanup_at: null`, and the banner reported "last
|
|
420
|
+
cleanup 29 days ago" against the 3 days of its own notes — with **no** mechanical signal
|
|
421
|
+
anywhere: zero `orchestrator.memory.cleanup*` events in 22,887. `/memory-cleanup` now emits the
|
|
422
|
+
event and `emit-session.mjs` derives the stamp from it.
|
|
423
|
+
- **The host registry lost living sessions (#1047).** `hooks/on-stop.mjs` deleted the registry
|
|
424
|
+
entry and refreshed the lock heartbeat 12 lines further down — same `if` block, at every turn
|
|
425
|
+
end — while `heartbeat()` had had zero production callers since its birth commit. The fix is
|
|
426
|
+
atomic (deletion out **and** heartbeat wired), because either half alone would have been worse
|
|
427
|
+
than the bug: entries would survive but never age, drop out of `detectPeers` after 15 minutes
|
|
428
|
+
and be swept as "stale" after 60. Deregistration moved to `hooks/on-session-end.mjs`. The
|
|
429
|
+
platform census in the comment was wrong at first and corrected by review — pi does deregister,
|
|
430
|
+
Cursor never registers and so cannot leak; Codex is the only affected platform.
|
|
431
|
+
- **`gh repo view -R` broke the GitHub CI banner for every external user of this plugin
|
|
432
|
+
(#1022).** `gh repo view` takes the repository as a **positional** argument and has no
|
|
433
|
+
`-R`/`--repo` flag at all, so the host-pinning added in #872 made `gh` exit 1 with
|
|
434
|
+
`unknown shorthand flag: 'R'` — an error `checkCiStatus`'s outer catch swallowed to
|
|
435
|
+
`null`, leaving the session-start Phase 4 banner silently dead on every GitHub repo. Fixed
|
|
436
|
+
positionally, and the swallow narrowed: a CLI that is *present* but fails now reports
|
|
437
|
+
`degraded` instead of `null`. The asymmetry across the three neighbouring call sites is
|
|
438
|
+
real and must not be unified — `glab repo view` **does** take `-R`, and `gh api`/`glab
|
|
439
|
+
api` take neither `-R` nor a positional, only `--hostname`.
|
|
440
|
+
- **`hooks/on-stop.mjs` printed a 10-frame stack trace at every turn end when `node_modules`
|
|
441
|
+
was absent** (interrupted install, EPERM sandbox, half-synced plugin cache): a static
|
|
442
|
+
`import { $ } from 'zx'` fails at module-load time, with no hint that `npm install` is the
|
|
443
|
+
fix. `zx` is now imported lazily and the failure degrades to one rate-limited stderr line
|
|
444
|
+
per 6h window, mirroring the missing-`node` degradation in `hooks/run-node.sh`.
|
|
445
|
+
(GH Kanevry/session-orchestrator#63.)
|
|
446
|
+
- **A board row inherited one legacy entry's terminal status to every same-named repo,
|
|
447
|
+
permanently (#871).** `_active-sessions.md` rows were keyed by `path.basename(repoRoot)`,
|
|
448
|
+
so two repos with the same directory name under different parents were one row — and both
|
|
449
|
+
are enumerable, since the dispatcher walks to depth 2. Rows are now keyed by a path-derived
|
|
450
|
+
hash, case-folded on APFS/NTFS so `…/Some-Repo` and `…/some-repo` do not split back into
|
|
451
|
+
the duplicate rows #719 had already fixed at the name layer. The key length carries a named
|
|
452
|
+
ceiling and a revisit trigger, not an intention to revisit.
|
|
453
|
+
- **Two vault write channels published unmasked secrets (#974/#1025)** —
|
|
454
|
+
`scripts/lib/vault-status/narrative-mirror.mjs` and `scripts/lib/vault-mirror/process.mjs`,
|
|
455
|
+
both writing tracked, pushed artefacts. The masker's needle set is a function of the
|
|
456
|
+
caller's env, which is not a defect but does mean two runs over the same records mask
|
|
457
|
+
differently: a consumer comparing a written artefact against a fresh candidate must treat
|
|
458
|
+
an already-redacted span as a **wildcard** (`matchesModuloRedaction`), or a later
|
|
459
|
+
partially-populated run rewrites the raw value it had already redacted. The tempting fix —
|
|
460
|
+
persist the needle set — is rejected: it puts a plaintext secrets file on disk to defend
|
|
461
|
+
against secrets on disk. Masking runs **after** `extractNarrative`, never before, because
|
|
462
|
+
masking the raw string first lets `[REDACTED]` land inside the structure and silently
|
|
463
|
+
delete a whole table from the mirrored file.
|
|
464
|
+
- **`agents/*.md` frontmatter is parsed, not pattern-matched.** `check-agents.mjs` gained the
|
|
465
|
+
same `js-yaml` `CORE_SCHEMA` rule, reported alone with no fall-through to the field rules —
|
|
466
|
+
a field rule evaluated on an unparseable block is a guess. Its existing block-scalar ban
|
|
467
|
+
stays: that rule forbids one particular *valid* YAML form, which is a different question
|
|
468
|
+
from whether the block is YAML at all.
|
|
469
|
+
- **Deleted `scripts/lib/mission-status-schema.mjs`** — 4 exports, zero production callers,
|
|
470
|
+
while three prose locations promised its application. Found by S3 above, on the day S3 was
|
|
471
|
+
written.
|
|
472
|
+
|
|
473
|
+
### Notes
|
|
474
|
+
|
|
475
|
+
- **3.18.0 is missing from npm.** The tag `v3.18.0`, the GitHub release and the CHANGELOG entry
|
|
476
|
+
all exist; the registry has never seen the version — `npm view session-orchestrator versions`
|
|
477
|
+
returns `…, "3.16.0", "3.17.0", "3.19.0", "3.20.0"`. That gap is the incident #978 names and
|
|
478
|
+
the reason `scripts/release.mjs --publish` now creates the annotated tag only **after** a
|
|
479
|
+
registry-verified publish. Nothing in this line republishes it; the version number stays
|
|
480
|
+
skipped rather than reused.
|
|
481
|
+
- **This session damaged its own repository, and the first recovery missed half the damage.** A
|
|
482
|
+
coordinator diagnostic command exported `GIT_DIR` and ran the suite. In the real `.git`: HEAD
|
|
483
|
+
detached, three fixture commits, plus a foreign remote and the fixture identity in
|
|
484
|
+
`.git/config` — which then authored two commits. The first recovery checked HEAD, the index and
|
|
485
|
+
all 1614 files and **overlooked `.git/config` entirely**, because `git status` does not show it;
|
|
486
|
+
an agent measuring something else found it two hours later. History rewriting failed on both
|
|
487
|
+
remotes' branch protection (GitLab "not allowed to force push", GitHub GH006), so authorship
|
|
488
|
+
was corrected via `.mailmap` — which incidentally refutes part of this session's own #1079
|
|
489
|
+
finding: `enforce_admins: false` does **not** make the protection bypassable for force-push.
|
|
490
|
+
One correction to the record: the claim "git runs every hook with `GIT_DIR` set", written in
|
|
491
|
+
`334ea2d`, is **false** — measured on git 2.x / macOS, `pre-push`, `pre-commit`, `commit-msg`
|
|
492
|
+
and `post-commit` all run with `GIT_DIR` unset. The scrub remains right as defence in depth;
|
|
493
|
+
its stated reason was not.
|
|
494
|
+
- The sharpest instances of the frontmatter class are the ones where the check was green. A
|
|
495
|
+
hand-rolled `getDescription` regex in
|
|
496
|
+
`tests/lib/validate/skill-description-quality.test.mjs` terminated on the first folded line,
|
|
497
|
+
measuring **97 characters where the real YAML value is 329** (`session-start`; `autopilot`
|
|
498
|
+
107 vs 555, `bootstrap` 98 vs 341) — which made its own `>= 250` assertion **vacuous** for
|
|
499
|
+
every skill already using the block-scalar form. It surfaced only because repairing the 12
|
|
500
|
+
unparseable blocks moved files across the threshold. `yaml.load` is now the single reader.
|
|
501
|
+
- **Found and deliberately not fixed here**, because both sit outside this line's scope and
|
|
502
|
+
both are the same shape as the bugs above: `agents/eval-judge.md` is **valid** YAML and
|
|
503
|
+
loses **96%** of its description — 1180 raw characters, 51 visible to a parser — because
|
|
504
|
+
` #803` opens a YAML comment at column 51, so no parse rule can catch it. And
|
|
505
|
+
`skills/discovery/probes-arch.md:39` calls
|
|
506
|
+
`npx madge --circular --extensions ts,tsx,js,jsx src/` in an all-`.mjs` repo that has no
|
|
507
|
+
`src/` directory: it processes zero files and reports success.
|
|
508
|
+
- **Open, deliberately.** `site/llms-full.txt` still carries its numbers by hand — the generator
|
|
509
|
+
only touches HTML. They were corrected and cross-checked against the generator, but that is
|
|
510
|
+
exactly the defect this line fixes. Revisit trigger: as soon as a second text file carries
|
|
511
|
+
measured values. And the `scanBacklog` fix has no test of its own; it is covered only by the
|
|
512
|
+
171 existing tests of the touched modules staying green, with the fake regression outstanding.
|
|
513
|
+
- One reported number did not survive review and is recorded here rather than quietly dropped:
|
|
514
|
+
"274 elements checked" for the site's contrast sweep is **not reproducible** — two independent
|
|
515
|
+
checkers measure 260 / 282 / 288 depending on an inclusion rule that was never shipped with the
|
|
516
|
+
number. The **value** 4.81:1 (worst text contrast) is stable across all three counting rules;
|
|
517
|
+
the count is not. A count without its rule is worthless — the same lesson #979 taught once
|
|
518
|
+
already in this line.
|
|
519
|
+
- Gate at `8984224`, re-run for this entry rather than quoted: **587 test files, 14,244 passed
|
|
520
|
+
/ 0 failed / 11 skipped** (`npm test`, exit 0), typecheck **404 files OK** (exit 0),
|
|
521
|
+
owner-leakage **1548 files / 0 findings**, unicode-safety 0 findings. The release commit also
|
|
522
|
+
reports `validate-plugin` 172/0 and lint 0 at that sha; both are quoted from it, not
|
|
523
|
+
re-measured here.
|
|
524
|
+
|
|
10
525
|
## [3.20.0] - 2026-08-13
|
|
11
526
|
|
|
12
527
|
Memory-pipeline line. The learning store had been accumulating for 233 sessions and delivering
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Session Orchestrator
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
|
-
[](CHANGELOG.md)
|
|
5
5
|
[](https://www.npmjs.com/package/session-orchestrator)
|
|
6
6
|
[](docs/telemetry/telemetry-claims.md)
|
|
7
7
|
|
|
@@ -23,10 +23,14 @@ The same skills and commands run across all four, with platform-adapted hooks an
|
|
|
23
23
|
For Claude Code, also install Node dependencies **once** (hooks import `zx`) and restart Claude Code:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
# Claude Code has no `plugin dir` subcommand, so resolve the install path from the cache.
|
|
27
|
+
SO_DIR="$(dirname "$(find ~/.claude/plugins/cache -path '*session-orchestrator*' -name package.json 2>/dev/null | head -1)")"
|
|
28
|
+
cd "$SO_DIR" && npm install
|
|
28
29
|
```
|
|
29
30
|
|
|
31
|
+
If `SO_DIR` comes back empty, the plugin is not installed from a marketplace — check
|
|
32
|
+
`/plugin list` inside Claude Code first.
|
|
33
|
+
|
|
30
34
|
Setup guides: [Codex](docs/codex-setup.md) · [Cursor IDE](docs/cursor-setup.md) · [Pi](docs/pi-setup.md). Per-IDE notes on `CLAUDE.md` vs `AGENTS.md`: [instruction-file-resolution](skills/_shared/instruction-file-resolution.md).
|
|
31
35
|
|
|
32
36
|
## What makes it different
|
|
@@ -127,17 +131,18 @@ The system is markdown-driven config plus a thin Node runtime — skills, comman
|
|
|
127
131
|
- **Cross-session learning is opt-in and inspectable.** Every session writes a record; after 5+ sessions `/evolve analyze` extracts confidence-scored patterns you can read and prune. Nothing is hidden.
|
|
128
132
|
- **VCS dual support, no lock-in.** Auto-detects GitLab or GitHub from your remote and drives the full lifecycle for both.
|
|
129
133
|
|
|
130
|
-
## Recent highlights (v3.
|
|
134
|
+
## Recent highlights (v3.22.0)
|
|
131
135
|
|
|
132
|
-
Every release is additive and backward-compatible. Highlights of the v3.
|
|
136
|
+
Every release is additive and backward-compatible. Highlights of the v3.22.0 line:
|
|
133
137
|
|
|
134
|
-
- **
|
|
135
|
-
- **
|
|
136
|
-
- **
|
|
137
|
-
- **
|
|
138
|
-
- **The
|
|
138
|
+
- **The warning that fired on 99.0% of all session starts (#1089)** — measured over **1,477 session-start events across 18 repos**, against 4,884 stop events with zero OOM markers. Three independent measurement errors, not one threshold among them: `os.freemem()` on Darwin reports only `Pages free` (median **0.4 GB** on hosts with 24–128 GB installed), a threshold named for *sessions* was compared against a *process* count (measured ratio **6.0:1** — same number, right denominator: 93.6% → **4.2%**), and one noisy axis could cap a wave alone. The verdict had reached the ledger for **15 of 1,734 sessions (0.9%)**, which is why the false alarm survived four months while six repos independently wrote it into their learnings store — one at confidence **1.0**. A warning that fires on almost everything changes nothing except how fast you learn to ignore it.
|
|
139
|
+
- **The questions this tool asks its operator had never been measured (#1107)** — `scripts/auq-audit.mjs` now scores every choice block against eight criteria and two hard hurdles, all thresholds from one registry. The census found **three populations where every prior count had seen one**: 40 Claude Code blocks, 10 Codex prose lists, and 17 nobody had counted. Baseline **21 of 72 (29%)**, after the pass **72 of 72**. The dominant cause was structural rather than editorial — `header` caps at 12 codepoints and truncates silently, **26 of 42 exceeded it**, and one NFD-composed header measured 12 visible characters as 14 and destroyed its own question.
|
|
140
|
+
- **A probe that sized the wrong set and never asked git** — `checkStaleArtifacts` announced "37 files (11 MB)" while computing the megabytes over the entire directory instead of the 37 it names; they weigh **0.68 MB**, a factor of ~18. Separately it proposed pruning seven **tracked, runtime-read** source files, including the policy file a hook reads on every Bash call. The git exclusion is fail-**silent**, not fail-open: falling back to "nothing is tracked" *is* the defect it closes.
|
|
141
|
+
- **Only the raw session ID releases a lock (#1085)** — one session carried three identities (STATE.md, the lock, and issue-budget each held a different one) and `findPeers` reported the session's own presence as a foreign peer. The companion issue-budget fix removed a data-loss path where an identity-less invocation erased a live session's parked overflow.
|
|
142
|
+
- **The npm receipt is the boundary, and a `throw` lay behind it (#1088)** — `publish()` threw *after* a successful publish during a 12-second propagation poll: published, not tagged, not pushed, reported as "failed, retryable". Each post-receipt step now gets exactly one attempt and the rest is collected as reconciliation. Leak detection decides on an extracted path, not a regex over `npm notice` prose — `contest` is not `tests`.
|
|
143
|
+
- **Six waves ran with zero scope injection, and it looked exactly like a clean run (#1083)** — the scope declaration has two shapes; a coordinator that writes only the aggregate one degrades the chain to signal-free ALLOW. `scripts/materialize-wave-scope.mjs` is now the canonical writer of both.
|
|
139
144
|
|
|
140
|
-
Previous line (v3.
|
|
145
|
+
Previous line (v3.21.0): guard mechanics, the public site, and cross-session messaging — which turned out to be one strand: a fact maintained in two places, and a guard that is green without biting.
|
|
141
146
|
|
|
142
147
|
Full version history: [CHANGELOG.md](CHANGELOG.md).
|
|
143
148
|
|
package/agents/analyst.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: analyst
|
|
3
|
-
description: Use this agent for read-only PRD-quality review. Checks acceptance-criteria specificity, scope drift detection, and completeness of /plan output. <example>Context: /plan feature produced a PRD. user: "Review the PRD before /go." assistant: "I'll dispatch analyst to check acceptance-criteria specificity and scope drift before wave execution." <commentary>Analyst catches vague acceptance criteria before they cause carryover at session end.</commentary></example>
|
|
3
|
+
description: 'Use this agent for read-only PRD-quality review. Checks acceptance-criteria specificity, scope drift detection, and completeness of /plan output. <example>Context: /plan feature produced a PRD. user: "Review the PRD before /go." assistant: "I''ll dispatch analyst to check acceptance-criteria specificity and scope drift before wave execution." <commentary>Analyst catches vague acceptance criteria before they cause carryover at session end.</commentary></example>'
|
|
4
4
|
model: inherit
|
|
5
5
|
color: yellow
|
|
6
6
|
tools: Read, Grep, Glob, Bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: architect-reviewer
|
|
3
|
-
description: Use this agent for read-only architectural audits between waves. Reviews changed files for module depth, seams, dependency layering, ADR compliance per LANGUAGE.md vocabulary. <example>Context: After Impl-Core wave shipped 8 files. user: "Audit the W2 architecture before proceeding." assistant: "I'll dispatch architect-reviewer to check module depth, seams, and adapter quality before W3." <commentary>Architect-reviewer catches design smells (shallow modules, speculative seams) earlier than Quality-Lite, which only catches lint/typecheck.</commentary></example>
|
|
3
|
+
description: 'Use this agent for read-only architectural audits between waves. Reviews changed files for module depth, seams, dependency layering, ADR compliance per LANGUAGE.md vocabulary. <example>Context: After Impl-Core wave shipped 8 files. user: "Audit the W2 architecture before proceeding." assistant: "I''ll dispatch architect-reviewer to check module depth, seams, and adapter quality before W3." <commentary>Architect-reviewer catches design smells (shallow modules, speculative seams) earlier than Quality-Lite, which only catches lint/typecheck.</commentary></example>'
|
|
4
4
|
model: inherit
|
|
5
5
|
color: blue
|
|
6
6
|
tools: Read, Grep, Glob, Bash
|