instar 1.3.537 → 1.3.539
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/server.d.ts.map +1 -1
- package/dist/commands/server.js +133 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/core/CredentialAuditEmit.d.ts +82 -0
- package/dist/core/CredentialAuditEmit.d.ts.map +1 -0
- package/dist/core/CredentialAuditEmit.js +132 -0
- package/dist/core/CredentialAuditEmit.js.map +1 -0
- package/dist/core/CredentialLocationGate.d.ts +99 -0
- package/dist/core/CredentialLocationGate.d.ts.map +1 -0
- package/dist/core/CredentialLocationGate.js +127 -0
- package/dist/core/CredentialLocationGate.js.map +1 -0
- package/dist/core/CredentialManualLevers.d.ts +57 -0
- package/dist/core/CredentialManualLevers.d.ts.map +1 -0
- package/dist/core/CredentialManualLevers.js +0 -0
- package/dist/core/CredentialManualLevers.js.map +1 -0
- package/dist/core/CredentialRestoreEnrollment.d.ts +76 -0
- package/dist/core/CredentialRestoreEnrollment.d.ts.map +1 -0
- package/dist/core/CredentialRestoreEnrollment.js +76 -0
- package/dist/core/CredentialRestoreEnrollment.js.map +1 -0
- package/dist/core/CredentialSwapExecutor.d.ts +10 -0
- package/dist/core/CredentialSwapExecutor.d.ts.map +1 -1
- package/dist/core/CredentialSwapExecutor.js +19 -0
- package/dist/core/CredentialSwapExecutor.js.map +1 -1
- package/dist/core/InUseAccountResolver.d.ts +28 -1
- package/dist/core/InUseAccountResolver.d.ts.map +1 -1
- package/dist/core/InUseAccountResolver.js +34 -1
- package/dist/core/InUseAccountResolver.js.map +1 -1
- package/dist/core/QuotaPoller.d.ts +22 -0
- package/dist/core/QuotaPoller.d.ts.map +1 -1
- package/dist/core/QuotaPoller.js +42 -8
- package/dist/core/QuotaPoller.js.map +1 -1
- package/dist/core/SessionManager.d.ts +19 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +37 -5
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/AccountSwitcher.d.ts.map +1 -1
- package/dist/monitoring/AccountSwitcher.js +12 -1
- package/dist/monitoring/AccountSwitcher.js.map +1 -1
- package/dist/monitoring/CredentialProvider.d.ts +32 -1
- package/dist/monitoring/CredentialProvider.d.ts.map +1 -1
- package/dist/monitoring/CredentialProvider.js +32 -2
- package/dist/monitoring/CredentialProvider.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +16 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +20 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +228 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +48 -48
- package/upgrades/1.3.538.md +51 -0
- package/upgrades/1.3.539.md +29 -0
- package/upgrades/side-effects/ws52-step6-census-rerouting.md +143 -0
- package/upgrades/side-effects/ws52-step7-routes-audit.md +39 -0
- package/upgrades/1.3.537.md +0 -27
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Side-Effects Review — WS5.2 Step 6: census consumer re-routing (live credential re-pointing, dark)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `ws52-step6-census-rerouting`
|
|
4
|
+
**Date:** `2026-06-13`
|
|
5
|
+
**Author:** `Echo`
|
|
6
|
+
**Second-pass reviewer:** `4-adversarial-lens self-review (folded as named tests)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Step 6 of live credential re-pointing (spec §2.2). Every place in the live code that today treats a
|
|
11
|
+
pool account's enrollment `configHome` as the LIVE location of its credential is re-routed through a
|
|
12
|
+
single new chokepoint, `CredentialLocationGate`, which reads the `CredentialLocationLedger` (Steps
|
|
13
|
+
1–5, merged). The gate is FLAG-GATED on the EXISTING `subscriptionPool.credentialRepointing.enabled`
|
|
14
|
+
(no new config flag → the dark-gate line-map is UNCHANGED), back-compat-on-unknown (a never-seeded /
|
|
15
|
+
UNKNOWN ledger falls back to today's enrollment-home behavior), and fail-open-loud (an UNKNOWN-mode
|
|
16
|
+
read returns the fallback + ONE HIGH attention, never throws into a hot path). Files touched:
|
|
17
|
+
`src/core/CredentialLocationGate.ts` (new), `src/core/QuotaPoller.ts` (census #1–#4),
|
|
18
|
+
`src/core/InUseAccountResolver.ts` (census #8, the E4a liar), `src/core/SessionManager.ts` (census
|
|
19
|
+
#5/#6 spawn placement), `src/monitoring/CredentialProvider.ts` (census #9 manager-level refusal gate),
|
|
20
|
+
`src/monitoring/AccountSwitcher.ts` (surfaces the #9 refusal cleanly),
|
|
21
|
+
`src/core/CredentialSwapExecutor.ts` (an `onSlotsChanged` commit hook for the #8 cache-bust),
|
|
22
|
+
`src/server/routes.ts` (census #10/#11 PATCH configHome → 409), `src/commands/server.ts` (the wiring
|
|
23
|
+
chain: ledger + gate + consumers + the process-shared refusal gate).
|
|
24
|
+
|
|
25
|
+
## Decision-point inventory
|
|
26
|
+
|
|
27
|
+
- `CredentialLocationGate.slotForAccount/tenantForSlot` — add — the single re-route chokepoint; flag-gated, back-compat-on-unknown, fail-open-loud.
|
|
28
|
+
- `QuotaPoller.accountForReads` — add — resolves each account's live slot for token read / 401-refresh / needs-reauth; email auto-patch SUPPRESSED while enabled.
|
|
29
|
+
- `InUseAccountResolver.resolve` — modify — when enabled + ledger-known, resolves the default badge from the ledger instead of a `claude auth status` re-probe; `bustCache()` added.
|
|
30
|
+
- `SessionManager.resolvePinnedSpawnHome` — add — a pinned account's spawn home resolves through the ledger; an explicit caller home (account-swap path) still wins.
|
|
31
|
+
- `writeCredentialsSerialized` refusal gate — add — a manager-level competing-writer refusal at the funnel chokepoint (not only the route).
|
|
32
|
+
- `PATCH /subscription-pool/:id` configHome — modify — refused 409 while enabled.
|
|
33
|
+
- `CredentialSwapExecutor.onSlotsChanged` — add — a best-effort commit hook that busts the in-use badge on a default-slot swap.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. Over-block
|
|
38
|
+
|
|
39
|
+
The only block surfaces are: (a) the PATCH-409 on `configHome` — refuses ONLY when the flag is
|
|
40
|
+
enabled (always off while dark), and ONLY the `configHome` field (every other field still PATCHes);
|
|
41
|
+
(b) the manager-level competing-writer refusal — refuses ONLY when the flag is enabled AND the ledger
|
|
42
|
+
holds a tenant for the (canonicalized) slot. A legitimate switch to a slot the ledger does NOT own is
|
|
43
|
+
not refused. With the flag off (the shipped dark state) neither block fires: no over-block surface
|
|
44
|
+
exists in the shipped configuration.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. Under-block
|
|
49
|
+
|
|
50
|
+
The competing-writer refusal keys on "the ledger holds a tenant for this canonical slot." If a writer
|
|
51
|
+
targets a slot the ledger has never recorded (a brand-new enrollment home not yet seeded), it is NOT
|
|
52
|
+
refused — but that slot is not repointing-owned, so writing it cannot clobber a ledger tenant; this is
|
|
53
|
+
correct back-compat, not an under-block. The refusal is at the SOLE manager funnel
|
|
54
|
+
(`writeCredentialsSerialized`), so a non-route caller cannot dodge it — the spec §2.7 "every item a
|
|
55
|
+
unique source dodge" is closed by construction.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. Level-of-abstraction fit
|
|
60
|
+
|
|
61
|
+
`CredentialLocationGate` is a thin DETECTOR/router (sync in-memory ledger read + a flag check), not an
|
|
62
|
+
authority — it never decides policy, it answers "where does account X live now?" The single authority
|
|
63
|
+
in the change is the manager-level refusal, which is a deterministic ownership check (does the ledger
|
|
64
|
+
own this slot?) — the correct altitude per spec §2.7 (manager chokepoint, not a brittle route guard).
|
|
65
|
+
The gate FEEDS the existing consumers; it does not run parallel to them. The ledger (Step 2) is the
|
|
66
|
+
lower-level primitive the gate USES; nothing is re-implemented.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 4. Signal vs authority compliance
|
|
71
|
+
|
|
72
|
+
**Reference:** docs/signal-vs-authority.md
|
|
73
|
+
|
|
74
|
+
- [x] No — the re-routing reads are SIGNAL (advisory location resolution feeding existing consumers);
|
|
75
|
+
the two block surfaces (PATCH-409, competing-writer refusal) are deterministic OWNERSHIP checks
|
|
76
|
+
over durable ledger state, not brittle heuristics — they have full structural context (the
|
|
77
|
+
ledger is the single source of truth), so they are smart-gate-equivalent, not brittle detectors.
|
|
78
|
+
|
|
79
|
+
The gate reads never own block authority: an UNKNOWN-mode read fails OPEN (returns the enrollment
|
|
80
|
+
fallback + an attention item), so a corrupt ledger degrades to today's behavior, never blocks a poll
|
|
81
|
+
or a spawn.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 5. Interactions
|
|
86
|
+
|
|
87
|
+
- **Shadowing:** the PATCH-409 runs BEFORE `subscriptionPool.update` in `/subscription-pool/:id` — when it fires the update never runs (intended; the field-edit must not land). Other fields are unaffected. The competing-writer refusal runs BEFORE the funnel lock in `writeCredentialsSerialized` — when it fires no lock is taken and no write occurs (intended, non-destructive).
|
|
88
|
+
- **Double-fire:** the swap-commit `onSlotsChanged` fires exactly once per committed swap; it is best-effort and a throwing consumer is swallowed (the commit is the load-bearing op and is never rolled back by a cache-bust failure — proven by a named test).
|
|
89
|
+
- **Races:** the gate reads are sync in-memory (no shared mutable state); the refusal gate reads the ledger's in-memory assignments (single-writer process). The InUseAccountResolver cache-bust is idempotent.
|
|
90
|
+
- **Feedback loops:** the email auto-patch SUPPRESSION removes a feedback loop that previously cross-contaminated pool emails after a swap (spec §2.2 #3) — a net REDUCTION in a feedback path.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 6. External surfaces
|
|
95
|
+
|
|
96
|
+
- **Other agents on the same machine:** none — the ledger/gate are per-machine and read-only over the local ledger.
|
|
97
|
+
- **Install base:** none while dark (flag off → byte-identical behavior; proven by the E2E strict-no-op test + per-consumer flag-off unit tests).
|
|
98
|
+
- **External systems:** none — no new network calls; the gate is a pure in-memory read-router.
|
|
99
|
+
- **Persistent state:** reads the existing `state/credential-locations.json` (Step 2); writes nothing new (Step 6 is read re-routing + two refusal surfaces — neither adds a credential write; the `lint-no-unfunneled-credential-write` lint is clean).
|
|
100
|
+
- **Operator surface:** the PATCH-409 changes a `/subscription-pool/:id` response (409 with a plain-English message pointing at `POST /credentials/set-default`) ONLY when the flag is enabled. The AccountSwitcher refusal returns a `SwitchResult` with a plain-English message — phone-surfaced via the existing `/switch-account` Telegram command path. No new operator-only API action is introduced.
|
|
101
|
+
|
|
102
|
+
## 6b. Operator-surface quality
|
|
103
|
+
|
|
104
|
+
No operator surface (no dashboard renderer / approval page / grant form) is added or touched — the
|
|
105
|
+
dashboard Subscriptions-tab `/credentials/locations` fetch is census #11, which Step 7 owns per the
|
|
106
|
+
build plan §6/§7 sequencing. Not applicable to this step.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
111
|
+
|
|
112
|
+
**machine-local BY DESIGN.** The reason it SHOULD differ per machine: each machine's
|
|
113
|
+
`CredentialLocationLedger` is the truth for ITS OWN slots (`SubscriptionPool` decision 1A —
|
|
114
|
+
per-machine enrollment = independent grant lineages). An N-machine pool has N independent ledgers and
|
|
115
|
+
this step adds NO cross-machine read. A swap on machine A never needs to bust machine B's in-use cache
|
|
116
|
+
(B's `~/.claude` is a different physical slot with a different credential). Degrades safely:
|
|
117
|
+
ledger-unknown on any machine → that machine shows today's (re-probed) badge / enrollment-home reads.
|
|
118
|
+
No user-facing notice is emitted by the re-routing (the only notice is the UNKNOWN-mode HIGH attention,
|
|
119
|
+
which is a per-machine local degradation surface — correct as machine-local). No durable cross-machine
|
|
120
|
+
state is created; no URLs are generated. (Phase C of the build prompt, answered explicitly.)
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Rollback
|
|
125
|
+
|
|
126
|
+
Pure dark-ship: the feature is gated on `subscriptionPool.credentialRepointing.enabled` (default
|
|
127
|
+
false). With the flag off — the only shipped state — every census consumer is byte-for-byte today's
|
|
128
|
+
behavior, the refusal gate refuses nothing, the PATCH-409 never fires. To roll back the code entirely,
|
|
129
|
+
revert this commit; nothing persists (no migration, no new state file — the ledger file predates this
|
|
130
|
+
step). No two-flag flip is part of this step.
|
|
131
|
+
|
|
132
|
+
## 4-adversarial-lens verdict
|
|
133
|
+
|
|
134
|
+
1. **E4a-liar resurrection (THE blocker) — PASS.** `InUseAccountResolver` does NOT re-probe `claude auth status` for the default badge when enabled + ledger-known; it reads `ledger.tenantOf('~/.claude')`. `bustCache()` clears the cache, fired at swap-commit by `onSlotsChanged` for any `~/.claude` swap. Named tests: "resolves from the ledger, NEVER re-probes auth status" (`probe` spy asserted `.not.toHaveBeenCalled()`) + "bustCache clears a cached probe result" + the E2E flag-on `probeCalls() === 0`.
|
|
135
|
+
2. **Competing-writer clobber — PASS.** The refusal lives at the MANAGER (`writeCredentialsSerialized`), before the funnel lock; a non-route caller inherits it. Named tests: "REFUSES at the manager when the slot is repointing-owned (no write occurs)" + AccountSwitcher "REFUSES the switch (no write) … active account unchanged."
|
|
136
|
+
3. **Hot-path safety — PASS.** A ledger UNKNOWN-mode read returns the enrollment fallback + ONE deduped HIGH attention, never throws. Named tests: "UNKNOWN mode → fallback + ONE deduped HIGH attention, never throws" + "a THROWING attention emitter never escapes the read."
|
|
137
|
+
4. **Dark-ship inertness — PASS.** Flag off → every consumer byte-for-byte today. Named tests: the per-consumer flag-OFF unit tests (every census row) + the E2E "FLAG OFF: the full wiring is alive AND strictly inert" (in-use re-probes, quota reads enrollment home, PATCH allowed).
|
|
138
|
+
|
|
139
|
+
## Test tiers
|
|
140
|
+
|
|
141
|
+
- Unit: `tests/unit/credential-location-gate.test.ts` (19), additions to `credential-swap-executor.test.ts` (#8 cache-bust + throw-safety), `interactive-session-pin.test.ts` (#6 spawn re-route ×3), `account-switcher-provider.test.ts` (#9 refusal ×2).
|
|
142
|
+
- Integration: `tests/integration/credential-repointing-census-routes.test.ts` (PATCH-409 live, flag-off 200, non-configHome field still PATCHes).
|
|
143
|
+
- E2E: `tests/e2e/credential-repointing-census-lifecycle.test.ts` (feature-alive: flag-off strict no-op end-to-end + flag-on ledger short-circuit).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Side-effects review — WS5.2 Step 7 (credential re-pointing routes + audit-scrub chokepoint)
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/live-credential-repointing-rebalancer.md` (approved:true, converged) §2.4/§2.9/§0.g, build-plan §7.
|
|
4
|
+
**Tier:** 2 (src code + HTTP routes; ships DARK behind the EXISTING `subscriptionPool.credentialRepointing` flag — no new gate).
|
|
5
|
+
**Parent principle:** Cross-Machine Coherence — One Agent, Robust Under Degraded Conditions.
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
|
|
9
|
+
- **NEW `src/core/CredentialAuditEmit.ts`** — the SINGLE secret-scrub chokepoint (`scrub`/`scrubString` + a `CredentialAuditEmit` handle). Every `logs/credential-swaps.jsonl` write, every `/credentials/*` response body, and every attention-item routes through it; it deep-walks records and redacts token-shaped runs via the existing `redactToken` (CredentialProvider.ts:56 — reused, not re-authored).
|
|
10
|
+
- **NEW `src/core/CredentialRestoreEnrollment.ts`** — `classifyRestoreCoherence`: the identity-coherence decision (access-tenant == refresh-lineage). Incoherent / unparseable / refresh-token-less / oracle-unavailable → one-directional park verdict (never exchanged into a healthy slot).
|
|
11
|
+
- **NEW `src/core/CredentialManualLevers.ts`** — per-pair cooldown + §0.g `force:true` budget (`maxForcedManualSwapsPerWindow`). Surfaced refusals, never silent.
|
|
12
|
+
- **`src/commands/server.ts`** — constructs the live `CredentialSwapExecutor` (the Step-5 residual "nothing constructs the executor live yet" closes here), the audit emit (jsonl sink + telegram attention), the composed oracle→pool `resolveIdentity`, and the levers; passes the `credentialRepointing` bundle to AgentServer.
|
|
13
|
+
- **`src/server/routes.ts`** — registers `POST /credentials/swap|set-default|restore-enrollment` (Bearer), `GET /credentials/locations` (census #11), `GET /credentials/rebalancer` (503 in Increment A). Every response sent through `audit.response(...)`.
|
|
14
|
+
- **`src/server/AgentServer.ts`** — threads the `credentialRepointing` bundle option → routeCtx.
|
|
15
|
+
- **`src/server/CapabilityIndex.ts`** — registers the `/credentials` prefix + endpoints.
|
|
16
|
+
- **`src/core/types.ts`** — adds `maxForcedManualSwapsPerWindow?` + `forcedManualSwapWindowMs?` config knobs (NUMBER knobs — NO `enabled: false` literal, so the dark-gate line-map is UNCHANGED, verified 16/16 clean).
|
|
17
|
+
- **`site/src/content/docs/architecture/under-the-hood.md`** — documents `CredentialAuditEmit` (the new exported class; ≥2 mentions for docs-coverage).
|
|
18
|
+
|
|
19
|
+
## State files / migrations
|
|
20
|
+
|
|
21
|
+
- Writes `logs/credential-swaps.jsonl` (size-rotation is Increment B; Step 7 appends). No new schema; reuses the Step-2 `credential-locations.json` ledger.
|
|
22
|
+
- **No migration in Step 7.** CLAUDE.md awareness template + `migrateConfig`/`migrateClaudeMd` are explicitly scoped to **build-plan §9 (Step 9 — Migration parity)** — tracked there, not deferred silently. The new config knobs are optional with code-level defaults, so an existing agent with no value behaves identically.
|
|
23
|
+
|
|
24
|
+
## Blast radius / reversibility
|
|
25
|
+
|
|
26
|
+
- **Ships DARK.** Every lever 503s/no-ops while `subscriptionPool.credentialRepointing.enabled` is false (always, fleet-wide). The executor's own two-flag gate (`enabled`+`dryRun`) makes it a strict no-op too. E2E proves byte-for-byte today's behavior with the flag off.
|
|
27
|
+
- A wrong swap is a reversible, oracle-verified permutation (spec §2.4 supervision Tier 0); no token material is ever returned (the scrub chokepoint).
|
|
28
|
+
- `rebalancer` is 503 in Increment A — the autonomous balancer is Increment B; the route exists + is discoverable now.
|
|
29
|
+
|
|
30
|
+
## Adversarial review (4 lenses, folded as named tests)
|
|
31
|
+
|
|
32
|
+
1. **secret-leak-via-audit (THE blocker)** — `scrub` redacts on all 3 surfaces; named test feeds a real-shaped `sk-ant-…` token through jsonl/response/attention and asserts the token core appears in NONE; integration + e2e also assert no token in a response body. Wiring: the route's `credSend` is the only response path and it routes through `audit.response`.
|
|
33
|
+
2. **restore-enrollment poison** — `classifyRestoreCoherence` parks Frankenstein/unparseable/refresh-token-less/oracle-down one-directionally; integration test asserts an incoherent slot is parked, never exchanged.
|
|
34
|
+
3. **auth boundary** — every POST lever + the GET reads 401 without Bearer (integration + e2e named tests); rebalancer 503 dark.
|
|
35
|
+
4. **dark-ship inertness** — flag OFF → 503 on every lever (e2e strict-no-op test, the single most important test).
|
|
36
|
+
|
|
37
|
+
## Silent-fallback tags
|
|
38
|
+
|
|
39
|
+
Every new catch is tagged `@silent-fallback-ok` with justification (audit-write best-effort, attention best-effort, cache-bust best-effort, unparseable-blob → coherence-park). no-silent-fallbacks + no-empty-catch-blocks tests pass; no baseline bump needed.
|
package/upgrades/1.3.537.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Upgrade Guide — vNEXT
|
|
2
|
-
|
|
3
|
-
<!-- assembled-by: assemble-next-md -->
|
|
4
|
-
<!-- bump: minor -->
|
|
5
|
-
|
|
6
|
-
## What Changed
|
|
7
|
-
|
|
8
|
-
A new dev-gated monitoring capability — **Build-Session Yield Safety** (ACT-839) — closes the "a background build died standing-by-for-tests with its work uncommitted" gap. R1: a session reaped while its worktree holds real uncommitted work is now resume-eligible on that alone — the killer (`SessionReaper`) runs a bounded, cached, fail-open `worktreeDirtyCheck` on the session's worktree PRE-kill (never a synchronous git call on the terminate chokepoint) and tags a new `uncommitted-worktree-work` STRONG `WorkEvidence` value. R2 (Close-the-Loop, signal not block): the revived session's continuation prompt leads with a verbatim commit-first directive, and `ResumeQueueDrainer` fires `onWorktreeRevival`, which registers a deduped, beacon-enabled `CommitmentTracker` obligation so a *stalled* revived session is re-surfaced by PromiseBeacon. The *die-again* case reuses the already-shipped, dev-live `OrphanedWorkSentinel` (#1113) for detect+preserve+surface — no duplicate scanner. An explicit operator/user/emergency kill is never auto-revived on a dirty worktree alone (the origin veto is final). Ships ENABLED on developer agents, dark on the fleet, per the new **Maturation Path** constitutional standard.
|
|
9
|
-
|
|
10
|
-
audience: agent-only
|
|
11
|
-
maturity: experimental
|
|
12
|
-
|
|
13
|
-
## What to Tell Your User
|
|
14
|
-
|
|
15
|
-
Nothing to announce proactively — it's an experimental safety net that runs on developer agents. If anyone asks: a background build that gets shut down before it saves its work now gets brought back and reminded to commit, instead of the work quietly vanishing.
|
|
16
|
-
|
|
17
|
-
## Summary of New Capabilities
|
|
18
|
-
|
|
19
|
-
| Capability | How to Use |
|
|
20
|
-
|-----------|-----------|
|
|
21
|
-
| Revive a session reaped with a dirty worktree | Automatic (dev-enabled, dark on fleet) |
|
|
22
|
-
| Durable "commit your worktree" obligation | `GET /commitments` (beacon re-surfaces a stalled revive) |
|
|
23
|
-
| Tune the dirty-check / residue denylist | `monitoring.yieldSafety.*` config |
|
|
24
|
-
|
|
25
|
-
## Evidence
|
|
26
|
-
|
|
27
|
-
Not a bug fix — a new dark, dev-gated feature; "not reproducible in dev" in the bug-fix sense. Verified by the 3-tier test plan: 19 unit tests for the shared `worktreeDirtyCheck` (both sides of every boundary + fail-open + cache), 5 reaper tests (dirty → evidence, clean → empty, feature-dark → empty, throw → fail-open, no-cwd → not-consulted), 7 drainer tests (the directive + the revival-obligation hook), and an e2e wired-pipeline test (a reaped dirty-worktree session is revived → the directive appears → a real beacon-enabled commitment is registered + deduped). tsc clean; full lint + dark-gate golden line-map + docs-coverage green.
|