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.
- package/dist/cli.js +20 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/nuke.d.ts +28 -0
- package/dist/commands/nuke.d.ts.map +1 -1
- package/dist/commands/nuke.js +276 -0
- package/dist/commands/nuke.js.map +1 -1
- package/dist/core/SafeFsExecutor.d.ts.map +1 -1
- package/dist/core/SafeFsExecutor.js +35 -0
- package/dist/core/SafeFsExecutor.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +31 -31
- package/upgrades/1.2.7.md +82 -0
- package/upgrades/1.2.8.md +69 -0
- package/upgrades/side-effects/feat-nuke-here.md +123 -0
- package/upgrades/side-effects/safe-fs-agent-state-carveout.md +98 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-05-
|
|
5
|
-
"instarVersion": "1.2.
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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.
|