session-orchestrator 3.19.0 → 3.20.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/CHANGELOG.md +80 -0
- package/README.md +9 -9
- package/commands/session.md +6 -2
- package/docs/USER-GUIDE.md +1 -1
- package/docs/instruction-delivery.md +350 -0
- package/docs/session-config-reference.md +1 -41
- package/docs/session-config-template.md +0 -23
- package/hooks/_lib/guard-source-loader.mjs +304 -91
- package/hooks/enforce-commands.mjs +216 -17
- package/hooks/enforce-scope.mjs +133 -9
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/on-session-start.mjs +7 -4
- package/hooks/pre-bash-destructive-guard.mjs +146 -59
- package/hooks/pre-bash-sessions-ledger-guard.mjs +493 -66
- package/package.json +2 -2
- package/scripts/backfill-learnings-from-vault.mjs +967 -0
- package/scripts/emit-session.mjs +3 -40
- package/scripts/lib/command-blocker.mjs +322 -62
- package/scripts/lib/hardening.mjs +9 -9
- package/scripts/lib/learnings/affinity.mjs +434 -0
- package/scripts/lib/learnings/candidates.mjs +736 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +408 -53
- package/scripts/lib/learnings/judgment.mjs +782 -0
- package/scripts/lib/learnings/kebab.mjs +128 -0
- package/scripts/lib/learnings/select.mjs +550 -0
- package/scripts/lib/reconcile/emitter.mjs +107 -22
- package/scripts/lib/reconcile/engine.mjs +9 -15
- package/scripts/lib/reconcile/renderer.mjs +141 -25
- package/scripts/lib/reconcile/sanitize.mjs +518 -0
- package/scripts/lib/reconcile/writer.mjs +95 -1
- package/scripts/lib/scope-gate.mjs +194 -72
- package/scripts/lib/session-close-backfill.mjs +2 -2
- package/scripts/lib/session-record-repair.mjs +551 -0
- package/scripts/lib/session-schema/serializer.mjs +54 -0
- package/scripts/lib/session-schema.mjs +1 -0
- package/scripts/lib/session-token-rollup.mjs +68 -6
- package/scripts/lib/soul-resolve.mjs +12 -0
- package/scripts/lib/tmux-layout/telemetry.mjs +43 -10
- package/scripts/lib/validate/check-banner-parity.mjs +376 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +1148 -0
- package/scripts/lib/validate/check-learning-provenance.mjs +511 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +3 -3
- package/scripts/lib/validate/check-rules.mjs +31 -5
- package/scripts/lib/validate/check-unwired-features.mjs +549 -0
- package/scripts/print-applicable-rules.mjs +170 -7
- package/scripts/print-learnings-index.mjs +474 -0
- package/scripts/repair-invalid-sessions.mjs +209 -0
- package/scripts/sweep-expired-learnings.mjs +192 -32
- package/scripts/validate-plugin.mjs +21 -0
- package/skills/brainstorm/soul.md +47 -1
- package/skills/evolve/SKILL.md +116 -18
- package/skills/gitlab-ops/SKILL.md +5 -0
- package/skills/grill/soul.md +44 -1
- package/skills/plan/soul.md +46 -3
- package/skills/session-end/SKILL.md +1 -24
- package/skills/session-end/phase-3-6-tail.md +30 -1
- package/skills/session-end/plan-verification.md +1 -5
- package/skills/session-end/session-metrics-write.md +2 -0
- package/skills/session-start/SKILL.md +2 -0
- package/skills/session-start/soul.md +41 -1
- package/skills/wave-executor/SKILL.md +1 -5
- package/skills/wave-executor/wave-loop.md +36 -71
|
@@ -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.20.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.20.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.20.0+codex.20260813195914",
|
|
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",
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.20.0] - 2026-08-13
|
|
11
|
+
|
|
12
|
+
Memory-pipeline line. The learning store had been accumulating for 233 sessions and delivering
|
|
13
|
+
to nobody: 100 learnings, and not one read path into a dispatched agent. Closing that gap
|
|
14
|
+
surfaced two defects underneath it — agent-authored text reaching every agent's project
|
|
15
|
+
instructions unfiltered, and a prune path that deleted learnings into neither the store nor
|
|
16
|
+
the archive. The headline is not a feature; it is that the review panel then found the same
|
|
17
|
+
class **inside the fix**: this line hardened one delivery channel and shipped a second,
|
|
18
|
+
unhardened one alongside it.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Learnings reach wave agents (#1014)** — a per-agent index in the dispatch prompt, selected
|
|
23
|
+
from the agent's own declared file scope. Two-tier with **split** caps (scope-matched, then
|
|
24
|
+
top-scored fill), because only 17 of 100 learnings carry `file_paths` and a single shared cap
|
|
25
|
+
lets the global tier crowd out the per-agent guarantee that is the acceptance criterion.
|
|
26
|
+
Rides the channel the coordinator already composes — `docs/instruction-delivery.md` §5
|
|
27
|
+
measured a *separate* injection path at **+72.3%**; this one costs **+0.69% to +1.15%** of a
|
|
28
|
+
178,096-byte baseline. Factor 92. `LEARNINGS_INDEX_MAX_CHARS = 2000` is derived (1.12% of the
|
|
29
|
+
measured baseline, 0.92× the median rule file), not chosen. Delivery is observable rather than
|
|
30
|
+
assumed: `orchestrator.learnings.index.injected` carries entry count, scope-matched split and
|
|
31
|
+
byte size, so "did the injector run?" is a grep instead of an inference.
|
|
32
|
+
- **Semantic dedup + contradiction detection (#1016)** — IDF-Dice top-K candidate pool
|
|
33
|
+
(K=8, FLOOR=0.085), per-seed, bounded, deliberately **non-transitive**: even at K=3 the
|
|
34
|
+
similarity graph collapses into a 99-of-100 giant component, so a clustering pass returns
|
|
35
|
+
"the corpus". `type` neither filters nor boosts — both strongest ground-truth links are
|
|
36
|
+
cross-type, and a type gate drops connectivity 6/6 → 4/6 while still retaining 26% of pairs.
|
|
37
|
+
A German stoplist is mandatory, not cosmetic: without it the top-scoring pair in the whole
|
|
38
|
+
corpus is two records sharing function words. The judgment layer is fail-closed and atomic
|
|
39
|
+
(one malformed decision voids the batch, never the valid subset), and enforces structurally
|
|
40
|
+
that **rendering an AUQ from an unreadable judgment is itself a write** — the AUQ renderer
|
|
41
|
+
sits in the same effect map as the archive writer, behind one gate.
|
|
42
|
+
- **`scripts/print-learnings-index.mjs`**, **`scripts/backfill-learnings-from-vault.mjs`**,
|
|
43
|
+
**`scripts/lib/validate/check-learning-provenance.mjs`**, and a `--prune` mode on
|
|
44
|
+
`sweep-expired-learnings.mjs`. Five new leaf modules under `scripts/lib/learnings/`
|
|
45
|
+
(`affinity`, `select`, `candidates`, `judgment`, `kebab`).
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- **Agent-authored text is neutralised at the render point (#1015)** — the reconcile renderer
|
|
50
|
+
interpolated it verbatim into `.claude/rules/<slug>.md`, a file Claude Code then delivers to
|
|
51
|
+
every agent in every session as a project instruction, with no revocation: the frontmatter
|
|
52
|
+
gates are not a containment boundary for the body. Machine values now **reject** (a repaired
|
|
53
|
+
`learning-key` breaks idempotency; a dropped `globs[]` element can leave `globs: []`, which
|
|
54
|
+
the loader excludes everywhere — silently dead beats loudly rejected); prose is framed and
|
|
55
|
+
capped without a meaning filter, because a blocklist is the guard that looks green and does
|
|
56
|
+
not bite. Two premises in the issue text did not survive verification and are corrected in
|
|
57
|
+
the issue: `description` was already defended against newlines, and the frontmatter parser is
|
|
58
|
+
hand-rolled — so the newline is the *only* escape and a YAML-shaped sanitiser would have been
|
|
59
|
+
both wrong and destructive (14 `description` lines in the corpus carry a second colon).
|
|
60
|
+
- **The learning store is durable again (#1017)** — `/evolve` pruned by rewriting the store with
|
|
61
|
+
**no archive append**; 11 of 13 provenance pointers in generated rules resolved to nothing, in
|
|
62
|
+
neither store nor archive nor any backup. Both callers now share one `archiveThenRewrite()`
|
|
63
|
+
(KEEP-probe, archive **before** rewrite so a crash leaves the record in both places and never
|
|
64
|
+
in neither, `.bak` snapshot), and `pruneLearnings()` archives any record the caller's next
|
|
65
|
+
generation omits — reconciled by `id`, or by content fingerprint when a record carries no
|
|
66
|
+
usable one. **11 of 11 lost records were recovered** from the vault mirror with per-field
|
|
67
|
+
origin labelling; dangling pointers **11 → 0**.
|
|
68
|
+
- **Rule-block delivery boundaries were unrecoverable without any adversarial input** — rule
|
|
69
|
+
`content` is byte-identical to disk *including* its YAML fence, so joining rules on `---`
|
|
70
|
+
produced 56 separator-shaped lines for 18 rules. Replaced with per-rule fence tags carrying a
|
|
71
|
+
content-derived token and the source path (information an agent previously could not get at
|
|
72
|
+
all). An existing test had pinned the broken contract.
|
|
73
|
+
- **`learning_key` was derived five ways** on a *stored* contract, with the writer kebabbing the
|
|
74
|
+
`type` half and all four readers not. One shared derivation now; proven byte-identical across
|
|
75
|
+
107 records, all 13 stamped keys reproduced.
|
|
76
|
+
- **Two tracked `.mjs` files carried NUL bytes** and were therefore skipped **silently** by every
|
|
77
|
+
grep-based audit — exit 1, no output, which reads exactly like a clean result.
|
|
78
|
+
|
|
79
|
+
### Notes
|
|
80
|
+
|
|
81
|
+
- The review panel (security, qa, architect, independently) returned **FIX_REQUIRED** with four
|
|
82
|
+
HIGH blockers; all four were closed in one fix cycle, which found three further holes of the
|
|
83
|
+
same class. Every new guard is proven by fake regression, not by a green test.
|
|
84
|
+
- `CANDIDATE_FLOOR` is deliberately **unchanged** despite its justification no longer
|
|
85
|
+
reproducing: this line's own backfill moved every IDF weight, and the wave-1 ground-truth
|
|
86
|
+
labels were never persisted. A constant whose evidence has evaporated is not re-guessed from
|
|
87
|
+
the evaporated evidence (#1021).
|
|
88
|
+
- Follow-ups: #1018, #1019, #1020, #1021 — each with a named revisit trigger.
|
|
89
|
+
|
|
10
90
|
## [3.19.0] - 2026-08-04
|
|
11
91
|
|
|
12
92
|
Guard-hardening and release-mechanics line. Headline: the destructive-command guard closed
|
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
|
|
|
@@ -127,17 +127,17 @@ The system is markdown-driven config plus a thin Node runtime — skills, comman
|
|
|
127
127
|
- **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
128
|
- **VCS dual support, no lock-in.** Auto-detects GitLab or GitHub from your remote and drives the full lifecycle for both.
|
|
129
129
|
|
|
130
|
-
## Recent highlights (v3.
|
|
130
|
+
## Recent highlights (v3.20.0)
|
|
131
131
|
|
|
132
|
-
Every release is additive and backward-compatible. Highlights of the v3.
|
|
132
|
+
Every release is additive and backward-compatible. Highlights of the v3.20.0 line:
|
|
133
133
|
|
|
134
|
-
- **
|
|
135
|
-
- **
|
|
136
|
-
- **
|
|
137
|
-
- **
|
|
138
|
-
- **
|
|
134
|
+
- **Learnings finally reach the agents doing the work (#1014)** — 233 sessions of accumulated memory had exactly zero read paths into a dispatched agent. Now a per-agent index, selected from that agent's declared file scope, riding the dispatch prompt the coordinator already writes. Measured at **+0.69%–1.15%** of the prompt an agent already receives, against **+72.3%** for the separate-injection path the delivery doc forbids — factor 92. The character cap is derived from that measurement, not chosen, and delivery emits an event so "did it run?" is a grep rather than an inference.
|
|
135
|
+
- **Agent-authored text no longer reaches every agent unfiltered (#1015)** — the reconcile renderer wrote it verbatim into `.claude/rules/`, which Claude Code hands to every agent in every session, with no revocation. Machine values now reject, prose is framed and capped. Two premises in the issue itself did not survive verification and were corrected rather than implemented.
|
|
136
|
+
- **The learning store is durable again (#1017)** — `/evolve` pruned by rewriting with no archive append; **11 of 13 provenance pointers in generated rules resolved to nothing**. One shared archive-then-rewrite path, and **11 of 11 lost records recovered** from the vault mirror. Dangling pointers 11 → 0.
|
|
137
|
+
- **Semantic dedup + contradiction detection (#1016)** — a bounded, deliberately non-transitive candidate pool and a fail-closed judgment layer in which rendering an approval prompt from an unreadable verdict counts, structurally, as a write.
|
|
138
|
+
- **The review panel found the same class inside the fix** — three independent reviewers returned FIX_REQUIRED: this line had hardened one delivery channel and shipped a second, unhardened one beside it. Closed in one cycle, which surfaced three further holes of the same shape. Every new guard is proven by fake regression, not by a green test.
|
|
139
139
|
|
|
140
|
-
Previous line (v3.
|
|
140
|
+
Previous line (v3.19.0): guard hardening and release mechanics — six wrapper bypasses closed (#982), blocked-commands floor ∪ overlay (#972), session-lock ownership proof, and release as one dispatch (#978).
|
|
141
141
|
|
|
142
142
|
Full version history: [CHANGELOG.md](CHANGELOG.md).
|
|
143
143
|
|
package/commands/session.md
CHANGED
|
@@ -5,9 +5,13 @@ argument-hint: "[housekeeping|feature|deep]"
|
|
|
5
5
|
|
|
6
6
|
# Session Start
|
|
7
7
|
|
|
8
|
-
You are beginning a new development session. The user has invoked `/session` with type: **$ARGUMENTS** (if empty,
|
|
8
|
+
You are beginning a new development session. The user has invoked `/session` with type: **$ARGUMENTS** (if empty, default to **`deep`**).
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**Default rationale (measured, not assumed):** `deep` is the default because it is what operators actually run — 77.3 % of 489 recorded sessions across 5 repos, and 115 of 228 (50.4 %) in this repo's own `.orchestrator/metrics/sessions.jsonl`. The former `feature` default made the majority case the one that had to be typed out every time. A `deep` default costs a downgrade keystroke in the minority case; a `feature` default cost an upgrade keystroke in the majority case.
|
|
11
|
+
|
|
12
|
+
**Argument validation:** Valid session types are `housekeeping`, `feature`, and `deep`. An explicit `$ARGUMENTS` value ALWAYS wins over the default — `/session housekeeping` and `/session feature` behave exactly as before. If `$ARGUMENTS` is not empty and does not match any valid type, inform the user: "Invalid session type '$ARGUMENTS'. Valid types: housekeeping, feature, deep." Then fall back to `deep`.
|
|
13
|
+
|
|
14
|
+
> **Not read from Session Config.** There is deliberately no `session-type:` (or equivalent) key in the `## Session Config` block — `scripts/lib/config.mjs` `parseSessionConfig()` does not emit one, so any such key in a repo's CLAUDE.md (or its Codex CLI equivalent AGENTS.md) is inert prose. The `session-type:` scalar that IS live lives in STATE.md frontmatter (read by `scripts/print-applicable-rules.mjs` for rule mode-gating) and is written per session, not configured per repo. Do not reintroduce a Session Config key here without wiring it into the parser first.
|
|
11
15
|
|
|
12
16
|
## Resume Support
|
|
13
17
|
|
package/docs/USER-GUIDE.md
CHANGED
|
@@ -1297,7 +1297,7 @@ No. The orchestrator never commits code until you run `/close`. During wave exec
|
|
|
1297
1297
|
|
|
1298
1298
|
### What happens to unfinished work?
|
|
1299
1299
|
|
|
1300
|
-
During `/close`, any work that was planned but not completed is documented. The orchestrator creates carryover issues on your VCS platform with the title prefix `[Carryover]`, including context on what was done and
|
|
1300
|
+
During `/close`, any work that was planned but not completed is documented. The orchestrator creates carryover issues on your VCS platform with the title prefix `[Carryover]`, including context on what was done, what remains, and a mandatory Revisit-Trigger naming the condition that reopens the work. Nothing is silently dropped.
|
|
1301
1301
|
|
|
1302
1302
|
### Can I use this across multiple repos?
|
|
1303
1303
|
|
|
@@ -415,6 +415,337 @@ appears.
|
|
|
415
415
|
|
|
416
416
|
---
|
|
417
417
|
|
|
418
|
+
## 7. The learnings index is not a second delivery path (GitLab #1014)
|
|
419
|
+
|
|
420
|
+
> Measured **2026-08-13** at commit `c87102b`, branch `feat/memory-pipeline-1015-1014-1016`.
|
|
421
|
+
> `.claude/rules/` and `CLAUDE.md` were **clean at HEAD** for every measurement below
|
|
422
|
+
> (`git status --porcelain -- .claude/rules CLAUDE.md` → 0 lines); the dirty paths in the
|
|
423
|
+
> working tree were sibling agents' `scripts/` and `tests/` files, none of which this
|
|
424
|
+
> section measures.
|
|
425
|
+
|
|
426
|
+
§5 recommends against building a second delivery path, because injecting a scoped rule
|
|
427
|
+
block alongside undiminished native loading costs **+72%**. Issue #1014 then shipped an
|
|
428
|
+
injected block. This section exists to answer the obvious question — *is the learnings
|
|
429
|
+
index the thing §5 forbids?* — with numbers rather than with an assurance.
|
|
430
|
+
|
|
431
|
+
**It is not, by a factor of 92.** The forbidden path re-sends 122,875 bytes the agent is
|
|
432
|
+
already receiving. The index sends **1,405–1,727 bytes the agent receives nowhere else**:
|
|
433
|
+
**+0.79% to +0.97%** of the re-measured baseline.
|
|
434
|
+
|
|
435
|
+
> **⚠ Amendment, 2026-08-13 — every byte figure in §7 below is PRE-FRAMING.**
|
|
436
|
+
>
|
|
437
|
+
> §7 was measured at `c87102b`. A review panel then found that this block shipped
|
|
438
|
+
> agent-authored text with none of the neutralisation `<APPLICABLE-RULES>` had received in
|
|
439
|
+
> the same session — no fence, no wrapper-forgery rejection, no invisible-character
|
|
440
|
+
> stripping. The fix added a content-derived block fence and a framing preamble, at a
|
|
441
|
+
> **constant +320 B** independent of entry count.
|
|
442
|
+
>
|
|
443
|
+
> Re-measured by the coordinator after the fix (2 scopes, `wc -c` on the real CLI):
|
|
444
|
+
> `scripts/lib/reconcile/**` → **2,047 B** (was 1,727); `docs/**` → **1,234 B** (was 914).
|
|
445
|
+
>
|
|
446
|
+
> | | §7 as measured | after framing |
|
|
447
|
+
> |---|---:|---:|
|
|
448
|
+
> | band | 912–1,727 B | **1,234–2,047 B** |
|
|
449
|
+
> | share of the 178,096 B baseline | +0.51% – +0.97% | **+0.69% – +1.15%** |
|
|
450
|
+
> | ceiling (entry caps lifted) | 2,221 B / +1.25% | **2,541 B / +1.43%** |
|
|
451
|
+
>
|
|
452
|
+
> The 92× argument is unaffected — the comparison is against +122,875 B, and +320 B does not
|
|
453
|
+
> move it. `LEARNINGS_INDEX_MAX_CHARS = 2000` is likewise untouched: the framing sits outside
|
|
454
|
+
> the capped body, as the header and retrieval pointer already did.
|
|
455
|
+
>
|
|
456
|
+
> The per-scope table in §7.2 and the figures in §7.4 are left at their measured values rather
|
|
457
|
+
> than overwritten, so each keeps the SHA it was taken at. Read them as pre-framing.
|
|
458
|
+
|
|
459
|
+
### 7.1 The baseline, re-measured — and it did not drift
|
|
460
|
+
|
|
461
|
+
The wave-1 baseline was captured at `0fbea29`. Re-measuring at `c87102b` (3 commits later,
|
|
462
|
+
`git rev-list --count 0fbea29..HEAD` → `3`) gives a **byte-identical** rule corpus:
|
|
463
|
+
|
|
464
|
+
```console
|
|
465
|
+
$ find .claude/rules -name '*.md' | wc -l
|
|
466
|
+
29
|
|
467
|
+
$ find .claude/rules -name '*.md' -exec cat {} + | wc -c
|
|
468
|
+
165097
|
|
469
|
+
$ wc -c CLAUDE.md
|
|
470
|
+
9666
|
|
471
|
+
$ wc -c ~/.claude/CLAUDE.md
|
|
472
|
+
1220
|
|
473
|
+
$ sed -n '266,286p' skills/wave-executor/SKILL.md | wc -c # the fenced memory.propose block
|
|
474
|
+
2113
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
| Component | Bytes @ `0fbea29` | Bytes @ `c87102b` | Drift | Fires |
|
|
478
|
+
|---|---:|---:|---:|---|
|
|
479
|
+
| `.claude/rules/**` (29 files) | 165,097 | 165,097 | 0 | every agent, native project-instruction loading |
|
|
480
|
+
| `CLAUDE.md` / `AGENTS.md` (root) | 9,666 | 9,666 | 0 | every agent |
|
|
481
|
+
| `~/.claude/CLAUDE.md` (user-level) | 1,220 | 1,220 | 0 | every agent on this host, outside repo control |
|
|
482
|
+
| `memory.propose` boilerplate | 2,112 | **2,113** | +1 | every Impl-Core / Impl-Polish / Quality agent |
|
|
483
|
+
| **TOTAL** | **178,095** | **178,096** | **+1** | per Impl-wave agent, before task text |
|
|
484
|
+
|
|
485
|
+
The corpus figures are reproducible from the git object store rather than from the working
|
|
486
|
+
tree, which is what makes the zero-drift claim checkable:
|
|
487
|
+
|
|
488
|
+
```console
|
|
489
|
+
$ git ls-tree -r --name-only 0fbea29 -- .claude/rules | grep '\.md$' \
|
|
490
|
+
| while read f; do git cat-file blob "0fbea29:$f"; done | wc -c
|
|
491
|
+
165097
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
The single byte is the boilerplate's trailing newline: the block is lines 266–286 of
|
|
495
|
+
`skills/wave-executor/SKILL.md`, and wave 1 evidently measured it without the final `\n`.
|
|
496
|
+
Recording it rather than rounding it away is the point — a figure that reproduces to ±1
|
|
497
|
+
byte across two SHAs is a measurement; one that "matches" is an assertion.
|
|
498
|
+
|
|
499
|
+
**The rules corpus did not change this session.** The 100-record learnings corpus did. Those
|
|
500
|
+
are different corpora, and only the second one is what §7 adds.
|
|
501
|
+
|
|
502
|
+
### 7.2 What the index actually costs, per scope
|
|
503
|
+
|
|
504
|
+
Four **disjoint** agent file scopes, each naming real tracked files
|
|
505
|
+
(`git ls-files --error-unmatch` → TRACKED for all eight paths):
|
|
506
|
+
|
|
507
|
+
```console
|
|
508
|
+
$ echo '["hooks/pre-bash-destructive-guard.mjs","hooks/on-session-start.mjs"]' > /tmp/scope-hooks.json
|
|
509
|
+
$ node scripts/print-learnings-index.mjs --file-scope /tmp/scope-hooks.json --no-event | wc -c
|
|
510
|
+
1405
|
|
511
|
+
$ node scripts/print-learnings-index.mjs --file-scope /tmp/scope-hooks.json --no-event --json # count + scopeMatched
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
| Agent file scope | Bytes | Entries | Scoped | Global fill | % of 178,096 |
|
|
515
|
+
|---|---:|---:|---:|---:|---:|
|
|
516
|
+
| `hooks/**` (2 files) | 1,405 | 7 | 3 | 4 | +0.789% |
|
|
517
|
+
| `scripts/lib/reconcile/**` (2 files) | 1,727 | 9 | 5 | 4 | +0.970% |
|
|
518
|
+
| `tests/**` (2 files) | 912 | 4 | 0 | 4 | +0.512% |
|
|
519
|
+
| `docs/**` (2 files — this agent's own scope) | 914 | 4 | 0 | 4 | +0.513% |
|
|
520
|
+
| *control:* `tests/**` naming two paths the corpus does carry | 1,240 | 6 | 2 | 4 | +0.696% |
|
|
521
|
+
|
|
522
|
+
The corpus behind these numbers, measured rather than quoted:
|
|
523
|
+
|
|
524
|
+
```console
|
|
525
|
+
$ wc -l < .orchestrator/metrics/learnings.jsonl
|
|
526
|
+
100
|
|
527
|
+
$ node -e '…JSON.parse each line; count Array.isArray(r.file_paths) && r.file_paths.length>0…'
|
|
528
|
+
total=100 with_file_paths=17 pct=17.0%
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**17 of 100 records carry `file_paths`.** That fraction, not the CLI, is what governs the
|
|
532
|
+
scoped column — and the `tests/**` row is the honest demonstration. It returned **0 scoped
|
|
533
|
+
matches** even though the corpus holds 6 `tests/` path entries, because those 6 name
|
|
534
|
+
different files (`tests/lib/session-registry.test.mjs`,
|
|
535
|
+
`tests/scripts/gates/gate-helpers.test.mjs`, …) than the two a plausible agent declared.
|
|
536
|
+
The control row re-runs the same scope against two paths the corpus *does* carry and
|
|
537
|
+
recovers 2 scoped matches. Matching is exact-path; the 0 is correct behaviour, not a defect.
|
|
538
|
+
|
|
539
|
+
The practical reading: **at 17% `file_paths` coverage, a majority of scopes will fall back
|
|
540
|
+
entirely to the global fill.** The index degrades to "top 4 general learnings" rather than
|
|
541
|
+
to nothing, which is the right failure mode, but it is not yet per-agent for most agents.
|
|
542
|
+
The lever is `--file-paths` adoption at proposal time, not the selector.
|
|
543
|
+
|
|
544
|
+
### 7.3 The delta, and the comparison that settles the question
|
|
545
|
+
|
|
546
|
+
Absolute: **+912 to +1,727 bytes**, ceiling **+2,221** (§7.4). As a share of the
|
|
547
|
+
re-measured 178,096-byte baseline: **+0.51% to +0.97%**, ceiling **+1.25%**.
|
|
548
|
+
|
|
549
|
+
| Path | Added bytes | As % of baseline |
|
|
550
|
+
|---|---:|---:|
|
|
551
|
+
| §5's forbidden path — scoped rule block alongside native delivery | +122,875 | **+72.3%** |
|
|
552
|
+
| §7's learnings index — measured worst case of four scopes | +1,727 | **+0.97%** |
|
|
553
|
+
|
|
554
|
+
**92×.** Two structural facts produce that gap, and each is checkable:
|
|
555
|
+
|
|
556
|
+
1. **It rides a channel the repo already writes itself.** The index is prepended to the
|
|
557
|
+
dispatch prompt — the same channel that already carries the task text and the 2,113-byte
|
|
558
|
+
`memory.propose` boilerplate. It does not add a delivery mechanism; it adds a block to a
|
|
559
|
+
prompt the coordinator was already composing. §5's +72% is entirely the cost of
|
|
560
|
+
*duplicating* a channel that keeps delivering regardless.
|
|
561
|
+
2. **Learnings have no native delivery to duplicate.** `.orchestrator/metrics/learnings.jsonl`
|
|
562
|
+
is not a `.md` file under `.claude/rules/`, and neither `CLAUDE.md` nor its Codex-CLI
|
|
563
|
+
alias `AGENTS.md` imports it (`grep -c "^@" CLAUDE.md` → `0`, unchanged from §1). Nothing
|
|
564
|
+
in the 178,096-byte baseline carries this content. The index is the *first* delivery of
|
|
565
|
+
it, not the second.
|
|
566
|
+
|
|
567
|
+
#### 7.3.1 …except for 13 records, and the index does not exclude them
|
|
568
|
+
|
|
569
|
+
Fact 2 is true of the corpus but not of every record in it. `/reconcile` converts qualifying
|
|
570
|
+
learnings into `.claude/rules/*.md` files — which **are** natively delivered. Those rules
|
|
571
|
+
carry their source in provenance, so the overlap is measurable:
|
|
572
|
+
|
|
573
|
+
```console
|
|
574
|
+
$ grep -rlha "learning-id:" .claude/rules/*.md | wc -l
|
|
575
|
+
13
|
|
576
|
+
$ grep -rha "^- learning-id:" .claude/rules/*.md | sed 's/.*`\(.*\)`.*/\1/' > /tmp/rule-lids.txt
|
|
577
|
+
$ # then: for each scope, intersect the index's learning ids with /tmp/rule-lids.txt
|
|
578
|
+
hooks: entries=7 already_a_rule=1
|
|
579
|
+
scripts: entries=9 already_a_rule=0
|
|
580
|
+
tests: entries=4 already_a_rule=0
|
|
581
|
+
docs: entries=4 already_a_rule=1
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**13 of 100 learnings have already become natively-delivered rules, and the selector does
|
|
585
|
+
not filter them out.** In two of four scopes, one indexed entry was content the agent was
|
|
586
|
+
already receiving in full — a genuine, if small, second delivery of that record. The
|
|
587
|
+
duplicated unit is one ~150-byte index line against a multi-KB rule file, so the waste is
|
|
588
|
+
bounded and far below the measurement noise of §7.1; it does not change the +0.97% figure
|
|
589
|
+
or the 92× conclusion. But it is a real instance of the exact pattern §5 forbids, found by
|
|
590
|
+
looking for it, and it is cheap to close: the selector has each record's id and the rule
|
|
591
|
+
files carry theirs. **Filed as a follow-up rather than fixed here — this section's file
|
|
592
|
+
scope is documentation, and the fix is a change to `scripts/lib/learnings/select.mjs`.**
|
|
593
|
+
|
|
594
|
+
### 7.4 The 2,000-character cap is slack; the entry-count caps bind
|
|
595
|
+
|
|
596
|
+
`LEARNINGS_INDEX_MAX_CHARS = 2000` (`scripts/lib/learnings/select.mjs:89`) is a hard cap on
|
|
597
|
+
the rendered **body**. Under the shipped defaults it is never reached:
|
|
598
|
+
|
|
599
|
+
```console
|
|
600
|
+
$ node scripts/print-learnings-index.mjs --file-scope /tmp/scope-hooks.json --no-event | wc -c
|
|
601
|
+
1405
|
|
602
|
+
$ node scripts/print-learnings-index.mjs --file-scope /tmp/scope-hooks.json --no-event \
|
|
603
|
+
--max-scoped 100 --max-global 100 | wc -c
|
|
604
|
+
2221
|
|
605
|
+
$ node scripts/print-learnings-index.mjs --file-scope /tmp/scope-hooks.json --no-event \
|
|
606
|
+
--max-scoped 100 --max-global 100 --max-chars 100000 | wc -c
|
|
607
|
+
15588
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
| Constraint | Result | Binds? |
|
|
611
|
+
|---|---:|---|
|
|
612
|
+
| default caps (8 scoped / 4 global / 2,000 chars) | 1,405–1,727 B | entry caps bind |
|
|
613
|
+
| entry caps lifted, char cap at 2,000 | 2,221 B | **char cap binds** |
|
|
614
|
+
| both lifted (whole corpus as an index) | 15,588 B | nothing binds |
|
|
615
|
+
|
|
616
|
+
The binding constraint under defaults is **`--max-global 4`**, not the char cap. The
|
|
617
|
+
telemetry's `truncated: true` says so precisely: it is set at `select.mjs:389` by
|
|
618
|
+
`scoped.length > maxScoped || global.length > maxGlobal` — an **entry-count** overflow, not
|
|
619
|
+
a character overflow. Reading `truncated: true` on a 1,405-byte block as "the 2,000 cap bit"
|
|
620
|
+
would be wrong.
|
|
621
|
+
|
|
622
|
+
Slack, stated plainly: **816 bytes of body headroom on the `hooks` scope, 494 on `scripts`**
|
|
623
|
+
(the rendered block carries a 221-byte header outside the capped body, which is why the
|
|
624
|
+
lifted-caps run reports 2,221 rather than 2,000). The cap is a backstop against a corpus
|
|
625
|
+
that grows an order of magnitude — at 100 records it never fires. The number that would
|
|
626
|
+
make it fire is 15,588: the whole corpus rendered as an index is **7.8×** the cap, so the
|
|
627
|
+
cap is doing real work as a ceiling even while slack today.
|
|
628
|
+
|
|
629
|
+
### 7.5 The per-agent claim, verified empirically
|
|
630
|
+
|
|
631
|
+
Not read off the code — run, on two disjoint scopes, comparing the emitted subjects:
|
|
632
|
+
|
|
633
|
+
```console
|
|
634
|
+
$ # hooks scope
|
|
635
|
+
## Learnings Index (selected for your file scope)
|
|
636
|
+
|
|
637
|
+
7 entries (3 matched your declared file scope, 4 general). One line each — this is an INDEX, not the corpus.
|
|
638
|
+
Full text of any line: `grep -F '"subject":"<subject>"' .orchestrator/metrics/learnings.jsonl`
|
|
639
|
+
|
|
640
|
+
- anti-pattern/an inline @returns-never warn helper at a rule-loop warn site flips a later block to ALLOW: …
|
|
641
|
+
- anti-pattern/policy rule with a new type field must ship with its consuming hook branch in the same change: …
|
|
642
|
+
- anti-pattern/release() proof gate hinges on proof !== undefined — every call site must spread-guard: …
|
|
643
|
+
[4 more]
|
|
644
|
+
|
|
645
|
+
$ # scripts scope
|
|
646
|
+
## Learnings Index (selected for your file scope)
|
|
647
|
+
|
|
648
|
+
9 entries (5 matched your declared file scope, 4 general). One line each — this is an INDEX, not the corpus.
|
|
649
|
+
Full text of any line: `grep -F '"subject":"<subject>"' .orchestrator/metrics/learnings.jsonl`
|
|
650
|
+
|
|
651
|
+
- anti-pattern/A hardcoded expected value that coincides with a clamp/floor for one day is a green that proves nothing: …
|
|
652
|
+
- proven-pattern/security-floor policy loaders must merge, not first-hit-resolve: …
|
|
653
|
+
- recurring-issue/Eine Session ohne durchgelaufenes /close hinterlaesst unpushed Commits …
|
|
654
|
+
[6 more]
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
Set-compared rather than eyeballed:
|
|
658
|
+
|
|
659
|
+
```console
|
|
660
|
+
$ # intersect the two --json subject lists
|
|
661
|
+
hooks entries: 7 | scripts entries: 9
|
|
662
|
+
intersection: 2 | hooks-only: 5 | scripts-only: 7 | Jaccard: 0.143
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Two disjoint scopes share 2 of 14 distinct entries (Jaccard 0.143).** Both shared entries
|
|
666
|
+
are global fill; the scoped selections are fully disjoint. The block is genuinely
|
|
667
|
+
per-agent, not a constant wearing a scope's name — which is the claim §3.1 could *not* make
|
|
668
|
+
about glob-scoped rules, where the "scoped" saving turned out to be a constant 40,254 bytes
|
|
669
|
+
of tier filtering.
|
|
670
|
+
|
|
671
|
+
### 7.6 The telemetry answers "did the injector ever run?"
|
|
672
|
+
|
|
673
|
+
§1 could establish that `rule-loader.mjs` does not run at delivery time only by grepping for
|
|
674
|
+
the absence of a caller. That is an argument from silence, and §1.2 records how it failed:
|
|
675
|
+
a census keyed on the payload missed the prose call site entirely. The index does not
|
|
676
|
+
require that argument — it emits an event:
|
|
677
|
+
|
|
678
|
+
```console
|
|
679
|
+
$ grep -ac 'orchestrator.learnings.index.injected' .orchestrator/metrics/events.jsonl
|
|
680
|
+
1
|
|
681
|
+
$ node scripts/print-learnings-index.mjs --file-scope /tmp/scope-hooks.json > /dev/null
|
|
682
|
+
$ grep -ac 'orchestrator.learnings.index.injected' .orchestrator/metrics/events.jsonl
|
|
683
|
+
2
|
|
684
|
+
$ grep -a 'orchestrator.learnings.index.injected' .orchestrator/metrics/events.jsonl | tail -1
|
|
685
|
+
{
|
|
686
|
+
"timestamp": "2026-08-13T05:53:29.824Z",
|
|
687
|
+
"event": "orchestrator.learnings.index.injected",
|
|
688
|
+
"count": 7,
|
|
689
|
+
"scope_matched": 3,
|
|
690
|
+
"global_count": 4,
|
|
691
|
+
"candidates": 94,
|
|
692
|
+
"truncated": true,
|
|
693
|
+
"bytes": 1405,
|
|
694
|
+
"scope_source": "file-scope"
|
|
695
|
+
}
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
The counter moves 1 → 2 on one invocation, and the record's `bytes: 1405` is independently
|
|
699
|
+
equal to this section's `wc -c`. "Did it run, on what scope, and how big was it" is now a
|
|
700
|
+
`grep` over `events.jsonl` instead of an inference. `candidates: 94` also shows the pool is
|
|
701
|
+
the 94 *active* records of the 100 on disk — 6 are filtered before ranking.
|
|
702
|
+
|
|
703
|
+
### 7.7 What is NOT established — explicitly unbelegt
|
|
704
|
+
|
|
705
|
+
Four gaps. Each would move the denominator, and none is closed by anything above.
|
|
706
|
+
|
|
707
|
+
- **Whether this Claude Code build loads a root `AGENTS.md`.** One exists on disk —
|
|
708
|
+
`ls -la AGENTS.md` → 14,661 bytes, **untracked** (`git ls-files AGENTS.md | wc -l` → `0`).
|
|
709
|
+
The coordinator of this session confirmed from its own context window that it is *not*
|
|
710
|
+
delivered to the coordinator. **Whether a dispatched subagent receives it is unverified**,
|
|
711
|
+
and this agent cannot settle it: a subagent introspecting its own context is the
|
|
712
|
+
self-report §1.1 already flagged as corroborating-not-load-bearing. If subagents do
|
|
713
|
+
receive it, the baseline is 192,757 rather than 178,096 and the index's share *falls* to
|
|
714
|
+
+0.73%. Tracked as issue **#973**.
|
|
715
|
+
- **Whether the `<APPLICABLE-RULES>` block fires in practice.** `wave-loop.md` makes the
|
|
716
|
+
pre-dispatch injection a SHOULD, not a gate. In **this** session it was deliberately
|
|
717
|
+
skipped and logged as a deviation, so the 178,096 figure **excludes it**. If it fires, the
|
|
718
|
+
denominator grows by up to the 122,875 bytes §3 measured for a live wave scope and the
|
|
719
|
+
index's share falls correspondingly — to roughly +0.57%. Every percentage in §7.3 is
|
|
720
|
+
therefore a *conservative* upper bound on the index's share: the honest reading is
|
|
721
|
+
"≤1% under the smallest defensible denominator".
|
|
722
|
+
- **Whether 17% `file_paths` coverage is representative.** It is the coverage of a 100-record
|
|
723
|
+
corpus, 11 of whose records were recovered from a data-loss incident. Whether the recovered
|
|
724
|
+
records are systematically poorer in `file_paths` than organically-proposed ones was not
|
|
725
|
+
measured, and it would bias the scoped column if so.
|
|
726
|
+
- **Whether the §7.3.1 rule/learning overlap grows.** Measured once, at 13/100, on four
|
|
727
|
+
scopes. `/reconcile` promotes learnings into rules continuously, so this ratio rises by
|
|
728
|
+
construction. Nothing currently measures it on a schedule.
|
|
729
|
+
|
|
730
|
+
### 7.8 Correction to §1.3
|
|
731
|
+
|
|
732
|
+
§1.3 records a second delivery source outside this repo — the parent workspace's
|
|
733
|
+
`.claude/rules/parallel-sessions.md`, 5,623 bytes — and concludes the real delivered corpus
|
|
734
|
+
is 175,584 rather than 169,961. **That file no longer exists on this host:**
|
|
735
|
+
|
|
736
|
+
```console
|
|
737
|
+
$ ls -la "$(dirname "$PWD")"/.claude/rules/
|
|
738
|
+
ls: /Users/…/Projects/.claude/rules/: No such file or directory
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
§7's baseline therefore does not carry a parent-workspace term. This is a host-local
|
|
742
|
+
observation about one machine at one date, not proof the mechanism is gone — the directory
|
|
743
|
+
could be recreated at any time, and §1.3's structural point (that
|
|
744
|
+
`print-applicable-rules.mjs` reads only `<repoRoot>/.claude/rules` and cannot see such a
|
|
745
|
+
file) stands unchanged.
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
418
749
|
## Reproducing this document
|
|
419
750
|
|
|
420
751
|
```bash
|
|
@@ -434,6 +765,23 @@ node scripts/print-applicable-rules.mjs --wave-scope /tmp/scope.json | wc -c #
|
|
|
434
765
|
|
|
435
766
|
# blast radius
|
|
436
767
|
git grep -l "\.claude/rules/" | wc -l
|
|
768
|
+
|
|
769
|
+
# §7 — baseline re-measure, reproducible from the object store at any SHA
|
|
770
|
+
git ls-tree -r --name-only <sha> -- .claude/rules | grep '\.md$' \
|
|
771
|
+
| while read f; do git cat-file blob "<sha>:$f"; done | wc -c
|
|
772
|
+
sed -n '266,286p' skills/wave-executor/SKILL.md | wc -c # memory.propose boilerplate
|
|
773
|
+
|
|
774
|
+
# §7 — the learnings index for one agent scope (never writes; --no-event suppresses telemetry)
|
|
775
|
+
echo '["hooks/on-session-start.mjs"]' > /tmp/scope.json
|
|
776
|
+
node scripts/print-learnings-index.mjs --file-scope /tmp/scope.json --no-event | wc -c
|
|
777
|
+
node scripts/print-learnings-index.mjs --file-scope /tmp/scope.json --no-event --json
|
|
778
|
+
|
|
779
|
+
# §7.4 — which cap binds
|
|
780
|
+
node scripts/print-learnings-index.mjs --file-scope /tmp/scope.json --no-event \
|
|
781
|
+
--max-scoped 100 --max-global 100 --max-chars 100000 | wc -c
|
|
782
|
+
|
|
783
|
+
# §7.6 — did the injector run?
|
|
784
|
+
grep -ac 'orchestrator.learnings.index.injected' .orchestrator/metrics/events.jsonl
|
|
437
785
|
```
|
|
438
786
|
|
|
439
787
|
## See also
|
|
@@ -442,3 +790,5 @@ git grep -l "\.claude/rules/" | wc -l
|
|
|
442
790
|
- `scripts/print-applicable-rules.mjs` — the CLI bridge; reads only `<repoRoot>/.claude/rules`
|
|
443
791
|
- `scripts/lib/instruction-budget-guard.mjs` — the existing measurement + ceiling instrument
|
|
444
792
|
- `docs/rule-authoring.md` — frontmatter contract (and the stale claim at line 8)
|
|
793
|
+
- `scripts/print-learnings-index.mjs` — §7's per-agent index CLI; emits `orchestrator.learnings.index.injected`
|
|
794
|
+
- `scripts/lib/learnings/select.mjs` — the selector; `LEARNINGS_INDEX_MAX_CHARS = 2000` at line 89, `truncated` at line 389
|
|
@@ -74,7 +74,7 @@ Some sub-configs live in dedicated policy files under `.orchestrator/policy/`:
|
|
|
74
74
|
|
|
75
75
|
| Field | Type | Default | Description |
|
|
76
76
|
|-------|------|---------|-------------|
|
|
77
|
-
| `agents-per-wave` | integer or integer with overrides | `6` | Maximum parallel subagents per wave. Supports session-type overrides: `6 (deep: 18)` outputs `{"default": 6, "deep": 18}`. Plain integers remain plain. |
|
|
77
|
+
| `agents-per-wave` | integer or integer with overrides | `6` | Maximum parallel subagents per wave. Supports session-type overrides: `6 (deep: 18)` outputs `{"default": 6, "deep": 18}`. Plain integers remain plain. The override key names a session type but does **not** create one: there is no `session-type:` Session Config key — `parseSessionConfig()` emits none, so writing one into a repo's `## Session Config` block is inert prose. The session type comes from the `/session` argument (default `deep`, see `commands/session.md`) and is persisted to STATE.md frontmatter as `session-type:`, which is the only live read (`scripts/print-applicable-rules.mjs` rule mode-gating). |
|
|
78
78
|
| `agent-mapping` | object | null | Optional mapping of role keys to agent names for explicit agent binding. Keys: `impl`, `test`, `db`, `ui`, `security`, `compliance`, `docs`, `perf`. Example: `{ impl: code-editor, test: test-specialist }`. Overrides auto-discovery when present. |
|
|
79
79
|
| `waves` | integer | `5` | Number of execution waves for feature and deep sessions. |
|
|
80
80
|
| `recent-commits` | integer | `20` | Number of recent commits to display during session start git analysis. |
|
|
@@ -1638,46 +1638,6 @@ Leave disabled (default) when:
|
|
|
1638
1638
|
- `skills/wave-executor/wave-loop.md` § 3b — the wave-executor hook contract.
|
|
1639
1639
|
- `agents/schemas/persona-panel-sidecar.schema.json` — sidecar JSON Schema enforced before write.
|
|
1640
1640
|
|
|
1641
|
-
## Compact Nudge (#620)
|
|
1642
|
-
|
|
1643
|
-
Advisory-only checkpoint surfaced at inter-wave boundaries in the wave-executor loop. Never auto-compacts — `/compact` is a user slash-command, and the coordinator/operator decides when to invoke it. When the gate conditions are met, the wave-executor appends ONE advisory bullet to the wave progress update suggesting a `/compact` before the next wave.
|
|
1644
|
-
|
|
1645
|
-
All fields live under a top-level `compact-nudge` object in your Session Config host file (`CLAUDE.md` or `AGENTS.md`), for example:
|
|
1646
|
-
|
|
1647
|
-
```yaml
|
|
1648
|
-
compact-nudge:
|
|
1649
|
-
enabled: false # opt-in advisory /compact nudge at inter-wave checkpoints (never auto-compacts)
|
|
1650
|
-
after: [discovery, impl] # wave boundaries that may fire the nudge — subset of {discovery, impl, failed-wave}
|
|
1651
|
-
mode: warn # warn (surface one bullet in the wave progress update) | off (silent no-op)
|
|
1652
|
-
```
|
|
1653
|
-
|
|
1654
|
-
| Field | Type | Default | Description |
|
|
1655
|
-
|-------|------|---------|-------------|
|
|
1656
|
-
| `compact-nudge.enabled` | boolean | `false` | Master toggle. When `false` (or the block is absent), the nudge never fires — zero behaviour change. |
|
|
1657
|
-
| `compact-nudge.after` | list of `"discovery"` \| `"impl"` \| `"failed-wave"` | `[discovery, impl]` | Wave boundaries that may fire the nudge. `discovery`/`impl` are wave ROLES, matched against the just-completed wave's role string. `failed-wave` is not a role — it keys off the wave's failure OUTCOME (any wave that did not pass its quality gate), so it can fire after a wave of any role. |
|
|
1658
|
-
| `compact-nudge.mode` | `warn` \| `off` | `warn` | `warn` emits the advisory bullet in the wave progress update. `off` is a silent no-op even when `enabled: true`. |
|
|
1659
|
-
|
|
1660
|
-
**Used by:** `skills/wave-executor/wave-loop.md` § 3c "Strategic Compact-Nudge". Issue #620. See `.claude/rules/loop-and-monitor.md` for the broader `/loop` vs `/goal` vs Monitor routing this nudge composes with.
|
|
1661
|
-
|
|
1662
|
-
## Goal Integration (#636)
|
|
1663
|
-
|
|
1664
|
-
Opt-in advisory continuation anchor that surfaces a suggested `/goal` command at named seams — the inter-wave fix-loop (`inter-wave-fixloop`) and the session-end backlog drain (`session-end-backlog`). Never auto-invokes `/goal`, never blocks forward progress; `/goal` remains a user slash-command the operator chooses to run. Per ADR-0010, `/goal` provides CONTINUATION, never JUDGMENT — the suggested condition always references freshly-run deterministic gate output and embeds a bound (e.g. "or stop after N attempts"); the exit-code result of the underlying gate stays the authority.
|
|
1665
|
-
|
|
1666
|
-
All fields live under a top-level `goal-integration` object in your Session Config host file (`CLAUDE.md` or `AGENTS.md`), for example:
|
|
1667
|
-
|
|
1668
|
-
```yaml
|
|
1669
|
-
goal-integration:
|
|
1670
|
-
enabled: false # opt-in advisory; default off — zero behaviour change when absent
|
|
1671
|
-
seams: [session-end-backlog, inter-wave-fixloop] # subset of {session-end-backlog, inter-wave-fixloop}; one goal per session — pick ONE seam at a time
|
|
1672
|
-
```
|
|
1673
|
-
|
|
1674
|
-
| Field | Type | Default | Description |
|
|
1675
|
-
|-------|------|---------|-------------|
|
|
1676
|
-
| `goal-integration.enabled` | boolean | `false` | Master toggle. When `false` (or the block is absent), no `/goal` suggestion is ever surfaced — zero behaviour change. |
|
|
1677
|
-
| `goal-integration.seams` | list of `"session-end-backlog"` \| `"inter-wave-fixloop"` | `[session-end-backlog, inter-wave-fixloop]` | Which seam(s) may surface the advisory `/goal` suggestion. Only ONE `/goal` can be active per session — if both seams are listed, the operator picks a single seam to actually invoke; the two cannot hold simultaneous active goals. |
|
|
1678
|
-
|
|
1679
|
-
**Used by:** `skills/wave-executor/wave-loop.md` § "/goal Continuation Anchor" (inter-wave-fixloop seam), `skills/session-end/SKILL.md` § 1.3a "Optional /goal Backlog-Drain" (session-end-backlog seam). Lever 5 / issue #636. See `.claude/rules/loop-and-monitor.md` § LM-008 for the full continuation-vs-judgment contract.
|
|
1680
|
-
|
|
1681
1641
|
## Skill Evolution (#646)
|
|
1682
1642
|
|
|
1683
1643
|
Opt-in configuration for the Skill Self-Evolution Foundation (Epic #643, Sub-issue #646). Controls whether `/evolve` surfaces skill health signals for operator review only (`advisory`) or additionally applies deterministic repairs to local config artifacts behind an evidence gate (`autonomous-gated`). The default is `off` — no behavior change for repos that omit this block.
|