session-orchestrator 3.23.0 → 3.24.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/skills/remote-offload/SKILL.md +13 -0
- package/CHANGELOG.md +278 -0
- package/README.md +16 -14
- package/agents/db-specialist.md +0 -1
- package/docs/ci-setup.md +180 -25
- package/docs/codex-setup.md +1 -1
- package/docs/components.md +3 -3
- package/docs/events-schema.md +46 -8
- package/docs/scope-collision-guard.md +4 -4
- package/docs/session-config-reference.md +64 -6
- package/docs/session-config-template.md +34 -4
- package/docs/telemetry/telemetry-claims.md +11 -10
- package/docs/telemetry.md +30 -1
- package/hooks/_lib/atomic-json.mjs +111 -0
- package/hooks/_lib/subagent-paths.mjs +143 -0
- package/hooks/cwd-change-restore.mjs +9 -29
- package/hooks/enforce-scope.mjs +35 -6
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/on-session-end.mjs +278 -12
- package/hooks/on-session-start.mjs +50 -2
- package/hooks/on-stop.mjs +349 -20
- package/hooks/post-bash-write-verify.mjs +104 -4
- package/hooks/post-subagent-discovery-validator.mjs +148 -18
- package/hooks/post-tool-batch-wave-signal.mjs +154 -40
- package/hooks/post-tool-failure-corrective-context.mjs +9 -32
- package/hooks/pre-bash-memory-propose-audit.mjs +13 -7
- package/hooks/subagent-telemetry.mjs +11 -26
- package/package.json +1 -1
- package/scripts/autopilot.mjs +26 -12
- package/scripts/backfill-abandoned-sessions.mjs +80 -11
- package/scripts/emit-event.mjs +10 -2
- package/scripts/lib/auq/parse.mjs +5 -29
- package/scripts/lib/auto-dialectic.mjs +68 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +82 -6
- package/scripts/lib/build-live-signals.mjs +25 -22
- package/scripts/lib/cold-start-detector.mjs +23 -14
- package/scripts/lib/config/block-header.mjs +55 -0
- package/scripts/lib/config/discovery-validator.mjs +7 -2
- package/scripts/lib/config/health-endpoints.mjs +383 -0
- package/scripts/lib/config/remote-hosts.mjs +233 -0
- package/scripts/lib/config.mjs +31 -3
- package/scripts/lib/dispatcher/enumerate.mjs +2 -17
- package/scripts/lib/events-schema.mjs +48 -0
- package/scripts/lib/events.mjs +238 -5
- package/scripts/lib/evolve/autonomy-verdict.mjs +9 -4
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +18 -1
- package/scripts/lib/gitlab-portfolio/cli.mjs +3 -15
- package/scripts/lib/harness-audit/categories/category1.mjs +17 -6
- package/scripts/lib/memory-banner.mjs +20 -8
- package/scripts/lib/peer-discovery.mjs +20 -2
- package/scripts/lib/reconcile/engine.mjs +236 -5
- package/scripts/lib/scope-gate.mjs +36 -0
- package/scripts/lib/session-close-backfill.mjs +59 -10
- package/scripts/lib/session-discovery.mjs +57 -3
- package/scripts/lib/session-end/phase-skip.mjs +2 -2
- package/scripts/lib/session-identity/own-session.mjs +62 -1
- package/scripts/lib/session-transition.mjs +1 -1
- package/scripts/lib/sessions-canonical.mjs +446 -0
- package/scripts/lib/telemetry/schema.mjs +74 -8
- package/scripts/lib/telemetry/sync.mjs +49 -12
- package/scripts/lib/tmux-layout/telemetry.mjs +14 -2
- package/scripts/lib/validate/check-doc-cli-commands.mjs +9 -33
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +370 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +436 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +10 -0
- package/scripts/lib/validate/check-unwired-features.mjs +0 -7
- package/scripts/lib/validate/check-validator-registration.mjs +248 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +6 -28
- package/scripts/lib/validate/markdown-fences.mjs +196 -0
- package/scripts/lib/vault-status/board-lock.mjs +185 -0
- package/scripts/lib/vault-status/board-writer.mjs +174 -135
- package/scripts/lib/vault-status/narrative-mirror.mjs +2 -19
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +2 -2
- package/scripts/lib/wave-executor/remote-dispatch.mjs +504 -0
- package/scripts/lib/wave-resource-gate.mjs +127 -7
- package/scripts/lib/wave-transcript-tail.mjs +24 -4
- package/scripts/materialize-wave-scope.mjs +20 -4
- package/scripts/memory-propose.mjs +132 -8
- package/scripts/promote-vault-strict.mjs +4 -15
- package/scripts/site-numbers.mjs +36 -4
- package/scripts/validate-plugin.mjs +26 -0
- package/scripts/vault-consolidate.mjs +3 -11
- package/scripts/vault-integration-watcher.mjs +2 -4
- package/scripts/vault-mirror.mjs +111 -26
- package/skills/_shared/parallel-aware-auq.md +31 -2
- package/skills/_shared/parallel-aware-preamble.md +17 -4
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/contract-version-bump/SKILL.md +1 -1
- package/skills/ecosystem-health/SKILL.md +4 -1
- package/skills/ecosystem-health/wizard.md +5 -0
- package/skills/evolve/SKILL.md +38 -1
- package/skills/journey-audit/SKILL.md +6 -5
- package/skills/reconcile/SKILL.md +5 -2
- package/skills/remote-offload/SKILL.md +89 -0
- package/skills/session-end/phase-3-6-tail.md +9 -6
- package/skills/session-start/SKILL.md +26 -3
- package/skills/wave-executor/SKILL.md +1 -1
- package/skills/wave-executor/wave-loop.md +43 -5
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.24.0"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "session-orchestrator",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.24.0",
|
|
5
5
|
"description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bernhard Goetzendorfer",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "session-orchestrator",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0+codex.20260902192928",
|
|
4
4
|
"description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"session",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remote-offload
|
|
3
|
+
description: Use when local resource pressure would shrink or coordinator-direct a wave, a wave plan carries heavy build/test/audit roles (test, ui, perf), or the operator says offload, remote host, or auslagern — reference for routing that wave role to a declared SSH-reachable host instead of reducing agent count
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# remote-offload
|
|
8
|
+
|
|
9
|
+
Canonical skill: `skills/remote-offload/SKILL.md`
|
|
10
|
+
|
|
11
|
+
Read that file and follow it exactly. Resolve relative links against `skills/remote-offload/`, not this wrapper.
|
|
12
|
+
|
|
13
|
+
Cursor has no Skill tool. Treat "invoke the remote-offload skill" as: Read `skills/remote-offload/SKILL.md`.
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,284 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.24.0] - 2026-09-02
|
|
11
|
+
|
|
12
|
+
Five commits since v3.23.0 (4 `feat`, 1 `docs`; 152 files, +14,596/−942), no
|
|
13
|
+
`BREAKING CHANGE:` footer and no `!` subject.
|
|
14
|
+
|
|
15
|
+
One `docs` commit (`c3ab4801`) adds ADR-0013, naming the ownership-check-before-deregister
|
|
16
|
+
ordering that closed Wave 4 of the previous session. One `feat` commit (`a019d5a4`, deep
|
|
17
|
+
session, 5 waves, 37 agents + 3 reviewers + 3 panel) closes the GH#67 peer-filter class
|
|
18
|
+
across every affected site, plus #1170, #1176, #1177, #1166, #1167, #1174, #1180 and #1175.
|
|
19
|
+
One `feat` commit (`936dae8a`) closes a Fleet-Mining v2 instrument audit across 18 repos
|
|
20
|
+
(1,587 learnings, 144k events, 347 telemetry records) with five repairs to the fleet's own
|
|
21
|
+
measurement instruments (#1189, #1190, #1191, #1192, #1193).
|
|
22
|
+
|
|
23
|
+
Two further `feat` commits (`2ccea0f2`, `3b352d78`) are Waves 2 and 3 of a new session (11
|
|
24
|
+
agents each; Opus for guards/identity/protocol, Sonnet for consolidation/polish). The
|
|
25
|
+
through-line: identity and scope boundaries are now drawn at the PROCESS, not the working
|
|
26
|
+
copy — a peer session's manifest in the same checkout is `foreign`, a dispatched subagent's
|
|
27
|
+
raw session id belongs to its coordinator, and four duplicated helper implementations
|
|
28
|
+
collapse to one canonical each.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **`orchestrator.reconcile.completed` (#1192).** Wrapper around `runReconcile` covering all
|
|
33
|
+
three return paths (empty corpus, normal, engine-error) with `trigger`
|
|
34
|
+
(`skill|session-end|phase-skip|unknown`), `targets` (allowlisted `repo-local|baseline`),
|
|
35
|
+
counters including measured zeros, `duration_ms`, and `aborted`+`reason` on the catch path.
|
|
36
|
+
Skipped with a stderr WARN when no `repoRoot` is passed, so test runs never write the fleet
|
|
37
|
+
ledger. First live record: `eligible 102 / proposals 10 / rejected 154 / capped 72 /
|
|
38
|
+
already_materialized 20`.
|
|
39
|
+
- **`docs/audits/2026-09-02-fleet-instruments.md`** — the public scrubbed extract of the
|
|
40
|
+
Fleet-Mining v2 instrument audit: 18 repos, 1,587 learnings, 143,967 events, 347 telemetry
|
|
41
|
+
records (6 `anon_id`), 13 instrument verdicts and 12 orchestrator candidates (K1–K12).
|
|
42
|
+
- **A new blocking validator catches dead script paths cited in skill/command/agent prose
|
|
43
|
+
(#1176).** `scripts/lib/validate/check-skill-script-paths.mjs` scans `skills/`, `commands/`
|
|
44
|
+
and `agents/` for `scripts/**.mjs` citations that neither exist nor carry a same-line/
|
|
45
|
+
line-above `<!-- path-check: planned #<iid> | historical | example -->` marker; fenced code
|
|
46
|
+
blocks are skipped as illustrative shape, not a claim. Measured 2026-09-02 @ `c3ab480`: 237
|
|
47
|
+
distinct citations, 7 dead, 4 of the 7 inside fences.
|
|
48
|
+
- **`events.jsonl` records are now schema-versioned and validated before they are written
|
|
49
|
+
(#1177).** `emitEvent()` stamps every record `schema_version: 1` (never overwriting a
|
|
50
|
+
caller-supplied value) and runs `validateEventRecord()` BEFORE the append and BEFORE any
|
|
51
|
+
webhook POST — an invalid record throws `EventValidationError` and produces neither.
|
|
52
|
+
`scripts/emit-event.mjs` maps that to exit `1` (I/O failures keep exit `2`);
|
|
53
|
+
`scripts/lib/tmux-layout/telemetry.mjs` stamps + validates synchronously and drops invalid
|
|
54
|
+
lines. The read path stays lenient — the 33k+ pre-#1177 records without the field remain
|
|
55
|
+
valid.
|
|
56
|
+
- **A canonical reader collapses `sessions.jsonl`'s append-only duplication (#1167).**
|
|
57
|
+
`scripts/lib/sessions-canonical.mjs` applies newest-wins per `session_id`, drops any record a
|
|
58
|
+
later `supersedes` pointer refutes, and narrowly collapses the systemic double-stub class
|
|
59
|
+
(two `abandoned` records sharing an exact `started_at`/`completed_at` pair, one synthetic).
|
|
60
|
+
The startup/CLI backfill path (`scripts/backfill-abandoned-sessions.mjs`) now also emits
|
|
61
|
+
`orchestrator.session.backfill_completed` per record — previously only the SessionEnd hook
|
|
62
|
+
did — and stamps `raw_session_id`; `hooks/on-session-end.mjs`'s backfill gained a second
|
|
63
|
+
UUID→semantic bridge via the `session.ended` event for sessions that lost the
|
|
64
|
+
lock-acquire race.
|
|
65
|
+
- **A vault-scoped lock protects the shared live-status board's read-modify-write (#1180).**
|
|
66
|
+
`scripts/lib/vault-status/board-lock.mjs` wraps `sweepBoard()`'s merge in a cross-repo mutex
|
|
67
|
+
(mtime-based staleness — the vault dir can be synced cross-host, so a recorded pid isn't
|
|
68
|
+
probeable); fail-open on acquire timeout or fs-error (one stderr WARN, then runs unlocked)
|
|
69
|
+
since a board update is best-effort telemetry. `.gitignore` gained `state.lock` and
|
|
70
|
+
`rules.lock`.
|
|
71
|
+
- **A GitLab Project Access Token recipe for the schema-drift CI job (#1175).**
|
|
72
|
+
`docs/ci-setup.md` documents Option A (Project Access Token, recommended) and Option B
|
|
73
|
+
(personal PAT) for `SCHEMA_DRIFT_TOKEN`. Activation is blocked on the documented
|
|
74
|
+
vendor-ahead drift tracked in #531: the token is provisioned but the CI/CD variable is not
|
|
75
|
+
yet set upstream.
|
|
76
|
+
- **A `remote-hosts:` config block routes wave work to another machine over SSH (#1160).**
|
|
77
|
+
`scripts/lib/config/remote-hosts.mjs` adds the block (opt-in template + reference doc) and
|
|
78
|
+
an `ssh:<alias>` channel in `agent-mapping`; the wave-resource gate adds an `offload`
|
|
79
|
+
decision AFTER the HR-004 heavy-repo cap, gated on an injected readiness witness
|
|
80
|
+
(`remoteReady` map or async `probeFn`) — the gate never probes the network itself, so with
|
|
81
|
+
no witness the decision stays local. `dispatchRemote()`/`remoteDoctor()`/
|
|
82
|
+
`remoteReadyProbe()` wrap the host-side `offload` CLI (prompt travels on stdin, never argv;
|
|
83
|
+
exit codes 1–8 mapped to typed refusal reasons; an empty returned patch counts as failure,
|
|
84
|
+
not success). Emits `orchestrator.remote_dispatch.completed` on every attempt AND every
|
|
85
|
+
refusal (`ok:false`, `exit_code: null`) — the payload carries the configured alias, never a
|
|
86
|
+
hostname or IP. `skills/remote-offload/SKILL.md` documents the channel generically for any
|
|
87
|
+
consuming repo (skill count measured 48 → 49); wave-3 added the fourth wave-executor
|
|
88
|
+
dispatch branch (`ssh:<alias>` via `dispatchRemote()`) to `wave-loop.md` alongside the
|
|
89
|
+
existing three.
|
|
90
|
+
- **A Peer-Scope-Union protocol lets two coordinator sessions in the same working copy share
|
|
91
|
+
scope without racing (#1195).** `skills/_shared/parallel-aware-auq.md` documents the
|
|
92
|
+
four-step handshake plus a message template; `skills/wave-executor/wave-loop.md` §
|
|
93
|
+
Scope Manifest 3.1a adds a `peer-session-<id>` record (rolled over each wave, cleared at
|
|
94
|
+
session-end) that is included in the disjoint check but excluded from the coordinator's own
|
|
95
|
+
file-scope union; a new branch in the `parallel-sessions.md` decision tree routes to it; and
|
|
96
|
+
`hooks/post-bash-write-verify.mjs` now partitions a peer's writes from genuine scope
|
|
97
|
+
violations instead of flagging both alike.
|
|
98
|
+
- **Session-start now writes its own identity before anything can read it wrong (#1199).**
|
|
99
|
+
Phase 1.05 adds a self-report block (`session_id: null` before Phase 1.2 resolves it) so
|
|
100
|
+
downstream consumers see an explicit unresolved state instead of inferring one.
|
|
101
|
+
- **`/evolve` and the auto-dialectic nudge now emit their own completion events (#1200).**
|
|
102
|
+
`orchestrator.evolve.completed` and `dialectic.completed` are emitted via `emit-event.mjs`
|
|
103
|
+
from inside the skill; `decideAndRecordAutoDialectic()` emits `dialectic.nudge_decided`
|
|
104
|
+
mechanically rather than leaving the decision only in skill prose, consumed by
|
|
105
|
+
`phase-3-6-tail.md`.
|
|
106
|
+
|
|
107
|
+
### Changed
|
|
108
|
+
|
|
109
|
+
- **BREAKING (behavior): `discovery-validator.enabled` defaults to `false` again (#1191).**
|
|
110
|
+
Reverts the #690 flip of 2026-06-25. Fleet measurement (2026-09-02, 18 repos): 6,946
|
|
111
|
+
`discovery_validator_violation` events had accumulated in repos that never declared the
|
|
112
|
+
block, and a scope-adjusted n=60 sample put real precision at 0/60 — the hook was scanning
|
|
113
|
+
the MAIN transcript on `SubagentStop`, not the subagent's own. It now reads the subagent
|
|
114
|
+
transcript (`agent_transcript_path` when the harness sends it, else
|
|
115
|
+
`<dir>/<base>/subagents/agent-<agent_id>.jsonl` — no fallback to the main transcript), and
|
|
116
|
+
the violation event now carries `agent_id`. Repos that relied on the silent default lose the
|
|
117
|
+
signal until they set `enabled: true` explicitly.
|
|
118
|
+
- **Scope enforcement now treats every identity as process-local, including your own
|
|
119
|
+
(#1194).** `enforce-scope.mjs` reads session ids via a new `readProcessLocalSessionIds()`
|
|
120
|
+
instead of `readOwnSessionIds()` — a peer's manifest sitting in the SAME working copy now
|
|
121
|
+
classifies as `foreign`, not `own`. Two existing tests had asserted the inverted behaviour
|
|
122
|
+
and were rewritten with a named ceiling (a harness with no session id, e.g. bare
|
|
123
|
+
Codex/Cursor, resolves to `unknown` → enforce, never skip); `scope-collision-guard.md` and
|
|
124
|
+
`state-ownership.md` were updated to match.
|
|
125
|
+
- **`memory.propose`'s lock check is now a raw→semantic lookup, not a trust boundary
|
|
126
|
+
(#1188).** The lock is consulted only to translate the caller's raw id into its semantic
|
|
127
|
+
session id, authorised by a process-local match against the RAW id — measured: a dispatched
|
|
128
|
+
subagent carries the COORDINATOR's raw UUID in `CLAUDE_CODE_SESSION_ID`, never its own.
|
|
129
|
+
`sessions-canonical.mjs` and the backfill path now distinguish silent `ENOENT` from loud
|
|
130
|
+
`EACCES`/`EISDIR` in two separate ledger readers instead of treating every read failure the
|
|
131
|
+
same.
|
|
132
|
+
- **Four duplicated helper implementations collapse to one each.**
|
|
133
|
+
`scripts/lib/validate/markdown-fences.mjs` (#1181) replaces 4 line-start-only fence
|
|
134
|
+
scanners with one line-start-AND-end-anchored automaton shared by all 4 callers
|
|
135
|
+
(`validate-plugin` output byte-identical before/after, 203/0). `expandTilde` (#1182)
|
|
136
|
+
replaces 8 copies of home-directory expansion with the existing `common.mjs`
|
|
137
|
+
implementation — one of the deleted copies (`gitlab-portfolio/cli.mjs`) mis-expanded
|
|
138
|
+
`~user` (a different user's home, not the caller's). `hooks/_lib/subagent-paths.mjs`
|
|
139
|
+
(#1196) replaces 4 divergent sidecar-path derivations with one carrying the strictest rules
|
|
140
|
+
from any of them (agent-id length `{1,64}`, `'unknown'` rejected,
|
|
141
|
+
`agent_transcript_path` confined inside the transcript directory).
|
|
142
|
+
`hooks/_lib/atomic-json.mjs` (#1197) replaces 4 byte-identical copies with one whose
|
|
143
|
+
read-modify-write default only replaces a genuinely MISSING file (`ENOENT`) — an
|
|
144
|
+
unparsable or unreadable file is no longer silently overwritten with the default.
|
|
145
|
+
- **A block-header matcher gained an indent+inline-value form, additively (#1185).**
|
|
146
|
+
`matchBlockHeaderDetailed` is now shared so `health-endpoints.mjs` no longer maintains its
|
|
147
|
+
own `HEADER_RE`.
|
|
148
|
+
- **Five readers now go through the canonical `sessions.jsonl` collapse instead of counting
|
|
149
|
+
raw lines (#1186).** `telemetry/sync.mjs` and `build-live-signals.mjs` (wave 2), plus
|
|
150
|
+
`site-numbers.mjs` and `vault-mirror.mjs --kind session` (wave 3), now read
|
|
151
|
+
newest-per-`session_id`; on this repo the session count measured 289 lines → 278 sessions
|
|
152
|
+
(11 duplicate lines collapsed). Two tests pinning the old raw-line count were removed as no
|
|
153
|
+
longer testing real behaviour.
|
|
154
|
+
- **Wave-completion refusals are now named events, not silent skips (#1201).**
|
|
155
|
+
`KNOWN_TRIGGERS` plus three explicit wrapper functions (`runReconcileFromSkill`/
|
|
156
|
+
`AtSessionEnd`/`FromPhaseSkip`) replace an implicit trigger string — an unrecognised
|
|
157
|
+
trigger now throws instead of silently defaulting. `emitFinalWaveCompleted` emits
|
|
158
|
+
`orchestrator.wave.final_refused` for all six refusal paths instead of leaving them
|
|
159
|
+
unrecorded — closing the same class of instrument gap named in
|
|
160
|
+
`.claude/rules/host-resources.md` § HR-105.
|
|
161
|
+
|
|
162
|
+
### Fixed
|
|
163
|
+
|
|
164
|
+
- **`orchestrator.agent.stopped` carried `agent: ""` in 86.7% of fleet records (#1190).**
|
|
165
|
+
89,991 of 103,763 historical records carried an empty `agent` because
|
|
166
|
+
`input?.agent_type ?? 'unknown'` never fires on an empty string — the harness's actual
|
|
167
|
+
shape. `agent` is now omitted when empty (never `'unknown'`, never a fabricated `0`/`false`).
|
|
168
|
+
Seven optional sidecar-derived fields were added — `agent_id`, `transcript_found`,
|
|
169
|
+
`tool_use_id`, `agent_type_meta`, `duration_ms` + `duration_source` (`meta-birthtime`), and
|
|
170
|
+
`status` (`done|partial|blocked|failed|no-tests-needed`, from the last line-anchored
|
|
171
|
+
`STATUS:` marker in the final 64 KiB of the agent transcript) — each independently omitted
|
|
172
|
+
when unmeasured, each wrapped in its own try/catch, with a charset guard on `agent_id`
|
|
173
|
+
before path interpolation. `CURRENT_SCHEMA_VERSION` is unchanged (additive). Measured
|
|
174
|
+
`status` coverage: 61.7% (71 done / 3 partial / 46 absent) over 120 completed sidecars.
|
|
175
|
+
- **`commands[]` was empty in every usage ping (#1189).** `classifyInvocationName` routes
|
|
176
|
+
plugin-prefixed command names (`session-orchestrator:session`) into `commands[]` under the
|
|
177
|
+
bare name — the prefix is required, so a bare foreign name never becomes one of our
|
|
178
|
+
commands — and `.command`-field names are prefixed before classification, since that field
|
|
179
|
+
is itself the "this is one of ours" provenance signal a bare `.skill` arrival lacks.
|
|
180
|
+
- **The final `orchestrator.wave.completed` of every session was never emitted (#1193).**
|
|
181
|
+
Fleet-wide: 1,018 `started` vs. 722 `completed`, a gap of exactly 296 — one per wave run.
|
|
182
|
+
`hooks/on-session-end.mjs` now emits it at SessionEnd, idempotent via a
|
|
183
|
+
`last_wave_completed` high-water mark written monotonically by both emitters, gated on
|
|
184
|
+
ownership of `current-session.json` via the RAW stdin session id (not the resolved one), and
|
|
185
|
+
skipped on `reason: clear`. `hooks/post-tool-batch-wave-signal.mjs` gained the same
|
|
186
|
+
raw-stdin-id ownership check, so it can no longer write wave keys into a peer session's
|
|
187
|
+
record either.
|
|
188
|
+
- **`orchestrator.session.stopped.duration_ms` was a literal `0` in 8,127 of 8,127 fleet
|
|
189
|
+
records.** The harness never sends `start_ms`, so the old expression fell back to a
|
|
190
|
+
fabricated zero — indistinguishable from a real instant turn. It is now derived from an
|
|
191
|
+
OWNED `.orchestrator/session.lock` (`duration_source: 'session-lock'`, session-elapsed
|
|
192
|
+
measured at turn end) or omitted entirely, never `0`.
|
|
193
|
+
- **`resolveSession()`'s ownership predicate was self-fulfilling** when the stdin `session_id`
|
|
194
|
+
was absent or non-UUID: it fell back to the recorded id and then compared the fallback
|
|
195
|
+
against itself. Fixed at the root (`rawStdinId !== null && rawStdinId === recordedId`) in
|
|
196
|
+
`hooks/on-session-end.mjs` for `duration_ms`, `semantic_session_id` and the new wave-completed
|
|
197
|
+
emit, plus the sibling site `resolveSessionId()` in `hooks/on-stop.mjs`.
|
|
198
|
+
- **A live local `session.lock` no longer hides a same-working-copy registry peer (GH#67).**
|
|
199
|
+
Registry-sourced entries in `scripts/lib/session-discovery.mjs` / `peer-discovery.mjs` now
|
|
200
|
+
carry additive `registryOnly` / `lockSuperseded` / `lockOwnerId` annotations instead of
|
|
201
|
+
being filtered out — `lockSuperseded: true` is a HINT (the lock is advisory, so the entry
|
|
202
|
+
may still be a live session that lost the acquire race), never a verdict. Only
|
|
203
|
+
PROMOTION_OFFER decisions downgrade such a peer to advisory; peer-count and display
|
|
204
|
+
consumers keep it. Lock-sourced sessions carry none of the three fields, so their shape
|
|
205
|
+
stays byte-identical to pre-GH#67.
|
|
206
|
+
- **Worktree-promotion teardown is now mechanical, not skill prose (#1170).**
|
|
207
|
+
`enterWorktree()` takes an optional `rawSessionId`; when given, it calls `leaveSourceRoot()`
|
|
208
|
+
on the OLD root after the destination provably exists and records the outcome on
|
|
209
|
+
`result.left`. Before #1170, four promotion call sites relied on coordinator prose to
|
|
210
|
+
release the source root — measured zero `deregisterSelf`/`release(` call sites across the
|
|
211
|
+
affected skill docs, so the abandoned registry entry advertised a phantom peer for up to an
|
|
212
|
+
hour.
|
|
213
|
+
- **`memory.propose` filed wave-N+1 proposals into the wave-N quota bucket (#1166).**
|
|
214
|
+
`scripts/memory-propose.mjs` now resolves the RUNNING wave from `<state-dir>/wave-scope.json`
|
|
215
|
+
first (when present and unbound or bound to this session), falling back to `STATE.md
|
|
216
|
+
current-wave + 1` only when no usable manifest exists — `current-wave` itself always
|
|
217
|
+
records the JUST-COMPLETED wave.
|
|
218
|
+
- **`health-endpoints` silently dropped the wizard's own nested-block output (#1174).** The
|
|
219
|
+
key was read off the flat KV map via `_coerceList`, which bails to `null` on any `{` and
|
|
220
|
+
cannot see a nested YAML block at all. `scripts/lib/config/health-endpoints.mjs` now parses
|
|
221
|
+
it content-scoped, accepting inline object arrays, the nested block form (top-level or
|
|
222
|
+
under `ecosystem-health:`), and a bare URL list; `ecosystem-health`'s own valueless
|
|
223
|
+
block-header form is now read as a fallback when the scalar key is absent.
|
|
224
|
+
- **`emitEvent()`'s automatic session/semantic-session fill now requires a process-local
|
|
225
|
+
witness (#1177).** `CLAUDE_CODE_SESSION_ID` (or the hook-input session id) must exactly
|
|
226
|
+
equal the lock's raw `session_id` before `session_id`/`semantic_session_id` are
|
|
227
|
+
auto-filled — STATE.md is never treated as a witness. `wave` is filled the same way, only
|
|
228
|
+
from a `wave-scope.json` manifest bound to this same session, and is coerced to an integer.
|
|
229
|
+
See `docs/events-schema.md` § Correlation keys.
|
|
230
|
+
- **The memory banner's "sessions ever" stat now counts distinct sessions, not JSONL lines
|
|
231
|
+
(#1167).** `sessions.jsonl` is append-only, so one physical session can occupy two lines;
|
|
232
|
+
on this repo the stat drops from 286 lines to 275 sessions. Five readers now go through
|
|
233
|
+
the canonical collapse: cold-start detection, the memory banner, harness-audit Category 1,
|
|
234
|
+
evolve's autopilot-effectiveness/verdict, and autopilot's id-set guard.
|
|
235
|
+
- **`check-skill-script-paths` no longer goes blind past an unbalanced code fence (#1176).**
|
|
236
|
+
An unclosed/unbalanced fence now reports as its own `unbalanced-fence` finding instead of
|
|
237
|
+
silently absorbing the rest of the file as "inside a fence"; a fence opened inside a
|
|
238
|
+
blockquote is now recognised as a fence too.
|
|
239
|
+
- **`sessions-canonical.mjs`'s `supersedes` collapse now requires an attestable join key
|
|
240
|
+
(#1167).** A `supersedes` pointer on an abandoned record is honoured only when it can be
|
|
241
|
+
matched to the record it claims to supersede; rule ordering was fixed so a superseded
|
|
242
|
+
record's synthetic double-stub twin is collapsed too. A new `keepUnidentified` option lets
|
|
243
|
+
callers retain records the canonicalizer could not confidently classify instead of
|
|
244
|
+
dropping them.
|
|
245
|
+
- **`memory.propose` no longer trusts an unbound `wave-scope.json` (#1177).** A manifest
|
|
246
|
+
without a `semantic_session` binding is a peer's or a stale artefact, not this session's
|
|
247
|
+
own — it is now ignored with a stderr note instead of adopted; an unreadable/malformed
|
|
248
|
+
manifest also WARNs on stderr before falling back to `current-wave + 1`, rather than
|
|
249
|
+
failing silently.
|
|
250
|
+
- **The vault-status board lock's stale-override is now surfaced, not silent (#1180).**
|
|
251
|
+
`onLockOutcome` reports a `staleOverride` reason when the lock was force-acquired past a
|
|
252
|
+
stale holder, and `sweepBoard()`'s board event now records it too — distinguishing "the
|
|
253
|
+
mutex worked as designed" from "a slow host looked like a crash".
|
|
254
|
+
- **`check-hooks-emit-event-guard` and `check-validator-registration` both went live in
|
|
255
|
+
`validate-plugin` (#1183, #1184).** `check-hooks-emit-event-guard.mjs` (#1183) parses hooks
|
|
256
|
+
via `@babel/parser` (AST, not regex) to catch a hook that mutates state without emitting a
|
|
257
|
+
corresponding event; 5 pre-existing sites are grandfathered into an explicit WARN baseline
|
|
258
|
+
with a reason and stale-detection instead of silently passing. `check-validator-registration.mjs`
|
|
259
|
+
(#1184) confirms every validator module is actually wired into `validate-plugin.mjs`
|
|
260
|
+
(measured 33 of 33 wired, using comment-stripped source so a commented-out registration
|
|
261
|
+
doesn't count as live).
|
|
262
|
+
- **`check-skill-script-paths`'s new `--strict-sh` mode found its own premise refuted
|
|
263
|
+
(#1187).** Extending the dead-path scan to shell-script citations, only 1 of 27 `.sh`
|
|
264
|
+
references sits inside the tool's own scan directories (`skills/`, `commands/`, `agents/`)
|
|
265
|
+
— the other 26 are in `docs/`, alongside 7 dead `.mjs` citations inside `docs/adr/` the same
|
|
266
|
+
run surfaced. Shipped as a WARN-only opt-in flag; the `docs/` sweep is a follow-up.
|
|
267
|
+
- **The discovery-validator's dedupe key, gate-summary skip, and masking order all landed
|
|
268
|
+
together (#1198).** A dedupe key (`session_id`, `agent_id`, `sha256(claim)`) collapses
|
|
269
|
+
repeated flags of the identical claim; gate-summary recap lines (the coordinator's own
|
|
270
|
+
restatement of a subagent's findings, not a fresh claim) are now skipped entirely; claim
|
|
271
|
+
masking now runs before every pattern is checked, not a subset. Measured against the
|
|
272
|
+
flagged corpus: 186 of 400 flagged claims were false positives. Two claims from this
|
|
273
|
+
session's own discovery briefing were checked against the fix and refuted.
|
|
274
|
+
- **A pre-push scratch-vs-publish check now reads the two arguments git already passes it.**
|
|
275
|
+
`.husky/pre-push` had never read `$1` (remote name/URL) or `$2` (remote URL) — confirmed by
|
|
276
|
+
`rg -n '\$1|\$2' .husky/pre-push` finding only a local function's own arguments — so every
|
|
277
|
+
push ran the full quality gate regardless of destination. It now skips the gate for a raw,
|
|
278
|
+
unconfigured scratch URL (e.g. the remote-offload sync target) while continuing to gate
|
|
279
|
+
real publish remotes; a named ceiling covers a real but unlisted third publish host, which
|
|
280
|
+
still reads as scratch.
|
|
281
|
+
- **`check-untracked-test-deps` misclassified `.git` as an untracked file inside a linked
|
|
282
|
+
worktree.** In a normal checkout `.git` is a directory and was already excluded; in a
|
|
283
|
+
`git worktree add` checkout `.git` is a FILE containing `gitdir: <path>`, which satisfied
|
|
284
|
+
the same `isFile()` check the untracked-candidate branch uses. `.git` is now explicitly
|
|
285
|
+
exempted regardless of which shape it takes — measured 62 of 62 tests passing across the 5
|
|
286
|
+
affected test files inside a linked worktree checkout.
|
|
287
|
+
|
|
10
288
|
## [3.23.0] - 2026-08-28
|
|
11
289
|
|
|
12
290
|
Two commits today (1 `feat`, 1 `fix`; 64 files, +4,971/−184) close Waves 3 and 4 of the
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Session Orchestrator
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
|
-
[](CHANGELOG.md)
|
|
5
5
|
[](https://www.npmjs.com/package/session-orchestrator)
|
|
6
|
-
[](docs/telemetry/telemetry-claims.md)
|
|
7
7
|
|
|
8
8
|
Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on **Claude Code, Codex CLI, Cursor, and [Pi](docs/pi-setup.md)**.
|
|
9
9
|
|
|
@@ -72,11 +72,11 @@ Everything else is opt-in. See [`docs/session-config-template.md`](docs/session-
|
|
|
72
72
|
|
|
73
73
|
## What you get
|
|
74
74
|
|
|
75
|
-
- **
|
|
75
|
+
- **49 skills** for the session lifecycle (start, plan, execute, close, evolve), discovery, vault sync, MCP authoring, debugging, brainstorming, plan grilling, persona panels, cross-repo dispatch, learning→rule reconciliation, session-process eval, audits, and more
|
|
76
76
|
- **28 slash commands** (`/session`, `/go`, `/close`, `/discovery`, `/plan`, `/grill`, `/evolve`, `/autopilot`, `/dispatcher`, `/reconcile`, `/eval`, `/test`, `/debug`, …)
|
|
77
77
|
- **15 typed subagents** (code-implementer, test-writer, security-reviewer, session-reviewer, qa-strategist, architect-reviewer, …)
|
|
78
78
|
- **10 hook event types** enforcing scope, blocking destructive commands, gating templates-first, capturing telemetry — full on Claude Code; experimental, post-hoc, or bridged on the other platforms ([Platform support](#platform-support))
|
|
79
|
-
- **
|
|
79
|
+
- **15,000+ vitest tests** run on every commit ([telemetry methodology](docs/telemetry/telemetry-claims.md))
|
|
80
80
|
|
|
81
81
|
Full component inventory: [`docs/components.md`](docs/components.md).
|
|
82
82
|
|
|
@@ -133,19 +133,21 @@ The system is markdown-driven config plus a thin Node runtime — skills, comman
|
|
|
133
133
|
- **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.
|
|
134
134
|
- **VCS dual support, no lock-in.** Auto-detects GitLab or GitHub from your remote and drives the full lifecycle for both.
|
|
135
135
|
|
|
136
|
-
## Recent highlights (v3.
|
|
136
|
+
## Recent highlights (v3.24.0)
|
|
137
137
|
|
|
138
|
-
Every release is additive and backward-compatible. Highlights of the v3.
|
|
138
|
+
Every release is additive and backward-compatible. Highlights of the v3.24.0 line: parallel-session identity moves from repo-scoped to process-local, remote work gets a declared host channel, and four duplicated-copy classes collapse to one module each:
|
|
139
139
|
|
|
140
|
-
- **
|
|
141
|
-
- **
|
|
142
|
-
- **
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
145
|
-
- **
|
|
146
|
-
- **
|
|
140
|
+
- **Parallel-session identity moves from repo-scoped to process-local (#1194 / #1188)** — `enforce-scope` now reads only process-local session ids (`readProcessLocalSessionIds`), so a peer's manifest in the same working copy is treated as foreign, not as your own; `memory-propose` authorizes off a process-local match of the raw session id rather than trusting the lock file alone. A peer's declared scope also gets its own place in the wave manifest now, as a `peer-session-<id>` record instead of a silent gap (#1195).
|
|
141
|
+
- **Remote-offload: declare hosts, route heavy roles to a ready host instead of halving the wave (#1160)** — a `remote-hosts:` config block, an `ssh:<alias>` dispatch channel, and an offload decision wired into the resource gate right after the HR-004 cap (readiness is injected, never measured inside the gate itself). `skills/remote-offload/SKILL.md` is now the generic entry point, with adapters around the `offload` CLI.
|
|
142
|
+
- **Four copy-classes collapsed to one module each (#1181 / #1182 / #1196 / #1197)** — atomic-JSON handling, subagent sidecar-path derivation, `expandHome`, and markdown-fence parsing each had 4–8 divergent copies; consolidating `atomic-json.mjs` also closed a data-loss path where a read-modify-write default silently replaced an unparsable or unreadable file.
|
|
143
|
+
- **Refusals are now events, not silence (#1200 / #1201)** — `emitFinalWaveCompleted` writes `orchestrator.wave.final_refused` for six refusal paths, evolve and dialectic completions emit their own events, and reconcile's trigger set now lives in code (`KNOWN_TRIGGERS`) instead of being asserted in prose.
|
|
144
|
+
- **Canonical session counting, fleet-wide (#1186)** — `site-numbers` and `vault-mirror --kind session` now count the same way everywhere: 289 raw lines in this repo's own ledger collapsed to 278 real sessions once double-counts were removed.
|
|
145
|
+
- **Discovery-validator false positives cut — 186 of 400 measured hits were the harness's own gate-summary lines (#1198)** — a dedupe key and a gate-summary skip removed the noise; two brief claims about the masking order were also measured and refuted along the way.
|
|
146
|
+
- **Three new mechanical guards land in `validate-plugin` (#1183 / #1184 / #1187)** — `emitEvent` try/catch coverage, validator-registration parity (every check actually wired in), and dead `.sh` citations in docs.
|
|
147
|
+
- **The pre-push gate distinguishes a publish push from a scratch push; linked-worktree gates run green (15,829/0 on the remote host)** — a `.git` file (not a directory) in a linked worktree was previously misread as an untracked candidate, flagging 13 tracked files as missing.
|
|
148
|
+
- **The AUQ clarity guard's delivery is proven live, not asserted (#1122)** — `check-auq-clarity` is wired into `validate-plugin` and blocks on the two hard hurdles at a measured 0% false-positive rate; its own wiring closes with a fake-regression proof, not a claim.
|
|
147
149
|
|
|
148
|
-
Previous line (v3.
|
|
150
|
+
Previous line (v3.23.0): three external bug reports fixed end-to-end (Codex session-id versions, missing `/close` fields, plugin-root resolution), the worktree-promotion process boundary, and three measurement fixes (host-registry numbering, the mode-selector's mis-shaped read, two regained Semgrep rules).
|
|
149
151
|
|
|
150
152
|
Full version history: [CHANGELOG.md](CHANGELOG.md).
|
|
151
153
|
|
package/agents/db-specialist.md
CHANGED
|
@@ -93,7 +93,6 @@ Append a fenced ```json block at the end of your response per `agents/schemas/db
|
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
Required: `status`, `task_id`, `files_changed`, `blockers`. Optional: `verdict`. **Emit `verdict` alongside `status` (status→verdict mapping: done→PROCEED, partial→PROCEED_WITH_FOLLOWUPS, blocked→BLOCKED). `status` is deprecated and will be removed in v4.0 (#472).** The coordinator parses the LAST fenced ```json block.
|
|
96
|
-
```
|
|
97
96
|
|
|
98
97
|
## Edge Cases
|
|
99
98
|
|