instar 1.3.175 → 1.3.177

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.175",
3
+ "version": "1.3.177",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-31T19:05:35.298Z",
5
- "instarVersion": "1.3.175",
4
+ "generatedAt": "2026-05-31T20:20:49.223Z",
5
+ "instarVersion": "1.3.177",
6
6
  "entryCount": 193,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -11,7 +11,7 @@
11
11
  "domain": "identity",
12
12
  "sourcePath": "src/core/PostUpdateMigrator.ts",
13
13
  "installedPath": ".instar/hooks/instar/session-start.sh",
14
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
14
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
15
15
  "since": "2025-01-01"
16
16
  },
17
17
  "hook:dangerous-command-guard": {
@@ -20,7 +20,7 @@
20
20
  "domain": "safety",
21
21
  "sourcePath": "src/core/PostUpdateMigrator.ts",
22
22
  "installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
23
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
23
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
24
24
  "since": "2025-01-01"
25
25
  },
26
26
  "hook:grounding-before-messaging": {
@@ -29,7 +29,7 @@
29
29
  "domain": "safety",
30
30
  "sourcePath": "src/core/PostUpdateMigrator.ts",
31
31
  "installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
32
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
32
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
33
33
  "since": "2025-01-01"
34
34
  },
35
35
  "hook:compaction-recovery": {
@@ -38,7 +38,7 @@
38
38
  "domain": "identity",
39
39
  "sourcePath": "src/core/PostUpdateMigrator.ts",
40
40
  "installedPath": ".instar/hooks/instar/compaction-recovery.sh",
41
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
41
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
42
42
  "since": "2025-01-01"
43
43
  },
44
44
  "hook:external-operation-gate": {
@@ -47,7 +47,7 @@
47
47
  "domain": "safety",
48
48
  "sourcePath": "src/core/PostUpdateMigrator.ts",
49
49
  "installedPath": ".instar/hooks/instar/external-operation-gate.js",
50
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
50
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
51
51
  "since": "2025-01-01"
52
52
  },
53
53
  "hook:deferral-detector": {
@@ -56,7 +56,7 @@
56
56
  "domain": "safety",
57
57
  "sourcePath": "src/core/PostUpdateMigrator.ts",
58
58
  "installedPath": ".instar/hooks/instar/deferral-detector.js",
59
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
59
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
60
60
  "since": "2025-01-01"
61
61
  },
62
62
  "hook:post-action-reflection": {
@@ -65,7 +65,7 @@
65
65
  "domain": "evolution",
66
66
  "sourcePath": "src/core/PostUpdateMigrator.ts",
67
67
  "installedPath": ".instar/hooks/instar/post-action-reflection.js",
68
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
68
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
69
69
  "since": "2025-01-01"
70
70
  },
71
71
  "hook:external-communication-guard": {
@@ -74,7 +74,7 @@
74
74
  "domain": "safety",
75
75
  "sourcePath": "src/core/PostUpdateMigrator.ts",
76
76
  "installedPath": ".instar/hooks/instar/external-communication-guard.js",
77
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
77
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
78
78
  "since": "2025-01-01"
79
79
  },
80
80
  "hook:scope-coherence-collector": {
@@ -83,7 +83,7 @@
83
83
  "domain": "coherence",
84
84
  "sourcePath": "src/core/PostUpdateMigrator.ts",
85
85
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
86
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
86
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:scope-coherence-checkpoint": {
@@ -92,7 +92,7 @@
92
92
  "domain": "coherence",
93
93
  "sourcePath": "src/core/PostUpdateMigrator.ts",
94
94
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
95
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
95
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
96
96
  "since": "2025-01-01"
97
97
  },
