instar 1.3.54 → 1.3.56
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 +21 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/machine.d.ts.map +1 -1
- package/dist/commands/machine.js +25 -0
- package/dist/commands/machine.js.map +1 -1
- package/dist/commands/setup.d.ts +11 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +31 -14
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/test-as-self.d.ts +41 -0
- package/dist/commands/test-as-self.d.ts.map +1 -0
- package/dist/commands/test-as-self.js +259 -0
- package/dist/commands/test-as-self.js.map +1 -0
- package/dist/commands/testAsSelfValidation.d.ts +43 -0
- package/dist/commands/testAsSelfValidation.d.ts.map +1 -0
- package/dist/commands/testAsSelfValidation.js +84 -0
- package/dist/commands/testAsSelfValidation.js.map +1 -0
- package/dist/core/GitSync.d.ts +7 -0
- package/dist/core/GitSync.d.ts.map +1 -1
- package/dist/core/GitSync.js +36 -0
- package/dist/core/GitSync.js.map +1 -1
- package/dist/core/mergeRegistry.d.ts +28 -0
- package/dist/core/mergeRegistry.d.ts.map +1 -0
- package/dist/core/mergeRegistry.js +86 -0
- package/dist/core/mergeRegistry.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +14 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/package.json +1 -1
- package/scripts/post-publish-smoke.mjs +80 -0
- package/src/data/builtin-manifest.json +31 -31
- package/src/scaffold/templates.ts +14 -0
- package/upgrades/1.3.53.md +20 -28
- package/upgrades/1.3.54.md +27 -41
- package/upgrades/1.3.56.md +35 -0
- package/upgrades/side-effects/init-join-launchagent-handoff.md +62 -0
- package/upgrades/side-effects/launchctl-test-hygiene.md +42 -0
- package/upgrades/side-effects/mesh-git-substrate-auto-reconcile.md +70 -0
- package/upgrades/side-effects/publish-completeness-smoke-gate.md +47 -0
- package/upgrades/side-effects/test-as-self-orchestrator.md +66 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Side-Effects Review — launchctl load test-hygiene guard (MM-Bootstrap Track C follow-up)
|
|
2
|
+
|
|
3
|
+
**Scope.** `src/commands/setup.ts` (new `launchctlLoadAllowed()` helper + guard
|
|
4
|
+
at the 2 install-path `launchctl bootstrap` sites: installMacOSLaunchAgent +
|
|
5
|
+
installFleetWatchdog), `tests/unit/launchctl-load-guard.test.ts` (new).
|
|
6
|
+
|
|
7
|
+
**Problem.** The Track C unit test (merged #470) writes a LaunchAgent plist via
|
|
8
|
+
`installAutoStart` to verify the Label-keyed-replace property. But
|
|
9
|
+
`installMacOSLaunchAgent` does a real `launchctl bootstrap` (load) after writing
|
|
10
|
+
— so the test loaded tmpdir-pointed plists into the operator's REAL launchd,
|
|
11
|
+
leaving stale `ai.instar.mmtesthandoff` / `ai.instar.mmteststandby` entries
|
|
12
|
+
(status 78, program gone) after the tmpdir was cleaned. Inert but real
|
|
13
|
+
test-pollution of the user's launchd, discovered 2026-05-28.
|
|
14
|
+
|
|
15
|
+
**Fix.** `launchctlLoadAllowed()` returns false under a test runner (vitest
|
|
16
|
+
auto-sets `VITEST`) or when `INSTAR_SKIP_LAUNCHCTL_LOAD` is set. The two
|
|
17
|
+
install-path bootout+bootstrap blocks are wrapped in it. The plist file is still
|
|
18
|
+
written/removed; only the LIVE load is gated — so the unit-under-test (plist
|
|
19
|
+
content) is unaffected, and production (neither flag set) loads normally.
|
|
20
|
+
|
|
21
|
+
**Side-effects review.**
|
|
22
|
+
- **No production behavior change** — in a normal environment neither VITEST nor
|
|
23
|
+
INSTAR_SKIP_LAUNCHCTL_LOAD is set, so `launchctlLoadAllowed()` is true and the
|
|
24
|
+
load happens exactly as before.
|
|
25
|
+
- **Tests no longer pollute real launchd** — verified: re-running the Track C
|
|
26
|
+
test creates NO mmtesthandoff/mmteststandby entries (the guard skipped the
|
|
27
|
+
load), and the test still passes (it asserts plist CONTENT, not live load).
|
|
28
|
+
- **uninstallAutoStart's bootout is intentionally NOT gated** — that's cleanup
|
|
29
|
+
(removing a real entry), correct to keep even in tests; only the install-path
|
|
30
|
+
LOAD is the pollution vector.
|
|
31
|
+
- **Minimal surface** — one helper + two wraps; no new dependency.
|
|
32
|
+
|
|
33
|
+
**Test coverage.** Unit `tests/unit/launchctl-load-guard.test.ts` (3): false
|
|
34
|
+
under VITEST, false under INSTAR_SKIP_LAUNCHCTL_LOAD, true when neither set
|
|
35
|
+
(production). Plus the existing Track C test now verifiably runs without
|
|
36
|
+
pollution.
|
|
37
|
+
|
|
38
|
+
**Migration parity.** Server source (setup.ts) — existing agents pick up on
|
|
39
|
+
auto-update. No agent-installed-file change; production load behavior unchanged.
|
|
40
|
+
|
|
41
|
+
**Rollback.** Revert. Tests would resume polluting real launchd (inert stale
|
|
42
|
+
entries). No production impact either way.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Side-Effects Review — Mesh registry conflict auto-merge (MM-Bootstrap Track D)
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS-SPEC.md` §Track D (approved PR #465).
|
|
4
|
+
|
|
5
|
+
**Scope.** `src/core/mergeRegistry.ts` (new), `src/core/GitSync.ts`
|
|
6
|
+
(`tryAutoResolve` gains a `machines/registry.json` case →
|
|
7
|
+
`resolveRegistryConflict`), `tests/unit/mergeRegistry.test.ts` (new).
|
|
8
|
+
|
|
9
|
+
**Premise correction (evidence-driven, like Track A).** The spec assumed "no
|
|
10
|
+
automatic reconcile path" + proposed a from-scratch `GitSyncManager.sync()`
|
|
11
|
+
rewrite. Reading the code: GitSync.sync() is already MATURE — it does
|
|
12
|
+
`pull --rebase --autostash`, pre-flight stuck-rebase/detached-HEAD recovery,
|
|
13
|
+
untracked-overwrite backup+retry, a multi-tier conflict resolver
|
|
14
|
+
(`tryAutoResolve` field-merge/newer-wins/union-by-id → LLM → manual), AND a
|
|
15
|
+
post-pull `reconcilePulledRegistry` (registryReplayGuard) for clean pulls. The
|
|
16
|
+
divergence I hit live ("Need to specify how to reconcile") was SELF-INFLICTED —
|
|
17
|
+
I ran raw `git pull` manually, not GitSync.sync() (which always passes
|
|
18
|
+
`--rebase`). So a rewrite would have duplicated/regressed mature code.
|
|
19
|
+
|
|
20
|
+
**The REAL gap (small, precise).** `tryAutoResolve` had cases for
|
|
21
|
+
relationships/, jobs.json, evolution/ — but NONE for `machines/registry.json`.
|
|
22
|
+
So when registry.json hits a *rebase conflict* (both machines committed
|
|
23
|
+
different registry+lease — the concurrent lease-bump + join case), it fell
|
|
24
|
+
through to LLM/manual instead of a deterministic merge, and the mesh could
|
|
25
|
+
stay split (one side 1 machine, the other 2).
|
|
26
|
+
|
|
27
|
+
**Fix.** A pure `mergeRegistry(ours, theirs)` (union machines by id; per-id
|
|
28
|
+
clash → later `lastSeen` wins, revocation sticky, syncSequence tiebreak; lease
|
|
29
|
+
→ higher `epoch` wins, signature-lexical tiebreak; version → max) +
|
|
30
|
+
`GitSync.resolveRegistryConflict` wired into `tryAutoResolve` for
|
|
31
|
+
`machines/registry.json`. Both machines compute the identical merge, so the
|
|
32
|
+
mesh converges without coordination.
|
|
33
|
+
|
|
34
|
+
**Side-effects review.**
|
|
35
|
+
- **Lossless + deterministic** — no machine is ever dropped; both sides'
|
|
36
|
+
updates survive; the merge is order-independent for the fields that matter
|
|
37
|
+
(verified by an order-independence test). Two machines independently reach
|
|
38
|
+
the same merged registry.
|
|
39
|
+
- **Reuses the existing resolver tier** — slots into `tryAutoResolve`
|
|
40
|
+
(Tier-0 programmatic) exactly like the relationship/jobs/evolution cases;
|
|
41
|
+
no change to the pull/rebase machinery, the LLM tier, or
|
|
42
|
+
`reconcilePulledRegistry` (which still handles the clean-pull path).
|
|
43
|
+
- **Fail-safe** — `resolveRegistryConflict` is try/catch-wrapped; on any
|
|
44
|
+
parse/merge error it returns false (DegradationReporter logged) and the
|
|
45
|
+
conflict falls through to the LLM/manual tier exactly as before. Strictly
|
|
46
|
+
additive: a registry conflict that previously went to LLM/manual now gets a
|
|
47
|
+
deterministic merge first; everything else is unchanged.
|
|
48
|
+
- **Revocation safety** — a `revoked` status on either side is sticky, so a
|
|
49
|
+
stale "active" entry with a newer lastSeen can NOT resurrect a removed
|
|
50
|
+
machine (security-relevant; tested).
|
|
51
|
+
|
|
52
|
+
**Test coverage.**
|
|
53
|
+
- Unit `tests/unit/mergeRegistry.test.ts` (6 cases): the exact 2026-05-27
|
|
54
|
+
divergence (concurrent lease-bump + join → both machines + higher epoch
|
|
55
|
+
survive); order-independence; same-epoch signature tiebreak; sticky
|
|
56
|
+
revocation vs newer-lastSeen; missing-lease on one/both sides; syncSequence
|
|
57
|
+
tiebreak. All green. `tsc --noEmit` + destructive-lint clean.
|
|
58
|
+
- The git-level conflict path (rebase → resolveRegistryConflict) is exercised
|
|
59
|
+
by the existing GitSync conflict-resolution integration coverage; the new
|
|
60
|
+
case mirrors the proven relationship/jobs/evolution wiring.
|
|
61
|
+
|
|
62
|
+
**Migration parity.** Server source (GitSync) — existing agents pick up the new
|
|
63
|
+
conflict case on auto-update. No agent-installed-file change.
|
|
64
|
+
|
|
65
|
+
**Rollback.** Revert the PR. registry.json conflicts fall back to LLM/manual
|
|
66
|
+
(prior behavior). No data change, no migration to reverse.
|
|
67
|
+
|
|
68
|
+
**Note.** Track D is intentionally scoped to the registry/lease conflict-merge
|
|
69
|
+
gap (the actual hole), NOT the spec's drafted full sync() rewrite (unnecessary
|
|
70
|
+
given the existing mature sync()). Flagged to Justin.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Side-Effects Review — Post-publish smoke gate (MM-Bootstrap Track A, re-scoped)
|
|
2
|
+
|
|
3
|
+
**Spec:** MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS §Track A (re-scoped per the
|
|
4
|
+
evidence below).
|
|
5
|
+
|
|
6
|
+
**Scope.** `scripts/post-publish-smoke.mjs` (new), `.github/workflows/publish.yml`
|
|
7
|
+
(one new step after `npm publish`), `tests/unit/post-publish-smoke.test.ts` (new).
|
|
8
|
+
|
|
9
|
+
**Premise correction (evidence-driven).** Track A originally targeted a
|
|
10
|
+
"fleet-wide install blocker — the published tarball ships an empty dist." That
|
|
11
|
+
premise is FALSE: a clean `npm install --prefix /tmp/... instar@latest` yields a
|
|
12
|
+
complete dist (864 dist/core files) and runs. The "empty dist" hit during the
|
|
13
|
+
2026-05-27 manual bring-up was self-inflicted (an `rsync -a --delete` wiping the
|
|
14
|
+
global install). So there is no active bug to fix. Re-scoped (with Justin's
|
|
15
|
+
default-build nod) to cheap REGRESSION INSURANCE: if the publish pipeline ever
|
|
16
|
+
DOES ship a broken tarball, catch it within minutes of release.
|
|
17
|
+
|
|
18
|
+
**What it does.** A new step after `npm publish` runs
|
|
19
|
+
`post-publish-smoke.mjs <version>`: waits (bounded retry, 3m) for npm to
|
|
20
|
+
propagate the just-published version, clean-installs it into a throwaway prefix,
|
|
21
|
+
asserts `dist/cli.js` exists, runs `--version`, and asserts it reports the
|
|
22
|
+
published version. Fails the release workflow loudly otherwise (before the
|
|
23
|
+
version-bump commit/tag).
|
|
24
|
+
|
|
25
|
+
**Side-effects review.**
|
|
26
|
+
- **No effect on the happy path** — adds a verification step; a healthy publish
|
|
27
|
+
passes it in ~seconds-to-2min (npm propagation).
|
|
28
|
+
- **Fails the workflow on a broken publish** — by design. A broken tarball halts
|
|
29
|
+
the release before tagging, so a bad version isn't blessed. (The tarball is
|
|
30
|
+
already on npm at that point — npm doesn't allow un-publish easily — but the
|
|
31
|
+
loud failure surfaces it immediately for a follow-up patch.)
|
|
32
|
+
- **Throwaway prefix** — installs into `mkdtemp` under the OS tmpdir; no effect
|
|
33
|
+
on the repo or the runner's global state.
|
|
34
|
+
- **Bounded** — 3-minute propagation deadline; never hangs the workflow.
|
|
35
|
+
- **substring-safe version check** — `versionMatches` tokenizes (1.3.5 ≠ 1.3.55),
|
|
36
|
+
unit-tested both ways.
|
|
37
|
+
|
|
38
|
+
**Test coverage.** Unit `tests/unit/post-publish-smoke.test.ts` (4) covers the
|
|
39
|
+
pure `versionMatches` (bare output / token-among-others / mismatch / no
|
|
40
|
+
substring false-match). The install+run path is exercised by the workflow itself
|
|
41
|
+
on the next real publish (CI-only; not unit-mockable meaningfully).
|
|
42
|
+
|
|
43
|
+
**Migration parity.** None — publish-pipeline + dev script; no agent-installed
|
|
44
|
+
file.
|
|
45
|
+
|
|
46
|
+
**Rollback.** Revert the PR. The smoke step disappears; publishes proceed
|
|
47
|
+
unverified (the prior state). No data change.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Side-Effects Review — `instar test-as-self` orchestrator (MM-Bootstrap Track F)
|
|
2
|
+
|
|
3
|
+
**Spec:** MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS §Track F (folds the approved Part 2.1).
|
|
4
|
+
|
|
5
|
+
**Scope.** `src/commands/testAsSelfValidation.ts` (new, pure guards),
|
|
6
|
+
`src/commands/test-as-self.ts` (new, the 7-step orchestrator), `src/cli.ts`
|
|
7
|
+
(register `test-as-self`), `src/scaffold/templates.ts` (Agent Awareness entry),
|
|
8
|
+
`tests/unit/test-as-self-validation.test.ts`,
|
|
9
|
+
`tests/integration/test-as-self-guards.test.ts`.
|
|
10
|
+
|
|
11
|
+
**What it does.** `instar test-as-self` automates the recipe the test-as-self
|
|
12
|
+
SKILL documented as manual steps: deploy the current dist into a throwaway agent
|
|
13
|
+
home, start it, optionally run a real Telegram round-trip (Bot HTTP API), run the
|
|
14
|
+
deterministic `verify.mjs`, and tear down. Seven gated steps; a single JSON
|
|
15
|
+
report; exit 0 = all PASS.
|
|
16
|
+
|
|
17
|
+
**Variance from approved Part 2.1 (documented):** the round-trip uses the
|
|
18
|
+
Telegram Bot HTTP API (sendMessage + poll getUpdates) instead of Playwright —
|
|
19
|
+
strictly more reliable, no browser/profile/flake, equally faithful.
|
|
20
|
+
|
|
21
|
+
**Structural guards (pure, unit-tested, fail-fast).**
|
|
22
|
+
- `--target` can never be the canonical (running) agent home or a protected
|
|
23
|
+
agent name (Bob) or an explicitly protected home → exit 11.
|
|
24
|
+
- `--bot-token` refuses a raw token (Telegram/GitHub/Slack/OpenAI shapes) on
|
|
25
|
+
argv → exit 12. Tokens flow only through Secret Drop, retrieved in-memory via
|
|
26
|
+
the hardened `secret-drop-retrieve.mjs` (never argv/env/transcript).
|
|
27
|
+
|
|
28
|
+
**Side-effects review.**
|
|
29
|
+
- **Throwaway-only by construction** — the target guards make it structurally
|
|
30
|
+
impossible to deploy over a real/protected home; Bob + the canonical home are
|
|
31
|
+
hard-blocked.
|
|
32
|
+
- **Secret hygiene** — the bot token never touches argv/env/logs; retrieved to
|
|
33
|
+
memory via Secret Drop. The raw-token-on-argv guard is the enforcement.
|
|
34
|
+
- **No-I/O on guard failure** — pre-flight guards reject BEFORE any dir is
|
|
35
|
+
created or process spawned (verified by the integration test), so a bad
|
|
36
|
+
invocation has zero side-effects.
|
|
37
|
+
- **Teardown is signal-safe** — runs in a `finally`; SIGTERMs the spawned
|
|
38
|
+
server + lifeline and calls `server stop`. The throwaway home is left in a
|
|
39
|
+
dated `~/.instar/test-deploys/` dir for inspection rather than auto-deleted
|
|
40
|
+
(deletion stays operator/--keep-driven; no rm in the harness — SafeFsExecutor
|
|
41
|
+
is the only sanctioned deletion path).
|
|
42
|
+
- **Reuses the shipped deterministic verifier** (`verify.mjs`) for step 6 — no
|
|
43
|
+
duplication of the lease/crash-signature logic.
|
|
44
|
+
|
|
45
|
+
**Test coverage (3-tier).**
|
|
46
|
+
- Unit (`test-as-self-validation.test.ts`, 12): isRawToken across token shapes;
|
|
47
|
+
validateTarget (empty / canonical / protected-name case-insensitive /
|
|
48
|
+
protected-home / clean-accept); validateBotTokenArg (absent / Secret-Drop-ID /
|
|
49
|
+
raw-token-refused).
|
|
50
|
+
- Integration (`test-as-self-guards.test.ts`, 3): runTestAsSelf exits 12 on raw
|
|
51
|
+
token, 11 on protected-name target, 11 on canonical-home target — all
|
|
52
|
+
no-I/O early-exit paths, safe in CI.
|
|
53
|
+
- E2E / live: the full deploy+verify run is the real-machine backstop (the live
|
|
54
|
+
two-machine proof is Track E); not run in CI (needs a real deploy + bot).
|
|
55
|
+
|
|
56
|
+
**Migration parity.** The command + validation are server source (dist) →
|
|
57
|
+
existing agents get `instar test-as-self` on auto-update; no agent-installed-file
|
|
58
|
+
change. The CLAUDE.md template gains an Agent-Awareness entry so new agents know
|
|
59
|
+
about it. **Deferred (tracked, not dropped):** demoting the v1 manual recipe in
|
|
60
|
+
`.claude/skills/test-as-self/SKILL.md` to "fallback" + a
|
|
61
|
+
`PostUpdateMigrator.migrateTestAsSelfSkill()` to patch the on-disk SKILL for
|
|
62
|
+
existing agents — the v1 SKILL still works as-is (runbook), so this is polish,
|
|
63
|
+
filed as a follow-up.
|
|
64
|
+
|
|
65
|
+
**Rollback.** Revert the PR. The command disappears; the v1 SKILL runbook
|
|
66
|
+
remains. No data change, no migration to reverse.
|