instar 1.3.53 → 1.3.55
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/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/dist/server/AgentServer.d.ts +11 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +239 -211
- package/dist/server/AgentServer.js.map +1 -1
- package/package.json +1 -1
- package/scripts/post-publish-smoke.mjs +80 -0
- package/src/data/builtin-manifest.json +32 -32
- package/src/scaffold/templates.ts +14 -0
- package/upgrades/1.3.53.md +20 -28
- package/upgrades/1.3.54.md +43 -0
- package/upgrades/1.3.55.md +38 -0
- package/upgrades/side-effects/init-join-launchagent-handoff.md +62 -0
- package/upgrades/side-effects/mentee-reply-leg.md +61 -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
package/upgrades/1.3.53.md
CHANGED
|
@@ -4,43 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
## What Changed
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`npm run lint`) bans credentialed-URL logging going forward.
|
|
15
|
-
|
|
16
|
-
Also: the outbound-message convergence-check gate now trusts the agent's own tunnel
|
|
17
|
-
domain (read live from tunnel state) + the common tunnel domains, so an agent can
|
|
18
|
-
send links to its own private views without the gate flagging them as fabricated.
|
|
7
|
+
**`instar join` now installs auto-start for the joined home (mesh bootstrap fix).**
|
|
8
|
+
Previously a joined mesh agent had no LaunchAgent/systemd unit, so it never
|
|
9
|
+
auto-started — and a stale auto-start entry from a prior `instar init` of the same
|
|
10
|
+
name (at a different directory) would keep respawning a server against the WRONG
|
|
11
|
+
home, fighting the joined one for the port. join now installs auto-start for its
|
|
12
|
+
own home; because the unit is keyed on the agent name, this cleanly replaces any
|
|
13
|
+
stale same-name unit. One unit, pointing at the right home.
|
|
19
14
|
|
|
20
15
|
## What to Tell Your User
|
|
21
16
|
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
agent
|
|
26
|
-
|
|
27
|
-
pages without my safety gate mistakenly treating my own tunnel address as suspicious.
|
|
17
|
+
- Joining a second machine to your agent's mesh now "just works" for staying alive
|
|
18
|
+
across reboots — the joined machine sets up its own background auto-start, and it
|
|
19
|
+
won't collide with leftovers from an earlier setup attempt of the same agent. You
|
|
20
|
+
no longer have to hand-start the joined agent or manually clean up a stale startup
|
|
21
|
+
entry.
|
|
28
22
|
|
|
29
23
|
## Summary of New Capabilities
|
|
30
24
|
|
|
31
25
|
| Capability | How to Use |
|
|
32
26
|
|-----------|-----------|
|
|
33
|
-
|
|
|
34
|
-
| `lint-no-direct-url-log` | Automatic in `npm run lint` / CI — fails the build if a credentialed URL is logged without redaction. |
|
|
35
|
-
| Convergence-check own-tunnel trust | Automatic — the message gate no longer flags the agent's own tunnel URLs as unfamiliar. |
|
|
27
|
+
| join installs auto-start | Automatic — `instar join` now registers the joined home's background auto-start (LaunchAgent on macOS, systemd on Linux), replacing any stale same-name unit. |
|
|
36
28
|
|
|
37
29
|
## Evidence
|
|
38
30
|
|
|
39
|
-
**
|
|
40
|
-
`tests/unit/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`upgrades/side-effects/
|
|
31
|
+
**Small, contained join-flow addition; no change to init.** Unit test
|
|
32
|
+
`tests/unit/init-join-launchd-handoff.test.ts` (2 cases, darwin-gated, sandboxed
|
|
33
|
+
$HOME) proves the Label-keyed replace: two installs for the same name + different
|
|
34
|
+
dirs leave ONE plist pointing at the second (joined) dir; standby install writes
|
|
35
|
+
server-start (not lifeline) args. `tsc --noEmit` clean. The autostart install is
|
|
36
|
+
try/catch-wrapped so a failure never fails the join. Side-effects review:
|
|
37
|
+
`upgrades/side-effects/init-join-launchagent-handoff.md`. Spec: Track C of
|
|
46
38
|
`docs/specs/MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS-SPEC.md`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Instar Upgrade Guide — NEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Mesh registry conflicts now auto-merge deterministically (multi-machine
|
|
8
|
+
robustness).** When two machines concurrently update the shared mesh state — e.g.
|
|
9
|
+
one bumps the awake-lease while another joins — their `machines/registry.json`
|
|
10
|
+
commits can collide on rebase. GitSync's conflict resolver previously had no case
|
|
11
|
+
for the registry, so such a conflict fell to LLM/manual and the mesh could stay
|
|
12
|
+
split (one machine seeing a different roster than the other). New `mergeRegistry`
|
|
13
|
+
merges them losslessly: union the machines (fresher `lastSeen` wins per machine,
|
|
14
|
+
revocations sticky), keep the higher-epoch lease. Both machines compute the
|
|
15
|
+
identical result, so the mesh converges on its own.
|
|
16
|
+
|
|
17
|
+
## What to Tell Your User
|
|
18
|
+
|
|
19
|
+
- Running your agent across two machines is now more robust: if both machines
|
|
20
|
+
record a change to the shared roster at the same moment, they'll automatically
|
|
21
|
+
reconcile to the same view instead of disagreeing about who's awake or which
|
|
22
|
+
machines are in the mesh. Nothing for you to do — it just self-heals.
|
|
23
|
+
|
|
24
|
+
## Summary of New Capabilities
|
|
25
|
+
|
|
26
|
+
| Capability | How to Use |
|
|
27
|
+
|-----------|-----------|
|
|
28
|
+
| `mergeRegistry(ours, theirs)` | Internal — deterministic semantic merge of two mesh registries (union machines, higher-epoch lease). |
|
|
29
|
+
| GitSync registry conflict auto-resolve | Automatic — a `machines/registry.json` rebase conflict now merges deterministically instead of escalating to LLM/manual. |
|
|
30
|
+
|
|
31
|
+
## Evidence
|
|
32
|
+
|
|
33
|
+
**Small, precise fix — NOT the spec's drafted sync() rewrite (sync() is already
|
|
34
|
+
mature; the real gap was a missing conflict-resolution case).** Unit test
|
|
35
|
+
`tests/unit/mergeRegistry.test.ts` (6 cases) covers the exact 2026-05-27
|
|
36
|
+
divergence (concurrent lease-bump + join → both machines + higher epoch survive),
|
|
37
|
+
order-independence, same-epoch tiebreak, sticky revocation, missing lease, and
|
|
38
|
+
syncSequence tiebreak. `tsc --noEmit` + destructive-lint clean. The fix slots into
|
|
39
|
+
GitSync's existing Tier-0 `tryAutoResolve` (same wiring as relationships/jobs/
|
|
40
|
+
evolution) and is try/catch fail-safe (falls back to the prior LLM/manual tier on
|
|
41
|
+
any error). Side-effects review:
|
|
42
|
+
`upgrades/side-effects/mesh-git-substrate-auto-reconcile.md`. Spec: Track D of
|
|
43
|
+
`docs/specs/MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS-SPEC.md`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Instar Upgrade Guide — NEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Post-publish smoke gate (publish-pipeline regression insurance).** A new step
|
|
8
|
+
after `npm publish` clean-installs the just-published version into a throwaway
|
|
9
|
+
prefix and runs `instar --version`, asserting the tarball actually installs and
|
|
10
|
+
reports the right version. If the publish pipeline ever ships a tarball missing
|
|
11
|
+
its compiled output, this catches it within minutes of release instead of when a
|
|
12
|
+
fresh install fails in the wild. (Track A was re-scoped from a "fix" to this gate:
|
|
13
|
+
the original "empty dist" scare was self-inflicted, not a real publish bug.)
|
|
14
|
+
|
|
15
|
+
## What to Tell Your User
|
|
16
|
+
|
|
17
|
+
- Internal release-pipeline hardening: after I publish a new version, CI now
|
|
18
|
+
immediately does a clean install of it and checks it runs, so a broken release
|
|
19
|
+
can't slip out unnoticed. Nothing changes for you — it's a safety net on my own
|
|
20
|
+
shipping process.
|
|
21
|
+
|
|
22
|
+
## Summary of New Capabilities
|
|
23
|
+
|
|
24
|
+
| Capability | How to Use |
|
|
25
|
+
|-----------|-----------|
|
|
26
|
+
| Post-publish smoke gate | Automatic in the publish workflow — clean-installs the just-published version + asserts `instar --version`. |
|
|
27
|
+
|
|
28
|
+
## Evidence
|
|
29
|
+
|
|
30
|
+
**Re-scoped to regression insurance (the original premise was a self-inflicted
|
|
31
|
+
artifact, not a bug).** `scripts/post-publish-smoke.mjs` waits for npm
|
|
32
|
+
propagation, clean-installs into a throwaway prefix, asserts `dist/cli.js` exists
|
|
33
|
+
+ `--version` matches. Unit test `tests/unit/post-publish-smoke.test.ts` (4)
|
|
34
|
+
covers the pure version-match logic incl the 1.3.5-vs-1.3.55 substring trap.
|
|
35
|
+
`publish.yml` validates as YAML; the step mirrors the existing publish steps'
|
|
36
|
+
structure + gating. Side-effects review:
|
|
37
|
+
`upgrades/side-effects/publish-completeness-smoke-gate.md`. Spec: Track A of
|
|
38
|
+
`docs/specs/MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS-SPEC.md`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Side-Effects Review — Init→Join LaunchAgent handoff (MM-Bootstrap Track C)
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/MULTI-MACHINE-BOOTSTRAP-ROBUSTNESS-SPEC.md` §Track C (approved PR #465).
|
|
4
|
+
|
|
5
|
+
**Scope.** `src/commands/machine.ts` (joinMesh — add Step 6: install auto-start
|
|
6
|
+
for the joined home), `tests/unit/init-join-launchd-handoff.test.ts` (new).
|
|
7
|
+
|
|
8
|
+
**Problem.** `instar join` never installed an auto-start plist for the joined
|
|
9
|
+
home. So a joined agent had no LaunchAgent/systemd unit (operator had to
|
|
10
|
+
hand-start it), and — worse — a stale `ai.instar.<projectName>` plist left by a
|
|
11
|
+
prior `instar init` of the SAME name at a DIFFERENT home kept respawning a
|
|
12
|
+
server against the wrong directory, fighting the joined one for the port +
|
|
13
|
+
identity (observed live 2026-05-27 during the mesh bring-up).
|
|
14
|
+
|
|
15
|
+
**Fix.** joinMesh now calls `installAutoStart(projectName, joinedProjectDir,
|
|
16
|
+
hasTelegram)` after the gitignore step. Because the auto-start plist Label is
|
|
17
|
+
keyed on `projectName` (`ai.instar.<name>`), installing for the joined dir
|
|
18
|
+
cleanly REPLACES any stale same-name plist — one unit, pointing at the joined
|
|
19
|
+
home, zero orphans.
|
|
20
|
+
|
|
21
|
+
**Design note (supersedes the spec's drafted pointer-file C-2).** The spec
|
|
22
|
+
drafted a pointer-file (`active-home`) + plist-wrapper approach. Implementation
|
|
23
|
+
found that unnecessary: the Label is already keyed on projectName, so a
|
|
24
|
+
re-install naturally replaces. The simpler fix (join calls installAutoStart) is
|
|
25
|
+
less code, no new mechanism, same guarantee. Flagged to Justin via Telegram.
|
|
26
|
+
|
|
27
|
+
**Side-effects review.**
|
|
28
|
+
- **No change to `instar init`'s behavior** — init still installs its plist as
|
|
29
|
+
before. Only join now ALSO installs (for its own home).
|
|
30
|
+
- **hasTelegram=false-derived-from-config for the standby** — a joined standby
|
|
31
|
+
installs a server-start unit (ready to take over), not a lifeline. The shipped
|
|
32
|
+
poll-ownership lease prevents dual-poll if telegram is configured on both ends,
|
|
33
|
+
so even if a future config sets hasTelegram=true the 409 class can't recur.
|
|
34
|
+
- **Idempotent + safe** — installAutoStart overwrites the same Label; re-running
|
|
35
|
+
join is harmless. The call is wrapped in try/catch (@silent-fallback-ok): a
|
|
36
|
+
failed auto-start install does NOT fail the join (join still succeeds; operator
|
|
37
|
+
can hand-start).
|
|
38
|
+
- **No new orphans** — the replace property is the whole point; verified by test.
|
|
39
|
+
|
|
40
|
+
**Test coverage.**
|
|
41
|
+
- Unit `tests/unit/init-join-launchd-handoff.test.ts` (2 cases, darwin-gated,
|
|
42
|
+
sandboxed $HOME so it never touches the real LaunchAgents dir): (1) two installs
|
|
43
|
+
same-name + different-dir → ONE plist pointing at the second (joined) dir, init
|
|
44
|
+
dir gone; (2) standby install (hasTelegram=false) writes server-start args, not
|
|
45
|
+
lifeline.
|
|
46
|
+
- E2E launchd lifecycle is quarantined per spec (INSTAR_E2E_LAUNCHD) — macOS-only,
|
|
47
|
+
not run in CI.
|
|
48
|
+
|
|
49
|
+
**Deferred (own follow-up, noted not silently dropped).** The join config-creation
|
|
50
|
+
sub-gap (joined home lacks config.json because it's gitignored; a config.json.bak
|
|
51
|
+
is committed) is a SEPARATE bootstrap gap surfaced the same night. Out of Track C's
|
|
52
|
+
tight autostart scope; tracked for a follow-up so it isn't lost.
|
|
53
|
+
|
|
54
|
+
**Migration parity.** Server source — existing agents pick up the fix on
|
|
55
|
+
auto-update. New joins get the behavior immediately. Existing JOINED agents (rare,
|
|
56
|
+
since join didn't install autostart before) won't have a plist; the forward fix
|
|
57
|
+
covers all new joins, and `instar server start` self-heals an autostart install at
|
|
58
|
+
boot (server.ts autostart-check), so an existing joined agent gets a plist the next
|
|
59
|
+
time its server starts anyway.
|
|
60
|
+
|
|
61
|
+
**Rollback.** Revert the PR. join stops installing autostart (prior behavior).
|
|
62
|
+
No migration to reverse, no data change.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Side-Effects Review — mentor-cycle reply leg over /a2a/inbox
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/MENTOR-LIVE-READINESS-SPEC.md` §Recipient side (§281:
|
|
4
|
+
"Codey's reply goes back via sendAgentMessage role=mentor-reply"; §250:
|
|
5
|
+
finding-emission-only capability handle). Fourth + final fast-follow closing
|
|
6
|
+
the round-trip. Same approved spec — implementing the reply half with the
|
|
7
|
+
HTTP-transport substitution established in #466 (Telegram blocks bot-to-bot).
|
|
8
|
+
|
|
9
|
+
**Change:** `src/server/AgentServer.ts` only (+ 1 new test file).
|
|
10
|
+
- `installMentorMessageHook` generalized: builds a combined a2a hook on the
|
|
11
|
+
primary adapter covering BOTH `mentor` (mentee side, from `config.mentee`)
|
|
12
|
+
and `mentor-reply` (mentor side, from `config.mentor.menteeBotId`).
|
|
13
|
+
- New `deliverA2aMessage` private helper: unified same-machine `/a2a/inbox`
|
|
14
|
+
(HTTP) + Telegram-fallback transport for both directions.
|
|
15
|
+
- `deliverToMentee` refactored to call `deliverA2aMessage` (removes the inline
|
|
16
|
+
#466 logic, fixes the `\n\n` marker bug).
|
|
17
|
+
- Mentee reply capture: last-non-empty-while-alive (reap-race fix).
|
|
18
|
+
- `tests/e2e/mentor-reply-via-inbox.test.ts` (new, 1).
|
|
19
|
+
|
|
20
|
+
## The seven questions
|
|
21
|
+
|
|
22
|
+
1. **Over-block.** N/A — additive. Non-a2a traffic falls through unchanged;
|
|
23
|
+
the consolidated hook only adds role-handlers.
|
|
24
|
+
2. **Under-block.** Spoof defense unchanged (decideRoute's bot-id allowlist,
|
|
25
|
+
covered by the primitive's own unit tests). The mentor-reply handler stays
|
|
26
|
+
finding-emission-only (persist to jsonl + clear OutstandingPromptTracker —
|
|
27
|
+
no spawn/deliver/schedule), preserving the spec §250 capability-handle
|
|
28
|
+
invariant.
|
|
29
|
+
3. **Level-of-abstraction fit.** Consolidates two previously-separate hook
|
|
30
|
+
installs (mentor-bot adapter + primary adapter) onto one primary-adapter
|
|
31
|
+
hook. `deliverA2aMessage` unifies what was duplicated transport logic.
|
|
32
|
+
Net reduction in surface.
|
|
33
|
+
4. **Signal vs authority.** The hook decides route/drop (spec routing matrix);
|
|
34
|
+
handlers are capture-only / finding-emission-only; `deliverA2aMessage`
|
|
35
|
+
audits every send to the ledger. No new authority.
|
|
36
|
+
5. **Interactions.** Reuses AgentRegistry + AgentTokenManager + the existing
|
|
37
|
+
`/a2a/inbox` route (#466) + `getOrCreateMentorOutstanding` + the a2a ledger.
|
|
38
|
+
`installMentorReceiverHook` (mentor-BOT adapter, Telegram path) is left in
|
|
39
|
+
place for cross-machine; the primary-adapter hook is the same-machine path.
|
|
40
|
+
Both write to the same `mentor-replies.jsonl` (append-only, safe).
|
|
41
|
+
6. **External surfaces.** No new routes, no new config keys. `mentor-replies.jsonl`
|
|
42
|
+
rows gain a `transport` field (additive).
|
|
43
|
+
7. **Rollback cost.** Trivial — revert restores the per-side installs +
|
|
44
|
+
inline deliverToMentee logic. No migration, no config change.
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
|
|
48
|
+
1 new E2E (`mentor-reply-via-inbox`): a `mentor-reply` marker POSTed to
|
|
49
|
+
`/a2a/inbox` on a mentor-configured server routes through the consolidated
|
|
50
|
+
hook + persists to `mentor-replies.jsonl` with the right corr/from/message/
|
|
51
|
+
transport. All 22 prior mentor/mentee/inbox tests still green (no regression).
|
|
52
|
+
`tsc --noEmit` clean.
|
|
53
|
+
|
|
54
|
+
Live round-trip verification (test-as-self) is performed post-release against
|
|
55
|
+
the live Echo↔Codey pair — the autonomous run's completion criterion.
|
|
56
|
+
|
|
57
|
+
## Migration parity
|
|
58
|
+
|
|
59
|
+
No new config keys; the consolidated hook reads the existing `config.mentee`
|
|
60
|
+
+ `config.mentor` blocks. Purely additive — agents with neither configured
|
|
61
|
+
get no hook (same as before). No PostUpdateMigrator change required.
|
|
@@ -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.
|