instar 1.2.6 → 1.2.8

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-21T17:15:04.109Z",
5
- "instarVersion": "1.2.6",
4
+ "generatedAt": "2026-05-21T18:31:26.539Z",
5
+ "instarVersion": "1.2.8",
6
6
  "entryCount": 191,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -752,7 +752,7 @@
752
752
  "type": "cli-command",
753
753
  "domain": "setup",
754
754
  "sourcePath": "src/cli.ts",
755
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
755
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
756
756
  "since": "2025-01-01"
757
757
  },
758
758
  "cli:setup": {
@@ -760,7 +760,7 @@
760
760
  "type": "cli-command",
761
761
  "domain": "setup",
762
762
  "sourcePath": "src/cli.ts",
763
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
763
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
764
764
  "since": "2025-01-01"
765
765
  },
766
766
  "cli:add": {
@@ -768,7 +768,7 @@
768
768
  "type": "cli-command",
769
769
  "domain": "setup",
770
770
  "sourcePath": "src/cli.ts",
771
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
771
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
772
772
  "since": "2025-01-01"
773
773
  },
774
774
  "cli:backup": {
@@ -776,7 +776,7 @@
776
776
  "type": "cli-command",
777
777
  "domain": "operations",
778
778
  "sourcePath": "src/cli.ts",
779
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
779
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
780
780
  "since": "2025-01-01"
781
781
  },
782
782
  "cli:git": {
@@ -784,7 +784,7 @@
784
784
  "type": "cli-command",
785
785
  "domain": "coordination",
786
786
  "sourcePath": "src/cli.ts",
787
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
787
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
788
788
  "since": "2025-01-01"
789
789
  },
790
790
  "cli:memory": {
@@ -792,7 +792,7 @@
792
792
  "type": "cli-command",
793
793
  "domain": "memory",
794
794
  "sourcePath": "src/cli.ts",
795
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
795
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
796
796
  "since": "2025-01-01"
797
797
  },
798
798
  "cli:knowledge": {
@@ -800,7 +800,7 @@
800
800
  "type": "cli-command",
801
801
  "domain": "memory",
802
802
  "sourcePath": "src/cli.ts",
803
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
803
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
804
804
  "since": "2025-01-01"
805
805
  },
806
806
  "cli:semantic": {
@@ -808,7 +808,7 @@
808
808
  "type": "cli-command",
809
809
  "domain": "memory",
810
810
  "sourcePath": "src/cli.ts",
811
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
811
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
812
812
  "since": "2025-01-01"
813
813
  },
814
814
  "cli:intent": {
@@ -816,7 +816,7 @@
816
816
  "type": "cli-command",
817
817
  "domain": "intent",
818
818
  "sourcePath": "src/cli.ts",
819
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
819
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
820
820
  "since": "2025-01-01"
821
821
  },
822
822
  "cli:feedback": {
@@ -824,7 +824,7 @@
824
824
  "type": "cli-command",
825
825
  "domain": "feedback",
826
826
  "sourcePath": "src/cli.ts",
827
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
827
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
828
828
  "since": "2025-01-01"
829
829
  },
830
830
  "cli:server": {
@@ -832,7 +832,7 @@
832
832
  "type": "cli-command",
833
833
  "domain": "server",
834
834
  "sourcePath": "src/cli.ts",
835
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
835
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
836
836
  "since": "2025-01-01"
837
837
  },
838
838
  "cli:status": {
@@ -840,7 +840,7 @@
840
840
  "type": "cli-command",
841
841
  "domain": "monitoring",
842
842
  "sourcePath": "src/cli.ts",
843
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
843
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
844
844
  "since": "2025-01-01"
845
845
  },
846
846
  "cli:user": {
@@ -848,7 +848,7 @@
848
848
  "type": "cli-command",
849
849
  "domain": "users",
850
850
  "sourcePath": "src/cli.ts",
851
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
851
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
852
852
  "since": "2025-01-01"
853
853
  },
