instar 1.3.196 → 1.3.198

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,74 @@
1
+ # Side-Effects Review — Skip WHATWG fetch-blocked ports in agent port allocation
2
+
3
+ **Version / slug:** `fix-fetch-blocked-ports`
4
+ **Date:** `2026-06-02`
5
+ **Author:** `echo`
6
+ **Tier:** 1 (small, low-risk; ELI16 + side-effects, no converged spec)
7
+ **Parent principle:** Cross-Machine Coherence — One Agent, Robust Under Degraded Conditions
8
+
9
+ ## Summary of the change
10
+
11
+ Instar's multi-machine mesh does ALL cross-machine I/O over node's global `fetch`
12
+ (pairing `POST /api/pair`, lease broadcast/pull, heartbeat). Node's `fetch` (undici)
13
+ throws `TypeError: fetch failed { cause: 'bad port' }` for any URL whose port is on the
14
+ WHATWG Fetch spec "bad ports" list. The default agent port-allocation range (4040–4099)
15
+ **contains 4045** (NFS `lockd`, a blocked port), so the allocator could hand an agent a
16
+ port on which it can **never** be reached over the mesh — silently, because `curl`
17
+ ignores the blocklist so the endpoint looks healthy.
18
+
19
+ **Observed live (2026-06-02):** a freshly-paired throwaway test agent landed on 4045 and
20
+ could not be joined — `instar join` failed with "fetch failed" despite `curl` to the same
21
+ `/api/pair` returning 400. Moving it off 4045 fixed the join instantly.
22
+
23
+ ### What changed
24
+ - `src/core/AgentRegistry.ts`: added `FETCH_BLOCKED_PORTS` (the WHATWG bad-ports set) +
25
+ exported `isFetchBlockedPort(port)`. Both `allocatePort` and `allocatePortByName` now
26
+ skip blocked ports (in addition to used + lsof-busy ones).
27
+ - `src/commands/server.ts`: an unconditional startup warning when `config.port` is a
28
+ blocked port — the migration-parity path for EXISTING agents already on one (the
29
+ allocator guard only protects newly-allocated agents).
30
+
31
+ ## Decision-point inventory
32
+ - allocator: skip vs allocate a port → skip if on the blocklist (the check precedes the
33
+ lsof probe, so it's deterministic).
34
+ - startup: warn vs silent → warn (loud, non-fatal) when an existing config sits on a
35
+ blocked port; we do NOT auto-re-port (that would change a running agent's port — left
36
+ to the operator).
37
+
38
+ ## 1. Over-correction risk
39
+ The blocklist is the fixed WHATWG set; legitimate agent ports (4040–4044, 4046–4099, etc.)
40
+ are unaffected. Only 4045 falls in the default range. Skipping reduces the usable range by
41
+ one port — negligible (60→59).
42
+
43
+ ## 2. Under-correction risk
44
+ Existing agents already on a blocked port aren't auto-fixed (that would change their port);
45
+ the startup warning surfaces it so the operator re-ports. Acceptable — auto-re-porting a
46
+ live agent is riskier than a loud warning.
47
+
48
+ ## 3. Level-of-abstraction fit
49
+ The skip lives in the single port allocator both callers use; the warning at the single
50
+ server-start path. No scattering.
51
+
52
+ ## 4. Signal vs Authority
53
+ Pure deterministic check against a fixed spec list — no LLM, no heuristic. Appropriate.
54
+
55
+ ## 5. External surfaces
56
+ None. No route/config-schema change. `isFetchBlockedPort` is a new pure export.
57
+
58
+ ## 6. Interactions with existing primitives
59
+ The allocator's existing used-port + lsof checks are unchanged; the blocklist is an
60
+ additional AND condition. The warning composes with the existing startup logs.
61
+
62
+ ## 7. Rollback cost
63
+ Trivial: remove the `&& !FETCH_BLOCKED_PORTS.has(port)` clauses + the warning. No state.
64
+
65
+ ## Migration parity
66
+ - New agents: allocator skips blocked ports (no action needed).
67
+ - Existing agents on a blocked port: the startup warning surfaces it (operator re-ports).
68
+ - Pure code; reaches existing agents on the normal dist update. No config/hook migration.
69
+
70
+ ## Tests
71
+ `tsc --noEmit` clean. 34 `agent-registry` unit tests green, including 3 new: `isFetchBlockedPort`
72
+ both sides of the boundary (4045/6000/6697/2049 blocked; 4040/4046/4050/4099 not),
73
+ `allocatePort` throws rather than return a sole blocked candidate, and `allocatePort` skips
74
+ a blocked port to return the next usable one.