# Side-effects — Boot-wrapper version-managed node candidates

## 1. What files/state does this touch at runtime?
The generated `<stateDir>/instar-boot.cjs` (regenerated by `installBootWrapper`
when the PostUpdateMigrator detects the new marker is missing). At boot, the
wrapper's self-heal may additionally run `which node` and, if it resolves an
ABI-compatible node, repoint the `.instar/bin/node` symlink to it. No config,
schema, or other files.

## 2. Does it change any functional behavior?
Yes, narrowly + additively: the node-heal candidate pool now also includes the
PATH-resolved node (`which node` → asdf/nvm/volta). Selection is unchanged — the
existing ABI-compatibility loop still picks the candidate that can load the
existing better-sqlite3 binary. The only new outcome is: a heal that previously
had NO compatible candidate (and stayed on the wrong-ABI node) can now find the
version-managed node and heal correctly.

## 3. What happens on failure / non-darwin / no `which`?
- `which` missing or no node on PATH: the probe is wrapped in try/catch and
  swallowed — the heal proceeds exactly as before with the well-known candidates.
- Non-darwin: the boot wrapper + its migration are darwin/launchd-specific; the
  migration short-circuits with a `non-darwin` skip.
- A resolved node that can't load the module: skipped by the existing
  ABI-compatibility loop, identical to any other incompatible candidate.

## 4. Migration parity — do existing agents get it?
Yes. `migrateBootWrapperAbiCheck` now requires BOTH the ABI-drift marker AND the
`version-managed node candidates` marker to consider a wrapper current; an install
missing the new marker (e.g. instar-codey) is regenerated via `installBootWrapper`
on its next update. Idempotent (both markers → skip). New agents get it at setup.

## 5. Could it spam / flood / burn resources?
No. The heal path runs only at boot and only when the current node fails the
better-sqlite3 load check (ABI drift) — the rare case. It adds at most one
`which node` exec (5s timeout) to that already-rare path. No loop, no network.

## 6. Rollback / off-switch?
Revert the PR. The marker bump means a reverted version would (correctly) see the
new marker as "extra" — the old sniff only required the ABI marker, so reverting
leaves existing regenerated wrappers working (they still contain the ABI marker).
No residual state, no flag.

## 7. Concurrency / ordering?
None new. The `which node` probe is a single synchronous exec inside the existing
single-threaded `selfHealNodeSymlink()`, before the ABI-compatibility loop.

## Blast radius
Small + additive on a critical path. Two source edits (`setup.ts` template +
`PostUpdateMigrator.ts` sniff) + test coverage. It only EXPANDS an already
ABI-filtered candidate pool, mirrors the already-correct `resolveNodeCandidates()`
in the same file, and cannot make a heal worse. Darwin/launchd-scoped.
