instar 1.3.1249 → 1.3.1250

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.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "890eec3dcf80e4388c396f43001a0bd3fabc80c88f79aa9525885d6402a6dfdf",
5
- "packageVersion": "1.3.1249",
5
+ "packageVersion": "1.3.1250",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "da9c464af31b76d291aca99a5c3c89118da1ff2a46d113e7a4af5409206a9f5c",
2
+ "sha256": "9d8e4e883e348a23f831acecf4ccf189799d1720dfa1b3ece88970908ac35d46",
3
3
  "registrySha256": "890eec3dcf80e4388c396f43001a0bd3fabc80c88f79aa9525885d6402a6dfdf",
4
- "packageVersion": "1.3.1249"
4
+ "packageVersion": "1.3.1250"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "890eec3dcf80e4388c396f43001a0bd3fabc80c88f79aa9525885d6402a6dfdf",
3
3
  "articleCount": 91,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1249"
5
+ "packageVersion": "1.3.1250"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.1249",
3
+ "version": "1.3.1250",
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-09-21T17:48:31.257Z",
5
- "instarVersion": "1.3.1249",
4
+ "generatedAt": "2026-09-21T20:47:12.436Z",
5
+ "instarVersion": "1.3.1250",
6
6
  "entryCount": 203,
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
14
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
23
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
32
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
41
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
50
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
59
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
68
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
77
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
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": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
86
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:telegram-origin-guard": {
@@ -101,7 +101,7 @@
101
101
  "domain": "coherence",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
104
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
104
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:scope-coherence-checkpoint": {
@@ -110,7 +110,7 @@
110
110
  "domain": "coherence",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
113
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
113
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:free-text-guard": {
@@ -119,7 +119,7 @@
119
119
  "domain": "safety",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
122
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
122
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:claim-intercept": {
@@ -128,7 +128,7 @@
128
128
  "domain": "coherence",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
131
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
131
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:claim-intercept-response": {
@@ -137,7 +137,7 @@
137
137
  "domain": "coherence",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
140
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
140
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "hook:stop-gate-router": {
@@ -146,7 +146,7 @@
146
146
  "domain": "safety",
147
147
  "sourcePath": "src/core/PostUpdateMigrator.ts",
148
148
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
149
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
149
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
150
150
  "since": "2025-01-01"
151
151
  },
152
152
  "hook:auto-approve-permissions": {
@@ -155,7 +155,7 @@
155
155
  "domain": "safety",
156
156
  "sourcePath": "src/core/PostUpdateMigrator.ts",
157
157
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
158
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
158
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
159
159
  "since": "2025-01-01"
160
160
  },
161
161
  "job:health-check": {
@@ -1571,7 +1571,7 @@
1571
1571
  "type": "subsystem",
1572
1572
  "domain": "updates",
1573
1573
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1574
- "contentHash": "5f7b43d410a9570065bdb15fd007a5fcbf938ec0c9ba210bf4db698c106bed80",
1574
+ "contentHash": "1444a86dc90dd560739c7eae26ef90f8d3876e313da6a505b96668a84b105e0a",
1575
1575
  "since": "2025-01-01"
1576
1576
  },
1577
1577
  "subsystem:scheduler": {
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "890eec3dcf80e4388c396f43001a0bd3fabc80c88f79aa9525885d6402a6dfdf",
5
- "packageVersion": "1.3.1249",
5
+ "packageVersion": "1.3.1250",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "da9c464af31b76d291aca99a5c3c89118da1ff2a46d113e7a4af5409206a9f5c",
2
+ "sha256": "9d8e4e883e348a23f831acecf4ccf189799d1720dfa1b3ece88970908ac35d46",
3
3
  "registrySha256": "890eec3dcf80e4388c396f43001a0bd3fabc80c88f79aa9525885d6402a6dfdf",
4
- "packageVersion": "1.3.1249"
4
+ "packageVersion": "1.3.1250"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "890eec3dcf80e4388c396f43001a0bd3fabc80c88f79aa9525885d6402a6dfdf",
3
3
  "articleCount": 91,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1249"
5
+ "packageVersion": "1.3.1250"
6
6
  }
@@ -5,6 +5,17 @@
5
5
 
