session-orchestrator 3.21.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/040-discovery.mdc +6 -6
- package/.cursor/rules/050-plan.mdc +8 -8
- package/CHANGELOG.md +101 -0
- package/README.md +10 -10
- package/agents/memory-proposal-collector.md +6 -4
- package/commands/eli5.md +33 -0
- package/commands/release.md +5 -3
- package/commands/test.md +2 -2
- package/docs/components.md +6 -5
- package/docs/scope-collision-guard.md +3 -3
- package/docs/session-config-reference.md +31 -8
- package/hooks/_lib/lock-bootstrap.mjs +19 -13
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +11 -1
- package/hooks/on-session-end.mjs +24 -92
- package/hooks/on-session-start.mjs +195 -104
- package/hooks/pre-auq-clarity.mjs +787 -0
- package/hooks/pre-bash-issue-budget.mjs +17 -18
- package/package.json +3 -1
- package/pi/prompts/eli5.md +12 -0
- package/scripts/auq-audit.mjs +825 -0
- package/scripts/autopilot.mjs +7 -8
- 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/ci-status-banner.mjs +63 -57
- 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/io.mjs +239 -0
- package/scripts/lib/issue-budget.mjs +63 -9
- package/scripts/lib/owner-interview.mjs +78 -32
- 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/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/spiral-carryover.mjs +23 -2
- package/scripts/lib/state-md/mission-status.mjs +147 -50
- package/scripts/lib/validate/check-auq-clarity.mjs +274 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +30 -0
- package/scripts/lib/validate/check-rules.mjs +153 -9
- package/scripts/lib/vault-backfill/glab.mjs +91 -58
- package/scripts/lib/vault-backfill/manifest.mjs +28 -8
- package/scripts/lib/vcs-repo-spec.mjs +182 -13
- package/scripts/lib/wave-resource-gate.mjs +67 -73
- package/scripts/materialize-wave-scope.mjs +281 -0
- package/scripts/release.mjs +443 -122
- package/scripts/run-quality-gate.mjs +14 -0
- package/scripts/validate-plugin.mjs +3 -0
- package/scripts/validate-wave-scope.mjs +6 -1
- package/scripts/vault-backfill.mjs +32 -5
- package/skills/_shared/parallel-aware-auq.md +30 -24
- package/skills/_shared/parallel-aware-preamble.md +31 -2
- package/skills/_shared/state-ownership.md +32 -6
- package/skills/bootstrap/SKILL.md +2 -1
- package/skills/brainstorm/SKILL.md +18 -18
- package/skills/brainstorm/soul.md +12 -0
- package/skills/discovery/SKILL.md +28 -24
- package/skills/eli5/SKILL.md +43 -0
- package/skills/evolve/SKILL.md +8 -9
- package/skills/gitlab-ops/SKILL.md +30 -26
- package/skills/grill/SKILL.md +6 -6
- package/skills/grill/soul.md +16 -0
- package/skills/memory-cleanup/SKILL.md +2 -2
- package/skills/npm-publish/SKILL.md +4 -4
- package/skills/peekaboo-driver/SKILL.md +3 -3
- package/skills/plan/SKILL.md +18 -16
- package/skills/plan/mode-feature.md +1 -1
- package/skills/plan/mode-new.md +35 -23
- package/skills/plan/soul.md +12 -0
- package/skills/reconcile/SKILL.md +3 -3
- package/skills/session-end/SKILL.md +53 -20
- package/skills/session-end/phase-3-6-tail.md +37 -2
- package/skills/session-start/SKILL.md +69 -35
- 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/test-runner/SKILL.md +2 -2
- package/skills/using-orchestrator/SKILL.md +1 -1
- package/skills/wave-executor/wave-loop.md +27 -5
- package/skills/write-executable-plan/SKILL.md +6 -6
- 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
|
@@ -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.22.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.22.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.22.0+codex.20260822193811",
|
|
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",
|
|
@@ -33,10 +33,11 @@ You have Session Orchestrator capabilities. This plugin provides structured sess
|
|
|
33
33
|
**No AskUserQuestion tool**: Present choices as numbered Markdown lists:
|
|
34
34
|
```
|
|
35
35
|
Choose one:
|
|
36
|
-
1. Option A —
|
|
37
|
-
2. Option B —
|
|
36
|
+
1. Option A (Recommended) — what it does, and why it is the safe default here.
|
|
37
|
+
2. Option B — when B applies instead, and what it costs.
|
|
38
38
|
Reply with the number of your choice.
|
|
39
39
|
```
|
|
40
|
+
Two rules the example above follows. `(Recommended)` goes on option 1, in the label — an operator skims labels, so a recommendation hidden in the description is a recommendation he never sees. And every option names a reason, a cost, or a consequence: a recommendation without one is a claim he can only believe or ignore.
|
|
40
41
|
|
|
41
42
|
**No TaskCreate/Update**: Use plain-text checklists to track progress:
|
|
42
43
|
```
|
|
@@ -186,9 +186,9 @@ Options:
|
|
|
186
186
|
1. [title] — [file_path]:[line] ([severity])
|
|
187
187
|
|
|
188
188
|
Options:
|
|
189
|
-
1. Accept all (Recommended) —
|
|
190
|
-
2. Review individually
|
|
191
|
-
3. Dismiss all
|
|
189
|
+
1. Accept all (Recommended) — one issue per finding. Fastest, and you can still close any of them later.
|
|
190
|
+
2. Review individually — decide finding by finding. Costs about a minute per item.
|
|
191
|
+
3. Dismiss all — no issues created, and the findings are gone when this session ends.
|
|
192
192
|
```
|
|
193
193
|
|
|
194
194
|
### Step 4: Batch Confirmation
|
|
@@ -196,9 +196,9 @@ Options:
|
|
|
196
196
|
```
|
|
197
197
|
Ready to create [N] issues? ([X] critical, [Y] high, [Z] medium, [W] low)
|
|
198
198
|
|
|
199
|
-
1. Create all [N] issues (Recommended)
|
|
200
|
-
2. Review list first
|
|
201
|
-
3. Cancel
|
|
199
|
+
1. Create all [N] issues (Recommended) — the list is already filtered, so nothing here is noise. You can close any of them later.
|
|
200
|
+
2. Review list first — read the titles before anything is written. Costs one extra step.
|
|
201
|
+
3. Cancel — nothing is written, and this run's findings are not kept.
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
## Phase 5: Issue Creation
|
|
@@ -29,9 +29,9 @@ If no mode specified, ask via numbered list:
|
|
|
29
29
|
```
|
|
30
30
|
Which planning mode?
|
|
31
31
|
|
|
32
|
-
1. new (Recommended) — Project kickoff
|
|
33
|
-
2. feature —
|
|
34
|
-
3. retro — Retrospective
|
|
32
|
+
1. new (Recommended) — Project kickoff: full PRD, repo setup, issue creation. Fits a repo that has no PRD yet.
|
|
33
|
+
2. feature — One feature: compact scope, acceptance criteria, issues. Cheaper, but assumes the project context already exists.
|
|
34
|
+
3. retro — Retrospective on finished work: metrics, reflection, improvement actions. Defines no new scope.
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
## Phase 2: Q&A Engine (Shared Core)
|
|
@@ -154,9 +154,9 @@ If any criterion fails, revise and re-review. Maximum **3 iterations**. After 3
|
|
|
154
154
|
The PRD reviewer flagged these remaining issues:
|
|
155
155
|
[list]
|
|
156
156
|
|
|
157
|
-
1. Accept as-is (Recommended) — issues are minor,
|
|
158
|
-
2. Manual edit —
|
|
159
|
-
3. Re-run review —
|
|
157
|
+
1. Accept as-is (Recommended) — the flagged issues are minor, so the PRD is usable now. You can still edit it later.
|
|
158
|
+
2. Manual edit — you edit the PRD yourself before continuing. Costs a round-trip.
|
|
159
|
+
3. Re-run review — one more revision round. Costs another review pass and may surface nothing new.
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
Then present the saved PRD path and ask for final approval:
|
|
@@ -164,8 +164,8 @@ Then present the saved PRD path and ask for final approval:
|
|
|
164
164
|
```
|
|
165
165
|
PRD saved to [path]. Ready for your review.
|
|
166
166
|
|
|
167
|
-
1. Approve PRD (Recommended) — proceed to issue creation
|
|
168
|
-
2. Request changes — describe what to change
|
|
167
|
+
1. Approve PRD (Recommended) — proceed to issue creation. You can still edit the PRD afterwards; the issues link to it rather than copy it.
|
|
168
|
+
2. Request changes — describe what to change. Costs one more revision round.
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
## Phase 5: Issue Creation
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,107 @@ 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
|
+
|
|
10
111
|
## [3.21.0] - 2026-08-19
|
|
11
112
|
|
|
12
113
|
Twenty-one commits (12 `fix`, 4 `feat`, 4 `docs`, 1 `chore`; 209 files, +28,906/−2,893), no
|
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
|
|
|
@@ -131,18 +131,18 @@ The system is markdown-driven config plus a thin Node runtime — skills, comman
|
|
|
131
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.
|
|
132
132
|
- **VCS dual support, no lock-in.** Auto-detects GitLab or GitHub from your remote and drives the full lifecycle for both.
|
|
133
133
|
|
|
134
|
-
## Recent highlights (v3.
|
|
134
|
+
## Recent highlights (v3.22.0)
|
|
135
135
|
|
|
136
|
-
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:
|
|
137
137
|
|
|
138
|
-
- **The
|
|
139
|
-
- **The
|
|
140
|
-
- **
|
|
141
|
-
- **
|
|
142
|
-
- **
|
|
143
|
-
- **
|
|
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.
|
|
144
144
|
|
|
145
|
-
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.
|
|
146
146
|
|
|
147
147
|
Full version history: [CHANGELOG.md](CHANGELOG.md).
|
|
148
148
|
|
|
@@ -135,8 +135,8 @@ and `{{options}}` at render time.
|
|
|
135
135
|
```
|
|
136
136
|
AskUserQuestion({
|
|
137
137
|
questions: [{
|
|
138
|
-
header: "Memory
|
|
139
|
-
question: "
|
|
138
|
+
header: "Memory",
|
|
139
|
+
question: "Batch {{N}} of {{M}} — which learnings should be stored permanently? Unselected ones are archived as declined.",
|
|
140
140
|
options: [
|
|
141
141
|
// one entry per proposal in this batch — see label format below
|
|
142
142
|
{ label: "{{label}}", description: "{{description}}" },
|
|
@@ -147,12 +147,14 @@ AskUserQuestion({
|
|
|
147
147
|
})
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
**When there is only one batch** (`M === 1`), omit the batch
|
|
150
|
+
**When there is only one batch** (`M === 1`), omit the batch prefix from the question:
|
|
151
151
|
|
|
152
152
|
```
|
|
153
|
-
|
|
153
|
+
question: "Which learnings should be stored permanently? Unselected ones are archived as declined."
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
+
`header` stays `"Memory"` in both cases: the field is cut off after 12 characters, so anything longer never reaches the operator.
|
|
157
|
+
|
|
156
158
|
### Option label format
|
|
157
159
|
|
|
158
160
|
Locked by D3 (Wave 1 decision):
|
package/commands/eli5.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Say the last answer again in plain words — same facts, in the order the operator needs them. Optional topic argument.
|
|
3
|
+
argument-hint: "[topic]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# eli5
|
|
7
|
+
|
|
8
|
+
Invokes the `eli5` skill (`skills/eli5/SKILL.md`). Restates my last substantial output — or the named topic — for someone who knows this project but did not watch the last ten minutes of it.
|
|
9
|
+
|
|
10
|
+
## Argument Validation
|
|
11
|
+
|
|
12
|
+
The optional argument is a topic, in prose. If absent, the target is my own last substantial output in this conversation; if there is none yet, say so rather than picking a topic for him.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
- `/eli5` — restate what I just said
|
|
16
|
+
- `/eli5 warum ist der Regel-Korpus voll?` — explain that, grounded in what this session measured
|
|
17
|
+
|
|
18
|
+
## Behavior
|
|
19
|
+
|
|
20
|
+
1. **Resolve the target** — last output, or `$ARGUMENTS`.
|
|
21
|
+
2. **Ground it** — prefer what this session already measured over recall, and name where it came from. Never measured here → say so.
|
|
22
|
+
3. **Restate** — consequence first (*must I act, and what if I don't?*), then the facts in the order he needs them.
|
|
23
|
+
4. **Check before sending** — every greppable token from the original still present; every noun the system does not contain gone.
|
|
24
|
+
|
|
25
|
+
## The limit that outranks the command
|
|
26
|
+
|
|
27
|
+
**Simplifying removes words, never facts.** A dropped path, number, error code, identifier, or instruction to act is data loss, not simplification — `skills/session-start/soul.md` § "Never traded for brevity" outranks brevity here as everywhere. And no invented pictures: say what happens, never what it is "like".
|
|
28
|
+
|
|
29
|
+
## Related
|
|
30
|
+
|
|
31
|
+
- `skills/eli5/SKILL.md` — the skill, in full
|
|
32
|
+
- `.claude/rules/ask-via-tool.md` § AUQ-006 — plain words, real things
|
|
33
|
+
- `skills/session-start/soul.md` § Register — the canonical register statement
|
package/commands/release.md
CHANGED
|
@@ -32,13 +32,13 @@ The same reading shows the other half: the GitHub releases for 3.15/3.18/3.19/3.
|
|
|
32
32
|
4. **Preflight.** `node scripts/release.mjs --check --json` — every row green. This runs *after* step 2, never before: `--check` derives its target from `package.json`, so on the pre-bump version the registry- and tag-collision rows are red by construction.
|
|
33
33
|
5. **Gate, commit, push.** Full quality gate, then commit and push to **both** remotes.
|
|
34
34
|
6. **CI green — on the commit that will be published.** Not on its predecessor. A green pipeline from before step 5's commit is evidence about a different tree.
|
|
35
|
-
7. **Publish.** `node scripts/release.mjs --publish` —
|
|
35
|
+
7. **Publish.** `node scripts/release.mjs --publish` — the target-confirmed npm receipt is the irreversible boundary. Before that receipt, any failure aborts normally. After it, never rerun `--publish`: registry propagation timeout/query/wait failures are reconciliation while the script still tags, pushes `main` + tag to both remotes, handles the GitHub release, and polls the live site. If tag/push fails after the receipt, the dependent GitHub-release and site steps are skipped and the script returns structured reconciliation guidance instead. Add the GitHub release for the new tag (`gh release create`) as part of this step, not "later" — "later" is what produced the three-second backfill above.
|
|
36
36
|
|
|
37
37
|
Steps 2–7 are one continuous act. A release left parked between step 5 and step 7 is exactly the `3.18.0` state: every surface says released, the registry disagrees.
|
|
38
38
|
|
|
39
39
|
## Abort criteria
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
**Before a target-confirmed npm publish receipt:** stop and report. Do not work around, do not "fix it after the publish" — an npm publish is not revocable.
|
|
42
42
|
|
|
43
43
|
| Signal | Why it stops the release |
|
|
44
44
|
|---|---|
|
|
@@ -51,7 +51,9 @@ Stop and report. Do not work around, do not "fix it after the publish" — an np
|
|
|
51
51
|
|
|
52
52
|
## After
|
|
53
53
|
|
|
54
|
-
`--publish`
|
|
54
|
+
`--publish` reports either **Release complete** or **Post-publish reconciliation required**. The latter means npm accepted the target release but registry propagation, tag/push, GitHub-release handling, or the live-site check still needs repair; a tag/push failure explicitly skips its dependent GitHub-release and site steps. **Do not rerun `--publish`**: reconcile the listed state directly, because a second publish cannot replace the immutable version. Exit `1` means a preflight/check failure or post-publish reconciliation is required; exit `2` remains a system/usage failure before the receipt.
|
|
55
|
+
|
|
56
|
+
After a complete release, `--publish` prints the remaining manual items (token rotation, async gallery indexing). Rotate the npm token — write tokens are short-lived by policy, and a token that transited a log or a chat is burned.
|
|
55
57
|
|
|
56
58
|
## See Also
|
|
57
59
|
|
package/commands/test.md
CHANGED
|
@@ -59,9 +59,9 @@ Present the user with a structured choice via `AskUserQuestion`:
|
|
|
59
59
|
AskUserQuestion({
|
|
60
60
|
questions: [{
|
|
61
61
|
question: "Which test profile should be run?",
|
|
62
|
-
header: "
|
|
62
|
+
header: "Profil",
|
|
63
63
|
options: [
|
|
64
|
-
{ label: "smoke (Recommended)", description: "Quick sanity pass — key flows, axe critical/serious, console errors." },
|
|
64
|
+
{ label: "smoke (Recommended)", description: "Quick sanity pass — key flows, axe critical/serious, console errors. The fastest of the four; run it first." },
|
|
65
65
|
{ label: "full", description: "All checks at full depth — slower, used before release." },
|
|
66
66
|
{ label: "a11y", description: "Accessibility-focused pass — axe-core exhaustive scan." },
|
|
67
67
|
{ label: "onboarding", description: "Onboarding step-count + Liquid Glass conformance checks." }
|
package/docs/components.md
CHANGED
|
@@ -7,8 +7,8 @@ Detailed component inventory and architecture reference for Session Orchestrator
|
|
|
7
7
|
```mermaid
|
|
8
8
|
flowchart LR
|
|
9
9
|
USER([Operator]) -->|invokes /session| COORD[Coordinator]
|
|
10
|
-
COORD -->|reads| SK[Skills<br/>
|
|
11
|
-
COORD -->|invokes| CMD[Commands<br/>
|
|
10
|
+
COORD -->|reads| SK[Skills<br/>47 user-facing]
|
|
11
|
+
COORD -->|invokes| CMD[Commands<br/>27 slash-cmds]
|
|
12
12
|
COORD -->|dispatches| AG[Agents<br/>15 typed sub-agents]
|
|
13
13
|
AG -.->|parallel waves| W1[code-implementer]
|
|
14
14
|
AG -.-> W2[test-writer]
|
|
@@ -18,7 +18,7 @@ flowchart LR
|
|
|
18
18
|
COORD -->|writes| METRIC[.orchestrator/metrics/<br/>sessions · learnings · events]
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
## Skills (
|
|
21
|
+
## Skills (47 user-facing)
|
|
22
22
|
|
|
23
23
|
- **Lifecycle:** `session-start`, `session-plan`, `wave-executor`, `session-end`, `quality-gates`, `using-orchestrator`
|
|
24
24
|
- **Authoring:** `skill-creator`, `mcp-builder`, `hook-development`, `frontmatter-guard`, `contract-version-bump`
|
|
@@ -29,11 +29,12 @@ flowchart LR
|
|
|
29
29
|
- **Ecosystem:** `bootstrap`, `gitlab-ops`, `gitlab-portfolio`, `ecosystem-health`, `mode-selector`, `autopilot`, `dispatcher`, `spinout`, `npm-publish`
|
|
30
30
|
- **Testing:** `test-runner`, `playwright-driver`, `peekaboo-driver`
|
|
31
31
|
- **Content review:** `persona-panel`
|
|
32
|
+
- **Operator ergonomics:** `eli5` (plain-language restatement of the last answer)
|
|
32
33
|
- **Visualization:** `tmux-layout` (opt-in operator side-channel — [ADR-0007](adr/0007-tmux-visualization-substrate.md))
|
|
33
34
|
|
|
34
|
-
## Commands (
|
|
35
|
+
## Commands (27)
|
|
35
36
|
|
|
36
|
-
`/session`, `/go`, `/close`, `/discovery`, `/plan`, `/evolve`, `/bootstrap`, `/harness-audit`, `/autopilot`, `/autopilot-multi`, `/repo-audit`, `/test`, `/memory-cleanup`, `/portfolio`, `/brainstorm`, `/debug`, `/persona-panel`, `/grill`, `/sunset-review`, `/templates-ack`, `/dispatcher`, `/reconcile`, `/spinout`, `/eval`, `/contract-version-bump`.
|
|
37
|
+
`/session`, `/go`, `/close`, `/discovery`, `/plan`, `/evolve`, `/bootstrap`, `/harness-audit`, `/autopilot`, `/autopilot-multi`, `/repo-audit`, `/test`, `/memory-cleanup`, `/portfolio`, `/brainstorm`, `/debug`, `/persona-panel`, `/grill`, `/sunset-review`, `/templates-ack`, `/dispatcher`, `/reconcile`, `/spinout`, `/eval`, `/release`, `/contract-version-bump`, `/eli5`.
|
|
37
38
|
|
|
38
39
|
## Agents (15 typed sub-agents)
|
|
39
40
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Scope-Collision Guard — Pre-Dispatch File-Scope Deconfliction
|
|
2
2
|
|
|
3
3
|
> Reference for the mechanism that stops a wave from handing the SAME file to two agents (issue #1020).
|
|
4
|
-
>
|
|
4
|
+
> Five moving parts: `scripts/materialize-wave-scope.mjs` (the canonical declaration writer), the per-agent scope files, `scripts/validate-wave-scope.mjs` (`--assert-disjoint` / `--union`), `findScopeCollisions()` + `unionFileScopes()` in [`scripts/lib/scope-gate.mjs`](../scripts/lib/scope-gate.mjs), and the `PreToolUse` hook [`hooks/pre-task-scope-disjoint.mjs`](../hooks/pre-task-scope-disjoint.mjs).
|
|
5
5
|
> The coordinator-side **runbook** is `skills/wave-executor/wave-loop.md` § Scope Manifest 3.1–3.3 — this document does not restate it. What lives here instead: how the mechanism works, how it fails, what it deliberately does not see, and how to debug it.
|
|
6
6
|
|
|
7
7
|
## 1. What the pre-existing gates could not see
|
|
@@ -17,8 +17,8 @@ Two things follow, and both are the point of #1020:
|
|
|
17
17
|
|
|
18
18
|
| # | Step | Artefact | Mechanism |
|
|
19
19
|
|---|------|----------|-----------|
|
|
20
|
-
| 1 |
|
|
21
|
-
| 2 | Assert disjointness | the sidecar array `[{id, files}, …]` | `validate-wave-scope.mjs --assert-disjoint` → `findScopeCollisions()` |
|
|
20
|
+
| 1 | Materialize declarations | `<state-dir>/filescopes/wave-<N>/<agent-id>.json` (one per agent, plus `coordinator.json`) and `<state-dir>/filescopes/wave-<N>.scopes.json` | one canonical `[{id, files}, …]` stdin array → `materialize-wave-scope.mjs` |
|
|
21
|
+
| 2 | Assert disjointness | the materialized sidecar array `[{id, files}, …]` | `validate-wave-scope.mjs --assert-disjoint` → `findScopeCollisions()` |
|
|
22
22
|
| 3 | Compute the union | stdout of `--union` → `allowedPaths` | `expandTestSiblings(unionFileScopes(scopes), { role })` |
|
|
23
23
|
| 4 | Inject | `FILE-SCOPE — exactly these:` + a fenced block in each agent prompt | the per-agent file from step 1 |
|
|
24
24
|
| 5 | Dispatch | `.orchestrator/wave-dispatch-scopes.json` (ledger) | `hooks/pre-task-scope-disjoint.mjs`, `PreToolUse` matcher `Agent` |
|
|
@@ -423,7 +423,7 @@ Introduced by Epic #157 / issue #166. Lets session-start sense the host (RAM, CP
|
|
|
423
423
|
|-------|------|---------|-------------|
|
|
424
424
|
| `resource-awareness` | boolean | `true` | Master toggle for the env-aware runtime. When `false`, skips Phase 4.5 adaptive wave sizing and the host banner. |
|
|
425
425
|
| `enable-host-banner` | boolean | `true` | Whether `hooks/on-session-start.mjs` emits the host + resource banner at the top of every session. Set `false` to silence. |
|
|
426
|
-
| `resource-thresholds` | object | see below | Numeric thresholds that drive Phase 4.5 adaptive rules. Unset sub-keys fall back to
|
|
426
|
+
| `resource-thresholds` | object | see below | Numeric thresholds that drive Phase 4.5 adaptive rules. Unset sub-keys fall back to the single canonical default set (`DEFAULT_RESOURCE_THRESHOLDS` in `scripts/lib/resource-probe/evaluate.mjs`). Sub-keys: `ram-free-min-gb`, `ram-free-critical-gb`, `cpu-load-max-pct`, `concurrent-sessions-warn`, `ssh-no-docker`, `zombie-threshold-min`. |
|
|
427
427
|
|
|
428
428
|
### resource-thresholds
|
|
429
429
|
|
|
@@ -431,17 +431,40 @@ Sub-key defaults:
|
|
|
431
431
|
|
|
432
432
|
```yaml
|
|
433
433
|
resource-thresholds:
|
|
434
|
-
ram-free-min-gb: 4 #
|
|
435
|
-
ram-free-critical-gb: 2 #
|
|
436
|
-
cpu-load-max-pct:
|
|
437
|
-
concurrent-sessions-warn: 5 #
|
|
434
|
+
ram-free-min-gb: 4 # soft memory signal (see precedence below)
|
|
435
|
+
ram-free-critical-gb: 2 # hard memory signal → coordinator-direct
|
|
436
|
+
cpu-load-max-pct: 90 # soft CPU signal, judged on min(1m, 5m)
|
|
437
|
+
concurrent-sessions-warn: 5 # soft signal at this many live peer SESSIONS
|
|
438
438
|
ssh-no-docker: true # when session is over SSH, steer the plan away from Docker-based tests
|
|
439
439
|
zombie-threshold-min: 30 # age (minutes) above which an idle Claude/Node process is a zombie candidate
|
|
440
440
|
```
|
|
441
441
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
442
|
+
**No single threshold caps a wave (#1089).** A cap requires either one *hard*
|
|
443
|
+
signal (→ `critical`, coordinator-direct) or **two independent soft signals**
|
|
444
|
+
agreeing (→ `warn`, cap 2). One soft signal alone is reported and acted on by
|
|
445
|
+
nobody. Rationale and the measured firing rates are in
|
|
446
|
+
[`.claude/rules/host-resources.md`](../.claude/rules/host-resources.md); the full
|
|
447
|
+
rule table is in `skills/session-start/phase-4-5-resource-health.md`.
|
|
448
|
+
|
|
449
|
+
**What the memory thresholds are compared against** is chosen by precedence, not
|
|
450
|
+
by configuration: `memory_pressure_pct_free` (macOS, hard `<15%` / soft `<30%`)
|
|
451
|
+
outranks `ram_available_gb`, which outranks `ram_free_gb`. The two GB-denominated
|
|
452
|
+
keys above therefore apply to *available* RAM on macOS and to `os.freemem()` on
|
|
453
|
+
Linux/Windows, where it is accurate. They are never compared against Darwin's
|
|
454
|
+
`Pages free`, whose median across 1477 measured session starts was **0.4 GB** on
|
|
455
|
+
hosts with 24-128 GB installed — gating on it fired `ram-free-critical-gb` on
|
|
456
|
+
84.0% of all starts.
|
|
457
|
+
|
|
458
|
+
**`concurrent-sessions-warn` counts SESSIONS, not processes.** The live count
|
|
459
|
+
comes from the session registry (`detectPeers()` — self excluded,
|
|
460
|
+
heartbeat-fresh). Until #1089 it was compared against `claude_processes_count`, a
|
|
461
|
+
measured 6x unit error (median processes:sessions = 6.0) that fired the threshold
|
|
462
|
+
on 93.6% of starts instead of 4.2%. When the registry is unreadable the probe
|
|
463
|
+
falls back to the process count rescaled by that same factor.
|
|
464
|
+
|
|
465
|
+
**`zombie-threshold-min`** (default: `30`): When set, the resource probe runs a secondary `ps` pass that counts Claude and Node processes older than this many minutes **and** with CPU% ≤ 1%. These are "zombie candidates" — stale sessions or orphaned workers that still hold RAM. The probe exposes them via `zombie_processes_count` in the snapshot. Since #1089 this is a *soft* signal: it is reported when `zombie_processes_count >= 1` **and** there is a live peer/process context, but on its own it caps nothing — sweeping stale sessions is housekeeping advice, not a reason to shrink a wave. The reason string surfaces the threshold and count so the session-start banner gives actionable context. Set to `0` to disable zombie detection entirely (the field is omitted from the default snapshot when absent from config).
|
|
466
|
+
|
|
467
|
+
Rationale: originated from the 2026-04-19 incident where 8 parallel Claude sessions on one Mac caused a hard freeze. That hazard is real and the rules still escalate for it — what #1089 changed is that they now recognise it, instead of reporting it on 99.0% of session starts where it was not happening. See Epic #157, Sub-Epic #158, and `.claude/rules/host-resources.md`.
|
|
445
468
|
|
|
446
469
|
### isolation graduation
|
|
447
470
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Schema v2 (Epic #583 D4 #587):
|
|
17
17
|
* {
|
|
18
|
-
* session_id: string, //
|
|
19
|
-
* semantic_session_id: string, //
|
|
18
|
+
* session_id: string, // native raw identity OR generated UUID; sole live lock/registry ownership key
|
|
19
|
+
* semantic_session_id: string, // attribution/history label only; never ownership
|
|
20
20
|
* started_at: ISO,
|
|
21
21
|
* last_heartbeat: ISO, // basis for liveness; replaces PID-liveness checks
|
|
22
22
|
* mode: string, // "deep"|"feature"|"housekeeping"|"session"|...
|
|
@@ -47,10 +47,12 @@ import { writeJsonAtomicSync } from '../../scripts/lib/io.mjs';
|
|
|
47
47
|
*
|
|
48
48
|
* @param {object} opts
|
|
49
49
|
* @param {string} opts.repoRoot — absolute path to the repository root.
|
|
50
|
-
* @param {string} opts.sessionId — the
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
50
|
+
* @param {string} opts.sessionId — the physical raw session id from the native
|
|
51
|
+
* harness, or a generated UUID when no trustworthy raw id exists. This is the
|
|
52
|
+
* only live lock/registry ownership key.
|
|
53
|
+
* @param {string} [opts.semanticSessionId] — the semantic attribution/history
|
|
54
|
+
* label, surfaced separately and never used for ownership. When omitted, the
|
|
55
|
+
* field is populated by mirroring sessionId for backward-compatible display only.
|
|
54
56
|
* @param {string} opts.mode — session mode (e.g. "deep", "feature").
|
|
55
57
|
* @param {number} [opts.ttlHours=4] — lock TTL in hours.
|
|
56
58
|
* @param {Function} [opts._acquireImpl] — DI for tests (defaults to importing acquire from session-lock.mjs).
|
|
@@ -88,9 +90,10 @@ export async function bootstrapLock({
|
|
|
88
90
|
|
|
89
91
|
// Step 1: try to acquire. If a fresh acquire succeeds, we are done.
|
|
90
92
|
// If a stale-PID-dead/-alive lock exists, force-overwrite it (the prior
|
|
91
|
-
// session has died;
|
|
92
|
-
//
|
|
93
|
-
//
|
|
93
|
+
// session has died; the current raw owner can take the worktree).
|
|
94
|
+
// Only an exact match of the existing physical raw sessionId permits the
|
|
95
|
+
// same-session force-refresh. semantic_session_id, STATE.md `session`, and
|
|
96
|
+
// an owner proof never make a different raw id the same owner.
|
|
94
97
|
let acquireResult;
|
|
95
98
|
try {
|
|
96
99
|
// quiet: true suppresses the unknown-mode stderr WARN in acquire() (#592 MED-2).
|
|
@@ -169,9 +172,10 @@ export async function bootstrapLock({
|
|
|
169
172
|
// last_heartbeat is the basis for liveness — set to started_at on bootstrap
|
|
170
173
|
// so an immediate liveness check (< ttl_hours from now) succeeds.
|
|
171
174
|
last_heartbeat: startedAt,
|
|
172
|
-
// semantic_session_id is
|
|
173
|
-
// a UUID-v4
|
|
174
|
-
//
|
|
175
|
+
// semantic_session_id is an attribution/history label, normally semantic
|
|
176
|
+
// even when the physical session_id is a UUID-v4. Fall back to mirroring
|
|
177
|
+
// session_id only for backward-compatible display when no label was provided;
|
|
178
|
+
// it never changes the raw ownership key.
|
|
175
179
|
semantic_session_id:
|
|
176
180
|
typeof semanticSessionId === 'string' && semanticSessionId.length > 0
|
|
177
181
|
? semanticSessionId
|
|
@@ -190,7 +194,9 @@ export async function bootstrapLock({
|
|
|
190
194
|
// Step 2b (#987 Part 1): persist the durable ownership proof at lock
|
|
191
195
|
// genesis. `enriched` is byte-identical to the on-disk lock at this point
|
|
192
196
|
// (the v2 overlay never touches pid/host/started_at), so the proof written
|
|
193
|
-
// here will verify via isLockOwnedByProof() against any later re-read.
|
|
197
|
+
// here will verify via isLockOwnedByProof() against any later re-read. The
|
|
198
|
+
// proof is supplementary evidence only: it never bridges a raw-id mismatch
|
|
199
|
+
// through semantic_session_id or STATE.md `session` equality.
|
|
194
200
|
// This single call covers BOTH the plain-acquire and the forceAcquire
|
|
195
201
|
// branch — both flow through the enriched write above. Best-effort like
|
|
196
202
|
// the surrounding breadcrumb writes: writeOwnerProof() is no-throw by
|
package/hooks/hooks-codex.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"hooks": [
|
|
8
8
|
{
|
|
9
9
|
"type": "command",
|
|
10
|
-
"command": "echo '🎯 Session Orchestrator v3.
|
|
10
|
+
"command": "echo '🎯 Session Orchestrator v3.22.0 — /session [housekeeping|feature|deep] | /plan [new|feature|retro] | /discovery [scope] | /evolve [analyze|review|list]'",
|
|
11
11
|
"async": false
|
|
12
12
|
},
|
|
13
13
|
{
|