You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fleet: Local fallback — deliberate offline hatch + rejoin (CONTEXT.md)
Important
Problem — Fleet client (MacBook) currently has no deliberate escape hatch when the canonical (Aarons-Mac-mini, co.harmoniqs.amicode-server, tunnel 127.0.0.1:4096) is unreachable (tailscale Online:false, ssh amico-mini timeout — as seen 2026-08-11 01:25, mini LastSeen 23:20, erlich still Online:true). Guard ~/.local/bin/amico-opencode-fleet-guardexit 1 is correct for never-fork (ADR 0005 #279) but with no fallback the panel is just stranded dead; Local fallback per CONTEXT.md (“explicit, user-invoked, visible state; sessions merge on Rejoin”) has no product surface and normal ssh (Match host amico-mini LAN-first) is conflated with fleet down. Approach — Ship the ADR 0005 hatch as user-visible product: a machine-scoped marker ~/.amico/ops/fleet/fallback.json ({active,since,previousBinary,previousPort}) checked by the guard before its exit 1 (fallback → fall through to frozen/VSIX/dev), plus three commands (Enter Local Fallback, Exit Local Fallback, Rejoin), a warning status bar (Fleet: LOCAL FALLBACK → click to Rejoin), and healthcheck awareness (guard/settings/tunnel skipped while fallback active, Fleet fallback: ACTIVE shown). Rejoin probes 127.0.0.1:4096/4096 and archives the local shard (~/.local/share/opencode/opencode.db → ~/.amico/fleet-recovery/<date>/) before restoring amicode.opencodeBinary+opencodePort (guard+4096) per the “preserve, never delete on unmappable drift” rule. Tunnel stays 15/2+TCPKeepAlive. Scope — in: fleet_fallback.ts + fleet_fallback.test.ts (4), guard fallback check (FALLBACK="$HOME/.amico/ops/fleet/fallback.json"), fleet_health.ts fallback row + aggregate skip (now 4 checks), extension.ts fallback status bar + amicode.fleet.fallback.enter/exit + amicode.fleet.rejoin (archive shard), package.json 3 commands, assert_fleet_guard.sh fallback assertion, tools/fleet/README.md Local fallback docs, VSIX !tools/** already ships it. out: full Rejoin server-side merge route + identity/Fleet-token (#284, #290) — this is a local-only shard with manual archive, not the fixture-locked merge. Assumptions — darwin fleet only; marker is machine-scoped file, never synced; tailscale/erlich proves normal ssh is healthy when mini is up; fallback port is ephemeral (opencodePort=0) while tunnel is 4096.
Acceptance Criteria
Guard tools/fleet/amico-opencode-fleet-guard checks ~/.amico/ops/fleet/fallback.json before scutil --get LocalHostName; with marker present amico-mini's exit 1 is bypassed and frozen/VSIX/dev is execd even on MacBook-Pro-de-Aaron (verified touch fallback.json && guard --help shows opencode not refusing).
Amicode: Fleet — Enter Local Fallback (palette, modal confirm) creates fallback.json with since + previousBinary/previousPort, clears amicode.opencodeBinary="" + opencodePort=0 (Global), restarts to vendored local server (ephemeral), status bar $(warning) Fleet: LOCAL FALLBACK (warning bg, tooltip fallbackStatusLabel, click → Rejoin) and amicode.healthcheck shows Fleet fallback: ACTIVE while Fleet guard/settings/tunnel are skipped — fallback active.
Amicode: Fleet — Exit Local Fallback (modal) deletes marker, restores previousBinary→~/.local/bin/amico-opencode-fleet-guard + previousPort→4096, restarts to ride tunnel (health probe fails closed if mini still offline, with Rejoin hint).
Amicode: Fleet — Rejoin when fallback active probes http://127.0.0.1:4096/ (and amicode.opencodePort); if tunnel still down offers Stay/Exit anyway/Check again; if up archives ~/.local/share/opencode/opencode.db → ~/.amico/fleet-recovery/<YYYY-MM-DD>/opencode-fallback-<ts>.db, then exits fallback and runs healthcheck.
tailscale statusaarons-mac-mini Online:false vs erlich Online:true proves normal ssh (ssh erlich ok) is unrelated to fleet ssh amico-mini timeout; Match host amico-mini LAN-first (ping Aarons-Mac-mini.local → .local vs 100.125.66.86) remains.
bash packages/extension/scripts/assert_fleet_guard.sh checks fallback marker/hint; pnpm test includes fleet_fallback.test.ts (4) + fleet_health fallback aggregate (1), typecheck clean, vsce package includes extension/tools/fleet/amico-opencode-fleet-guard2147B with fallback.
Testing Decisions
Pure fleet_fallback.ts (enter/exit/isActive/read + atomic tmp→rename, mkdir -p) unit-tested with temp dir, no scutil/launchctl. Guard tested via touch fallback.json + guard --help (exec vs exit 1). Health aggregate tested with injected read returning fallback JSON → 4 checks with skipped rows. Manual: enter fallback on MacBook while tailscale ping timeout, verify lsof -iTCP:4096 no ssh LISTEN but local opencode LISTEN on ephemeral, status bar warning, health ACTIVE; rejoin after tailscale recovers.
Key Decisions
Marker is a file, not a setting — amicode.* settings are machine but still JSON in settings.json; a file is the same machine-scoped contract as the Fleet token 0600 (ADR 0001) and is trivial for the bash guard to check without jq.
Guard check is first: if [ -f "$FALLBACK" ]; then :; elif [ "$(scutil …)" != ... ]; then exit 1; fi — fallback is explicit, not a silent bypass of never-fork.
Port in fallback is ephemeral (0) — avoids 4096 collision when the tunnel recovers; canonical 4096 is restored on exit.
Rejoin is archive, not merge for now — copies opencode.db to fleet-recovery per “preserve, never delete” (ADR 0005 unmappable drift); full server-side fixture-locked merge (Local fallback + Rejoin merge (fixture-locked) #290) lands with identity routes.
Constraints & Invariants
Exactly one canonical per fleet; client never spawns outside fallback; marker + settings never ride Settings Sync; loopback-only binding persists; shard preserved until verified merge.
Part of Managed Fleet hatch — explicit offline work.
Notes
Normal ssh (erlich.tail1c461a.ts.net → 100.104.59.70 ok) proves ~/.ssh/config tweak is not the fleet down cause; fleet down is aarons-mac-miniOnline:false (Tailscale).
Fleet: Local fallback — deliberate offline hatch + rejoin (CONTEXT.md)
Important
Problem — Fleet client (MacBook) currently has no deliberate escape hatch when the canonical (
Aarons-Mac-mini,co.harmoniqs.amicode-server, tunnel127.0.0.1:4096) is unreachable (tailscale Online:false,ssh amico-minitimeout — as seen 2026-08-11 01:25, miniLastSeen 23:20,erlichstillOnline:true). Guard~/.local/bin/amico-opencode-fleet-guardexit 1is correct for never-fork (ADR 0005 #279) but with no fallback the panel is just stranded dead;Local fallbackperCONTEXT.md(“explicit, user-invoked, visible state; sessions merge on Rejoin”) has no product surface and normalssh(Match host amico-miniLAN-first) is conflated with fleet down.Approach — Ship the ADR 0005 hatch as user-visible product: a machine-scoped marker
~/.amico/ops/fleet/fallback.json({active,since,previousBinary,previousPort}) checked by the guard before itsexit 1(fallback → fall through to frozen/VSIX/dev), plus three commands (Enter Local Fallback,Exit Local Fallback,Rejoin), a warning status bar (Fleet: LOCAL FALLBACK→ click to Rejoin), and healthcheck awareness (guard/settings/tunnel skipped while fallback active,Fleet fallback: ACTIVEshown).Rejoinprobes127.0.0.1:4096/4096and archives the local shard (~/.local/share/opencode/opencode.db → ~/.amico/fleet-recovery/<date>/) before restoringamicode.opencodeBinary+opencodePort(guard+4096) per the “preserve, never delete on unmappable drift” rule. Tunnel stays15/2+TCPKeepAlive.Scope — in:
fleet_fallback.ts+fleet_fallback.test.ts(4), guard fallback check (FALLBACK="$HOME/.amico/ops/fleet/fallback.json"),fleet_health.tsfallback row + aggregate skip (now 4 checks),extension.tsfallback status bar +amicode.fleet.fallback.enter/exit+amicode.fleet.rejoin(archive shard),package.json3 commands,assert_fleet_guard.shfallback assertion,tools/fleet/README.mdLocal fallback docs, VSIX!tools/**already ships it. out: full Rejoin server-side merge route + identity/Fleet-token (#284, #290) — this is a local-only shard with manual archive, not the fixture-locked merge.Assumptions —
darwinfleet only; marker ismachine-scoped file, never synced;tailscale/erlichproves normalsshis healthy when mini is up; fallback port is ephemeral (opencodePort=0) while tunnel is4096.Acceptance Criteria
tools/fleet/amico-opencode-fleet-guardchecks~/.amico/ops/fleet/fallback.jsonbeforescutil --get LocalHostName; with marker presentamico-mini'sexit 1is bypassed andfrozen/VSIX/devisexecd even onMacBook-Pro-de-Aaron(verifiedtouch fallback.json && guard --helpshowsopencodenotrefusing).Amicode: Fleet — Enter Local Fallback(palette, modal confirm) createsfallback.jsonwithsince+previousBinary/previousPort, clearsamicode.opencodeBinary=""+opencodePort=0(Global), restarts to vendored local server (ephemeral), status bar$(warning) Fleet: LOCAL FALLBACK(warning bg, tooltipfallbackStatusLabel, click → Rejoin) andamicode.healthcheckshowsFleet fallback: ACTIVEwhileFleet guard/settings/tunnelareskipped — fallback active.Amicode: Fleet — Exit Local Fallback(modal) deletes marker, restorespreviousBinary→~/.local/bin/amico-opencode-fleet-guard+previousPort→4096, restarts to ride tunnel (health probe fails closed if mini still offline, withRejoinhint).Amicode: Fleet — Rejoinwhenfallbackactive probeshttp://127.0.0.1:4096/(andamicode.opencodePort); if tunnel still down offersStay/Exit anyway/Check again; if up archives~/.local/share/opencode/opencode.db→~/.amico/fleet-recovery/<YYYY-MM-DD>/opencode-fallback-<ts>.db, then exits fallback and runshealthcheck.tailscale statusaarons-mac-mini Online:falsevserlich Online:trueproves normalssh(ssh erlichok) is unrelated to fleetssh amico-minitimeout;Match host amico-miniLAN-first (ping Aarons-Mac-mini.local→.localvs100.125.66.86) remains.bash packages/extension/scripts/assert_fleet_guard.shchecks fallback marker/hint;pnpm testincludesfleet_fallback.test.ts(4) +fleet_healthfallback aggregate (1),typecheckclean,vsce packageincludesextension/tools/fleet/amico-opencode-fleet-guard2147Bwith fallback.Testing Decisions
fleet_fallback.ts(enter/exit/isActive/read+ atomictmp→rename,mkdir -p) unit-tested with temp dir, noscutil/launchctl. Guard tested viatouch fallback.json+guard --help(exec vsexit 1). Health aggregate tested with injectedreadreturning fallback JSON → 4 checks with skipped rows. Manual: enter fallback on MacBook whiletailscale pingtimeout, verifylsof -iTCP:4096no ssh LISTEN but localopencodeLISTEN on ephemeral, status bar warning, healthACTIVE; rejoin aftertailscalerecovers.Key Decisions
amicode.*settings aremachinebut still JSON insettings.json; a file is the same machine-scoped contract as the Fleet token0600(ADR 0001) and is trivial for thebashguard to check withoutjq.if [ -f "$FALLBACK" ]; then :; elif [ "$(scutil …)" != ... ]; then exit 1; fi— fallback is explicit, not a silent bypass of never-fork.0) — avoids4096collision when the tunnel recovers; canonical4096is restored on exit.opencode.dbtofleet-recoveryper “preserve, never delete” (ADR 0005 unmappable drift); full server-side fixture-locked merge (Local fallback + Rejoin merge (fixture-locked) #290) lands with identity routes.Constraints & Invariants
Prior Art
Local fallback/Rejoin, ADR 0005Conditions of merge, Fleet hardening: lid-close/internet-loss disconnect and fork guard #324 guardexit 1, Fleet: preventive health + self-heal (guard/tunnel/settings drift) #328 preventivefleet_health+install.sh, normalsshMatch host amico-miniLAN-first.Source
Part of Managed Fleet hatch — explicit offline work.
Notes
ssh(erlich.tail1c461a.ts.net→100.104.59.70ok) proves~/.ssh/configtweak is not the fleet down cause; fleet down isaarons-mac-miniOnline:false(Tailscale).