6
6
  ## What Changed
7
7
 
8
+ Autonomous-run setup and its stop hook resolved the local server's bearer token
9
+ by reading `config.json.authToken` inline. On vault-migrated agents that field
10
+ is the SecretMigrator placeholder, so every call from those scripts was refused
11
+ — and on an admission-enforcing install a new run sat `preparing` forever with
12
+ no message. Both scripts now resolve the token vault-aware (config string first,
13
+ else `secret-get.mjs`), a refused registration prints a clear error (fatal only
14
+ where admission is required to arm), and a pre-existing `pipefail`+`grep -c`
15
+ trap that could kill setup silently is fixed. Existing agents receive both
16
+ scripts through PostUpdateMigrator marker bumps (`VAULT_AUTH_RESOLVE`);
17
+ customized copies are skipped, never overwritten.
18
+
8
19
  Codex quota readings are now LIVE. Until now the only source was the session log the codex CLI writes after each completed turn, which has a structural limit: the number is only as fresh as the last time something ran on the account, and a WALLED account — where no turn completes — produces no record at all. That is why an idle account sat on a days-old number and why an exhausted account could show nothing whatsoever.
9
20
 
10
21
  The codex CLI's app-server protocol exposes `account/rateLimits/read` — the exact call its own `/status` screen makes: a metadata fetch answered by OpenAI's backend without running a model turn. No tokens, no quota, measured at 450–850ms per account. A new reader (`codexLiveRateLimitReader`) speaks the protocol — spawn `codex app-server` against the account's config home, one initialize handshake, one read, kill — and maps the answer into the existing snapshot shape with `source: 'codex-app-server'`.
@@ -66,6 +77,10 @@ This is presentation only. No routing, placement, load-shedding, proactive-swap
66
77
 
67
78
  ## What to Tell Your User
68
79
 
80
+ If you start a long unattended work session and its setup can't actually take
81
+ hold, I now tell you immediately with a plain reason — instead of you
82
+ discovering the next day that nothing ever ran.
83
+
69
84
  Your Codex usage bars are now live. Before, the number could only update when something actually ran on an account, so an idle account showed a days-old reading and an account that had hit its weekly wall showed nothing at all. Now every poll asks Codex directly — the same question its own status screen asks — and that costs nothing: no tokens, no quota. Walled accounts show their true 100% with the real reset time instead of a blank card.
70
85
 
71
86
  If anything ever goes wrong with the live check, the dashboard quietly falls back to the old log-reading method, so the worst case is a stale number clearly labelled with its age — never a missing or wrong one.
@@ -90,6 +105,11 @@ Your Subscriptions dashboard now shows each account's quota bar in green, amber
90
105
 
91
106
  ## Summary of New Capabilities
92
107
 
108
+ - Autonomous setup + stop hook work on vault-migrated agents (token from the vault).
109
+ - A refused run registration is loud, and stops setup where arming is impossible.
110
+ - A silent shell-trap death in setup's concurrency fallback is fixed.
111
+ - Both fixes reach deployed agents via migration.
112
+
93
113
  - Live, zero-spend Codex quota via `codex app-server`'s `account/rateLimits/read` — the poller and `GET /codex/usage` are live-first with rollout-tail fallback.
94
114
  - Walled accounts (which write no rollout records) now report their true utilization and reset time.
95
115
  - Provenance on every reading: `source: 'codex-app-server'` (live) vs `'codex-rollout'` (fallback), through the pool, the route, and WS5.2 replication.
@@ -121,6 +141,10 @@ Your Subscriptions dashboard now shows each account's quota bar in green, amber
121
141
 
122
142
  ## Evidence
123
143
 
