instar 1.3.343 → 1.3.345
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/commands/init.d.ts +4 -4
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/reflect.d.ts.map +1 -1
- package/dist/commands/reflect.js +7 -0
- package/dist/commands/reflect.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +126 -8
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/Config.d.ts +12 -3
- package/dist/core/Config.d.ts.map +1 -1
- package/dist/core/Config.js +40 -6
- package/dist/core/Config.js.map +1 -1
- package/dist/core/FrameworkSessionStore.d.ts +1 -1
- package/dist/core/FrameworkSessionStore.d.ts.map +1 -1
- package/dist/core/PiCliIntelligenceProvider.d.ts +52 -0
- package/dist/core/PiCliIntelligenceProvider.d.ts.map +1 -0
- package/dist/core/PiCliIntelligenceProvider.js +71 -0
- package/dist/core/PiCliIntelligenceProvider.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +57 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PreCompactionFlush.d.ts +1 -1
- package/dist/core/PreCompactionFlush.d.ts.map +1 -1
- package/dist/core/ResumeValidator.d.ts +1 -1
- package/dist/core/ResumeValidator.d.ts.map +1 -1
- package/dist/core/SessionManager.d.ts +130 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +545 -10
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/TopicFrameworksStore.d.ts.map +1 -1
- package/dist/core/TopicFrameworksStore.js +3 -0
- package/dist/core/TopicFrameworksStore.js.map +1 -1
- package/dist/core/frameworkInjectionProcesses.d.ts.map +1 -1
- package/dist/core/frameworkInjectionProcesses.js +4 -0
- package/dist/core/frameworkInjectionProcesses.js.map +1 -1
- package/dist/core/frameworkSessionLaunch.d.ts +8 -0
- package/dist/core/frameworkSessionLaunch.d.ts.map +1 -1
- package/dist/core/frameworkSessionLaunch.js +73 -0
- package/dist/core/frameworkSessionLaunch.js.map +1 -1
- package/dist/core/intelligenceProviderFactory.d.ts +15 -1
- package/dist/core/intelligenceProviderFactory.d.ts.map +1 -1
- package/dist/core/intelligenceProviderFactory.js +39 -1
- package/dist/core/intelligenceProviderFactory.js.map +1 -1
- package/dist/core/types.d.ts +88 -8
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/messaging/SpawnRequestManager.d.ts +14 -0
- package/dist/messaging/SpawnRequestManager.d.ts.map +1 -1
- package/dist/messaging/SpawnRequestManager.js +17 -0
- package/dist/messaging/SpawnRequestManager.js.map +1 -1
- package/dist/messaging/shared/telegramRelayPrompt.d.ts +1 -1
- package/dist/messaging/shared/telegramRelayPrompt.d.ts.map +1 -1
- package/dist/monitoring/ApprenticeshipCycleStore.d.ts +59 -1
- package/dist/monitoring/ApprenticeshipCycleStore.d.ts.map +1 -1
- package/dist/monitoring/ApprenticeshipCycleStore.js +77 -2
- package/dist/monitoring/ApprenticeshipCycleStore.js.map +1 -1
- package/dist/monitoring/ReapLog.d.ts +6 -0
- package/dist/monitoring/ReapLog.d.ts.map +1 -1
- package/dist/monitoring/ReapLog.js +1 -0
- package/dist/monitoring/ReapLog.js.map +1 -1
- package/dist/monitoring/SessionReaper.d.ts +1 -1
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/monitoring/frameworkActivitySignals.d.ts.map +1 -1
- package/dist/monitoring/frameworkActivitySignals.js +21 -0
- package/dist/monitoring/frameworkActivitySignals.js.map +1 -1
- package/dist/monitoring/frameworkProcessSignals.d.ts.map +1 -1
- package/dist/monitoring/frameworkProcessSignals.js +22 -0
- package/dist/monitoring/frameworkProcessSignals.js.map +1 -1
- package/dist/providers/adapters/pi-cli/capabilities.d.ts +31 -0
- package/dist/providers/adapters/pi-cli/capabilities.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/capabilities.js +40 -0
- package/dist/providers/adapters/pi-cli/capabilities.js.map +1 -0
- package/dist/providers/adapters/pi-cli/config.d.ts +65 -0
- package/dist/providers/adapters/pi-cli/config.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/config.js +43 -0
- package/dist/providers/adapters/pi-cli/config.js.map +1 -0
- package/dist/providers/adapters/pi-cli/control/piHardKill.d.ts +19 -0
- package/dist/providers/adapters/pi-cli/control/piHardKill.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/control/piHardKill.js +67 -0
- package/dist/providers/adapters/pi-cli/control/piHardKill.js.map +1 -0
- package/dist/providers/adapters/pi-cli/errors.d.ts +43 -0
- package/dist/providers/adapters/pi-cli/errors.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/errors.js +71 -0
- package/dist/providers/adapters/pi-cli/errors.js.map +1 -0
- package/dist/providers/adapters/pi-cli/index.d.ts +39 -0
- package/dist/providers/adapters/pi-cli/index.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/index.js +69 -0
- package/dist/providers/adapters/pi-cli/index.js.map +1 -0
- package/dist/providers/adapters/pi-cli/observability/sessionId.d.ts +27 -0
- package/dist/providers/adapters/pi-cli/observability/sessionId.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/observability/sessionId.js +53 -0
- package/dist/providers/adapters/pi-cli/observability/sessionId.js.map +1 -0
- package/dist/providers/adapters/pi-cli/policy.d.ts +44 -0
- package/dist/providers/adapters/pi-cli/policy.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/policy.js +74 -0
- package/dist/providers/adapters/pi-cli/policy.js.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/agenticSessionRpc.d.ts +34 -0
- package/dist/providers/adapters/pi-cli/transport/agenticSessionRpc.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/agenticSessionRpc.js +194 -0
- package/dist/providers/adapters/pi-cli/transport/agenticSessionRpc.js.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/oneShotCompletion.d.ts +33 -0
- package/dist/providers/adapters/pi-cli/transport/oneShotCompletion.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/oneShotCompletion.js +166 -0
- package/dist/providers/adapters/pi-cli/transport/oneShotCompletion.js.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/piSpawn.d.ts +87 -0
- package/dist/providers/adapters/pi-cli/transport/piSpawn.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/piSpawn.js +242 -0
- package/dist/providers/adapters/pi-cli/transport/piSpawn.js.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/rpcClient.d.ts +103 -0
- package/dist/providers/adapters/pi-cli/transport/rpcClient.d.ts.map +1 -0
- package/dist/providers/adapters/pi-cli/transport/rpcClient.js +238 -0
- package/dist/providers/adapters/pi-cli/transport/rpcClient.js.map +1 -0
- package/dist/providers/bootRegistration.d.ts +38 -0
- package/dist/providers/bootRegistration.d.ts.map +1 -1
- package/dist/providers/bootRegistration.js +52 -0
- package/dist/providers/bootRegistration.js.map +1 -1
- package/dist/providers/parity/rules/skillParityRule.d.ts.map +1 -1
- package/dist/providers/parity/rules/skillParityRule.js +6 -0
- package/dist/providers/parity/rules/skillParityRule.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -2
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +22 -0
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +10 -3
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/PipeSessionSpawner.d.ts +23 -0
- package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -1
- package/dist/threadline/PipeSessionSpawner.js +27 -0
- package/dist/threadline/PipeSessionSpawner.js.map +1 -1
- package/package.json +2 -2
- package/scripts/lint-no-direct-destructive.js +3 -0
- package/scripts/lint-no-unfunneled-headless-launch.js +128 -0
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +3 -2
- package/upgrades/1.3.344.md +46 -0
- package/upgrades/1.3.345.md +154 -0
- package/upgrades/side-effects/apprenticeship-layer-balance.md +50 -0
- package/upgrades/side-effects/june15-headless-spawn-reroute.md +105 -0
- package/upgrades/side-effects/pi-harness-integration.md +82 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The apprenticeship program can now SEE when its deepest layer is starving.
|
|
9
|
+
`GET /apprenticeship/instances/:id/role-coverage` gains an observe-only
|
|
10
|
+
`keystoneBalance` block:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
keystoneBalance: {
|
|
14
|
+
keystoneAxis: 'mentor-mentee-differential',
|
|
15
|
+
keystoneCycleCount, lastKeystoneAt,
|
|
16
|
+
oversightCycleCount, oversightSinceKeystone,
|
|
17
|
+
starved: boolean, starvationThreshold, reason
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`starved:true` means the deepest layer (the real mentor→mentee drive) is
|
|
22
|
+
under-firing relative to ongoing activity — the silent "mentor-heavy /
|
|
23
|
+
mentee-light" drift. It generalizes the old narrow `driftWarning` to catch the
|
|
24
|
+
common "keystone fired once long ago, then drifted into pure review" case, not
|
|
25
|
+
just "never fired." Tune the threshold per call with
|
|
26
|
+
`?oversightStarvationThreshold=N` (default 3).
|
|
27
|
+
|
|
28
|
+
Why: a 2026-06-06 holistic check found the program lopsided — the mentor layer
|
|
29
|
+
had run 13 cycles while the mentee layer ran 3, and nothing surfaced it. By
|
|
30
|
+
"Observation Needs Structure," a duty to notice is a wish unless the structure
|
|
31
|
+
makes it visible.
|
|
32
|
+
|
|
33
|
+
PR 2 of the June-15 interactive-only readiness arc (PR 1 = #873). The
|
|
34
|
+
provider substrate could route INTERNAL intelligence calls onto the
|
|
35
|
+
subscription lane, but Instar's other headless surface — full agentic
|
|
36
|
+
`claude -p` one-shots for scheduled jobs, mentor loops, dispatch actions,
|
|
37
|
+
`POST /sessions/spawn`, and Threadline A2A cold replies — still billed the
|
|
38
|
+
Agent SDK pot with no reroute. This PR adds the reroute at the single funnel
|
|
39
|
+
those spawns share (`SessionManager.spawnSession`'s headless branch,
|
|
40
|
+
claude-code only): under `intelligence.subscriptionPath.mode` `force` (or
|
|
41
|
+
`auto` when the shared credit decision says subscription), the spawn
|
|
42
|
+
launches an INTERACTIVE claude session instead — same tmux session, same
|
|
43
|
+
watchdogs/reaper semantics, same `--allowedTools`/`--strict-mcp-config`
|
|
44
|
+
flags (spliced for parity), wide-pane geometry, with the prompt delivered
|
|
45
|
+
via the existing ready-wait + guarded-inject machinery. Because an
|
|
46
|
+
interactive REPL never exits, each rerouted task carries a deterministic
|
|
47
|
+
completion sentinel (`INSTAR_JOB_COMPLETE_<id>`) the monitor reaps as
|
|
48
|
+
SUCCESS (job history records success, not timeout), plus a hard
|
|
49
|
+
`maxLifetimeMinutes` backstop (default 45). Safety gates from the 5-reviewer
|
|
50
|
+
convergence: a rerouted-session concurrency cap (`maxRerouted`, default 3) +
|
|
51
|
+
memory-pressure pre-spawn gate (auto → degradation-reported headless
|
|
52
|
+
fallback; force → loud refusal); subscription-quota backpressure on the A2A
|
|
53
|
+
(`SpawnRequestManager`) and pipe (`PipeSessionSpawner`) surfaces so peer
|
|
54
|
+
traffic cannot rate-limit the operator's own account; force-mode pipe
|
|
55
|
+
refusal that falls through to the rerouted A2A path; a paste-escape
|
|
56
|
+
sanitizer in the tmux inject path; per-slug double-run guard + boot-time
|
|
57
|
+
reconciliation so a restart can't double-execute a mid-flight job; an F6
|
|
58
|
+
recurrence cap so a silently-dead reroute escalates instead of hiding behind
|
|
59
|
+
its own fallback; a persisted `Session.launchLane` field surfaced in
|
|
60
|
+
`GET /sessions` and the reap-log (the soak's machine-checkable criterion);
|
|
61
|
+
a new CI lint (`lint-no-unfunneled-headless-launch`) that fails any future
|
|
62
|
+
direct `buildHeadlessLaunch` callsite outside the funnel; three
|
|
63
|
+
factory-bypass intelligence fallbacks in server.ts routed through the
|
|
64
|
+
factory (carrying breaker + router); and the CLAUDE.md awareness block
|
|
65
|
+
corrected (+ a new migration that reaches already-deployed agents).
|
|
66
|
+
Everything ships dark: mode `off` (the fleet default) is pinned
|
|
67
|
+
byte-for-byte to today's argv by test at every touched callsite.
|
|
68
|
+
|
|
69
|
+
## What to Tell Your User
|
|
70
|
+
|
|
71
|
+
Your agent can now answer "is my apprenticeship loop actually exercising its
|
|
72
|
+
deepest layer, or has it drifted into just reviewing?" — and flag when the
|
|
73
|
+
mentee side is starving, so the imbalance can't silently persist.
|
|
74
|
+
|
|
75
|
+
After June 15, the one-shot commands I use for background jobs and
|
|
76
|
+
agent-to-agent replies start drawing from a prepaid credit pot — and when it
|
|
77
|
+
empties, they'd just fail. I already have a switch (from the last release)
|
|
78
|
+
that can move my internal thinking onto normal interactive sessions; this
|
|
79
|
+
release teaches that SAME switch to also cover my scheduled jobs and
|
|
80
|
+
agent-to-agent replies. It also adds the guardrails that make flipping it
|
|
81
|
+
safe: caps so rerouted work can't overload the machine or eat your
|
|
82
|
+
subscription's rate window (other agents messaging me can never crowd out
|
|
83
|
+
YOUR conversations), a marker system so finished jobs are recorded as
|
|
84
|
+
successes, restart protection so a job can't accidentally run twice, and a
|
|
85
|
+
visible label on every session showing which billing lane it used — so when
|
|
86
|
+
we run the full-day proof, "everything ran on the subscription lane" is
|
|
87
|
+
something you can check, not something you have to take my word for.
|
|
88
|
+
Nothing changes at this release — the switch is still OFF everywhere.
|
|
89
|
+
|
|
90
|
+
## Summary of New Capabilities
|
|
91
|
+
|
|
92
|
+
- `keystoneBalance` on the role-coverage response — observe-only deepest-layer
|
|
93
|
+
health, with a plain-English `reason`. Tunable via
|
|
94
|
+
`?oversightStarvationThreshold=N`. Never gates.
|
|
95
|
+
|
|
96
|
+
- `intelligence.subscriptionPath.mode` now governs headless job/A2A/dispatch
|
|
97
|
+
spawns too (claude-code only): `force` → interactive sessions with a
|
|
98
|
+
completion sentinel + lifetime backstop; `auto` → credit-driven, with
|
|
99
|
+
loud, capped fallback.
|
|
100
|
+
- New config knobs: `subscriptionPath.maxRerouted` (concurrent rerouted
|
|
101
|
+
sessions, default 3), `subscriptionPath.maxReroutedLifetimeMinutes`
|
|
102
|
+
(default 45).
|
|
103
|
+
- `Session.launchLane` (`headless` | `rerouted-interactive`) in
|
|
104
|
+
`GET /sessions` and the reap-log — machine-checkable billing-lane audit.
|
|
105
|
+
- Quota backpressure on A2A + pipe spawn admission when the reroute is
|
|
106
|
+
active (wired to the same QuotaTracker gate jobs already use).
|
|
107
|
+
- Per-slug job double-run guard + boot reconciliation of rerouted job
|
|
108
|
+
sessions.
|
|
109
|
+
- CI funnel lint: direct `buildHeadlessLaunch` use outside the allowlist
|
|
110
|
+
fails the build.
|
|
111
|
+
|
|
112
|
+
## Evidence
|
|
113
|
+
|
|
114
|
+
Computed from existing cycle rows (no new storage, no migration). Three test
|
|
115
|
+
tiers green: unit (both sides of every boundary incl. shortcut-still-un-driven
|
|
116
|
+
and exactly-at-threshold), integration (route surfaces it + honors the tuning
|
|
117
|
+
query), e2e (alive through the real AgentServer). Migration-parity + agent-
|
|
118
|
+
awareness covered (template line + idempotent PostUpdateMigrator backfill, 4
|
|
119
|
+
tests). `tsc --noEmit` clean.
|
|
120
|
+
|
|
121
|
+
5-reviewer adversarial convergence (correctness/wiring, security/cost,
|
|
122
|
+
ops/scale, standards/lessons, spec-vs-reality — ALL initially BLOCK; every
|
|
123
|
+
finding folded into the spec, `docs/specs/june15-headless-spawn-reroute.md`
|
|
124
|
+
+ `.eli16.md`). 56+ new tests across all three tiers: 43 unit
|
|
125
|
+
(`headless-spawn-reroute` 23 incl. argv pins both sides / completionMode
|
|
126
|
+
both sides / cap + lifetime + sanitizer + reconciliation;
|
|
127
|
+
`subscription-quota-gates` 8; `lint-no-unfunneled-headless-launch` 4;
|
|
128
|
+
`PostUpdateMigrator-subscriptionPathScope` 5; `job-scheduler-double-run-
|
|
129
|
+
guard` 3), 1 integration (`sessions-launch-lane` — launchLane through the
|
|
130
|
+
real route pipeline), 4 e2e (`june15-headless-spawn-reroute` — production-
|
|
131
|
+
mirroring construction: force spawn has no `-p` + wide pane, sentinel
|
|
132
|
+
completes as success through the real monitor, launchLane via real HTTP,
|
|
133
|
+
default-off invariance). Funnel lint clean on the tree + self-tested both
|
|
134
|
+
ways. Silent-fallback ratchet held at 458.
|
|
135
|
+
|
|
136
|
+
## Scope (honest)
|
|
137
|
+
|
|
138
|
+
Observe-only this slice. It surfaces the imbalance; it does NOT yet
|
|
139
|
+
auto-correct. The natural phase-2 (a cadence rule that drives the mentee layer
|
|
140
|
+
at least once per K mentor cycles) is deliberately deferred so the signal
|
|
141
|
+
proves out first — the same ship-the-observation-before-the-law order used for
|
|
142
|
+
#856/#864 ahead of the #861 constitution article.
|
|
143
|
+
|
|
144
|
+
## Incidental fix surfaced by the merge (Zero-Failure Standard)
|
|
145
|
+
|
|
146
|
+
Merging current `main` in surfaced a pre-existing route-completeness ratchet
|
|
147
|
+
break introduced by #884 (P1 Coherence Journal): the new `/coherence/journal`
|
|
148
|
+
route's catch handled `InvalidCursorError` then `throw err` for everything
|
|
149
|
+
else — leaving the routes.ts catch/instanceof count at 225/224 and producing an
|
|
150
|
+
unhandled Express 500 (HTML stack leak) on any non-cursor error. Fixed in this
|
|
151
|
+
PR by returning a proper JSON 500 (`err instanceof Error ? err.message :
|
|
152
|
+
String(err)`) instead of re-throwing — strictly better behavior, and it
|
|
153
|
+
rebalances the ratchet (225/225). Owned per the Zero-Failure Standard ("there
|
|
154
|
+
is no such thing as a pre-existing failure").
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Side-Effects Review - apprenticeship layer-balance signal
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `apprenticeship-layer-balance`
|
|
4
|
+
**Date:** `2026-06-06`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required (Tier 1)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Adds an observe-only `keystoneBalance` block to `ApprenticeshipCycleStore.roleCoverage()` (surfaced via `GET /apprenticeship/instances/:id/role-coverage`). It makes the deepest-layer (mentor→mentee keystone) health a queryable fact: starved when the keystone never fired while oversight did, or enough oversight accrued since the last drive (default 3, tunable via `?oversightStarvationThreshold`). Generalizes the existing narrow `driftWarning`.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `computeKeystoneBalance` (new, private) - pure function over the already-tallied axes + oversight timestamps; no I/O.
|
|
15
|
+
- `roleCoverage(instanceId, opts?)` - modify - gains an optional opts param (back-compat: existing single-arg callers unchanged) and returns the new `keystoneBalance` field.
|
|
16
|
+
- role-coverage route - modify - parses optional `?oversightStarvationThreshold` and passes it through.
|
|
17
|
+
|
|
18
|
+
## 1. Behavior change / gating
|
|
19
|
+
|
|
20
|
+
NONE. This is observe-only. It adds a computed read-only field; it never blocks a cycle record, never gates a transition, never alters the loop. The existing `driftWarning` is untouched (kept for back-compat). No new failure modes.
|
|
21
|
+
|
|
22
|
+
## 2. Over/under-signal
|
|
23
|
+
|
|
24
|
+
- Over-signal (false "starved"): a long-idle instance whose keystone fired once then sat through 3+ reviews reads starved even if that's intended. Acceptable — it's a surfaced signal a human/loop reads, not an action; and "intended long review phase" is exactly the thing worth surfacing.
|
|
25
|
+
- Under-signal: an instance with keystone fired recently + <threshold oversight reads healthy even if the mentee is actually stuck. Acceptable for this slice — the signal is about cadence/balance, not per-drive quality (that's operatorSeatUx + the transcript auditor's job).
|
|
26
|
+
|
|
27
|
+
## 3. Blast radius
|
|
28
|
+
|
|
29
|
+
`roleCoverage()` callers: the one route + tests. The added param is optional, so every existing call compiles and behaves identically (just gains the extra field). No persistent-state change (computed from existing rows), so NO migration and NO data backfill. The field appears automatically for all instances, including legacy/grandfathered ones (their keystone history is read as-is).
|
|
30
|
+
|
|
31
|
+
## 4. Failure modes
|
|
32
|
+
|
|
33
|
+
`computeKeystoneBalance` is pure and total — no throw paths, no I/O. A malformed threshold from the query string falls back to the default (parsed-int guard). ISO string timestamp comparison is safe (createdAt is always normalized ISO).
|
|
34
|
+
|
|
35
|
+
## 5. Migration parity
|
|
36
|
+
|
|
37
|
+
No agent-installed files change. The signal is a new read-only API field, so Agent Awareness gets a one-line mention in the CLAUDE.md template + a PostUpdateMigrator backfill so deployed agents learn the state question ("is my mentee layer starving?"). No config, hooks, or skills touched.
|
|
38
|
+
|
|
39
|
+
## 6. Scope honesty (what this is NOT)
|
|
40
|
+
|
|
41
|
+
Observe-only by design. It does NOT auto-correct the imbalance — the natural phase-2 (a cadence rule that makes the autonomous loop drive the mentee layer at least once per K mentor cycles) is deliberately deferred so the signal proves out first (Graduated-Feature-Rollout discipline; ship the observation structure before the enforcement law — the same order as #856/#864 before the #861 constitution article).
|
|
42
|
+
|
|
43
|
+
## 7. Incidental fix carried by this PR (`src/server/routes.ts`)
|
|
44
|
+
|
|
45
|
+
Merging current `main` surfaced a pre-existing route-completeness ratchet break introduced by #884 (P1 Coherence Journal): the new `/coherence/journal` route caught `InvalidCursorError` then `throw err` for everything else. That left routes.ts at 225 `catch (err)` / 224 `err instanceof Error` (ratchet red) and leaked an unhandled Express HTML 500 on any non-cursor error.
|
|
46
|
+
|
|
47
|
+
- **Decision point:** the journal route's catch block — `modify`. Behavior change: a non-cursor error now returns a clean JSON `{ error }` 500 instead of propagating to Express's default HTML error handler. Strictly better (no stack-trace leak), no new failure mode.
|
|
48
|
+
- **Causal autopsy:** balance was even (224/224) at `6c2af5a6a`, went to 225/224 at `7afa768f5` (#884). Confirmed via per-commit catch/instanceof bisect.
|
|
49
|
+
- **Blast radius:** one route's error path. No state, no migration, no gating. Rebalances the ratchet to 225/225 (route-completeness green).
|
|
50
|
+
- **Why carried here vs separate PR:** the merge made this PR's CI red and #893 can't go green without it; owned per the Zero-Failure Standard ("there is no such thing as a pre-existing failure"). Documented loudly rather than silently bundled.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Side-Effects Review — june15-headless-spawn-reroute (PR 2)
|
|
2
|
+
|
|
3
|
+
Spec: `docs/specs/june15-headless-spawn-reroute.md` (converged, 5-reviewer
|
|
4
|
+
panel, approved by Justin in topic 9984 2026-06-05: "you have my approval if
|
|
5
|
+
needed. please continue!").
|
|
6
|
+
|
|
7
|
+
## Behavior changes when `mode` is `off`/unset (the fleet default)
|
|
8
|
+
|
|
9
|
+
NONE intended, and pinned: V1/V5 argv pin tests assert the headless launch
|
|
10
|
+
argv is byte-for-byte unchanged at every touched callsite for claude-code,
|
|
11
|
+
codex-cli, and gemini-cli. Admission behavior of SpawnRequestManager and
|
|
12
|
+
PipeSessionSpawner is unchanged (the quota/mode seams are absent unless the
|
|
13
|
+
server wires them, and the server only wires them when mode is auto/force).
|
|
14
|
+
Two deliberate always-on deltas, both additive:
|
|
15
|
+
|
|
16
|
+
1. **`Session.launchLane` is now always stamped** (`'headless'` on the
|
|
17
|
+
default lane) and serialized via the existing `{ ...s }` spreads in
|
|
18
|
+
`GET /sessions` + the reap-log. Consumers that enumerate fields see one
|
|
19
|
+
more key. No existing field changes meaning.
|
|
20
|
+
2. **`rawInject` strips bracketed-paste boundary sequences**
|
|
21
|
+
(`\x1b[200~` / `\x1b[201~`) from injected text (finding S2). Legitimate
|
|
22
|
+
prompts never contain raw paste-boundary bytes; anything that did was a
|
|
23
|
+
forged-submit vector, not a feature.
|
|
24
|
+
3. **Three server.ts intelligence FALLBACK sites** (shared, relationships,
|
|
25
|
+
topic-summarizer) now construct via `buildIntelligenceProvider` instead
|
|
26
|
+
of `new ClaudeCliIntelligenceProvider` directly. When the factory returns
|
|
27
|
+
null (no claude binary), the fallback is now an honest absence instead of
|
|
28
|
+
a provider with an undefined binary path that fails at call time — a
|
|
29
|
+
strict improvement on an already-degraded path.
|
|
30
|
+
|
|
31
|
+
## New failure modes introduced (and their bounds)
|
|
32
|
+
|
|
33
|
+
- **Rerouted session never prints the sentinel** → reaped at
|
|
34
|
+
`maxLifetimeMinutes` (default 45) as timeout + DegradationReporter event.
|
|
35
|
+
Bound: the lifetime cap; tested.
|
|
36
|
+
- **Reroute silently dead under `auto`** (cap stuck / pressure stuck /
|
|
37
|
+
continuity gap) → every spawn falls back headless; the F6 recurrence cap
|
|
38
|
+
raises ONE escalated degradation event per 30-min window instead of
|
|
39
|
+
letting the self-heal hide it.
|
|
40
|
+
- **Restart with a rerouted job mid-flight** → boot reconciliation kills the
|
|
41
|
+
surviving REPL (job reruns cleanly); per-slug guard prevents concurrent
|
|
42
|
+
double-execution. Tested both.
|
|
43
|
+
- **Force-mode refusal paths** (cap/memory/pipe) are loud: throw or
|
|
44
|
+
`{spawned:false}` + degradation event — never a silent drop. The pipe
|
|
45
|
+
refusal shape (`{spawned:false}` from inside `spawn()`) is pinned by test
|
|
46
|
+
because only that shape falls through to the rerouted A2A path.
|
|
47
|
+
|
|
48
|
+
## Quota / cost surface
|
|
49
|
+
|
|
50
|
+
- Rerouted spawns bill the subscription 5h window. Bounds: `maxRerouted`
|
|
51
|
+
(default 3) + memory-pressure pre-spawn gate + QuotaTracker backpressure
|
|
52
|
+
on A2A/pipe admission (the same gate scheduled jobs already use). The
|
|
53
|
+
worst case (window already ≥95%) denies non-critical spawns BEFORE they
|
|
54
|
+
land on the window.
|
|
55
|
+
- The `auto` decision reuses PR 1's TTL-cached credit reader via
|
|
56
|
+
`setSdkCreditReader` — one credit source for both routing layers, no
|
|
57
|
+
drift.
|
|
58
|
+
|
|
59
|
+
## Framework generality (launch/inject surface)
|
|
60
|
+
|
|
61
|
+
The reroute is **claude-code-only by design** — it exists to dodge
|
|
62
|
+
Anthropic's June-15 SDK-pot billing; codex-cli and gemini-cli headless
|
|
63
|
+
spawns are untouched on every mode (V5 pin test). The launch builders are
|
|
64
|
+
not modified; the reroute composes `buildInteractiveLaunch` +
|
|
65
|
+
`claudeHeadlessExtraFlags`, both existing per-framework abstractions. Works
|
|
66
|
+
for codex-cli/gemini-cli: yes — by explicit non-interference, pinned by
|
|
67
|
+
test. The funnel lint allowlist names the two legitimate
|
|
68
|
+
`buildHeadlessLaunch` callsites; a future framework adding a third must
|
|
69
|
+
justify it at review.
|
|
70
|
+
|
|
71
|
+
## Migration / fleet rollout
|
|
72
|
+
|
|
73
|
+
- New config keys are optional with safe defaults; `migrateConfig` additions
|
|
74
|
+
are not needed (absence = off).
|
|
75
|
+
- CLAUDE.md template block corrected + a NEW idempotent migration
|
|
76
|
+
(`headless job / agent-to-agent / dispatch spawns` sniff) patches the
|
|
77
|
+
already-deployed PR-1 wording on update — both PR-1 wordings ("routes
|
|
78
|
+
them" / "can route them") handled; hand-edited sections left untouched.
|
|
79
|
+
Tested (5 cases incl. idempotency).
|
|
80
|
+
|
|
81
|
+
## Evidence pointers
|
|
82
|
+
|
|
83
|
+
- Unit: `tests/unit/headless-spawn-reroute.test.ts` (23),
|
|
84
|
+
`tests/unit/subscription-quota-gates.test.ts` (8),
|
|
85
|
+
`tests/unit/lint-no-unfunneled-headless-launch.test.ts` (4),
|
|
86
|
+
`tests/unit/PostUpdateMigrator-subscriptionPathScope.test.ts` (5),
|
|
87
|
+
`tests/unit/job-scheduler-double-run-guard.test.ts` (3)
|
|
88
|
+
- Integration: `tests/integration/sessions-launch-lane.test.ts` (1)
|
|
89
|
+
- E2E: `tests/e2e/june15-headless-spawn-reroute.test.ts` (4 —
|
|
90
|
+
production-mirroring construction, the spawn-path "200 not 503")
|
|
91
|
+
- Spec + ELI16: `docs/specs/june15-headless-spawn-reroute.md` / `.eli16.md`
|
|
92
|
+
- Lint: `scripts/lint-no-unfunneled-headless-launch.js` (chained in
|
|
93
|
+
`pnpm lint`)
|
|
94
|
+
|
|
95
|
+
## CI-green follow-up #2 (Zero-Failure Standard)
|
|
96
|
+
|
|
97
|
+
A main-drift imbalance surfaced by this PR's CI (route-completeness:
|
|
98
|
+
225 `catch (err)` vs 224 `err instanceof Error` in routes.ts — landed on
|
|
99
|
+
main via a [skip ci] merge that never ran this shard). Per the Zero-Failure
|
|
100
|
+
Standard ("no pre-existing failures — if you see it you own it"), fixed by
|
|
101
|
+
converting one `/providers/...` route's `(err as Error).message` cast to the
|
|
102
|
+
safe `err instanceof Error ? err.message : String(err)` guard — balances the
|
|
103
|
+
count AND is strictly safer (an `as Error` cast lies if err isn't an Error).
|
|
104
|
+
Also: the codex-model-swap structure-pin window widened past the reroute
|
|
105
|
+
branch (test-only — invariant unchanged).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Side-Effects Review — pi-harness-integration (Phases A-D)
|
|
2
|
+
|
|
3
|
+
## Blast radius
|
|
4
|
+
|
|
5
|
+
Additive at every layer; ships DARK behind `enabledFrameworks` containing
|
|
6
|
+
`'pi-cli'` (default absent) AND a detectable `pi` binary. With the default
|
|
7
|
+
config, NOTHING registers and NO dispatch path changes outcome.
|
|
8
|
+
|
|
9
|
+
Shared-file behavior changes (the only non-additive surfaces):
|
|
10
|
+
|
|
11
|
+
1. **SessionManager.isMarkerStuckAtPrompt** — adds a new detection branch
|
|
12
|
+
(marker sandwiched between two `─` rule lines, pi's input box). Requires
|
|
13
|
+
BOTH the injected marker AND a rule within 2 lines above AND below —
|
|
14
|
+
normal transcript output and the other frameworks' panes cannot satisfy
|
|
15
|
+
it (their input areas use prompt chars, not rule sandwiches). Existing
|
|
16
|
+
prompt-char branches untouched; pinned by tests against real captured
|
|
17
|
+
pane bytes for all four frameworks.
|
|
18
|
+
2. **SessionManager binary resolution** — pi-cli now resolves to bare `'pi'`
|
|
19
|
+
instead of falling back to `claudePath` (which holds a CLAUDE binary).
|
|
20
|
+
Without this, a pi topic on a claude install would silently launch
|
|
21
|
+
Claude — the exact additive-only violation this project forbids. Other
|
|
22
|
+
frameworks' resolution is byte-identical.
|
|
23
|
+
3. **framework-arg-rendering-matrix test** — now DERIVED from
|
|
24
|
+
SUPPORTED_FRAMEWORKS instead of a hand-list (which had silently dropped
|
|
25
|
+
gemini-cli). Test-only.
|
|
26
|
+
|
|
27
|
+
## Framework generality
|
|
28
|
+
|
|
29
|
+
This change EXTENDS the framework abstraction rather than special-casing:
|
|
30
|
+
'pi-cli' rides the same Record<IntelligenceFramework, …> dispatch maps as
|
|
31
|
+
claude-code / codex-cli / gemini-cli (launch builders, injection processes,
|
|
32
|
+
activity + process signals, parity renderer), and the never-exhaustiveness
|
|
33
|
+
checks force every future dispatch site to handle all four. The
|
|
34
|
+
framework-agnosticism test suite passes with pi included; the arg-rendering
|
|
35
|
+
matrix now derives from the registry so a fifth framework cannot silently
|
|
36
|
+
miss coverage. Warm-session injection works for pi via the
|
|
37
|
+
FRAMEWORK_INJECTION_PROCESS_NAMES entry ('pi'), same as every framework.
|
|
38
|
+
|
|
39
|
+
## Security / spend surfaces
|
|
40
|
+
|
|
41
|
+
- **Subscription guard (structural)**: Anthropic/Claude-routed pi model
|
|
42
|
+
patterns are DENIED at every call-construction path (one-shot transport,
|
|
43
|
+
RPC session start, intelligence provider construction), including
|
|
44
|
+
aggregator pass-throughs (`openrouter/anthropic/...`) and the
|
|
45
|
+
pattern-less case (pi's ambient default could be an Anthropic login).
|
|
46
|
+
Override is file-config only (`piCli.allowAnthropicProviders`), never env
|
|
47
|
+
or per-call; allowed calls are audit-logged with a cost warning.
|
|
48
|
+
- **Child env hardening**: pi child processes get an explicit allowlist env
|
|
49
|
+
with UNCONDITIONAL deletion of all billing-capable vars (ANTHROPIC/
|
|
50
|
+
OPENAI/GEMINI/GOOGLE/XAI/GROQ/OPENROUTER keys + CLAUDE_CODE_OAUTH_TOKEN),
|
|
51
|
+
mirroring the gemini Rule-1a analog. pi authenticates via its own
|
|
52
|
+
~/.pi/agent/auth.json (subscription OAuth) or models.json custom keys.
|
|
53
|
+
- **No permission system in pi**: instar's gate layer wraps pi sessions
|
|
54
|
+
exactly like other frameworks; containerization noted as future hardening
|
|
55
|
+
in the spec (§ kickoff Risks).
|
|
56
|
+
|
|
57
|
+
## Rollback
|
|
58
|
+
|
|
59
|
+
Revert the commit. No migrations mutate state destructively (the CLAUDE.md
|
|
60
|
+
migration is content-sniffed append-only; no config defaults are written).
|
|
61
|
+
Agents that opted in simply lose the pi framework value on downgrade —
|
|
62
|
+
their claude-code/codex-cli/gemini-cli behavior is untouched.
|
|
63
|
+
|
|
64
|
+
## Deliberate non-goals (tracked in spec §9, not silent drops)
|
|
65
|
+
|
|
66
|
+
Replacing any Claude Code path; pi-ai adoption for non-pi internal calls;
|
|
67
|
+
OAuth flow automation; cross-provider mid-session handoff; event-stream
|
|
68
|
+
dashboard renderer (optional Phase E, decided after the core lands).
|
|
69
|
+
|
|
70
|
+
## CI-fix follow-up (post-first-commit)
|
|
71
|
+
|
|
72
|
+
Greening CI surfaced expected churn, no behavior change:
|
|
73
|
+
- Three existing tests hardcoded the 3-framework SUPPORTED list → added
|
|
74
|
+
'pi-cli' to the expectations.
|
|
75
|
+
- The `no-silent-fallbacks` ratchet counted the intelligenceProviderFactory
|
|
76
|
+
pi-cli degrade-to-null catch → marked `@silent-fallback-ok` (it warns
|
|
77
|
+
loudly and the router emits its own DegradationReporter; a second report
|
|
78
|
+
here would double-count the same degrade).
|
|
79
|
+
- The release-fragment lint required `## Summary of New Capabilities` → added
|
|
80
|
+
it and switched to the canonical `<!-- bump -->` comment.
|
|
81
|
+
None of these touch runtime behavior; they align the new framework with the
|
|
82
|
+
existing structural gates.
|