instar 1.3.403 → 1.3.405
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/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +24 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js +7 -2
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/monitoring/frameworkActivitySignals.d.ts +15 -0
- package/dist/monitoring/frameworkActivitySignals.d.ts.map +1 -1
- package/dist/monitoring/frameworkActivitySignals.js +18 -1
- package/dist/monitoring/frameworkActivitySignals.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +7 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +31 -3
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/src/scaffold/templates.ts +7 -0
- package/upgrades/1.3.404.md +37 -0
- package/upgrades/1.3.405.md +44 -0
- package/upgrades/side-effects/reaper-live-activity-idle.md +46 -0
- package/upgrades/side-effects/subscription-auth-graduate.md +72 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-07T17:10:48.879Z",
|
|
5
|
+
"instarVersion": "1.3.405",
|
|
6
6
|
"entryCount": 199,
|
|
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": "
|
|
14
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
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": "
|
|
23
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
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": "
|
|
32
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
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": "
|
|
41
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
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": "
|
|
50
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
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": "
|
|
59
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:self-stop-guard": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "coherence",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/self-stop-guard.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:post-action-reflection": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "evolution",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:external-communication-guard": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "safety",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-collector": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "coherence",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:free-text-guard": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "safety",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:claim-intercept-response": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "coherence",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"hook:stop-gate-router": {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"domain": "safety",
|
|
138
138
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
139
139
|
"installedPath": ".instar/hooks/instar/stop-gate-router.js",
|
|
140
|
-
"contentHash": "
|
|
140
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
141
141
|
"since": "2025-01-01"
|
|
142
142
|
},
|
|
143
143
|
"hook:auto-approve-permissions": {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"domain": "safety",
|
|
147
147
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
148
148
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
149
|
-
"contentHash": "
|
|
149
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1538,7 +1538,7 @@
|
|
|
1538
1538
|
"type": "subsystem",
|
|
1539
1539
|
"domain": "updates",
|
|
1540
1540
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1541
|
-
"contentHash": "
|
|
1541
|
+
"contentHash": "91751f5d379223d5219959314caaeff35e6534ade7acf5193b5768bc1fc79198",
|
|
1542
1542
|
"since": "2025-01-01"
|
|
1543
1543
|
},
|
|
1544
1544
|
"subsystem:scheduler": {
|
|
@@ -695,6 +695,13 @@ This routes feedback to the Instar maintainers automatically. Valid types: \`bug
|
|
|
695
695
|
- Returns \`{ available, usage: { primary, secondary, model, planType, rateLimitReachedType } }\` where each window has \`usedPercent\`, \`remainingPercent\`, \`windowMinutes\`, \`resetsAt\`/\`resetsAtIso\`, \`resetsInSeconds\`. \`available:false\` means no codex session data on disk yet (e.g. a pure-Claude agent).
|
|
696
696
|
- **When to use**: when asked "how much codex usage is left?" / "am I near the limit?", before scheduling heavy codex work, or to drive a model-swap when a window is exhausted (\`rateLimitReachedType\` is non-null, or \`secondary.remainingPercent\` is low).
|
|
697
697
|
|
|
698
|
+
**Subscription Pool (multi-account quota + auto-swap + enrollment)** — Hold ALL of your subscriptions for a provider (e.g. several Claude logins) and use them as one pool: I read each account's live quota, drain each before its reset, and when a session hits an account's limit I resume it on another account instead of letting it die. The registry stores each account's login LOCATION (its config home), NEVER a token.
|
|
699
|
+
- See the pool + each account's live quota: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/subscription-pool\` · one account's quota + burn: \`GET /subscription-pool/:id/quota\` · poll all now: \`POST /subscription-pool/poll\`.
|
|
700
|
+
- **Continuity guarantee** — a long session that hits its account's quota resumes on another eligible account (conversation preserved via \`--resume\`), never dies. Manual lever: \`POST /subscription-pool/swap\` \`{"sessionName":"...","exhaustedAccountId":"..."}\`. Auto-swap on rate-limit ships OFF (opt-in via \`subscriptionPool.autoSwapOnRateLimit\` — it moves a live session, real authority).
|
|
701
|
+
- **Enroll a new account from your phone** — \`POST /subscription-pool/enroll\` \`{"id","label","provider","framework","configHome"}\` starts a login and returns a public code/URL (never a token); \`GET /subscription-pool/pending-logins\` is the surface; expired codes are auto-reissued. Mark done with \`POST /subscription-pool/enroll/:id/complete\`.
|
|
702
|
+
- **Dashboard**: the **Subscriptions tab** shows live quota bars (5h + weekly + reset countdown), status, and the Pending Logins panel — share the dashboard URL + PIN.
|
|
703
|
+
- **When to use** (PROACTIVE): "how much quota is left across my accounts?" / "am I about to hit a limit?" → \`GET /subscription-pool\`; the user wants to add another subscription → drive the enrollment wizard (never ask them to paste a token); a long job is at risk of a quota wall → the continuity guarantee + \`/swap\` keep it alive. Single-account pools are a no-op.
|
|
704
|
+
|
|
698
705
|
**Per-Feature LLM Metrics** — See what each of your LLM-driven gates/sentinels actually costs and how often it fires, so tuning them is evidence-based (which to thin, which to strengthen). Read-only observability (like token usage) — it never gates anything.
|
|
699
706
|
- Check: \`curl -H "Authorization: Bearer $AUTH" "http://localhost:${port}/metrics/features?sinceHours=24"\`
|
|
700
707
|
- Returns \`{ totals, features: [{ feature, calls, tokensIn, tokensOut, fired, noop, fireRate, p50LatencyMs, p95LatencyMs, ... }] }\` — one row per system (e.g. MessagingToneGate, CoherenceReviewer). Filter with \`?feature=<name>\`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The Subscription & Auth Standard's `/subscription-pool` routes graduated from the
|
|
9
|
+
internal/dark list to a surfaced capability, now that all its phases are merged —
|
|
10
|
+
the multi-account registry (P1.1), quota poller (P1.2), quota-aware auto-swap
|
|
11
|
+
scheduler with the session-continuity guarantee (P1.3), mobile-first enrollment
|
|
12
|
+
wizard (P2.1), and dashboard Subscriptions tab (P2.2).
|
|
13
|
+
|
|
14
|
+
Three no-behavior edits: `/subscription-pool` moves from `INTERNAL_PREFIXES` to a
|
|
15
|
+
`CAPABILITY_INDEX` entry (so it appears in `/capabilities` with its account count,
|
|
16
|
+
quota poller / scheduler / enrollment-wizard wiring status, and full endpoint
|
|
17
|
+
list); the CLAUDE.md template gains a "Subscription Pool" awareness blurb for new
|
|
18
|
+
agents; and an idempotent `migrateClaudeMd` migration appends the same blurb to
|
|
19
|
+
existing agents on update. No route, class, or runtime behavior is added — this
|
|
20
|
+
surfaces an already-shipped, already-tested capability honestly (the original
|
|
21
|
+
internal note set exactly this maturity bar).
|
|
22
|
+
|
|
23
|
+
## What to Tell Your User
|
|
24
|
+
|
|
25
|
+
I can now manage several of your subscriptions for a provider as one pool — show
|
|
26
|
+
you how much of each is left and when it resets, keep a long session alive by
|
|
27
|
+
moving it to another account when one hits its limit, and help you enroll a new
|
|
28
|
+
account from your phone. Ask me "how much quota is left across my accounts?" or
|
|
29
|
+
tell me you want to add another subscription.
|
|
30
|
+
|
|
31
|
+
## Summary of New Capabilities
|
|
32
|
+
|
|
33
|
+
- **`/subscription-pool` is now a surfaced capability** — visible in
|
|
34
|
+
`/capabilities` and documented in the agent's CLAUDE.md (new + existing agents).
|
|
35
|
+
- **No behavior change** — the routes, scheduler, continuity guarantee, and
|
|
36
|
+
enrollment wizard already shipped (P1.1–P2.2); this makes the finished capability
|
|
37
|
+
discoverable and known.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The idle-session reaper checked "is this session working?" with a pattern that matched
|
|
9
|
+
tool-call names (Read(/Write(/Bash() and the bare word "claude" anywhere in the captured
|
|
10
|
+
terminal — but those persist in an IDLE session's scrollback, so every idle Claude
|
|
11
|
+
session read as "working" and the reaper never reaped one (0 reaps in 9.5h live; the
|
|
12
|
+
residual blocker after #961). Fix: `isPositivelyIdle` now uses a new live-only signal
|
|
13
|
+
(`liveActivity`: spinner glyphs + "Working (Ns" + "generating"), which vanish when a turn
|
|
14
|
+
ends), not the scrollback-persistent `toolCallOrSpinner`. Also removed the bare word
|
|
15
|
+
"claude" from the Claude activity pattern (the documented codex "do not match bare
|
|
16
|
+
'codex'" lesson).
|
|
17
|
+
|
|
18
|
+
## What to Tell Your User
|
|
19
|
+
|
|
20
|
+
Nothing required — internal reaper classifier; reaper stays opt-in. For operators who
|
|
21
|
+
enable it: the reaper can now correctly recognize a genuinely-idle session and reclaim it
|
|
22
|
+
(it previously could not). The change also makes the silence/presence sentinels stop
|
|
23
|
+
mis-reading idle Claude sessions as "working".
|
|
24
|
+
|
|
25
|
+
## Summary of New Capabilities
|
|
26
|
+
|
|
27
|
+
- `FrameworkActivitySignal.liveActivity` — live-generation-only markers per framework, so
|
|
28
|
+
callers that need "working RIGHT NOW" don't trip over scrollback history.
|
|
29
|
+
- `SessionReaper.isPositivelyIdle` uses `liveActivity`; bare "claude" removed from the
|
|
30
|
+
Claude `toolCallOrSpinner`.
|
|
31
|
+
|
|
32
|
+
## Evidence
|
|
33
|
+
|
|
34
|
+
`tests/unit/session-reaper.test.ts`: idle Claude pane (scrollback tool-names + "claude",
|
|
35
|
+
no live marker) → positively idle; working pane (spinner / spinner+esc) → not idle; bare
|
|
36
|
+
"claude" alone no longer forces not-idle. 57/57 green. `tsc --noEmit` clean. causalAutopsy:
|
|
37
|
+
latent (exposed by the #955/#958/#961 chain reaching the positive-idle check).
|
|
38
|
+
|
|
39
|
+
## Scope (honest)
|
|
40
|
+
|
|
41
|
+
Final functional fix in the chain (#952 → #955 → #958 → #961 → this): the reaper can now
|
|
42
|
+
tell idle from working and actually reclaim. Validated by dry-run before any live flip.
|
|
43
|
+
Does NOT address the transcript pile-up (151k files / 5.9 GB) — separate retention
|
|
44
|
+
follow-up.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Side-Effects Review - Reaper live-activity idle detection
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `reaper-live-activity-idle`
|
|
4
|
+
**Date:** `2026-06-07`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required (Tier 1)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`SessionReaper.isPositivelyIdle` used `sig.toolCallOrSpinner` to decide "is this session active?", but that regex matches tool-call names (Read(/Write(/Bash(/…) and the bare word "claude" — both of which PERSIST in an idle session's 200-line scrollback. So every idle Claude session read as active → never positively idle → reaper kept everything (0 reaps in 9.5h live; the residual blocker after #961 made transcripts resolve). Adds a `liveActivity` regex to `FrameworkActivitySignal` (live-generation-only: spinner glyphs + "Working (Ns" + "generating") and uses it in `isPositivelyIdle`; removes the bare `claude|` from the Claude `toolCallOrSpinner` (the documented codex "do not match bare 'codex'" lesson).
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `FrameworkActivitySignal.liveActivity` (new field) on all 4 signals (claude/codex/gemini/pi) — live-only markers.
|
|
15
|
+
- `SessionReaper.isPositivelyIdle`: active-check now `sig.liveActivity || sig.escapeToInterrupt || sig.runningIndicator` (was `sig.toolCallOrSpinner || …`).
|
|
16
|
+
- `CLAUDE_CODE_SIGNAL.toolCallOrSpinner`: bare `claude|` removed (tool-names + spinner retained).
|
|
17
|
+
|
|
18
|
+
## 1. Behavior change / gating
|
|
19
|
+
|
|
20
|
+
This makes `isPositivelyIdle` correctly recognize idle sessions; it does not change any other gate. The only behavioral effect: the reaper can now reach reap-eligibility for genuinely-idle sessions (it previously could not). Removing bare "claude" from toolCallOrSpinner also makes OTHER consumers (silence/presence sentinels) stop mis-reading idle Claude sessions as working — strictly more correct, matching the existing codex behavior. No API/route/state change.
|
|
21
|
+
|
|
22
|
+
## 2. Over/under-signal
|
|
23
|
+
|
|
24
|
+
Direction of risk: a session genuinely working but whose captured frame momentarily shows no live marker (between tool calls) could read idle. Mitigations: (a) transcript-growth fires first and KEEPS anything whose JSONL is growing — a working session is growing its transcript; (b) confirmObservations (3 ticks / render-stasis) requires sustained idle; (c) 8h-silence + the full guard chain still apply. So a momentary live-marker miss cannot produce a wrong reap. UNDER-signal (the prior "never recognizes idle") is the bug being fixed. `liveActivity` deliberately keeps every genuine live marker per framework (spinner always; +Working-status for codex; +generating/thinking for gemini/pi).
|
|
25
|
+
|
|
26
|
+
## 3. Blast radius
|
|
27
|
+
|
|
28
|
+
Pure regex/classifier logic in the shared signal module + one call-site in the reaper. No I/O, no new deps, no persistent state, no migration. Other consumers of `toolCallOrSpinner` are unaffected except they no longer match the bare word "claude" (a correctness improvement, not a regression — no test depended on it; 57 unit tests incl. the framework-signal suite pass).
|
|
29
|
+
|
|
30
|
+
## 4. Failure modes
|
|
31
|
+
|
|
32
|
+
`liveActivity` is a required field with a concrete regex on every signal (tsc-enforced — a missing one is a compile error). A malformed frame is just text the regexes test against; no throw path. The reaper's downstream stateful checks (transcript-growth, positive-idle prompt match, confirmObservations) all still run after this gate.
|
|
33
|
+
|
|
34
|
+
## 5. Migration parity
|
|
35
|
+
|
|
36
|
+
No agent-installed file changes; internal classifier logic shipped in code, effective on next server start. No config surface. The reaper remains opt-in + dry-run-first, so nothing changes for an operator until they enable it.
|
|
37
|
+
|
|
38
|
+
## 6. Scope honesty (what this is NOT)
|
|
39
|
+
|
|
40
|
+
- Final functional fix in the chain (#952 → #955 → #958 → #961 → this). With it the reaper can distinguish idle from working and actually reclaim; validated by dry-run before any live flip.
|
|
41
|
+
- Does NOT touch the gemini/codex/pi live-marker sets beyond adding the live-only subset (codex/gemini/pi toolCallOrSpinner were already largely live-only or carefully tuned; only Claude carried the bare-word + tool-name-in-idle bug at the reaper call-site).
|
|
42
|
+
- Does NOT address the transcript pile-up (151k files / 5.9 GB) — separate retention follow-up.
|
|
43
|
+
|
|
44
|
+
## 7. Causal autopsy
|
|
45
|
+
|
|
46
|
+
Origin: **latent**. The reaper's idle-check has used the scrollback-matching `toolCallOrSpinner` (incl. bare "claude") since `isPositivelyIdle` was written — always wrong for the "is it active NOW" question, but never hit because the earlier KEEP-guards short-circuited before the positive-idle check ran. The 2026-06-07 #955/#958/#961 chain peeled those layers (and #961 made transcripts resolve), so the reaper finally REACHED the positive-idle check and the latent bug surfaced as 0 reaps / universal `no-positive-idle`. No prior PR regressed it; it is the deepest layer of the same conservatism stack. The bare-"claude" half is the exact analogue of the already-fixed codex bare-word bug.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Side-Effects Review — Graduate the Subscription Pool capability (P-graduate)
|
|
2
|
+
|
|
3
|
+
## Scope of change
|
|
4
|
+
|
|
5
|
+
- `src/server/CapabilityIndex.ts` — remove `subscription-pool` from `INTERNAL_PREFIXES`; add a `subscriptionPool` entry to `CAPABILITY_INDEX` (key, prefixes, description, build → configured/accounts/quotaPoller/scheduler/enrollmentWizard/endpoints).
|
|
6
|
+
- `src/scaffold/templates.ts` (`generateClaudeMd`) — add the Subscription Pool awareness blurb so NEW agents know the capability.
|
|
7
|
+
- `src/core/PostUpdateMigrator.ts` (`migrateClaudeMd`) — add a content-sniffed, idempotent migration so EXISTING agents get the same blurb on update (Migration Parity).
|
|
8
|
+
- `upgrades/next/subscription-auth-graduate.md` — release-note fragment.
|
|
9
|
+
|
|
10
|
+
## What this does (and does NOT) change
|
|
11
|
+
|
|
12
|
+
This is a **classification + awareness** change. It surfaces an already-shipped,
|
|
13
|
+
already-tested capability (P1.1 registry, P1.2 poller, P1.3 scheduler + continuity
|
|
14
|
+
guarantee, P2.1 enrollment, P2.2 dashboard) to `/capabilities` and to the agent's
|
|
15
|
+
CLAUDE.md. It adds **no route, no class, no runtime behavior** — the routes, the
|
|
16
|
+
scheduler, the wizard already exist and are wired. The `build()` function is pure
|
|
17
|
+
(reads `ctx.subscriptionPool` etc. already on the context) and runs only when
|
|
18
|
+
`/capabilities` is probed.
|
|
19
|
+
|
|
20
|
+
## Why now (maturity honesty)
|
|
21
|
+
|
|
22
|
+
The original `INTERNAL_PREFIXES` entry explicitly said `subscription-pool`
|
|
23
|
+
"graduates to a surfaced CAPABILITY_INDEX entry (+ CLAUDE.md awareness blurb) once
|
|
24
|
+
the quota-aware scheduler (P1.3) and mobile enrollment wizard (P2.1) make it
|
|
25
|
+
user-usable. Surfacing the bare registry now would overclaim an unfinished
|
|
26
|
+
capability." P1.3 + P2.1 (+ P2.2 dashboard) are now merged — the maturity bar the
|
|
27
|
+
note set is met, so surfacing it is honest, not overclaiming.
|
|
28
|
+
|
|
29
|
+
## Authority / autonomy analysis
|
|
30
|
+
|
|
31
|
+
- **No new authority.** The auto-swap of live sessions remains OFF by default
|
|
32
|
+
(`subscriptionPool.autoSwapOnRateLimit`); the blurb documents it as opt-in. The
|
|
33
|
+
enrollment blurb tells the agent to drive the wizard and NEVER ask the user to
|
|
34
|
+
paste a token — reinforcing the existing credential-safety posture.
|
|
35
|
+
- **The migration edits agent-installed CLAUDE.md** — but only appends a doc
|
|
36
|
+
section, content-sniffed on a distinctive marker (idempotent: re-running is a
|
|
37
|
+
no-op), and never touches a custom/user-authored section. This is the standard
|
|
38
|
+
`migrateClaudeMd` append pattern used by ~30 prior sections.
|
|
39
|
+
|
|
40
|
+
## Failure modes considered
|
|
41
|
+
|
|
42
|
+
- Capability probed on an agent with no pool wired → `build()` returns
|
|
43
|
+
`{ configured:false, accounts:0, ... }` (no throw; the routes already answer
|
|
44
|
+
`200 { enabled:false }`).
|
|
45
|
+
- Migration run twice → content-sniff marker present → skipped (idempotent).
|
|
46
|
+
- Migration on an agent whose CLAUDE.md predates the section → appended once.
|
|
47
|
+
- The capabilities-discoverability lint: `/subscription-pool` is now claimed by
|
|
48
|
+
exactly one CAPABILITY_INDEX entry and absent from INTERNAL_PREFIXES (verified:
|
|
49
|
+
CapabilityIndex.test.ts green).
|
|
50
|
+
|
|
51
|
+
## Blast radius
|
|
52
|
+
|
|
53
|
+
Minimal. Classification metadata + a documentation blurb + an idempotent doc
|
|
54
|
+
migration. No behavior path changes; `/capabilities` gains one block; new + existing
|
|
55
|
+
agents gain one CLAUDE.md section.
|
|
56
|
+
|
|
57
|
+
## Framework generality
|
|
58
|
+
|
|
59
|
+
Framework-agnostic in the surface, Claude-first in the wired mechanism (matching the
|
|
60
|
+
standard's scope, Justin's decision 3): the CAPABILITY_INDEX entry + blurb describe
|
|
61
|
+
the pool generically (provider/framework-parameterized), and the enrollment + quota
|
|
62
|
+
machinery already carry `provider`/`framework` fields. The continuity-guarantee swap
|
|
63
|
+
is implemented for `claude-code` today (per-account `CLAUDE_CONFIG_DIR`); other
|
|
64
|
+
frameworks slot into the same registry + selector when their per-account config is
|
|
65
|
+
wired. Surfacing the capability does not bake in any Claude-only assumption beyond
|
|
66
|
+
what the underlying (already-merged) phases already chose.
|
|
67
|
+
|
|
68
|
+
## Migration / parity
|
|
69
|
+
|
|
70
|
+
This change IS the parity work: `generateClaudeMd` (new agents) + `migrateClaudeMd`
|
|
71
|
+
(existing agents) both get the blurb, satisfying the Agent Awareness + Migration
|
|
72
|
+
Parity standards. No config/hook/skill change. Ships via dist.
|