144
+ - Unit `tests/unit/autonomous-setup-vault-auth.test.ts` (4 passing): runs the REAL script against a real HTTP server — the vault value reaches the server when config holds the placeholder; config string wins; enforcing + 403 aborts loudly with no state file; enforcing + unreachable still starts `preparing` (contract preserved, also covers the pipefail fix).
145
+ - Unit `tests/unit/PostUpdateMigrator-vaultAuthResolve.test.ts` (5 passing): the genuine pre-change blobs (pinned by commit) are re-deployed idempotently; the pinned predecessor SHA matches the real PREPARATION_CARRIER-era hook; customized copies are skipped by name.
146
+ - All 12 existing stop-hook suites (142 tests) and the 6 other setup-consuming suites (40 tests) pass against the modified scripts.
147
+
124
148
  - Live, through the built dist against all five real Codex config homes (2026-09-20): justin@sagemindai.io 100% walled · dawn@sagemindai.io 100% walled (previously structurally unreadable) · headley.justin@gmail.com 100% walled · amrch 86% · adriana 96% — 450–850ms each, all `source=codex-app-server`, zero quota consumed (verified: the walled accounts answered while at 100%).
125
149
  - Protocol ground truth captured from `codex app-server generate-json-schema` (codex-cli 0.153.4).
126
150
  - Unit `tests/unit/codexLiveRateLimitReader.test.ts` (13 passing): response mapping, the walled-account marker, foreign-limit-family refusal, windowless and malformed windows, handshake order, and every failure shape → null (initialize refused, read error, silent child hitting the deadline, early child exit, spawn throw), plus the composition factory's both sides.