854
854
  "cli:relationship": {
@@ -856,7 +856,7 @@
856
856
  "type": "cli-command",
857
857
  "domain": "relationships",
858
858
  "sourcePath": "src/cli.ts",
859
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
859
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
860
860
  "since": "2025-01-01"
861
861
  },
862
862
  "cli:job": {
@@ -864,7 +864,7 @@
864
864
  "type": "cli-command",
865
865
  "domain": "scheduling",
866
866
  "sourcePath": "src/cli.ts",
867
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
867
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
868
868
  "since": "2025-01-01"
869
869
  },
870
870
  "cli:lifeline": {
@@ -872,7 +872,7 @@
872
872
  "type": "cli-command",
873
873
  "domain": "communication",
874
874
  "sourcePath": "src/cli.ts",
875
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
875
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
876
876
  "since": "2025-01-01"
877
877
  },
878
878
  "cli:list": {
@@ -880,7 +880,7 @@
880
880
  "type": "cli-command",
881
881
  "domain": "monitoring",
882
882
  "sourcePath": "src/cli.ts",
883
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
883
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
884
884
  "since": "2025-01-01"
885
885
  },
886
886
  "cli:autostart": {
@@ -888,7 +888,7 @@
888
888
  "type": "cli-command",
889
889
  "domain": "operations",
890
890
  "sourcePath": "src/cli.ts",
891
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
891
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
892
892
  "since": "2025-01-01"
893
893
  },
894
894
  "cli:migrate": {
@@ -896,7 +896,7 @@
896
896
  "type": "cli-command",
897
897
  "domain": "updates",
898
898
  "sourcePath": "src/cli.ts",
899
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
899
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
900
900
  "since": "2025-01-01"
901
901
  },
902
902
  "cli:machines": {
@@ -904,7 +904,7 @@
904
904
  "type": "cli-command",
905
905
  "domain": "coordination",
906
906
  "sourcePath": "src/cli.ts",
907
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
907
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
908
908
  "since": "2025-01-01"
909
909
  },
910
910
  "cli:whoami": {
@@ -912,7 +912,7 @@
912
912
  "type": "cli-command",
913
913
  "domain": "identity",
914
914
  "sourcePath": "src/cli.ts",
915
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
915
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
916
916
  "since": "2025-01-01"
917
917
  },
918
918
  "cli:pair": {
@@ -920,7 +920,7 @@
920
920
  "type": "cli-command",
921
921
  "domain": "coordination",
922
922
  "sourcePath": "src/cli.ts",
923
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
923
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
924
924
  "since": "2025-01-01"
925
925
  },
926
926
  "cli:join": {
@@ -928,7 +928,7 @@
928
928
  "type": "cli-command",
929
929
  "domain": "coordination",
930
930
  "sourcePath": "src/cli.ts",
931
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
931
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
932
932
  "since": "2025-01-01"
933
933
  },
934
934
  "cli:wakeup": {
@@ -936,7 +936,7 @@
936
936
  "type": "cli-command",
937
937
  "domain": "coordination",
938
938
  "sourcePath": "src/cli.ts",
939
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
939
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
940
940
  "since": "2025-01-01"
941
941
  },
942
942
  "cli:leave": {
@@ -944,7 +944,7 @@
944
944
  "type": "cli-command",
945
945
  "domain": "coordination",
946
946
  "sourcePath": "src/cli.ts",
947
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
947
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
948
948
  "since": "2025-01-01"
949
949
  },
950
950
  "cli:doctor": {
@@ -952,7 +952,7 @@
952
952
  "type": "cli-command",
953
953
  "domain": "monitoring",
954
954
  "sourcePath": "src/cli.ts",
955
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
955
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
956
956
  "since": "2025-01-01"
957
957
  },
958
958
  "cli:review": {
@@ -960,7 +960,7 @@
960
960
  "type": "cli-command",
961
961
  "domain": "monitoring",
962
962
  "sourcePath": "src/cli.ts",
963
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
963
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
964
964
  "since": "2025-01-01"
965
965
  },
