instar 1.3.542 → 1.3.543

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.
@@ -0,0 +1,114 @@
1
+ /**
2
+ * CredentialRepointingLivetest — the §5 livetest battery as testable orchestration
3
+ * (Step 10 of live credential re-pointing).
4
+ *
5
+ * Spec: docs/specs/live-credential-repointing-rebalancer.md §5 (livetest battery),
6
+ * §0.c (E3/E4 live experiments), §2.8 (dogfood (d) — the §0.c residual).
7
+ *
8
+ * ── What this is ──
9
+ * The livetest battery is the GATE for the dry-run → live promotion decision. It is
10
+ * NOT part of the merge CI and it NEVER runs autonomously: it exchanges REAL OAuth
11
+ * credentials between the operator's REAL subscription accounts, so executing it is an
12
+ * enablement-time action the OPERATOR takes (with the agent), never a dark-build step.
13
+ *
14
+ * This module is the ORCHESTRATION + VERDICT logic for battery items (a) and (b) —
15
+ * the automatable round-trip swaps — expressed over INJECTED dependencies so it is
16
+ * fully unit-testable WITHOUT touching a keychain (the same fake-deps discipline the
17
+ * CredentialSwapExecutor itself uses). Items (c) post-swap refresher correctness and
18
+ * (d) the §0.c at-expiry residual (a deliberately-minted disposable grant) are
19
+ * inherently manual/destructive and are surfaced as REQUIRED operator steps in the
20
+ * report rather than auto-run — the harness reports them as pending, it does not fake
21
+ * a pass.
22
+ *
23
+ * ── The load-bearing safety contract ──
24
+ * 1. ARMED GUARD. `run()` REFUSES unless explicitly armed (the live entrypoint sets
25
+ * `armed` only behind an operator flag + the feature's own enable check). An
26
+ * unarmed run returns a `refused` report and performs ZERO swaps — so importing
27
+ * or unit-testing this module can never move a real credential.
28
+ * 2. IDENTITY-VERIFIED ROUND TRIP. Every swap is verified by the identity oracle
29
+ * (NOT by `claude auth status`, disqualified in E4a as a lying oracle): after a
30
+ * swap the two slots' oracle identities must have EXCHANGED; after the swap-back
31
+ * they must be RESTORED to the originals. Any deviation fails the step.
32
+ * 3. ALWAYS SWAP BACK. A round-trip leaves the world as it found it. If the forward
33
+ * swap's verification fails the harness still attempts the restoring swap and
34
+ * reports the residual state honestly (never silently leaves slots exchanged).
35
+ *
36
+ * This module performs NO IO of its own: it calls injected `swap` + `resolveIdentity`
37
+ * functions. The live entrypoint wires those to the real CredentialSwapExecutor +
38
+ * CredentialIdentityOracle; a unit test wires fakes.
39
+ */
40
+ /** Identity of the account whose credential currently sits in a slot (oracle answer). */
41
+ export interface SlotIdentity {
42
+ /** The owning account id/email per the identity oracle, or null when unresolvable. */
43
+ accountId: string | null;
44
+ }
45
+ export interface CredentialRepointingLivetestDeps {
46
+ /**
47
+ * Executes a REAL staged swap of the credentials in slotA and slotB (the shipped
48
+ * CredentialSwapExecutor.swap in live mode). Resolves on success; rejects on a
49
+ * refusal/error (the harness records the rejection as a step failure).
50
+ */
51
+ swap: (slotA: string, slotB: string) => Promise<{
52
+ ok: boolean;
53
+ detail?: string;
54
+ }>;
55
+ /**
56
+ * Resolves which account a slot's credential belongs to via the identity oracle
57
+ * (GET /api/oauth/profile over the slot's blob — E4b). Returns `{ accountId: null }`
58
+ * when the oracle is unavailable/uncertain (the harness treats null as a verify
59
+ * failure, never a guess).
60
+ */
61
+ resolveIdentity: (slot: string) => Promise<SlotIdentity>;
62
+ }
63
+ /** A single battery step's verdict. */
64
+ export interface LivetestStepResult {
65
+ step: string;
66
+ passed: boolean;
67
+ detail: string;
68
+ /** Ordered observations (identities seen, swaps performed) for the operator report. */
69
+ observations: string[];
70
+ }
71
+ /** The full battery report. */
72
+ export interface LivetestReport {
73
+ armed: boolean;
74
+ /** True only when armed AND every automated step passed AND no manual step is outstanding. */
75
+ promotable: boolean;
76
+ refusedReason?: string;
77
+ steps: LivetestStepResult[];
78
+ /** Items (c)/(d): inherently manual/destructive — listed, never auto-passed. */
79
+ manualSteps: string[];
80
+ }
81
+ export interface CredentialRepointingLivetestOptions {
82
+ /**
83
+ * MUST be true to run any swap. The live entrypoint sets this only behind the
84
+ * operator flag + the feature enable check. Default false ⇒ a strict no-op refusal.
85
+ */
86
+ armed?: boolean;
87
+ }
88
+ export declare class CredentialRepointingLivetest {
89
+ private readonly swap;
90
+ private readonly resolveIdentity;
91
+ private readonly armed;
92
+ constructor(deps: CredentialRepointingLivetestDeps, opts?: CredentialRepointingLivetestOptions);
93
+ /** The §2.8 manual/destructive items — surfaced for the operator, never auto-run. */
94
+ static readonly MANUAL_STEPS: readonly string[];
95
+ /**
96
+ * Battery items (a)/(b): an identity-verified swap round-trip between two slots.
97
+ * Used for both an enrolled-home pair (a) and the default-home slot (b).
98
+ * ALWAYS attempts to restore the original layout, even when the forward verify fails.
99
+ */
100
+ private roundTrip;
101
+ /**
102
+ * Runs the automatable battery (items a + b). REFUSES (zero swaps) unless armed.
103
+ * @param enrolledPair two enrolled-home slots for item (a).
104
+ * @param defaultSlotPair the default-home slot paired with an enrolled slot for item (b).
105
+ */
106
+ run(enrolledPair: {
107
+ slotA: string;
108
+ slotB: string;
109
+ }, defaultSlotPair: {
110
+ defaultSlot: string;
111
+ enrolledSlot: string;
112
+ }): Promise<LivetestReport>;
113
+ }
114
+ //# sourceMappingURL=CredentialRepointingLivetest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialRepointingLivetest.d.ts","sourceRoot":"","sources":["../../src/core/CredentialRepointingLivetest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,yFAAyF;AACzF,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,gCAAgC;IAC/C;;;;OAIG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF;;;;;OAKG;IACH,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1D;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,8FAA8F;IAC9F,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,gFAAgF;IAChF,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,mCAAmC;IAClD;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,4BAA4B;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2C;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsD;IACtF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;gBAEpB,IAAI,EAAE,gCAAgC,EAAE,IAAI,CAAC,EAAE,mCAAmC;IAM9F,qFAAqF;IACrF,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAW7C;IAEF;;;;OAIG;YACW,SAAS;IA2CvB;;;;OAIG;IACG,GAAG,CACP,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAC9C,eAAe,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAC7D,OAAO,CAAC,cAAc,CAAC;CA6B3B"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * CredentialRepointingLivetest — the §5 livetest battery as testable orchestration
3
+ * (Step 10 of live credential re-pointing).
4
+ *
5
+ * Spec: docs/specs/live-credential-repointing-rebalancer.md §5 (livetest battery),
6
+ * §0.c (E3/E4 live experiments), §2.8 (dogfood (d) — the §0.c residual).
7
+ *
8
+ * ── What this is ──
9
+ * The livetest battery is the GATE for the dry-run → live promotion decision. It is
10
+ * NOT part of the merge CI and it NEVER runs autonomously: it exchanges REAL OAuth
11
+ * credentials between the operator's REAL subscription accounts, so executing it is an
12
+ * enablement-time action the OPERATOR takes (with the agent), never a dark-build step.
13
+ *
14
+ * This module is the ORCHESTRATION + VERDICT logic for battery items (a) and (b) —
15
+ * the automatable round-trip swaps — expressed over INJECTED dependencies so it is
16
+ * fully unit-testable WITHOUT touching a keychain (the same fake-deps discipline the
17
+ * CredentialSwapExecutor itself uses). Items (c) post-swap refresher correctness and
18
+ * (d) the §0.c at-expiry residual (a deliberately-minted disposable grant) are
19
+ * inherently manual/destructive and are surfaced as REQUIRED operator steps in the
20
+ * report rather than auto-run — the harness reports them as pending, it does not fake
21
+ * a pass.
22
+ *
23
+ * ── The load-bearing safety contract ──
24
+ * 1. ARMED GUARD. `run()` REFUSES unless explicitly armed (the live entrypoint sets
25
+ * `armed` only behind an operator flag + the feature's own enable check). An
26
+ * unarmed run returns a `refused` report and performs ZERO swaps — so importing
27
+ * or unit-testing this module can never move a real credential.
28
+ * 2. IDENTITY-VERIFIED ROUND TRIP. Every swap is verified by the identity oracle
29
+ * (NOT by `claude auth status`, disqualified in E4a as a lying oracle): after a
30
+ * swap the two slots' oracle identities must have EXCHANGED; after the swap-back
31
+ * they must be RESTORED to the originals. Any deviation fails the step.
32
+ * 3. ALWAYS SWAP BACK. A round-trip leaves the world as it found it. If the forward
33
+ * swap's verification fails the harness still attempts the restoring swap and
34
+ * reports the residual state honestly (never silently leaves slots exchanged).
35
+ *
36
+ * This module performs NO IO of its own: it calls injected `swap` + `resolveIdentity`
37
+ * functions. The live entrypoint wires those to the real CredentialSwapExecutor +
38
+ * CredentialIdentityOracle; a unit test wires fakes.
39
+ */
40
+ export class CredentialRepointingLivetest {
41
+ swap;
42
+ resolveIdentity;
43
+ armed;
44
+ constructor(deps, opts) {
45
+ this.swap = deps.swap;
46
+ this.resolveIdentity = deps.resolveIdentity;
47
+ this.armed = opts?.armed === true;
48
+ }
49
+ /** The §2.8 manual/destructive items — surfaced for the operator, never auto-run. */
50
+ static MANUAL_STEPS = [
51
+ '(c) Post-swap hourly-refresher correctness on BOTH slots — observe one QuotaPoller ' +
52
+ '401-refresh cycle per slot after a swap and confirm the refreshed token stays on the ' +
53
+ 'right account (requires waiting out an access-token expiry; operator-observed).',
54
+ '(d) §0.c at-expiry write-back residual — mint a DISPOSABLE second grant, swap it under a ' +
55
+ 'live session, drive it past access-token expiry, and confirm the scheduled identity ' +
56
+ 'audit detects-or-clears any old-lineage write-back. Destructive to a real lineage by ' +
57
+ 'design ⇒ operator-run with a throwaway grant only.',
58
+ 'Liveness (E4): run the round-trip while a real interactive session is pinned to one slot ' +
59
+ 'and confirm zero interruption (the harness cannot observe the operator\'s session; ' +
60
+ 'operator-observed).',
61
+ ];
62
+ /**
63
+ * Battery items (a)/(b): an identity-verified swap round-trip between two slots.
64
+ * Used for both an enrolled-home pair (a) and the default-home slot (b).
65
+ * ALWAYS attempts to restore the original layout, even when the forward verify fails.
66
+ */
67
+ async roundTrip(slotA, slotB, label) {
68
+ const observations = [];
69
+ const beforeA = await this.resolveIdentity(slotA);
70
+ const beforeB = await this.resolveIdentity(slotB);
71
+ observations.push(`before: ${slotA}=${beforeA.accountId ?? 'UNRESOLVED'}, ${slotB}=${beforeB.accountId ?? 'UNRESOLVED'}`);
72
+ if (beforeA.accountId === null || beforeB.accountId === null) {
73
+ return { step: label, passed: false, detail: 'oracle could not resolve a slot identity before the swap (fail-closed — never guess)', observations };
74
+ }
75
+ if (beforeA.accountId === beforeB.accountId) {
76
+ return { step: label, passed: false, detail: 'both slots already report the same account — cannot prove an exchange', observations };
77
+ }
78
+ // Forward swap.
79
+ const fwd = await this.swap(slotA, slotB).catch((e) => ({ ok: false, detail: e instanceof Error ? e.message : String(e) }));
80
+ observations.push(`forward swap: ${fwd.ok ? 'ok' : 'FAILED — ' + (fwd.detail ?? 'unknown')}`);
81
+ if (!fwd.ok) {
82
+ return { step: label, passed: false, detail: `forward swap did not complete: ${fwd.detail ?? 'unknown'}`, observations };
83
+ }
84
+ const afterA = await this.resolveIdentity(slotA);
85
+ const afterB = await this.resolveIdentity(slotB);
86
+ observations.push(`after swap: ${slotA}=${afterA.accountId ?? 'UNRESOLVED'}, ${slotB}=${afterB.accountId ?? 'UNRESOLVED'}`);
87
+ const exchanged = afterA.accountId === beforeB.accountId && afterB.accountId === beforeA.accountId;
88
+ // ALWAYS restore — even if the verify above failed, leave the world as we found it.
89
+ const back = await this.swap(slotA, slotB).catch((e) => ({ ok: false, detail: e instanceof Error ? e.message : String(e) }));
90
+ observations.push(`restoring swap: ${back.ok ? 'ok' : 'FAILED — ' + (back.detail ?? 'unknown')}`);
91
+ const restoredA = await this.resolveIdentity(slotA);
92
+ const restoredB = await this.resolveIdentity(slotB);
93
+ observations.push(`after restore: ${slotA}=${restoredA.accountId ?? 'UNRESOLVED'}, ${slotB}=${restoredB.accountId ?? 'UNRESOLVED'}`);
94
+ const restored = restoredA.accountId === beforeA.accountId && restoredB.accountId === beforeB.accountId;
95
+ if (!exchanged) {
96
+ return { step: label, passed: false, detail: 'oracle identities did NOT exchange after the forward swap — actuation unproven', observations };
97
+ }
98
+ if (!back.ok || !restored) {
99
+ return { step: label, passed: false, detail: 'forward swap verified, but the layout was NOT cleanly restored — left in a residual state, investigate before promotion', observations };
100
+ }
101
+ return { step: label, passed: true, detail: 'identity-verified round trip: slots exchanged then restored cleanly', observations };
102
+ }
103
+ /**
104
+ * Runs the automatable battery (items a + b). REFUSES (zero swaps) unless armed.
105
+ * @param enrolledPair two enrolled-home slots for item (a).
106
+ * @param defaultSlotPair the default-home slot paired with an enrolled slot for item (b).
107
+ */
108
+ async run(enrolledPair, defaultSlotPair) {
109
+ const manualSteps = [...CredentialRepointingLivetest.MANUAL_STEPS];
110
+ if (!this.armed) {
111
+ return {
112
+ armed: false,
113
+ promotable: false,
114
+ refusedReason: 'livetest is the dry-run→live PROMOTION gate — it swaps REAL credentials between REAL ' +
115
+ 'accounts and only runs when explicitly armed by the operator at enablement (never in CI, ' +
116
+ 'never as a dark-build step). No swaps performed.',
117
+ steps: [],
118
+ manualSteps,
119
+ };
120
+ }
121
+ const steps = [];
122
+ steps.push(await this.roundTrip(enrolledPair.slotA, enrolledPair.slotB, '(a) enrolled-home swap round-trip (E3/E4 vs the shipped executor)'));
123
+ steps.push(await this.roundTrip(defaultSlotPair.defaultSlot, defaultSlotPair.enrolledSlot, '(b) default-home slot swap + swap-back (CMT-1337 payoff; the claude-created ACL)'));
124
+ const allAutomatedPassed = steps.every((s) => s.passed);
125
+ return {
126
+ armed: true,
127
+ // Promotion still requires the operator to complete the manual items — the harness
128
+ // never declares promotable while manual steps remain outstanding.
129
+ promotable: allAutomatedPassed && manualSteps.length === 0,
130
+ steps,
131
+ manualSteps,
132
+ };
133
+ }
134
+ }
135
+ //# sourceMappingURL=CredentialRepointingLivetest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialRepointingLivetest.js","sourceRoot":"","sources":["../../src/core/CredentialRepointingLivetest.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.542",
3
+ "version": "1.3.543",
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-06-13T22:02:57.992Z",
5
- "instarVersion": "1.3.542",
4
+ "generatedAt": "2026-06-13T22:07:14.093Z",
5
+ "instarVersion": "1.3.543",
6
6
  "entryCount": 201,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -5,6 +5,13 @@
5
5
 
6
6
  ## What Changed
7
7
 
8
+ Delivers the §5 livetest battery as testable orchestration — the structural gate for the dry-run→live promotion decision, so "is this safe to turn on?" is a runnable, verified procedure rather than a memory.
9
+
10
+ - **`CredentialRepointingLivetest`** (src/core/CredentialRepointingLivetest.ts) — drives the automatable battery items against injected deps (wired to the real CredentialSwapExecutor + identity oracle only at enablement):
11
+ - **(a)** enrolled-home swap round-trip and **(b)** default-home slot round-trip — each verified by the identity oracle (NOT `claude auth status`, disqualified in E4a): after the swap the two slots' identities must have EXCHANGED; after the swap-back they must be RESTORED. The harness ALWAYS attempts the restoring swap, even when the forward verify fails, and reports any residual state honestly.
12
+ - **Manual items surfaced, never auto-passed** — (c) post-swap refresher correctness, (d) the §0.c at-expiry residual (a deliberately-minted disposable grant), and the E4 liveness observation are listed as required operator steps; `promotable` stays `false` while any remain outstanding.
13
+ - **Armed guard** — `run()` performs ZERO swaps unless explicitly armed. The arm is set only at enablement, behind an operator flag + the feature's own enable check — never in CI, never as a dark-build step. So importing or testing the module can never move a real credential.
14
+
8
15
  Closes the Migration Parity + Agent Awareness gap for live credential re-pointing: existing agents (not just fresh installs) learn the `/credentials/*` manual levers and the zero-touch default-account flip on their next update, and the dark config defaults are proven to install in place.
9
16
 
10
17
  - **CLAUDE.md awareness (new + existing agents)** — `generateClaudeMd()` (new agents) and `migrateClaudeMd()` (existing agents, content-sniffed + idempotent) both gain a "Live Credential Re-pointing" section documenting the levers (`GET /credentials/locations`, `POST /credentials/swap`, `POST /credentials/set-default`, `POST /credentials/restore-enrollment`, `GET /credentials/rebalancer`) and the two proactive triggers: "flip my default account" → `set-default`; "which account is this slot on?" → `GET /credentials/locations` (read the ledger, never infer from `claude auth status`).
@@ -13,13 +20,19 @@ Closes the Migration Parity + Agent Awareness gap for live credential re-pointin
13
20
 
14
21
  ## What to Tell Your User
15
22
 
23
+ Nothing changes for you yet — this is off-by-default validation tooling. What it adds: before the credential-moving feature is ever turned on, there's now a runnable, verified "is this actually safe to switch on?" check. It performs a real account-swap and swap-back and confirms — using the trustworthy identity check, not the misleading one — that the move landed on the right account and then cleanly undid itself, always leaving things exactly as they were. Two parts of that check are inherently hands-on (they involve waiting out a token refresh and deliberately stressing a throwaway login), so the tool lists those as steps you and I do together. Crucially, the whole battery refuses to run unless explicitly armed at turn-on time — it can never move a real credential just by existing or being tested. Turning the feature on, and running this gate, stays your decision.
24
+
16
25
  Nothing changes for you yet — the underlying feature is still switched off. What this does is make me *aware* of it so that, once it's turned on after a review window, you can just say "flip my default account to X" or "which account is this session on?" and I'll know exactly which lever to use instead of guessing. It also makes sure that awareness reaches agents that were set up before this feature existed, not only brand-new ones — so the capability isn't silently missing on an older install.
17
26
 
18
27
  ## Summary of New Capabilities
19
28
 
29
+ No new runtime capability — this is the dry-run→live promotion gate for live credential re-pointing, shipped unwired (it runs only when the operator arms it at enablement). New internal class `CredentialRepointingLivetest`: drives the automatable swap round-trip battery (identity-verified exchange-then-restore, always restoring), surfaces the manual items (refresher correctness, the at-expiry residual via a disposable grant, liveness) without ever auto-passing them, and refuses every swap unless explicitly armed. Not wired into any runtime path; no route, no config flag, no credential write path.
30
+
20
31
  No new runtime capability — this is the Migration Parity + Agent Awareness step. New + existing agents' CLAUDE.md now documents the live-credential-re-pointing levers and the "flip my default account" / "which account is this slot on?" proactive triggers, and the feature's dark config defaults are proven to install on existing agents without clobbering an operator's explicit settings. The feature itself remains dark behind `subscriptionPool.credentialRepointing.enabled`.
21
32
 
22
33
  ## Evidence
23
34
 
35
+ - `tests/unit/credential-repointing-livetest.test.ts` (8) — armed guard (refuses + zero swaps when unarmed); armed happy path (both round-trips exchange-then-restore, world left exactly as found, 4 swaps); not-promotable while manual items remain; fail-closed when the oracle can't resolve a slot; refusal when both slots already report one account; fail-AND-still-restore when the forward swap doesn't actuate; residual-state report when the restoring swap fails; the manual items (c)/(d)/E4 are surfaced. tsc + full lint clean; feature-delivery-completeness 97/97.
36
+
24
37
  - `tests/unit/PostUpdateMigrator-credentialRepointing.test.ts` (8) — migrateClaudeMd adds the section when absent (with both proactive triggers + the dark-posture line + the `claude auth status` honesty caveat present), is idempotent (no duplicate on re-run, byte-identical), preserves prior content, and gracefully skips a missing CLAUDE.md; the generateClaudeMd template emits the same section (source parity); and migrateConfig installs the DARK credentialRepointing block on a config lacking it, NEVER clobbers an operator's explicit `enabled:true` (fills only the missing sibling default), and is idempotent.
25
38
  - tsc clean; full `npm run lint` clean (dark-gate unchanged — no ConfigDefaults edit; the defaults already existed from Steps 1/7).
@@ -0,0 +1,43 @@
1
+ # Side-Effects Review — WS5.2 Step 10: livetest battery orchestration (dry-run→live promotion gate)
2
+
3
+ **Version / slug:** `ws52-step10-livetest`
4
+ **Date:** 2026-06-13
5
+ **Author:** echo
6
+ **Second-pass reviewer:** not required (no runtime gate/sentinel/session-lifecycle/messaging-block surface; the module is unwired-by-design tooling, run only at enablement)
7
+
8
+ ## Summary of the change
9
+
10
+ Adds `src/core/CredentialRepointingLivetest.ts` — the §5 livetest battery expressed as testable orchestration — plus its fake-deps unit test. The harness drives the automatable battery items (a) enrolled-home swap round-trip and (b) default-home slot round-trip against INJECTED `swap` + `resolveIdentity` deps (wired to the real CredentialSwapExecutor + identity oracle only at enablement), verifying each swap by oracle identity (exchange-then-restore) and ALWAYS restoring the original layout. The inherently manual/destructive items — (c) post-swap refresher correctness and (d) the §0.c at-expiry residual via a disposable grant — are surfaced as required operator steps, never auto-passed. The module is **not wired into any runtime path**: it is the dry-run→live PROMOTION gate, which by the spec is NOT part of merge CI and runs only when the operator arms it at enablement.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `CredentialRepointingLivetest.run()` — add — a guard (`armed`) and a verdict computation. It is NOT a runtime decision point in any live path: nothing in `server.ts` constructs or calls it. It only ever runs from a unit test (fakes) or, at enablement, an operator-armed entrypoint. The only authority it holds is over its own report (`promotable`), which is advisory input to the operator's enable decision — never an automated action.
15
+
16
+ ---
17
+
18
+ ## 1. Over-block
19
+ No block/allow surface — over-block not applicable. (The `armed` guard refuses to RUN the test battery when unarmed; it gates a test harness, not any user/agent action.)
20
+
21
+ ## 2. Under-block
22
+ No block/allow surface — under-block not applicable. Worth noting the deliberate under-claim: `promotable` is `false` while any manual step remains outstanding, so the harness can never green-light promotion on automated checks alone.
23
+
24
+ ## 3. Level-of-abstraction fit
25
+ Correct layer. This is test/validation tooling at the same abstraction as the executor it exercises, expressed over injected deps so the orchestration is unit-testable without IO (the established CredentialSwapExecutor fake-deps pattern). The real keychain/oracle wiring is deferred to the enablement entrypoint, keeping this module pure and verifiable.
26
+
27
+ ## 4. Signal vs authority compliance
28
+ - [x] No — this change has no block/allow surface in any live path.
29
+
30
+ The `promotable` verdict is a signal to the operator's enable decision, not an authority that performs any action. (Ref: docs/signal-vs-authority.md.)
31
+
32
+ ## 5. Interactions
33
+ - **Shadowing / double-fire / race:** none — the module is unwired; nothing else constructs or calls it. The `armed` default (`false`) guarantees that importing it (e.g. a future index re-export, or a test) performs zero swaps.
34
+ - **Executor interaction (at enablement only):** the harness calls the real `swap` exactly twice per round-trip (forward + restore) and verifies via the oracle between/after; it relies on the executor's own §2.3 safety (staging, verify, quarantine) — it does not re-implement any of it.
35
+
36
+ ## 6. External surfaces
37
+ - No runtime/API/agent-facing surface today (unwired). At enablement the armed entrypoint performs REAL credential swaps on the operator's REAL accounts — which is exactly why it is gated behind an explicit operator arm + the feature's own enable check, runs WITH the operator, and always restores. This module itself ships inert.
38
+
39
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
40
+ - **Machine-local BY DESIGN.** The battery validates THIS machine's keychain + slots before promoting THIS machine's feature to live. Credentials and the identity oracle answers are per-machine; there is no cross-machine surface. Running it on one machine makes no claim about another (each machine is promoted on its own battery).
41
+
42
+ ## 8. Rollback cost
43
+ Trivial. Revert the commit — the module is unwired, so removing it changes no runtime behavior. No state, no migration, no credential touch (the unit test uses fakes only). Any real swap the armed harness ever performs is itself the reversible, oracle-verified, always-restored round-trip from Step 5.