@@ -0,0 +1,180 @@
1
+ # Side-Effects Review — Autonomous setup/hook: vault-aware auth + loud registration refusal
2
+
3
+ **Version / slug:** `autonomous-auth-vault-resolve`
4
+ **Date:** `2026-09-21`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `independent reviewer subagent (see appended section)`
7
+
8
+ ## Summary of the change
9
+
10
+ `setup-autonomous.sh` and `autonomous-stop-hook.sh` read the server bearer token
11
+ with an inline `python3` read of `config.json.authToken`. On a vault-migrated
12
+ agent that field is the SecretMigrator placeholder (`{"secret": true}`), so every
13
+ server call from both scripts 403'd. On an admission-enforcing install
14
+ (windowRunLiveness enforcing), the refused registration left the run `preparing`
15
+ forever with no message — the 2026-09-19 silent no-start (ACT-030). This change:
16
+
17
+ - adds `resolve_auth_token()` (marker `VAULT_AUTH_RESOLVE`) to both scripts:
18
+ config string first, else the vault via `.instar/scripts/secret-get.mjs`;
19
+ replaces 3 reads in setup + 8 in the hook;
20
+ - makes a REFUSED registration (401/403) loud, and FATAL only on an
21
+ admission-enforcing install (where an unregistered run structurally cannot
22
+ arm); unreachable-server behaviour is unchanged (best-effort, preparing);
23
+ - fixes a pre-existing `set -euo pipefail` + `grep -c` trap in the local
24
+ concurrency fallback that killed the whole setup silently (exit 1, zero
25
+ output) whenever the server was unreachable/refusing and no other topic had a
26
+ state file;
27
+ - ships both to existing agents via PostUpdateMigrator: a fingerprint-gated
28
+ marker bump for the setup (`W32_PREPARING_LIVENESS` → `VAULT_AUTH_RESOLVE`)
29
+ and an exact-stock-SHA replacement for the hook (predecessor hash pinned as
30
+ `AUTONOMOUS_STOP_HOOK_PREPARATION_CARRIER_SHA256`). Customized copies are
31
+ skipped by name, never overwritten.
32
+
33
+ ## Decision-point inventory
34
+
35
+ - `setup-autonomous.sh` registration branch — **modify** — a 401/403 now aborts
36
+ setup on enforcing installs only; everywhere else it prints and proceeds
37
+ exactly as before. No new authority: the server's admission decision already
38
+ governed whether the run could arm; the script now surfaces it instead of
39
+ swallowing it.
40
+ - `autonomous-stop-hook.sh` — **no decision change** — same calls, correct
41
+ token; every fail-open/fail-closed path is untouched.
42
+ - PostUpdateMigrator — **extend** — two more marker-bump migrations in the
43
+ existing `migrateAutonomousStopHookTopicKeyed` chain.
44
+
45
+ ---
46
+
47
+ ## 1. Over-block
48
+
49
+ - The new fatal branch triggers only when (a) the install resolved
50
+ admission-enforcing at setup time AND (b) the server answered 401/403. A
51
+ wrong-but-reachable server cannot spuriously abort a non-enforcing agent's
52
+ run (it now prints the two-line ERROR instead of the old "unavailable" line,
53
+ and proceeds). On the enforcing install, aborting IS the correct behaviour: the
54
+ alternative was a run that never arms.
55
+ - Config token precedence: a real string in config always wins, so no agent that
56
+ still keeps a plaintext token changes behaviour. No issue identified.
57
+
58
+ ## 2. Under-block
59
+
60
+ - An enforcing install whose server is UNREACHABLE (not refusing) still writes a
61
+ `preparing` run, as designed — the preparation carrier owns that recovery.
62
+ Deliberately preserved (tested).
63
+ - A vault whose `secret-get.mjs` is missing/failing yields an empty token → the
64
+ server refuses → now loud. Previously the same state was silent.
65
+
66
+ ## 3. Level-of-abstraction fit
67
+
68
+ Token resolution belongs in the scripts themselves: they run standalone (hook
69
+ under Claude Code's Stop event, setup from a session) with no TypeScript layer
70
+ in-process. The function mirrors the hardened secret-get contract rather than
71
+ re-implementing vault decryption.
72
+
73
+ ## 4. Signal vs authority compliance
74
+
75
+ No new brittle authority. The fatal branch consumes the SERVER's authoritative
76
+ admission verdict; the script only stops pretending the verdict was a success.
77
+
78
+ ## 5. Interactions
79
+
80
+ - `resolve_auth_token()` is called via command substitution, so no memoization:
81
+ each call spawns python3 (+node only when config holds no string). The hook
82
+ already spawns python3 at each of those 8 sites, so the added cost is the node
83
+ startup on vault-migrated agents (~100–200 ms per call, only on paths already
84
+ making a network call).
85
+ - The migrations run inside the existing `migrateAutonomousStopHookTopicKeyed`
86
+ chain, AFTER the older bumps: an agent several releases behind is first
87
+ re-deployed by an earlier bump (whose bundled bytes already include this fix),
88
+ and the new bumps early-return on the marker. Order-independent.
89
+ - `sign-lockfile`/release hashing consume bundled bytes generically; no test pins
90
+ the setup/hook SHA outside the migration allowlists (checked: the only exact
91
+ hash is the new predecessor constant + the historical allowlist, both
92
+ append-only).
93
+
94
+ ## 6. External surfaces
95
+
96
+ None. The token still goes only to `localhost:<port>` in an Authorization
97
+ header. The vault value is never echoed (stdout of secret-get feeds the variable
98
+ only; `set -x` is not used in these scripts). Error messages name the HTTP code,
99
+ never the token.
100
+
101
+ ## 7. Multi-machine posture
102
+
103
+ Machine-local BY DESIGN: each machine's scripts read that machine's config and
104
+ vault; runs are per-machine state. The migrations run on every machine's own
105
+ update pass, so the fleet converges machine by machine. No user-facing notices
106
+ are added beyond the setup's own stderr (read by the session that ran it).
107
+
108
+ ## 8. Rollback cost
109
+
110
+ Revert the commit and release: the NEXT migration pass does not restore old
111
+ bytes (marker bumps only move forward), but the scripts are self-contained —
112
+ re-deploying the previous bundle via the same exact-hash path would need one
113
+ follow-on migration. Operationally, the fix is strictly additive (correct token,
114
+ louder errors); the plausible regression is the fatal branch, whose blast radius
115
+ is one refused setup with a printed reason, recoverable by re-running setup.
116
+
117
+ ## Second-pass review
118
+
119
+ **Reviewer:** independent reviewer subagent
120
+ **Verdict:** Concur with the review — no blocking concerns. Two minor notes below (neither warrants a code change).
121
+
122
+ What was checked:
123
+
124
+ - **`resolve_auth_token()` leak/hang/wrong-value:** the value only ever reaches the
125
+ function's stdout (captured by `$(…)` at every call site) and a curl
126
+ `Authorization` header; neither script uses `set -x`; `secret-get.mjs` writes the
127
+ value to stdout only (single write, no trailing newline), diagnostics to stderr
128
+ (redirected to /dev/null here), and reads nothing from stdin — no prompt, no hang
129
+ path (python3 `-c` likewise cannot prompt). The placeholder is a real JSON object
130
+ (`SecretMigrator` line 89: `{ secret: true }`), so `isinstance(v,str)` correctly
131
+ yields empty rather than a printed dict; the vault key path is exactly `authToken`
132
+ (KNOWN_SECRET_FIELDS), matching the script's argument. Trailing newlines are
133
+ stripped by command substitution at every caller. Guard placement is safe in both
134
+ scripts: setup runs `set -euo pipefail` and every failure inside the function is
135
+ absorbed (`|| echo ""`, `|| true`, and a false `[[ -f … ]]` inside `if` returns 0,
136
+ so the function always exits 0); the stop hook runs `set -uo pipefail` (no `-e`,
137
+ confirmed at line 26), where the same guards are harmless.
138
+ - **Registration branch:** empirically verified that a connection-refused curl under
139
+ `-s … -w '\n%{http_code}' … || echo ""` yields `REG_CODE=000` and an empty body —
140
+ never 401/403 — so an unreachable server still lands in the unchanged
141
+ "registration unavailable" path (also covered by the fourth setup test). The fatal
142
+ branch requires `RUN_STATUS == "preparing"`, which at that point can only come from
143
+ the pre-existing LOCAL enforcing resolution (a 401/403 body carries no
144
+ `initialStatus`), so only admission-enforcing installs abort. The whole block is
145
+ inside `if [[ -n "$REPORT_TOPIC" ]]` — empty-topic behaviour untouched.
146
+ - **COUNT pipefail fix:** empirically verified under `set -euo pipefail` that
147
+ `COUNT=$(ls … | grep -cv …) || true` captures grep's `0` stdout on zero matches
148
+ (assignment happens regardless of exit status) and counts correctly (2) when other
149
+ topics' state files exist; the cap comparison then proceeds normally.
150
+ - **Migrations:** both are idempotent (marker-presence early-return; re-run asserted
151
+ byte-identical in the tests). Ordering is sound: the earlier hook/setup bumps
152
+ deploy the CURRENT bundled bytes (which contain `VAULT_AUTH_RESOLVE`), so the new
153
+ migrations early-return after them; conversely a PREPARATION_CARRIER-era stock hook
154
+ no-ops through `upgradePreparationCarrier` (marker present) and is caught by the
155
+ new exact-hash gate. Verified the pinned SHA `72027309…` equals the hook blob at
156
+ HEAD/origin-main AND at every other carrier-era commit touching the file
157
+ (bc5453d32, f7ee6efc6, 4272bda3b — all byte-identical), and older stock bytes are
158
+ in the append-only allowlist, which the new upgrade also accepts — so no
159
+ legitimately-stock lineage is stranded; only genuinely customized/unknown bytes
160
+ are skipped, with a named `result.skipped` row (visible, not silent). Executable
161
+ bit: temp-file written `mode: 0o755` + rename (hook) and `chmodSync 0o755` (setup
162
+ helper); asserted in the tests. Label and skip strings in the code match the test
163
+ expectations exactly. Both new test files run green (9/9).
164
+ - **Artifact accuracy:** the replaced-read counts (3 setup + 8 hook) match the diff;
165
+ the enforcing-only fatality, unreachable-server preservation, no-new-authority,
166
+ and rollback claims all match the code. Bonus not claimed by the artifact: the
167
+ hook's verification leak-scrub now greps for the REAL token on vault-migrated
168
+ agents instead of the placeholder string — a strict improvement.
169
+
170
+ Minor notes (non-blocking):
171
+
172
+ 1. §1's "everywhere else it prints and proceeds exactly as before" is right about
173
+ *proceeding*, but the printed text on a non-enforcing 401/403 changes from the
174
+ "registration unavailable" line to the new two-line ERROR — intended loudness,
175
+ just not literally "as before".
176
+ 2. The new setup comment says the local cap fallback ran "whenever the server was
177
+ unreachable/refusing"; a *refusing* server that returns JSON (`{"error":…}`)
178
+ actually parses `allowed` as `None`, which skips the `unknown` fallback branch —
179
+ pre-existing behaviour, unchanged by this diff; only the comment's wording is
180
+ slightly broad.