966
966
  "cli:nuke": {
@@ -968,7 +968,7 @@
968
968
  "type": "cli-command",
969
969
  "domain": "operations",
970
970
  "sourcePath": "src/cli.ts",
971
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
971
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
972
972
  "since": "2025-01-01"
973
973
  },
974
974
  "cli:playbook": {
@@ -976,7 +976,7 @@
976
976
  "type": "cli-command",
977
977
  "domain": "context",
978
978
  "sourcePath": "src/cli.ts",
979
- "contentHash": "ef620787cb2d40a51831b00ba7071e67aa0d4cac86027dc3a04635d344608d29",
979
+ "contentHash": "eba93ab5240154f808f97b72ffaf6dc2a11b36c426811ee3c08018c9a651a84a",
980
980
  "since": "2025-01-01"
981
981
  },
982
982
  "template:build-stop-hook.sh": {
@@ -0,0 +1,82 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
5
+
6
+ ## What Changed
7
+
8
+ **fix(safe-fs): runtime artifacts under `<root>/.instar/` get a carve-out from the source-tree guard.**
9
+
10
+ Closes the "agent goes dark on cold start" failure mode that surfaced after v1.2.4 in the field (Echo, 2026-05-21): every fresh launchd spawn hit `EADDRINUSE` on the stale unix socket at `.instar/listener.sock`, the `WakeSocketServer` stale-socket-recovery path correctly tried to unlink it, but `SafeFsExecutor` routed the unlink through `assertNotInstarSourceTree` and the guard fired because the agent's deployed directory IS a checkout of the instar source (same `.git`, same `.instar-source-tree` marker, same `package.json` with `name: "instar"`). The supervisor then escalated to its bind-failure recovery path and force-rebuilt better-sqlite3 on every cycle, which in turn produced an incompatible binary and the server never bound.
11
+
12
+ The fix is a narrow carve-out in `SafeFsExecutor.guard()`: when the canonicalized target path is under a `.instar/` subdirectory of the source root, skip the source-tree guard. The carve-out is intentionally interior-only — the `.instar` directory itself is still protected (`safeRmSync` on `<root>/.instar` still throws `SourceTreeGuardError`). All five subdirectories that the project's own `.gitignore` excludes (`.instar/state/`, `.instar/logs/`, `.instar/audit/`, `.instar/instar-dev-traces/`, plus `.instar/shared-state.jsonl`) are runtime state, not source code, and destructive ops on them are a normal part of operation rather than a 2026-04-22-class incident.
13
+
14
+ The guard's three layers (marker file, canonical origin URL, source-identity signature) are unchanged. The 2026-04-22 incident is still caught: that incident targeted source files at the tree root (`README.md`, `src/auth.ts`, `src/middleware.ts`), not anything under `.instar/`. The included tests verify both directions — the carve-out lets the right paths through, and source-tree paths outside `.instar/` (and `.instar` itself) still fault.
15
+
16
+ ## Why now
17
+
18
+ After v1.2.4 every fresh boot of Echo on dawn-macbook entered the loop: lifeline restarts → WakeSocketServer EADDRINUSE → stale-socket-recovery blocked by guard → supervisor declares health-check failed → bind-failure escalation → force-rebuild better-sqlite3 → binary mismatch → loop. Manually unlinking the socket and bootstrapping cleanly worked once, but the next launchd respawn re-entered the loop because nothing fixed the structural block.
19
+
20
+ ## Audit trail
21
+
22
+ The carve-out also leaves a positive audit signal: `safeUnlinkSync` (and siblings) emit an `outcome: "allowed"` entry to `.instar/audit/destructive-ops.jsonl` with `reason: "agent-runtime-state-carveout"` whenever the carve-out path is taken. Lets you see how often the runtime-state path is being exercised vs how often it would have been a false-positive block before.
23
+
24
+ ## Tests
25
+
26
+ 5 new tests in `tests/unit/SafeFsExecutor.test.ts` under `SafeFsExecutor agent-runtime-state carve-out`:
27
+
28
+ - `allows unlink on a socket file under <root>/.instar/`
29
+ - `allows unlink on a lock file under <root>/.instar/`
30
+ - `allows rm on a nested file under <root>/.instar/state/`
31
+ - `still BLOCKS rm on the .instar directory itself (not its contents)`
32
+ - `still BLOCKS unlink on source files at the tree root`
33
+
34
+ All 17 SafeFsExecutor tests pass (12 existing + 5 new).
35
+
36
+ ## Summary of New Capabilities
37
+
38
+ This is a fix-only release. No new user-facing capabilities. The carve-out unblocks `WakeSocketServer.stale-socket-recovery`, `AutoUpdater` shadow-install management, and lifeline lockfile cleanup — all of which were already supposed to work but were silently blocked by the source-tree guard in agent mode.
39
+
40
+ ## What to Tell Your User
41
+
42
+ If you're running an instar agent on macOS and have hit cold-start crash loops where the agent's logs say EADDRINUSE and reference SafeFsExecutor, this release fixes the underlying block. Pull v1.2.x (next), restart your agent. No config changes required, no migration needed.
43
+
44
+ If you've never seen this — you're on a deployment style where the agent dir is NOT a checkout of the instar source, and the guard was never the problem. This release is a no-op for you.
45
+
46
+ If you ran the manual workaround (rm .instar/listener.sock then bootstrap your launchd plist) — the workaround is no longer needed once this release is installed.
47
+
48
+ ## Evidence
49
+
50
+ **Reproduction**: Run instar in agent mode where the agent's deployed directory is also a checkout of the instar source tree (the `~/.instar/agents/echo` pattern on dawn-macbook). Cold-start with a stale `.instar/listener.sock` present. Observed before this fix: `WakeSocketServer` attempts to unlink the stale socket via `SafeFsExecutor.safeUnlinkSync`, which throws `SourceTreeGuardError`. The supervisor's bind-failure escalation triggers (`Bind-failure escalation armed: N consecutive spawns failed before binding`). After 5 retries the supervisor enters 300s cooldown. Server never binds. Tunnel returns 502.
51
+
52
+ **Observed before** (Echo, 2026-05-21T16:46Z, sample from `logs/lifeline-launchd.err`):
53
+
54
+ ```
55
+ Error: stale socket at /Users/justin/.instar/agents/echo/.instar/listener.sock could not be unlinked:
56
+ Refusing to run src/threadline/WakeSocketServer.ts:stale-socket-recovery against the instar source tree
57
+ (requested dir: /Users/justin/.instar/agents/echo/.instar/listener.sock,
58
+ resolved git root: /Users/justin/.instar/agents/echo).
59
+ [Supervisor] Bind-failure escalation armed: 34 consecutive spawns failed before binding.
60
+ [Supervisor] Max restart attempts (5) reached. Cooling down for 300s before retrying.
61
+ ```
62
+
63
+ **Observed after** (same agent, hot-patched 2026-05-21T17:00Z with the same source change applied to `shadow-install/.../SafeFsExecutor.js`):
64
+
65
+ ```
66
+ $ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:4042/health # 5 times, 3s apart
67
+ 200
68
+ 200
69
+ 200
70
+ 200
71
+ 200
72
+
73
+ $ curl -s -o /dev/null -w "%{http_code}\n" https://echo.dawn-tunnel.dev/health
74
+ 200
75
+ ```
76
+
77
+ Tunnel-fronted dashboard at `https://echo.dawn-tunnel.dev/dashboard?session=<...>` returns HTTP 200 with the live dashboard HTML (316KB). The bind-failure escalation never fires. The audit log shows the carve-out's positive signal:
78
+
79
+ ```
80
+ $ grep agent-runtime-state-carveout .instar/audit/destructive-ops.jsonl | head -3
81
+ {"timestamp":"2026-05-21T17:00:23.118Z","executor":"fs","operation":"WakeSocketServer.ts:stale-socket-recovery","verb":"safeUnlinkSync","target":".../.instar/listener.sock","outcome":"allowed","reason":"agent-runtime-state-carveout","caller":"..."}
82
+ ```
@@ -0,0 +1,69 @@
1
+ # Upgrade Guide — v1.2.8 (nuke --here for project-local installs)
2
+
3
+ <!-- bump: patch -->
4
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
5
+
6
+ ## What Changed
7
+
8
+ **New: `instar nuke --here` removes a project-local install.**
9
+
10
+ `instar nuke <name>` has always handled standalone agents (the ones
11
+ at `~/.instar/agents/<name>/`). There was no equivalent for the
12
+ project-bound install method — the result of `npx instar setup`
13
+ inside a project directory. Removing one of those required a manual
14
+ multi-path `rm -rf` of `.instar/`, `.claude/` (or `.codex/`),
15
+ `.mcp.json`, and the identity-shadow files, plus the invisible parts:
16
+ the tmux server, the launchd plist, the agent-registry entry, the
17
+ secret backup.
18
+
19
+ `instar nuke --here` collapses that into one command. Run it inside
20
+ the project directory and it:
21
+
22
+ 1. Stops the `<projectName>-server` tmux session and any spawned
23
+ `<projectName>-*` sessions.
24
+ 2. Removes the launchd / systemd auto-start entry for that project.
25
+ 3. Backs up secrets (Telegram token, dashboard PIN, etc.) so a
26
+ subsequent `npx instar setup` in the same directory auto-restores
27
+ them, identical to the standalone reinstall flow.
28
+ 4. Unregisters the directory from the agent registry.
29
+ 5. Removes filesystem artifacts:
30
+ - Always: `.instar/`, `.claude/`, `.codex/`, `.mcp.json`,
31
+ `instar.config.json`.
32
+ - For `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`: git decides.
33
+ Tracked-clean → kept (pre-existing). Tracked-modified → restored
34
+ to `HEAD`. Untracked → deleted.
35
+
36
+ Safety:
37
+ - Refuses to run inside the instar source repo (checks `package.json`
38
+ name and `src/cli.ts` presence).
39
+ - Refuses to run when `.instar/config.json` is absent.
40
+ - Confirmation prompt by default; bypassed with `--yes`.
41
+
42
+ Spec: `specs/dev-infrastructure/nuke-here.md`.
43
+ ELI16: `specs/dev-infrastructure/nuke-here.eli16.md`.
44
+ Side-effects review: `upgrades/side-effects/feat-nuke-here.md`.
45
+
46
+ ## What to Tell Your User
47
+
48
+ If you want to test installing instar a few different ways in a
49
+ project directory, there is now a built-in uninstall mode that runs
50
+ inside the project. It tears down everything instar installed in one
51
+ shot. Files you had committed before the install are kept (or
52
+ restored from git HEAD), so it is safe to run on a repo that already
53
+ had its own identity-shadow files.
54
+
55
+ ## Summary of New Capabilities
56
+
57
+ - New CLI mode: `instar nuke --here` (project-local install teardown).
58
+ - Standalone form `instar nuke <name>` unchanged.
59
+
60
+ ## Evidence
61
+
62
+ 14 new unit tests in `tests/unit/nuke-here.test.ts` cover the source-
63
+ repo refusal check (four cases), the shadow-file classifier (four
64
+ cases of pre-existing-vs-instar-added-vs-untracked-vs-modified), and
65
+ the end-to-end filesystem teardown against tmpdirs (six cases
66
+ including the missing-config refusal path). All pass locally and in CI.
67
+
68
+ Manual end-to-end verification will run against the `instar-codey`
69
+ test clone after publish.
@@ -0,0 +1,123 @@
1
+ # Side-effects review — `instar nuke --here`
2
+
3
+ Per L6. Seven dimensions.
4
+
5
+ ## 1. Over-block / under-block
6
+
7
+ Before: UNDER — project-bound installs had no uninstall command at all;
8
+ operators had to manually `rm -rf` the install artifacts (and forget the
9
+ tmux server / launchd plist / registry entry / secret backup).
10
+
11
+ After: precisely targeted. The new mode requires explicit `--here`, runs
12
+ only when `.instar/config.json` exists in the target dir, and refuses
13
+ to run inside the instar source repo. No over-block: the standalone
14
+ form is unchanged, and operators who never pass `--here` see no
15
+ behavior change.
16
+
17
+ ## 2. Level-of-abstraction fit
18
+
19
+ `nukeHere(options)` is a sibling function to the existing `nukeAgent`
20
+ in the same file. Both share the same Step 1-4 shape (tmux teardown,
21
+ auto-start removal, secret backup, registry unregister) and the same
22
+ SafeFsExecutor / SafeGitExecutor helpers for the filesystem half. The
23
+ new git-tracked-vs-untracked decision for identity-shadow files
24
+ delegates to `git ls-files --error-unmatch` + `git status --porcelain`
25
+ — the existing single source of truth for tracking state. No new
26
+ abstraction layer.
27
+
28
+ ## 3. Signal vs Authority compliance
29
+
30
+ - The `--here` flag is the operator-intent SIGNAL.
31
+ - `.instar/config.json` presence is the AUTHORITY for "is there an
32
+ install here to remove."
33
+ - `package.json` `name === "instar"` + `src/cli.ts` presence is the
34
+ AUTHORITY for "is this the instar source repo (refuse)."
35
+ - For identity-shadow files (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`):
36
+ - `git ls-files --error-unmatch <file>` is the AUTHORITY for
37
+ "tracked at HEAD."
38
+ - `git status --porcelain <file>` is the AUTHORITY for "has working-
39
+ tree diff."
40
+ No new heuristic is invented; existing git tooling is the single
41
+ source of truth.
42
+
43
+ ## 4. Interactions with adjacent systems
44
+
45
+ - **Standalone `nuke <name>`** — unchanged. Same function, same flags,
46
+ same prompts. The CLI route was changed from `nuke <name>` (required)
47
+ to `nuke [name]` (optional) plus a `--here` flag — when `--here` is
48
+ set, the positional is ignored; when `--here` is not set and no name
49
+ is given, usage is printed and the command exits 1.
50
+
51
+ - **`uninstallAutoStart(projectName)`** in `src/commands/setup.ts` —
52
+ reused as-is. The function is already idempotent and project-name-
53
+ scoped; the new mode hands it the same `projectName` the install
54
+ wrote into `.instar/config.json`.
55
+
56
+ - **`SecretManager.backupFromConfig`** — reused exactly as
57
+ `nukeAgent` does, with the same field mapping (Telegram token, chat
58
+ id, authToken, dashboardPin, tunnel token). This means a subsequent
59
+ `npx instar setup` in the same directory will auto-restore secrets,
60
+ matching the standalone reinstall flow.
61
+
62
+ - **`unregisterAgent(dir)`** — reused. Operates on the absolute
63
+ directory path, identical contract.
64
+
65
+ - **`SafeFsExecutor.safeRmSync`** — used for every delete. The recent
66
+ source-tree-guard carve-out (PR #294) for `<root>/.instar/` runtime
67
+ artifacts means `.instar/` deletions inside an agent's project dir
68
+ do not trip the guard.
69
+
70
+ - **`SafeGitExecutor`** — used for `ls-files`, `status`, and
71
+ `checkout HEAD --`. All three are already allow-listed git ops; the
72
+ audit trail records the new operations under
73
+ `src/commands/nuke.ts:nukeHere-*`.
74
+
75
+ - **Tmux session naming** (`<projectName>-server`, `<projectName>-*`) —
76
+ identical to `nukeAgent`. The new function uses the same
77
+ `isTmuxSessionRunning` helper that already exists in the file.
78
+
79
+ ## 5. Rollback cost
80
+
81
+ Trivial. One new exported function in `src/commands/nuke.ts`, one
82
+ modified CLI registration in `src/cli.ts`, one unit test, one ELI16
83
+ + spec + this artifact. `git revert` restores the prior CLI surface;
84
+ no other surface depends on `--here`. Standalone nuke users see no
85
+ change either way.
86
+
87
+ ## 6. Backwards compatibility / drift surface
88
+
89
+ Fully backwards-compatible.
90
+
91
+ - `instar nuke <name>` still works (positional became optional, but
92
+ passing a name still routes to `nukeAgent`).
93
+ - `instar nuke` with no arguments now prints usage and exits 1 —
94
+ before, commander would error out with a "missing required argument"
95
+ message. Same exit-1 outcome, slightly clearer error.
96
+ - `instar nuke --here` is a new code path; no prior agent has it, so
97
+ there's nothing to drift against.
98
+ - No config file changes, no template changes, no hook changes, no
99
+ built-in skill changes — therefore no `PostUpdateMigrator` work
100
+ needed (Migration Parity Standard not triggered: this is a CLI-only
101
+ surface change, not an agent-installed-files change).
102
+
103
+ ## 7. Authorization / Trust posture
104
+
105
+ No new authority. The command only acts on:
106
+
107
+ - The current working directory (or `--dir` override) — same scope as
108
+ every other instar CLI command that takes a `--dir` flag.
109
+ - The launchd plist / agent registry entry / secret store keyed to
110
+ the **projectName the install itself wrote**. The command cannot
111
+ delete data belonging to a different project name; it reads
112
+ `projectName` from `.instar/config.json` in the target dir.
113
+
114
+ The source-repo refusal check is an additional safety floor that
115
+ exists only to protect the instar development checkout — a one-way
116
+ guardrail with no positive authority.
117
+
118
+ ## Outcome
119
+
120
+ Ship. Closes the project-local uninstall gap, matches the standalone
121
+ flow's safety posture, no new authority, no migration work, and
122
+ removes the manual-rm-rf trap operators hit today during install
123
+ testing.
@@ -0,0 +1,98 @@
1
+ # Side-effects review — SafeFsExecutor agent-runtime-state carve-out
2
+
3
+ **Scope**: `SafeFsExecutor.guard()` now bypasses `assertNotInstarSourceTree`
4
+ when the canonicalized target path is under a `.instar/` subdirectory of the
5
+ source root. New helper `isUnderAgentRuntimeState()` performs the check.
6
+
7
+ **Root cause of original issue**: When instar is deployed in "agent" mode (e.g.
8
+ `~/.instar/agents/<name>/`), the agent dir IS a checkout of the instar source
9
+ — same `.git`, same `.instar-source-tree` marker file, same `package.json`
10
+ with `name: "instar"`. The source-tree guard's three layers (marker, canonical
11
+ remote URL, source-identity signature) all correctly identify the agent root
12
+ as the instar source. But the agent's runtime artifacts live at
13
+ `<root>/.instar/` (sockets, locks, logs, audit trail, shared-state JSONL),
14
+ which are explicitly `.gitignored` and are NOT source code. Destructive ops
15
+ on those paths (e.g., `WakeSocketServer.stale-socket-recovery` unlinking a
16
+ stale `listener.sock`) are a normal part of operation, not a 2026-04-22-class
17
+ incident.
18
+
19
+ Before this fix, every unlink-the-stale-socket attempt in agent mode failed
20
+ with `SourceTreeGuardError`, the supervisor declared the server unhealthy,
21
+ escalated to its bind-failure recovery path, force-rebuilt better-sqlite3 on
22
+ every restart cycle, and the agent never bound to its socket. Observed in
23
+ the field 2026-05-21 on dawn-macbook Echo, immediately after v1.2.4.
24
+
25
+ **Files touched**:
26
+ - `src/core/SafeFsExecutor.ts` — added `isUnderAgentRuntimeState()` helper
27
+ (interior-`/.instar/`-segment predicate, returns false when the marker is
28
+ trailing) and a carve-out branch in `guard()` that audits as `allowed` with
29
+ `reason: "agent-runtime-state-carveout"` when the predicate matches.
30
+ - `tests/unit/SafeFsExecutor.test.ts` — added `SafeFsExecutor agent-runtime-state
31
+ carve-out` describe block with five tests (3 positive carve-outs, 2 still-blocked).
32
+ - `upgrades/NEXT.md` — release notes.
33
+
34
+ **Under-block (what's no longer caught)**: Operations on individual files and
35
+ nested directories under `<source-root>/.instar/<anything>`. Specifically:
36
+
37
+ 1. **`.instar/listener.sock` (and other `.sock` files there)**. `.sock` files
38
+ are agent runtime IPC endpoints; the project's own `.gitignore` covers the
39
+ parent directories these would live under. The 2026-04-22 incident did not
40
+ touch this path; risk is low.
41
+
42
+ 2. **`.instar/lifeline.lock` (and other lockfiles there)**. Lockfile cleanup
43
+ is mandatory for any LaunchAgent-style supervisor. Under-block is required
44
+ for the supervisor to recover from stale locks.
45
+
46
+ 3. **`.instar/state/*`, `.instar/logs/*`, `.instar/audit/*`,
47
+ `.instar/instar-dev-traces/*`**. All four directories appear in the
48
+ project's `.gitignore`. None contain source code. Destructive ops on stale
49
+ entries here are normal house-cleaning. Under-block is intentional.
50
+
51
+ 4. **`.instar/shadow-install/`**. The agent's pinned vendored copy of the
52
+ instar package. Modified by `instar update` and `AutoUpdater`. Treated as
53
+ runtime state, not source. The auto-updater needs to wipe-and-reinstall
54
+ this directory; the carve-out unblocks that path under agent mode.
55
+
56
+ The carve-out is intentionally interior-only — the `.instar` directory itself
57
+ is still protected by the guard. A `safeRmSync` targeting `<source-root>/.instar`
58
+ still throws `SourceTreeGuardError`. This is verified by the
59
+ `still BLOCKS rm on the .instar directory itself (not its contents)` test.
60
+
61
+ **What stays caught**:
62
+
63
+ 1. **All source files at the tree root** — anything in `src/`, `tests/`,
64
+ `docs/`, `scripts/`, root-level files like `package.json`, `README.md`,
65
+ `tsconfig.json`. The 2026-04-22 incident damage (`README.md`, `src/auth.ts`,
66
+ `src/middleware.ts`) is still blocked. Verified by the `still BLOCKS unlink
67
+ on source files at the tree root` test.
68
+
69
+ 2. **The `.instar` directory itself** — `safeRmSync` on `<root>/.instar` with
70
+ `recursive: true` is still rejected. The carve-out predicate requires
71
+ `/.instar/` followed by at least one additional path segment; `<root>/.instar`
72
+ alone does not satisfy that.
73
+
74
+ 3. **All three detection layers** are unchanged. The marker file, canonical
75
+ remote URL, and source-identity signature checks all still run for paths
76
+ outside `.instar/`.
77
+
78
+ **Audit trail**: The carve-out leaves a positive `outcome: "allowed"` entry
79
+ with `reason: "agent-runtime-state-carveout"` in `.instar/audit/destructive-ops.jsonl`
80
+ on every match. This makes the false-positive rate (operations that would have
81
+ been wrongly blocked before) directly observable: `grep
82
+ agent-runtime-state-carveout .instar/audit/destructive-ops.jsonl | wc -l`.
83
+ Operators can confirm the carve-out is being exercised exactly when expected
84
+ (WakeSocketServer recovery, AutoUpdater install, etc.) and not in unexpected
85
+ places.
86
+
87
+ **Tests**: 5 new tests under `describe('SafeFsExecutor agent-runtime-state
88
+ carve-out', …)` in `tests/unit/SafeFsExecutor.test.ts`. All 17 SafeFsExecutor
89
+ tests pass (12 existing + 5 new). No existing tests modified.
90
+
91
+ **Spec impact**: `docs/specs/DESTRUCTIVE-TOOL-TARGET-GUARDS-SPEC.md` is not
92
+ modified by this change. The spec's tactical-guardrail design is preserved
93
+ exactly. The carve-out lives one layer below the guard (in `SafeFsExecutor`)
94
+ and is documented in the new helper's doc-comment with the reasoning above.
95
+
96
+ **Rollback**: Revert this commit. The carve-out is a single function and a
97
+ single branch in `guard()`. Rollback restores the brittle-block behavior
98
+ exactly; no migration needed.