98
98
  "hook:free-text-guard": {
@@ -101,7 +101,7 @@
101
101
  "domain": "safety",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
104
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
104
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:claim-intercept": {
@@ -110,7 +110,7 @@
110
110
  "domain": "coherence",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
113
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
113
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:claim-intercept-response": {
@@ -119,7 +119,7 @@
119
119
  "domain": "coherence",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
122
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
122
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:stop-gate-router": {
@@ -128,7 +128,7 @@
128
128
  "domain": "safety",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
131
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
131
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:auto-approve-permissions": {
@@ -137,7 +137,7 @@
137
137
  "domain": "safety",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
140
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
140
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "job:health-check": {
@@ -1489,7 +1489,7 @@
1489
1489
  "type": "subsystem",
1490
1490
  "domain": "updates",
1491
1491
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1492
- "contentHash": "e4c4130d4cb388133d46289aa411dc649732cbbf6591d00bb4dd7d135f8acc61",
1492
+ "contentHash": "6785feccb0b5dab73704c9002fb025222445da09255a2d124978b6a395c65c3d",
1493
1493
  "since": "2025-01-01"
1494
1494
  },
1495
1495
  "subsystem:scheduler": {
@@ -0,0 +1,97 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ **The stop gate now catches the "I'll do it now" silent stall — even in shadow
9
+ mode.** A recurring failure: an agent's final message tells the user it is about
10
+ to act this turn ("I'll build that now", "starting now", "Next phase: ship the
11
+ fix") and then the turn simply ends, leaving the user staring at silence after
12
+ being told work was continuing.
13
+
14
+ The existing `stop-gate-router.js` hook delegated the continue/stop decision to
15
+ the server-side Unjustified Stop Gate, which only blocks in `enforce` mode — but
16
+ the gate ships in `shadow` (telemetry-only) mode on most agents, so the exact
17
+ moment a stall happened, nothing intervened.
18
+
19
+ This adds a small, always-on guard at the top of the hook. When the final message
20
+ states an imminent this-turn action together with an imminence marker (now / right
21
+ now / this turn / next phase / on it), the hook blocks once and re-feeds the
22
+ agent: either actually do the work, or send the user one honest sentence that it
23
+ is stopping and why. The guard is mode-independent (works even when the gate is in
24
+ shadow mode or the server is unreachable) and fires at most once — the existing
25
+ loop guard prevents any continuation trap. "Report-back-later" intent (no
26
+ imminence marker) is not caught; that stays with the commitment tracker.
27
+
28
+ The Framework-Onboarding Mentor System's "lessons for the next framework"
29
+ playbook now actually populates. Generalizable framework issues (the portable
30
+ lessons — `framework-limitation` and `instar-integration-gap`) are promoted from
31
+ `none` to `candidate` the moment they're terminally resolved (fixed or won't-fix),
32
+ which is the auto-suggestion step the spec (§13.6) defined but that had never been
33
+ implemented. A one-time, idempotent backfill on ledger startup seeds lessons that
34
+ were resolved before this existed. Without this, the onboarding playbook returned
35
+ an empty list even with a full ledger of hard-won lessons — they were logged but
36
+ never reached the next framework's onboarding.
37
+
38
+ The `candidate → extracted` step (canonizing a lesson into the reusable onboarding
39
+ checklist) is unchanged: it still requires a non-Echo attestation, so an agent
40
+ cannot canonize its own lessons.
41
+
42
+ ## What to Tell Your User
43
+
44
+ Nothing to configure. Your agent can no longer tell you "I'll do that now" and
45
+ then quietly stop without doing it — if it tries, it gets nudged once to either
46
+ actually do the work or tell you plainly that it is stopping and why. The result
47
+ is fewer confusing silences after an agent says it is continuing.
48
+
49
+ Nothing required — this is internal mentor-system robustness. If asked: the
50
+ lessons learned onboarding one agent framework now actually carry forward to the
51
+ next one, instead of sitting unused in the ledger.
52
+
53
+ ## Summary of New Capabilities
54
+
55
+ - Local, mode-independent stated-continuation guard in the stop-gate-router hook
56
+ (`PostUpdateMigrator.getStopGateRouterHook()`); deploys to every agent on update
57
+ via the always-overwrite built-in hook path.
58
+
59
+ - `GET /framework-issues/playbook?targetFramework=X` now returns the generalizable,
60
+ terminally-resolved lessons from prior frameworks (previously always empty because
61
+ nothing ever moved an issue off `playbook_status='none'`).
62
+ - `FrameworkIssueLedger.backfillPlaybookCandidates()` seeds pre-existing eligible
63
+ lessons; runs automatically and idempotently on ledger construction.
64
+
65
+ ## Evidence
66
+
67
+ Reproduction (live, topic 13481, 2026-05-31): the agent posted "I'm going to
68
+ build that now ... then deploy and finish the round-trip proof" at 16:56 UTC,
69
+ then ended the turn and went silent for ~90 minutes without starting the work.
70
+ Operator response: "your session has stalled again ... this is incoherent."
71
+
72
+ Root cause observed: a direct probe of the running gate returned
73
+ GET /internal/stop-gate/hot-path -> {"mode":"shadow",...}. In shadow mode the
74
+ stop-gate-router records telemetry but never blocks, so the stall passed through
75
+ unchallenged — the safety net was off at the exact moment it was needed.
76
+
77
+ Before: rendering the prior hook and feeding it the exact stall message
78
+ ("Next phase: build the reverse reply-relay, then ship the fix") exits 0 — the
79
+ agent is allowed to stop silently.
80
+
81
+ After: the same rendered hook fed the same message exits 2 and writes
82
+ {"decision":"block", reason:"STOP-GATE (stated-continuation): ..."}, regardless
83
+ of the server's shadow/enforce mode and even with no server reachable. A benign
84
+ completion ("Done — all tests passed ...") still exits 0, and stop_hook_active=true
85
+ exits 0 (loop guard intact). Confirmed by executing the rendered hook as a
86
+ subprocess against each payload (tests/unit/stop-gate-stated-continuation.test.ts,
87
+ 5/5 green).
88
+
89
+ - Root cause: §13.6's `none→candidate` auto-suggestion was specified but never
90
+ implemented — the only writer of `candidate` was the manual promote route, so a
91
+ live ledger with 18 generalizable codex lessons (11 terminal) served `playbook: []`.
92
+ - Fix: deterministic auto-bump in `updateIssue` on terminal resolution + an
93
+ idempotent constructor backfill; the `candidate→extracted` non-Echo attestation
94
+ guard is untouched.
95
+ - Tests: +10 unit, +1 integration end-to-end; 192 ledger/mentor/route/E2E tests pass.
96
+ `npm run lint` clean.
97
+ - Spec: docs/specs/PLAYBOOK-CANDIDATE-AUTOSEED-SPEC.md (+ ELI16 companion).
@@ -0,0 +1,70 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ **A conversation moved to a second machine can now reply to you.** When you move
9
+ a conversation to another machine (e.g. "move this to the Mac Mini"), that machine
10
+ serves the session but deliberately holds NO Telegram bot token — only one machine
11
+ may hold it, or Telegram throws a 409 poller conflict. The reply path already had
12
+ the fix for this: a tokenless standby relays its reply through the token-owning
13
+ machine (bug #7, `outboundRelay`).
14
+
15
+ But the "am I tokenless?" check was `!this.config.token`. On a standby the bot
16
+ token is *externalized* and arrives unresolved as a placeholder object
17
+ (`{ secret: true }`), not `null` — and an object is truthy, so the check concluded
18
+ "I have a token", skipped the relay, and attempted a doomed direct API call with
19
+ the placeholder. The moved session's reply returned 200 internally but never
20
+ reached Telegram.
21
+
22
+ The fix treats only a non-empty STRING as a usable bot token. The placeholder,
23
+ `null`, `undefined`, and `''` all now correctly mean "no usable token" → relay
24
+ through the token-owning machine. Token-holding machines (the normal case) are
25
+ completely unaffected — they still send directly.
26
+
27
+ ## What to Tell Your User
28
+
29
+ Nothing to configure. If you run on more than one machine and move a conversation
30
+ to another one, that machine can now actually reply to you — previously a moved
31
+ session would go silent because its reply couldn't reach Telegram.
32
+
33
+ ## Summary of New Capabilities
34
+
35
+ - `TelegramAdapter.sendToTopic` correctly detects a tokenless pool standby when
36
+ the bot token is an unresolved (non-string) externalized placeholder, and routes
37
+ the reply through `outboundRelay` instead of a doomed direct send.
38
+
39
+ ## Evidence
40
+
41
+ **Reproduction (live, topic 8882, 2026-05-31 16:46 UTC):** POSTed a reply to the
42
+ Mac Mini standby's `/telegram/reply/8882`. Response was `{"ok":true}` (HTTP 200)
43
+ but the message never appeared in Telegram; the mini's `logs/server.log` showed NO
44
+ outbound-relay attempt and the laptop showed NO inbound relay. The mini's
45
+ `.instar/config.json` carried `botToken: { secret: true }` (externalized, never
46
+ resolved on the tokenless standby).
47
+
48
+ **Observed before/after (the relay decision, run in dev against the real
49
+ placeholder value):**
50
+
51
+ ```
52
+ token value: {"secret":true}
53
+ BEFORE (!token): HAS-TOKEN -> direct API send (DOOMED: placeholder is not a real token)
54
+ AFTER (typeof string): TOKENLESS -> relay (FIXED)
55
+ real string AFTER: HAS-TOKEN -> direct API send (unchanged)
56
+ ```
57
+
58
+ The truthy placeholder object flips the decision from "doomed direct send" to
59
+ "relay" — exactly the path the moved session needed. A real string token is
60
+ unchanged (still direct), so token-holding machines see no behavior change.
61
+
62
+ **Behavioral test:** `tests/unit/telegram-tokenless-relay.test.ts` constructs a
63
+ real `TelegramAdapter` with the `{ secret: true }` placeholder + spy `outboundRelay`
64
+ + spy `fetch`, calls the REAL `sendToTopic`, and observes `outboundRelay` invoked
65
+ once with `fetch` never called (before this fix that path called `fetch` with the
66
+ bad token). 3/3 green.
67
+
68
+ **Live round-trip confirmation:** the full "move via Telegram → mini serves →
69
+ reply lands in 8882" round trip is confirmed against the deployed release on the
70
+ two-machine setup.
@@ -0,0 +1,41 @@
1
+ # Side effects — stated-continuation stop guard
2
+
3
+ ## What changes at runtime
4
+
5
+ The built-in `stop-gate-router.js` Stop hook gains a local, mode-independent
6
+ guard. On every Stop, if the agent's final message states an imminent this-turn
7
+ action ("I'll build X now", "starting now", "next phase: ...", "on it ... now")
8
+ the hook returns `{decision:'block'}` (exit 2) ONCE, re-feeding the agent to
9
+ either do the work or tell the user plainly it is stopping and why.
10
+
11
+ ## Who is affected
12
+
13
+ - **Every Instar agent on update.** Built-in `instar/` hooks are always
14
+ overwritten on migration, so the guard deploys automatically — no opt-in,
15
+ no per-agent migration. New agents get it via `init`.
16
+
17
+ ## Behavioral side effects (intended)
18
+
19
+ - An agent that ends a turn right after saying "I'll do X now" will be blocked
20
+ once and pushed to either act or send an explicit stop message. This is the
21
+ intended fix; it converts silent stalls into either real work or an honest
22
+ "I'm stopping because…" message.
23
+ - Borderline matches (e.g. a summary heading "Next phase: …") cost exactly one
24
+ extra nudge and force an explicit sign-off — acceptable and desirable.
25
+
26
+ ## What is NOT affected
27
+
28
+ - The server-side Unjustified Stop Gate, its mode (shadow/enforce), its telemetry,
29
+ and its circuit breaker are all untouched.
30
+ - The autonomous-stop-hook and hook-event-reporter Stop hooks are untouched.
31
+ - The loop-prevention contract is preserved: the existing `stop_hook_active` guard
32
+ still short-circuits on the re-fire, so the guard fires at most once per stop —
33
+ no infinite continuation loop is possible.
34
+ - "Report-back-later" / scheduled commitments (no imminence marker) are NOT
35
+ caught — those remain the commitment tracker's domain.
36
+
37
+ ## Rollback
38
+
39
+ Revert the `getStopGateRouterHook()` change; the next migration overwrites the
40
+ installed hook back to the prior content. No state, config, or schema changes are
41
+ involved.
@@ -0,0 +1,33 @@
1
+ # Side effects — tokenless-standby relay token check
2
+
3
+ ## What this changes at runtime
4
+
5
+ `TelegramAdapter.sendToTopic` now decides "tokenless → relay" by checking that
6
+ the bot token is a non-empty STRING, instead of the old truthy check
7
+ (`!this.config.token`). The change is confined to the relay-vs-direct branch.
8
+
9
+ ## Who is affected
10
+
11
+ - **Token-holding adapters (the normal case — laptop, single-machine agents):**
12
+ NO behavior change. Their `config.token` is a real string, so
13
+ `hasUsableBotToken` is `true` and they continue to send directly via the
14
+ Telegram API exactly as before. Verified by the "sends DIRECTLY" unit test.
15
+ - **Tokenless pool standbys (a moved session on a machine without the token):**
16
+ Their externalized token (`{ secret: true }` / `null`) is now correctly seen
17
+ as "no usable token", so their replies route through `outboundRelay` to the
18
+ lease holder instead of silently failing a direct send.
19
+
20
+ ## Blast radius
21
+
22
+ - Single file (`src/messaging/TelegramAdapter.ts`), one branch condition.
23
+ - No config keys added or changed. No migration required (the change is in
24
+ compiled source, not in any agent-installed file/hook/skill).
25
+ - The relay target (`outboundRelay`, wired in `server.ts`) is unchanged.
26
+
27
+ ## Failure modes considered
28
+
29
+ - If a standby has no usable token AND `outboundRelay` is not wired, behavior is
30
+ unchanged from before (it falls through to the direct-send branch and fails the
31
+ same way) — no new failure introduced.
32
+ - The relay still surfaces a hard error if the lease holder is unreachable
33
+ (`telegram outbound relay failed`), so a lost reply is loud, not silent.
@@ -1,46 +0,0 @@
1
- # Upgrade Guide — vNEXT
2
-
3
- <!-- assembled-by: assemble-next-md -->
4
- <!-- bump: patch -->
5
-
6
- ## What Changed
7
-
8
- The Framework-Onboarding Mentor System's "lessons for the next framework"
9
- playbook now actually populates. Generalizable framework issues (the portable
10
- lessons — `framework-limitation` and `instar-integration-gap`) are promoted from
11
- `none` to `candidate` the moment they're terminally resolved (fixed or won't-fix),
12
- which is the auto-suggestion step the spec (§13.6) defined but that had never been
13
- implemented. A one-time, idempotent backfill on ledger startup seeds lessons that
14
- were resolved before this existed. Without this, the onboarding playbook returned
15
- an empty list even with a full ledger of hard-won lessons — they were logged but
16
- never reached the next framework's onboarding.
17
-
18
- The `candidate → extracted` step (canonizing a lesson into the reusable onboarding
19
- checklist) is unchanged: it still requires a non-Echo attestation, so an agent
20
- cannot canonize its own lessons.
21
-
22
- ## What to Tell Your User
23
-
24
- Nothing required — this is internal mentor-system robustness. If asked: the
25
- lessons learned onboarding one agent framework now actually carry forward to the
26
- next one, instead of sitting unused in the ledger.
27
-
28
- ## Summary of New Capabilities
29
-
30
- - `GET /framework-issues/playbook?targetFramework=X` now returns the generalizable,
31
- terminally-resolved lessons from prior frameworks (previously always empty because
32
- nothing ever moved an issue off `playbook_status='none'`).
33
- - `FrameworkIssueLedger.backfillPlaybookCandidates()` seeds pre-existing eligible
34
- lessons; runs automatically and idempotently on ledger construction.
35
-
36
- ## Evidence
37
-
38
- - Root cause: §13.6's `none→candidate` auto-suggestion was specified but never
39
- implemented — the only writer of `candidate` was the manual promote route, so a
40
- live ledger with 18 generalizable codex lessons (11 terminal) served `playbook: []`.
41
- - Fix: deterministic auto-bump in `updateIssue` on terminal resolution + an
42
- idempotent constructor backfill; the `candidate→extracted` non-Echo attestation
43
- guard is untouched.
44
- - Tests: +10 unit, +1 integration end-to-end; 192 ledger/mentor/route/E2E tests pass.
45
- `npm run lint` clean.
46
- - Spec: docs/specs/PLAYBOOK-CANDIDATE-AUTOSEED-SPEC.md (+ ELI16 companion).