instar 1.0.13 → 1.1.1
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/README.md +1 -1
- package/dist/cli.js +52 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +156 -64
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +68 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts +3 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +67 -28
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/worktree.d.ts +17 -4
- package/dist/commands/worktree.d.ts.map +1 -1
- package/dist/commands/worktree.js +28 -4
- package/dist/commands/worktree.js.map +1 -1
- package/dist/core/AgentWorktreeDetector.d.ts +63 -0
- package/dist/core/AgentWorktreeDetector.d.ts.map +1 -0
- package/dist/core/AgentWorktreeDetector.js +329 -0
- package/dist/core/AgentWorktreeDetector.js.map +1 -0
- package/dist/core/InstarWorktreeManager.d.ts +99 -0
- package/dist/core/InstarWorktreeManager.d.ts.map +1 -0
- package/dist/core/InstarWorktreeManager.js +447 -0
- package/dist/core/InstarWorktreeManager.js.map +1 -0
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +2 -0
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +25 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +258 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/memory/NativeModuleHealer.d.ts +15 -0
- package/dist/memory/NativeModuleHealer.d.ts.map +1 -1
- package/dist/memory/NativeModuleHealer.js +22 -0
- package/dist/memory/NativeModuleHealer.js.map +1 -1
- package/dist/monitoring/NativeHealDegradationBridge.d.ts +32 -0
- package/dist/monitoring/NativeHealDegradationBridge.d.ts.map +1 -0
- package/dist/monitoring/NativeHealDegradationBridge.js +61 -0
- package/dist/monitoring/NativeHealDegradationBridge.js.map +1 -0
- package/dist/monitoring/PromptGate.d.ts +12 -0
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +44 -0
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +17 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +6 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lint-no-direct-destructive.js +7 -0
- package/scripts/resolve-release-tier.mjs +192 -0
- package/src/data/builtin-manifest.json +57 -49
- package/src/scaffold/templates.ts +17 -0
- package/src/templates/scripts/instar-watchdog.sh +169 -6
- package/src/templates/scripts/instar-worktree-create.sh +79 -0
- package/upgrades/1.1.0.md +320 -0
- package/upgrades/1.1.1.md +58 -0
- package/upgrades/side-effects/agent-worktree-convention-layer-1-2-5.md +280 -0
- package/upgrades/side-effects/agent-worktree-convention-layer-3-4.md +285 -0
- package/upgrades/side-effects/feat-fix-parent-framework-intercept.md +59 -0
- package/upgrades/side-effects/feat-install-claude-gating.md +67 -0
- package/upgrades/side-effects/feat-install-framework-flag.md +65 -0
- package/upgrades/side-effects/feat-portability-shadow-capabilities.md +63 -0
- package/upgrades/side-effects/feat-setup-framework-flag.md +65 -0
- package/upgrades/side-effects/lockdown-layer-2.md +123 -0
- package/upgrades/side-effects/native-heal-degradation-bridge.md +206 -0
- package/upgrades/side-effects/watchdog-bind-failure-probe.md +174 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**fix(monitoring): native-module heal failures now surface on the DegradationReporter alert path.**
|
|
8
|
+
`NativeModuleHealer` already rebuilds better-sqlite3 when a NODE_MODULE_VERSION
|
|
9
|
+
mismatch breaks `SemanticMemory` / `TopicMemory` / `MemoryIndex` / `TokenLedger`,
|
|
10
|
+
but a *failed* rebuild only landed in `<stateDir>/native-module-heals.jsonl`
|
|
11
|
+
and a `console.error` line — the agent then silently ran with the affected
|
|
12
|
+
feature unavailable and no Telegram alert ever fired. The healer's own jsdoc
|
|
13
|
+
already named `DegradationReporter` as a consumer ("consumed by health checks
|
|
14
|
+
and DegradationReporter") but no consumer was wired. This PR adds the missing
|
|
15
|
+
bridge: a tiny `onHealEvent(listener)` registration surface on the healer
|
|
16
|
+
and a `NativeHealDegradationBridge` subscriber wired in `AgentServer` boot
|
|
17
|
+
that translates failure events into `DegradationReporter.report(...)` calls
|
|
18
|
+
with component-specific impact lines (per-component dedupe; success events
|
|
19
|
+
intentionally silent). No change to the heal path itself, no change to the
|
|
20
|
+
jsonl log, no new dependencies.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
Prior to this PR, `src/memory/NativeModuleHealer.ts` writes a `HealEvent`
|
|
25
|
+
row to `<stateDir>/native-module-heals.jsonl` and `console.error`s on
|
|
26
|
+
failure, but `grep -rn "DegradationReporter\|degradation"` against
|
|
27
|
+
`src/memory/NativeModuleHealer.ts` and
|
|
28
|
+
`src/remediation/runbooks/node-abi-mismatch.ts` returns zero call sites
|
|
29
|
+
— the heal-failure event never reached the existing Telegram alert path.
|
|
30
|
+
After this PR, nine new unit tests in
|
|
31
|
+
`tests/unit/NativeHealDegradationBridge.test.ts` pin the wiring (reports
|
|
32
|
+
on failure, silent on success, dedupes per component, returns an
|
|
33
|
+
unsubscribe handle that detaches the listener, swallows listener errors,
|
|
34
|
+
and exercises `NativeModuleHealer.onHealEvent` through the real
|
|
35
|
+
`healBetterSqlite3Sync` early-exit path with a mocked install-prefix
|
|
36
|
+
resolver) and `AgentServer` calls `bridgeNativeHealToDegradation()` next
|
|
37
|
+
to the existing `NativeModuleHealer.configure(...)` call so every
|
|
38
|
+
server boot gets the subscriber. Pre-existing `NativeModuleHealer`
|
|
39
|
+
(17 tests) and `degradation-reporter*` (22 tests) suites pass
|
|
40
|
+
unchanged.
|
|
41
|
+
|
|
42
|
+
Spec: `docs/specs/SELF-HEALING-REMEDIATOR-V2-SPEC.md` (DegradationReporter
|
|
43
|
+
is the v1 signal producer per §A28 / §A33; this PR adds the missing
|
|
44
|
+
emit-site for heal-failure events the W-1 runbook already implied).
|
|
45
|
+
Side-effects review:
|
|
46
|
+
`upgrades/side-effects/native-heal-degradation-bridge.md` with
|
|
47
|
+
second-pass independent audit appended (concurred).
|
|
48
|
+
|
|
49
|
+
## What to Tell Your User
|
|
50
|
+
|
|
51
|
+
- "When the agent's database driver gets out of sync with your Node version and can't repair itself, you now get a Telegram alert telling you what broke. Previously the agent quietly kept running with persistent memory turned off and you only found out by noticing things weren't sticking."
|
|
52
|
+
|
|
53
|
+
## Summary of New Capabilities
|
|
54
|
+
|
|
55
|
+
| Capability | How to Use |
|
|
56
|
+
|-----------|-----------|
|
|
57
|
+
| Native-module heal failures alert the user | Automatic. `AgentServer` wires `NativeHealDegradationBridge` at boot; any `NativeModuleHealer` rebuild that fails surfaces as a `DegradationReporter` event (Telegram alert via the agent-attention path, structured row in `<stateDir>/degradations.json`). Success heals stay silent. |
|
|
58
|
+
| `NativeModuleHealer.onHealEvent(listener)` | New public registration point on the healer singleton — listener fires after every persisted heal event. Returns an unsubscribe function. Errors thrown by a listener are swallowed so observability cannot break the heal path. |
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Side-Effects Review — Agent Worktree Convention (Layers 1, 2, 5)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `agent-worktree-convention-layer-1-2-5`
|
|
4
|
+
**Date:** `2026-05-19`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required`
|
|
7
|
+
(see §"Phase 5 trigger check" below — this change does not touch the
|
|
8
|
+
high-risk surfaces listed in the /instar-dev skill.)
|
|
9
|
+
|
|
10
|
+
## Summary of the change
|
|
11
|
+
|
|
12
|
+
Implements three of the five layers of the
|
|
13
|
+
`docs/specs/AGENT-WORKTREE-CONVENTION-SPEC.md` (status: approved by
|
|
14
|
+
Justin 2026-05-17 22:35 UTC):
|
|
15
|
+
|
|
16
|
+
- **Layer 1 — `instar worktree create <branch>` CLI subcommand.** New
|
|
17
|
+
command under the existing `worktree` group. Creates a sandbox-safe
|
|
18
|
+
git worktree of the shared instar repo at
|
|
19
|
+
`~/.instar/agents/<agent>/.worktrees/<slug>/`. Refuses any other
|
|
20
|
+
destination. All validation logic lives in a new
|
|
21
|
+
`src/core/InstarWorktreeManager.ts` so it can be unit-tested without
|
|
22
|
+
spawning the CLI.
|
|
23
|
+
- **Layer 2 — Scaffold seed for new agents.** `generateClaudeMd()`
|
|
24
|
+
gains a top-level "Worktree Convention" section with the literal
|
|
25
|
+
spec text plus the `GIT_AUTHOR_NAME` / `GIT_COMMITTER_EMAIL`
|
|
26
|
+
env-override caveat. `generateMemoryMd()` seeds a feedback-style
|
|
27
|
+
entry under "Project Patterns". `MachineIdentity.ts`
|
|
28
|
+
`GITIGNORE_ENTRIES` adds `.worktrees/` (consumed by `ensureGitignore`
|
|
29
|
+
on `instar init`).
|
|
30
|
+
- **Layer 5 — Documentation & discoverability.**
|
|
31
|
+
`docs/self-knowledge/worktrees.md` is the authoritative reference
|
|
32
|
+
(linked from the seed CLAUDE.md). The `worktree` command group's
|
|
33
|
+
`.description()` is updated to distinguish `create` (this spec) from
|
|
34
|
+
the existing `register-keypair` (parallel-dev cryptography).
|
|
35
|
+
|
|
36
|
+
**Files touched:**
|
|
37
|
+
- `src/core/InstarWorktreeManager.ts` (new, ~430 lines)
|
|
38
|
+
- `src/commands/worktree.ts` (existing, +~22 lines for `createWorktree` shim)
|
|
39
|
+
- `src/cli.ts` (existing, +~28 lines for `create` subcommand and updated group description)
|
|
40
|
+
- `src/scaffold/templates.ts` (existing, +~25 lines for Worktree Convention section + MEMORY.md entry)
|
|
41
|
+
- `src/core/MachineIdentity.ts` (existing, +2 lines in `GITIGNORE_ENTRIES`)
|
|
42
|
+
- `docs/self-knowledge/worktrees.md` (new)
|
|
43
|
+
- `tests/unit/InstarWorktreeManager.test.ts` (new, 27 cases)
|
|
44
|
+
- `tests/unit/scaffold-worktree-convention.test.ts` (new, 5 cases)
|
|
45
|
+
- `tests/integration/instar-worktree-create.test.ts` (new, 6 cases)
|
|
46
|
+
- `upgrades/NEXT.md` (appended one section)
|
|
47
|
+
|
|
48
|
+
Layers 3 (PostUpdateMigrator step for existing agents) and 4 (lifeline
|
|
49
|
+
detector) are deliberately deferred to the next `/instar-dev` cycle —
|
|
50
|
+
PostUpdateMigrator changed substantially in the v1.0.x portability
|
|
51
|
+
work that just landed on main, and splitting keeps the diff readable.
|
|
52
|
+
Layer 3 is what propagates the convention to existing agents on
|
|
53
|
+
update; until it ships, existing agents continue to use the hand-rolled
|
|
54
|
+
bash helper that already exists in each agent's `.bin/`. New agents
|
|
55
|
+
created via `instar init` get the seed (Layer 2) immediately.
|
|
56
|
+
|
|
57
|
+
## Decision-point inventory
|
|
58
|
+
|
|
59
|
+
- **CLI accept/refuse on worktree placement** — *add*. Refuses any
|
|
60
|
+
destination outside `<agent_home>/.worktrees/`. Structural
|
|
61
|
+
hard-invariant validation (path containment via `realpath`), not a
|
|
62
|
+
judgment call. Falls under the "safety guard on irreversible action"
|
|
63
|
+
carve-out in `docs/signal-vs-authority.md`.
|
|
64
|
+
- **CLI accept/refuse on agent-home resolution** — *add*. Refuses
|
|
65
|
+
unregistered homes, hostile-CWD-planted `AGENT.md`, and paths that
|
|
66
|
+
don't match the `^<instarHome>/agents/[a-z0-9_-]+/?$` regex.
|
|
67
|
+
Hard-invariant validation.
|
|
68
|
+
- **CLI accept/refuse on `INSTAR_REPO`** — *add*. Refuses repos whose
|
|
69
|
+
`remote.origin.url` is not in the bake-in allowlist (extendable via
|
|
70
|
+
`worktree.repoUrlAllowlist` in `~/.instar/config.json`) or whose
|
|
71
|
+
`core.hooksPath` resolves outside the repo. Hard-invariant.
|
|
72
|
+
- **Audit ledger writes** — *add*. Two append paths (local
|
|
73
|
+
`.worktrees/.ledger.jsonl` + audit mirror under
|
|
74
|
+
`<stateDir>/audit/worktree-ops.jsonl`). No block/allow surface;
|
|
75
|
+
signal-only for downstream consumers. `O_NOFOLLOW` + `fstat`
|
|
76
|
+
owner/mode gate is structural validation of a security invariant,
|
|
77
|
+
not a brittle filter.
|
|
78
|
+
|
|
79
|
+
No new authorities introduced. No detectors with blocking power.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 1. Over-block
|
|
84
|
+
|
|
85
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
86
|
+
|
|
87
|
+
- **Calls from a directory that is not under any registered agent
|
|
88
|
+
home** are refused. *Intentional* — the whole point of the
|
|
89
|
+
convention is "from inside an agent home, into the agent home." If
|
|
90
|
+
an operator legitimately wants to create a worktree from a non-agent
|
|
91
|
+
shell, they use raw `git worktree add` (still works, but the Layer 4
|
|
92
|
+
detector — once it ships — will surface a one-time attention item).
|
|
93
|
+
- **`INSTAR_REPO` pointing at a fork** with a non-allowlisted remote
|
|
94
|
+
URL is refused. *Operator-controllable* via
|
|
95
|
+
`worktree.repoUrlAllowlist` in `~/.instar/config.json`. Default
|
|
96
|
+
closed.
|
|
97
|
+
- **Slugs containing legitimate Unicode** (anything outside
|
|
98
|
+
`[A-Za-z0-9._-]`) are refused. *Intentional*: the slug becomes a
|
|
99
|
+
directory name passed to `git worktree add`, and the threat model
|
|
100
|
+
(prompt-injection-driven slug pollution → path traversal back into
|
|
101
|
+
the shared checkout) outweighs the convenience of accepting Unicode
|
|
102
|
+
branch labels. Operators with Unicode branch names get the default
|
|
103
|
+
hyphenation of slashes; if they want a custom slug, they pass an
|
|
104
|
+
ASCII one explicitly.
|
|
105
|
+
|
|
106
|
+
## 2. Under-block
|
|
107
|
+
|
|
108
|
+
**What failure modes does this still miss?**
|
|
109
|
+
|
|
110
|
+
- **Raw `git worktree add` into the shared checkout is not
|
|
111
|
+
intercepted.** Spec explicitly out-of-scope ("Non-goals"). Mitigation
|
|
112
|
+
is the Layer 4 detector (deferred to next `/instar-dev` cycle) +
|
|
113
|
+
scaffold seed memory + per-agent CLAUDE.md mention.
|
|
114
|
+
- **A compromised local user** (same uid, full execution privilege)
|
|
115
|
+
can rewrite `~/.instar/config.json` to widen the
|
|
116
|
+
`worktree.repoUrlAllowlist`, plant files in `~/.instar/agents/`, or
|
|
117
|
+
replace the binary. Explicitly out-of-scope per the spec's threat
|
|
118
|
+
model boundary in §Non-goals.
|
|
119
|
+
- **The agent-name segment can be any registered name**, not strictly
|
|
120
|
+
"the agent whose `INSTAR_AGENT_HOME` env var resolved here." This is
|
|
121
|
+
intentional — the env var IS the canonical identity transport set by
|
|
122
|
+
the launcher; if the env var is wrong, the launcher is broken and
|
|
123
|
+
the validation flags only structural problems (regex, registry
|
|
124
|
+
membership, realpath containment). The walk-up fallback only triggers
|
|
125
|
+
when the env var is unset and resolves against the real CWD, so
|
|
126
|
+
there's no path where a different agent's home is silently selected
|
|
127
|
+
while the current agent is running.
|
|
128
|
+
- **`GIT_AUTHOR_NAME` / `GIT_COMMITTER_EMAIL` env vars override** the
|
|
129
|
+
per-worktree identity the CLI sets. The seed CLAUDE.md and
|
|
130
|
+
self-knowledge doc both warn about this; we can't override env
|
|
131
|
+
precedence without breaking legitimate per-call attribution.
|
|
132
|
+
|
|
133
|
+
## 3. Level-of-abstraction fit
|
|
134
|
+
|
|
135
|
+
**Is this at the right layer?**
|
|
136
|
+
|
|
137
|
+
- The CLI subcommand and the manager class live where every other
|
|
138
|
+
instar CLI tool lives: `src/cli.ts` → `src/commands/<group>.ts` →
|
|
139
|
+
`src/core/<Manager>.ts`. The split between the picocolors-printing
|
|
140
|
+
command shim and the manager class matches `WorktreeKeyVault`,
|
|
141
|
+
`BackupManager`, etc. — manager has no I/O on stdout, command shim
|
|
142
|
+
has no business logic.
|
|
143
|
+
- The scaffold seed change is one entry in `GITIGNORE_ENTRIES` and one
|
|
144
|
+
literal-text section in `generateClaudeMd`/`generateMemoryMd`. No
|
|
145
|
+
new code path — it rides on top of `ensureGitignore` which already
|
|
146
|
+
runs on every relevant `instar init` codepath.
|
|
147
|
+
- The self-knowledge doc lives in `docs/self-knowledge/` (new
|
|
148
|
+
directory). The TreeGenerator integration (a node sourced from this
|
|
149
|
+
file) is left as a follow-up — the runtime discoverability that
|
|
150
|
+
matters for v1 is the seed CLAUDE.md mention + the `--help` text,
|
|
151
|
+
both of which are wired here.
|
|
152
|
+
|
|
153
|
+
A lower-level primitive (`GITIGNORE_ENTRIES`) already exists and is
|
|
154
|
+
used; the new `.worktrees/` entry just feeds it. A higher-level gate
|
|
155
|
+
(the Layer 4 detector) is what the convention will eventually feed —
|
|
156
|
+
that's a follow-up, but the audit ledger this change writes is the
|
|
157
|
+
signal that detector will consume.
|
|
158
|
+
|
|
159
|
+
## 4. Signal vs authority compliance
|
|
160
|
+
|
|
161
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
162
|
+
|
|
163
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
164
|
+
|
|
165
|
+
- [x] No — this change has no judgment-call block/allow surface. The
|
|
166
|
+
blocks are all structural hard-invariant validation (`realpath`
|
|
167
|
+
containment, regex on agent-name segment, registry-membership lookup,
|
|
168
|
+
`git check-ref-format`, `O_NOFOLLOW` + `fstat`), which fall under the
|
|
169
|
+
"hard-invariant validation" and "safety guards on irreversible
|
|
170
|
+
actions" carve-outs documented at the bottom of
|
|
171
|
+
`docs/signal-vs-authority.md`.
|
|
172
|
+
- The audit ledger is explicitly a **signal**. The spec restates this
|
|
173
|
+
in §Design ("Ledger is signal, never authority") and the manager
|
|
174
|
+
comment at `worktreeDedupeKey` carries the same invariant for the
|
|
175
|
+
Layer 4 detector — the detector's authority rule is path-based, not
|
|
176
|
+
ledger-membership-based.
|
|
177
|
+
- No new LLM-backed gates introduced. No new authorities introduced.
|
|
178
|
+
|
|
179
|
+
## 5. Interactions
|
|
180
|
+
|
|
181
|
+
- **Shadowing:** The `worktree` command group's existing
|
|
182
|
+
`register-keypair` subcommand is unaffected — different verb,
|
|
183
|
+
different action method, different command path. Group description
|
|
184
|
+
updated to distinguish them so `instar worktree --help` is honest
|
|
185
|
+
about both purposes.
|
|
186
|
+
- **Double-fire:** The hand-rolled bash helper in echo/bob's `.bin/`
|
|
187
|
+
(`instar-worktree-create.sh`) continues to exist and continues to
|
|
188
|
+
work. After Layer 3 ships, the wrapper refresh re-exports it to
|
|
189
|
+
`exec` into `instar worktree create` so the two paths converge; for
|
|
190
|
+
now, both are functional but the bash helper is the primary path on
|
|
191
|
+
echo's machine until Layer 3 lands.
|
|
192
|
+
- **Races:** The CLI runs `git worktree prune` before every `add`, and
|
|
193
|
+
on add-failure it does NOT remove any partial directory — git owns
|
|
194
|
+
rollback. Two agent homes targeting the same `INSTAR_REPO` cannot
|
|
195
|
+
produce partial state on either side: each path is contained inside
|
|
196
|
+
its own `.worktrees/`, and the `worktree-list`/`prune`/`add`
|
|
197
|
+
sequence is git's own atomicity contract.
|
|
198
|
+
- **Feedback loops:** None. The ledger feeds nothing in this PR (Layer
|
|
199
|
+
4 is the consumer, deferred). The scaffold seed is one-shot at
|
|
200
|
+
`instar init`.
|
|
201
|
+
|
|
202
|
+
## 6. External surfaces
|
|
203
|
+
|
|
204
|
+
- **Other agents on the same machine:** the seed change shows up only
|
|
205
|
+
in *newly initialized* agent homes (Layer 2 path). Existing agents
|
|
206
|
+
see nothing until Layer 3 ships.
|
|
207
|
+
- **Other users of the install base:** no — same as above. The new
|
|
208
|
+
CLI subcommand is available to anyone who upgrades, but it's purely
|
|
209
|
+
additive; no existing command's behaviour changes.
|
|
210
|
+
- **External systems:** none. No network calls. No Telegram. No
|
|
211
|
+
GitHub. The integration test creates a local bare git repo in `os.tmpdir()`.
|
|
212
|
+
- **Persistent state:** two new JSONL files per agent home
|
|
213
|
+
(`.worktrees/.ledger.jsonl` + `<stateDir>/audit/worktree-ops.jsonl`)
|
|
214
|
+
appended on every successful create. Both are bounded — the spec
|
|
215
|
+
defers ring-rotation to the PostUpdateMigrator (Layer 3) — and both
|
|
216
|
+
are git-ignored in the agent home. Rollback is `rm` of the two
|
|
217
|
+
files, no schema migration.
|
|
218
|
+
- **Timing / runtime conditions:** the manager calls out to `git`
|
|
219
|
+
shell-out (via `execFileSync`). The integration test exercises this
|
|
220
|
+
against a real `git init`. No long-running operations; no timeouts
|
|
221
|
+
needed.
|
|
222
|
+
|
|
223
|
+
## 7. Rollback cost
|
|
224
|
+
|
|
225
|
+
- **Code:** revert these commits. The hand-rolled bash helper in
|
|
226
|
+
echo/bob `.bin/` already exists, has been in production today, and
|
|
227
|
+
continues to work. No agent is dependent on `instar worktree create`
|
|
228
|
+
yet because Layer 3 (migrator) hasn't shipped — until it does, no
|
|
229
|
+
existing agent has the wrapper that calls into Layer 1.
|
|
230
|
+
- **Persistent state:** the two JSONL files. `rm
|
|
231
|
+
<agent_home>/.worktrees/.ledger.jsonl` and
|
|
232
|
+
`rm <stateDir>/audit/worktree-ops.jsonl` are safe one-liners; both
|
|
233
|
+
are signal-only and consumed by nothing in this PR.
|
|
234
|
+
- **Agent state repair:** none required.
|
|
235
|
+
- **User visibility during rollback:** the only user-visible surface
|
|
236
|
+
is the `instar worktree create` subcommand, which simply disappears.
|
|
237
|
+
Anyone using it would fall back to the bash helper.
|
|
238
|
+
|
|
239
|
+
Total rollback time: under 5 minutes (one revert, two `rm`).
|
|
240
|
+
|
|
241
|
+
## Conclusion
|
|
242
|
+
|
|
243
|
+
The change is purely additive at the CLI layer, additive in the
|
|
244
|
+
scaffold layer (new agents only), and one-shot at the `.gitignore`
|
|
245
|
+
seed. All decision points are structural validators in the spec's
|
|
246
|
+
hard-invariant / safety-guard carve-outs. No new authorities. No
|
|
247
|
+
detectors with blocking power. 38 new tests pass (27 unit + 5
|
|
248
|
+
scaffold-unit + 6 integration). Rollback is straightforward and the
|
|
249
|
+
hand-rolled bash helper continues to function as a fallback.
|
|
250
|
+
|
|
251
|
+
Phase 5 trigger check: the change does **not** touch outbound or
|
|
252
|
+
inbound messaging dispatch, session lifecycle (spawn/restart/kill),
|
|
253
|
+
compaction/respawn, coherence gates, idempotency at the transport
|
|
254
|
+
layer, trust levels, or anything named sentinel/guard/gate/watchdog.
|
|
255
|
+
No second-pass reviewer required.
|
|
256
|
+
|
|
257
|
+
Clear to ship.
|
|
258
|
+
|
|
259
|
+
## Evidence pointers
|
|
260
|
+
|
|
261
|
+
- **Unit tests:** `tests/unit/InstarWorktreeManager.test.ts` (27
|
|
262
|
+
cases — agent-home resolution including the hostile-CWD path,
|
|
263
|
+
INSTAR_REPO validation including unallowlisted remote +
|
|
264
|
+
out-of-repo `core.hooksPath`, slug/branch validation including
|
|
265
|
+
path-traversal and `--upload-pack=` injection, ledger O_NOFOLLOW
|
|
266
|
+
+ fstat owner/mode gate).
|
|
267
|
+
- **Scaffold tests:** `tests/unit/scaffold-worktree-convention.test.ts`
|
|
268
|
+
(5 cases — literal-text assertions on the seed sections + idempotent
|
|
269
|
+
`.gitignore` addition).
|
|
270
|
+
- **Integration tests:** `tests/integration/instar-worktree-create.test.ts`
|
|
271
|
+
(6 cases — happy path with identity verification + ledger + mirror,
|
|
272
|
+
node_modules default symlink, `--no-share-node-modules` opt-out,
|
|
273
|
+
slug collision without removing partial state, path-containment
|
|
274
|
+
refusal of a pre-planted symlink at `.worktrees/`, refusal of a
|
|
275
|
+
repo with unset remote).
|
|
276
|
+
- **Spec:** `docs/specs/AGENT-WORKTREE-CONVENTION-SPEC.md` (approved
|
|
277
|
+
2026-05-17 22:35 UTC).
|
|
278
|
+
- **ELI16:** `docs/specs/AGENT-WORKTREE-CONVENTION-ELI16.md`.
|
|
279
|
+
- **Convergence report:**
|
|
280
|
+
`docs/specs/reports/agent-worktree-convention-convergence.md`.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Side-Effects Review — Agent Worktree Convention (Layers 3 + 4)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `agent-worktree-convention-layer-3-4`
|
|
4
|
+
**Date:** `2026-05-19`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required` (see §"Phase 5 trigger check" below)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Completes the agent worktree convention spec by adding the two layers
|
|
11
|
+
that propagate the convention to **existing** agents and observe
|
|
12
|
+
violations on every agent boot.
|
|
13
|
+
|
|
14
|
+
- **Layer 3 — `PostUpdateMigrator.migrateWorktreeConvention`.** Runs on
|
|
15
|
+
every agent update tick. Validates the agent home against Layer 1's
|
|
16
|
+
shape contract (registry membership + anchored regex), refuses if
|
|
17
|
+
`<agent_home>/.bin` is a symlink, then installs/refreshes the
|
|
18
|
+
`instar-worktree-create.sh` wrapper (always-overwrite per Migration
|
|
19
|
+
Parity Standard), idempotently adds `.worktrees/` to
|
|
20
|
+
`<agent_home>/.gitignore`, and ensures `<agent_home>/.worktrees/`
|
|
21
|
+
exists with mode `0700`. Plus a **Migration Parity Standard backfill**
|
|
22
|
+
in `migrateClaudeMd`: the "Worktree Convention" section is appended
|
|
23
|
+
to existing agents' CLAUDE.md (the scope-coherence checkpoint caught
|
|
24
|
+
this gap earlier — without it, only fresh `instar init` agents would
|
|
25
|
+
ever see the convention text). Mirrored to AGENTS.md / GEMINI.md via
|
|
26
|
+
the v1.0.15 shadow-capability mirror (added one entry to the markers
|
|
27
|
+
list).
|
|
28
|
+
- **Layer 4 — `AgentWorktreeDetector`.** Runs once per agent server
|
|
29
|
+
boot, from `startServer()`. Resolves the canonical instar repo
|
|
30
|
+
deterministically (config `worktree.repoPath` or default fallback
|
|
31
|
+
chain — explicitly NOT `INSTAR_REPO` env, because env vars can
|
|
32
|
+
differ between lifeline boot and interactive sessions and the
|
|
33
|
+
detector wants consistent results across both). Runs `git worktree
|
|
34
|
+
list --porcelain` with a 2-second timeout, skips the main checkout
|
|
35
|
+
and bare entries, and emits an AttentionItem-shaped record for every
|
|
36
|
+
remaining worktree whose path is not under any registered agent's
|
|
37
|
+
`.worktrees/`. **Signal-only**: never blocks, never moves, never
|
|
38
|
+
deletes. Dedupe via the documented `worktree-misplaced:sha256(path)`
|
|
39
|
+
key. When no Telegram adapter is configured, appends to a JSONL
|
|
40
|
+
fallback at `<stateDir>/audit/worktree-detector.jsonl` (O_NOFOLLOW +
|
|
41
|
+
fstat owner/mode gate, 24h rolling-window dedupe).
|
|
42
|
+
|
|
43
|
+
**Files touched:**
|
|
44
|
+
|
|
45
|
+
- `src/core/PostUpdateMigrator.ts` (+~130 lines: `migrateWorktreeConvention` method, registration in `migrate()`, marker addition in `migrateFrameworkShadowCapabilities`, "Worktree Convention" section in `migrateClaudeMd`, import of `resolveAgentHome` from Layer 1's manager).
|
|
46
|
+
- `src/core/AgentWorktreeDetector.ts` (new, ~330 lines).
|
|
47
|
+
- `src/commands/server.ts` (+~35 lines: detector invocation after `registerAgent`).
|
|
48
|
+
- `src/templates/scripts/instar-worktree-create.sh` (new — canonical wrapper template the migrator installs into `<agent_home>/.bin/`).
|
|
49
|
+
- `scripts/lint-no-direct-destructive.js` (+8 lines — adds the wrapper template to `SHELL_ALLOWLIST` with documented rationale).
|
|
50
|
+
- `tests/unit/migrateWorktreeConvention.test.ts` (new, 5 cases).
|
|
51
|
+
- `tests/unit/AgentWorktreeDetector.test.ts` (new, 10 cases).
|
|
52
|
+
- `upgrades/NEXT.md` (appended section + capabilities-table rows + updated deferred list).
|
|
53
|
+
|
|
54
|
+
## Decision-point inventory
|
|
55
|
+
|
|
56
|
+
- **Migrator refusal on `.bin/` symlink** — *add*. Structural safety guard
|
|
57
|
+
on an irreversible action (would write a wrapper script via a symlink
|
|
58
|
+
that resolves outside the agent home, e.g., to `/usr/local/bin`).
|
|
59
|
+
Hard-invariant: `lstat`-based, no judgment call. Carve-out applies.
|
|
60
|
+
- **Migrator silent-skip on non-conforming agent home** — *add*. Refuses
|
|
61
|
+
to install the wrapper for project-bound agents whose home isn't
|
|
62
|
+
`~/.instar/agents/<name>/`. The convention only applies to
|
|
63
|
+
agent-home-living agents; project-bound agents have the project dir
|
|
64
|
+
as their primary working directory and the wrapper is meaningless
|
|
65
|
+
for them. Hard-invariant via the same regex+registry contract as
|
|
66
|
+
Layer 1.
|
|
67
|
+
- **Detector — AttentionItem emission** — *add*. Pure signal. Emits to
|
|
68
|
+
AttentionQueue (which already exists as the authority for what to do
|
|
69
|
+
with attention items). Never blocks; the agent boots regardless.
|
|
70
|
+
Compliant with signal-vs-authority per the spec's explicit
|
|
71
|
+
"signal only" framing.
|
|
72
|
+
- **Detector — path-based filter rule** — *add*. The detector's only
|
|
73
|
+
authoritative rule is `realpath(worktree_path) startsWith
|
|
74
|
+
realpath(<safe_root>)` for some registered agent. Pure structural
|
|
75
|
+
validator, no judgment.
|
|
76
|
+
|
|
77
|
+
No new authorities introduced. No detectors hold blocking power.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 1. Over-block
|
|
82
|
+
|
|
83
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
84
|
+
|
|
85
|
+
- **Migrator** silently skips agents whose home doesn't match the
|
|
86
|
+
`<instarHome>/agents/<name>/` shape. *Intentional* — project-bound
|
|
87
|
+
agents would receive a wrapper that points at their project dir but
|
|
88
|
+
the convention's spec is specific to agent-home-living agents.
|
|
89
|
+
- **Migrator** refuses if `<agent_home>/.bin` is a symlink. Operators
|
|
90
|
+
who deliberately symlink `.bin/` to a shared dir would be refused.
|
|
91
|
+
This is documented in the spec adversarial-finding section; the
|
|
92
|
+
alternative (silently writing into the symlink target) was rejected
|
|
93
|
+
as a security regression.
|
|
94
|
+
- **Detector** flags every worktree of the canonical instar repo
|
|
95
|
+
outside the safe-root set. **First-run on echo's machine will emit
|
|
96
|
+
~30 attention items** (one per pre-existing worktree). Each is
|
|
97
|
+
deduped for 24h. Operator must drain or relocate. This is the spec's
|
|
98
|
+
documented "one-time burst" and is in the Deferred list of NEXT.md.
|
|
99
|
+
|
|
100
|
+
## 2. Under-block
|
|
101
|
+
|
|
102
|
+
**What failure modes does this still miss?**
|
|
103
|
+
|
|
104
|
+
- **Migrator** does not detect or repair drift in `.worktrees/` mode
|
|
105
|
+
beyond re-asserting 0700 on every run. If something changes the
|
|
106
|
+
permissions on individual worktree directories underneath, that's
|
|
107
|
+
not policed. *Acceptable* — those are operator-managed
|
|
108
|
+
per-worktree.
|
|
109
|
+
- **Detector** treats stale `git worktree list` entries (paths whose
|
|
110
|
+
worktree dir was rm'd outside `git worktree prune`) as not-misplaced
|
|
111
|
+
(they're skipped via the existsSync check). This is intentional —
|
|
112
|
+
the spec wants signal on real misplacements, not on stale
|
|
113
|
+
metadata. A separate detector for stale-metadata could be a
|
|
114
|
+
follow-up.
|
|
115
|
+
- **Detector** does not yet feed AttentionQueue when Telegram is
|
|
116
|
+
configured — it falls back to JSONL in v1 because TelegramAdapter
|
|
117
|
+
initializes later in `startServer` than the detector. NEXT.md
|
|
118
|
+
Deferred list documents this; the JSONL trail is the durable
|
|
119
|
+
observation surface for v1.
|
|
120
|
+
- **Compromised local user** (same uid, full execution privilege)
|
|
121
|
+
remains explicitly out-of-scope per the spec's threat-model
|
|
122
|
+
boundary.
|
|
123
|
+
|
|
124
|
+
## 3. Level-of-abstraction fit
|
|
125
|
+
|
|
126
|
+
**Is this at the right layer?**
|
|
127
|
+
|
|
128
|
+
- **Migrator step** lives where every other `PostUpdateMigrator`
|
|
129
|
+
step lives, registered in `migrate()`. The bash-wrapper template is
|
|
130
|
+
in `src/templates/scripts/` alongside other agent-installed scripts
|
|
131
|
+
(`git-sync-gate.sh`, etc.) — same pattern as
|
|
132
|
+
`migrateConversationalCatalogPlaybookManifest`.
|
|
133
|
+
- **CLAUDE.md backfill** is a content-sniffing patch inside the
|
|
134
|
+
existing `migrateClaudeMd`, matching the pattern every other Capability
|
|
135
|
+
section already uses (`Self-Discovery`, `Cloudflare Tunnel`,
|
|
136
|
+
`Dashboard`, etc.). One marker added to
|
|
137
|
+
`migrateFrameworkShadowCapabilities` is the minimal touch needed for
|
|
138
|
+
Codex/Gemini shadow propagation.
|
|
139
|
+
- **Detector** is a standalone module under `src/core/` (alongside
|
|
140
|
+
Layer 1's `InstarWorktreeManager`). It's invoked once from
|
|
141
|
+
`startServer()` — a deliberate placement after `registerAgent` so
|
|
142
|
+
the registry is populated before `enumerateSafeRoots()` walks it.
|
|
143
|
+
- Reused primitives: `resolveAgentHome` and `resolveInstarRepo` (from
|
|
144
|
+
Layer 1's manager) — keeps the validation rules in one place so the
|
|
145
|
+
CLI and the migrator/detector can't disagree about "what an agent
|
|
146
|
+
home is" or "what a valid instar repo is."
|
|
147
|
+
|
|
148
|
+
A lower-level primitive (Layer 1's manager) is consumed; the higher
|
|
149
|
+
layer (AttentionQueue) is fed via signal-only emission. No new
|
|
150
|
+
authority introduced.
|
|
151
|
+
|
|
152
|
+
## 4. Signal vs authority compliance
|
|
153
|
+
|
|
154
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
155
|
+
|
|
156
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
157
|
+
|
|
158
|
+
- [x] No — the migrator's blocks are structural safety guards
|
|
159
|
+
(`.bin/` symlink refusal, agent-home regex+registry check) — both
|
|
160
|
+
carve-outs in `docs/signal-vs-authority.md` ("hard-invariant
|
|
161
|
+
validation" and "safety guards on irreversible actions").
|
|
162
|
+
- [x] No — the detector is **explicitly signal-only** per spec. It
|
|
163
|
+
emits AttentionItems / appends JSONL; the AttentionQueue is the
|
|
164
|
+
authority that decides what to do with them. The detector's filter
|
|
165
|
+
rule is path-based hard-invariant (no judgment).
|
|
166
|
+
|
|
167
|
+
The detector's invariant — "the rule lives here and only here, never
|
|
168
|
+
the audit ledger" — is restated in the file-level comment so future
|
|
169
|
+
maintainers can't drift toward ledger-membership.
|
|
170
|
+
|
|
171
|
+
## 5. Interactions
|
|
172
|
+
|
|
173
|
+
- **Shadowing:** the migrator runs in the `migrate()` chain after
|
|
174
|
+
`migrateConversationalCatalogPlaybookManifest` and before the async
|
|
175
|
+
`migrateParityRenderings` (in `migrateAsync()`). It doesn't share
|
|
176
|
+
state with adjacent steps. The `migrateClaudeMd` backfill rides on
|
|
177
|
+
top of the existing patched-content flow, sharing `patched` with
|
|
178
|
+
earlier sections so the file is written once.
|
|
179
|
+
- **Double-fire:** the wrapper template the migrator installs is now
|
|
180
|
+
the canonical source. The hand-rolled wrappers in echo + bob's
|
|
181
|
+
`.bin/` will be refreshed by the migrator on their next update tick.
|
|
182
|
+
Until then they coexist (both work — they're functionally
|
|
183
|
+
equivalent).
|
|
184
|
+
- **Races:** the detector runs once per agent boot, before the server
|
|
185
|
+
starts listening. No concurrent invocations from the same agent.
|
|
186
|
+
Two agents booting concurrently could each enumerate the same
|
|
187
|
+
worktree list — each emits its own AttentionItem with the same
|
|
188
|
+
`worktree-misplaced:sha256(path)` id, which the AttentionQueue
|
|
189
|
+
collapses to a single topic per the existing dedupe contract (line
|
|
190
|
+
2949 of `TelegramAdapter.ts`). The JSONL fallback dedup is per-file
|
|
191
|
+
(each agent has its own state dir).
|
|
192
|
+
- **Feedback loops:** the detector reads `git worktree list`. The
|
|
193
|
+
Layer 1 CLI writes to that list. There's no infinite-loop concern
|
|
194
|
+
because the detector emits to AttentionQueue (or JSONL), not back
|
|
195
|
+
into `git worktree`.
|
|
196
|
+
- **Migration Parity:** the Layer 2 changes (seed CLAUDE.md +
|
|
197
|
+
GITIGNORE_ENTRIES) are now also delivered to existing agents on
|
|
198
|
+
update — closing the parity gap. Without the Layer 3
|
|
199
|
+
`migrateClaudeMd` backfill, existing agents would never see the
|
|
200
|
+
convention text; with it, they pick it up on the next update tick.
|
|
201
|
+
|
|
202
|
+
## 6. External surfaces
|
|
203
|
+
|
|
204
|
+
- **Other agents on the same machine:** every agent picks up the
|
|
205
|
+
wrapper, the `.gitignore` entry, the `.worktrees/` directory, and
|
|
206
|
+
the CLAUDE.md section on its own next update tick. Single-agent
|
|
207
|
+
scope per spec — no cross-agent writes.
|
|
208
|
+
- **Other users of the install base:** purely additive. Existing
|
|
209
|
+
workflows are unchanged. The wrapper just makes the safe path the
|
|
210
|
+
default; raw `git worktree add` still works (and gets flagged by
|
|
211
|
+
the detector).
|
|
212
|
+
- **External systems:** none. No network. The detector calls `git`
|
|
213
|
+
locally, with a 2-second timeout.
|
|
214
|
+
- **Persistent state:**
|
|
215
|
+
- `<agent_home>/.bin/instar-worktree-create.sh` (new file per agent
|
|
216
|
+
on update tick — always-overwrite).
|
|
217
|
+
- `<agent_home>/.gitignore` (idempotent line addition).
|
|
218
|
+
- `<agent_home>/.worktrees/` (directory creation, mode 0700).
|
|
219
|
+
- `<agent_home>/CLAUDE.md` (single section appended, idempotent
|
|
220
|
+
via content-sniff).
|
|
221
|
+
- `<agent_home>/AGENTS.md` / `GEMINI.md` (single section mirrored
|
|
222
|
+
when those shadows exist).
|
|
223
|
+
- `<stateDir>/audit/worktree-detector.jsonl` (append-only signal
|
|
224
|
+
log; bounded by 24h rolling-window dedupe before append).
|
|
225
|
+
- All git-ignored or per-machine. Rollback is `rm` of these files.
|
|
226
|
+
- **Timing:** the detector's 2-second timeout means a hung
|
|
227
|
+
`git worktree list` doesn't block agent boot beyond that window.
|
|
228
|
+
|
|
229
|
+
## 7. Rollback cost
|
|
230
|
+
|
|
231
|
+
- **Code:** revert this commit. The Layer 1 CLI subcommand (PR #277)
|
|
232
|
+
continues to function for fresh `instar worktree create` calls.
|
|
233
|
+
Existing agents that already received the wrapper on a prior update
|
|
234
|
+
tick keep it (it works — it delegates to Layer 1).
|
|
235
|
+
- **Persistent state:**
|
|
236
|
+
- The wrapper, `.gitignore` entry, and `.worktrees/` directory in
|
|
237
|
+
each agent home are harmless leftovers. No removal required.
|
|
238
|
+
- JSONL fallback files are signal-only and consumed by nothing on
|
|
239
|
+
rollback. `rm` is a safe one-liner per agent.
|
|
240
|
+
- CLAUDE.md "Worktree Convention" section is documentation only —
|
|
241
|
+
leaving it in is benign.
|
|
242
|
+
- AGENTS.md / GEMINI.md mirrored sections — same.
|
|
243
|
+
- **Agent state repair:** none required.
|
|
244
|
+
- **User visibility during rollback:** none.
|
|
245
|
+
|
|
246
|
+
Total rollback time: under 5 minutes (one revert + optional cleanup
|
|
247
|
+
sweep).
|
|
248
|
+
|
|
249
|
+
## Conclusion
|
|
250
|
+
|
|
251
|
+
Completes the agent worktree convention spec. Migrator backfills
|
|
252
|
+
existing agents with the on-disk surface (wrapper, gitignore, dir
|
|
253
|
+
permissions, CLAUDE.md section, AGENTS/GEMINI mirror) so the
|
|
254
|
+
convention applies fleet-wide on the next update tick — closing the
|
|
255
|
+
Migration Parity gap the scope-coherence checkpoint flagged during
|
|
256
|
+
Layer 1+2+5 implementation. Detector emits signal per misplaced
|
|
257
|
+
worktree without ever blocking. All structural validators are in the
|
|
258
|
+
signal-vs-authority carve-outs. 15 new tests pass (5 migrator + 10
|
|
259
|
+
detector). Pre-push gate green.
|
|
260
|
+
|
|
261
|
+
Phase 5 trigger check: the change does **not** touch outbound/inbound
|
|
262
|
+
messaging dispatch, session lifecycle, compaction, coherence gates,
|
|
263
|
+
idempotency at transport, trust levels, or anything named
|
|
264
|
+
sentinel/guard/gate/watchdog (the "Detector" naming is intentional —
|
|
265
|
+
it's a signal producer, not a gate). No second-pass reviewer required.
|
|
266
|
+
|
|
267
|
+
Clear to ship.
|
|
268
|
+
|
|
269
|
+
## Evidence pointers
|
|
270
|
+
|
|
271
|
+
- **Migrator unit tests:** `tests/unit/migrateWorktreeConvention.test.ts`
|
|
272
|
+
(happy path with wrapper + .worktrees/0700 + .gitignore;
|
|
273
|
+
idempotency; always-overwrites tampered wrapper; refuses
|
|
274
|
+
`.bin/`-as-symlink; silently skips project-bound agents).
|
|
275
|
+
- **Detector unit tests:** `tests/unit/AgentWorktreeDetector.test.ts`
|
|
276
|
+
(silent when no misplaced; skips main checkout entry; emits one
|
|
277
|
+
AttentionItem per misplaced via injected emitter; skips when under
|
|
278
|
+
safe root; JSONL fallback writes; 24h JSONL dedupe; refuses
|
|
279
|
+
pre-planted symlink at the fallback path; resolveDetectorInstarRepo
|
|
280
|
+
honors config and returns null on no-repo).
|
|
281
|
+
- **Spec:** `docs/specs/AGENT-WORKTREE-CONVENTION-SPEC.md` (approved
|
|
282
|
+
2026-05-17 22:35 UTC).
|
|
283
|
+
- **Sibling artifact:**
|
|
284
|
+
`upgrades/side-effects/agent-worktree-convention-layer-1-2-5.md`
|
|
285
|
+
(Layers 1+2+5, merged in PR #277 as commit `bdf8508f`).
|