instar 1.2.5 → 1.2.7

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,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.