instar 1.3.75 → 1.3.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
1
+ # Side-Effects Review — Shared prior-heal retry (NativeModuleHealer)
2
+
3
+ **Version / slug:** `heal-shared-rebuild-retry`
4
+ **Date:** `2026-05-29`
5
+ **Author:** Echo (instar developer agent)
6
+ **Spec:** `docs/specs/token-ledger-native-heal.md` (2026-05-29 amendment — in-scope, finishes AC#7)
7
+ **Second-pass reviewer:** required (touches the fleet-wide native-module heal authority)
8
+
9
+ ## Summary of the change
10
+
11
+ `NativeModuleHealer`'s once-per-process heal guard (`healAttempted`) was too coarse: after the FIRST sqlite subsystem to open heals successfully (rebuilding the better-sqlite3 binding on disk), any LATER subsystem that hits `NODE_MODULE_VERSION` is short-circuited with `throw "(heal previously attempted)"` — even though the binding is already fixed and a cheap re-require would succeed. Observed live on the Codey agent (Telegram topic 13435): `/memory/search` (SemanticMemory) returned 200 while `/tokens/summary` (TokenLedger) returned 503, binding on disk already ABI-correct, heal log empty.
12
+
13
+ The fix branches the `healAttempted` block on the prior outcome: prior heal **succeeded** → `clearBetterSqlite3Cache()` + retry the open once (no second rebuild); prior heal **failed** → throw with the existing failure context (unchanged). Applied identically to both `openWithHeal` (async) and `openWithHealSync` (sync).
14
+
15
+ Files touched:
16
+ - `src/memory/NativeModuleHealer.ts` — the two `if (this.healAttempted)` blocks in `openWithHeal` and `openWithHealSync`. Additive branch; no other surface changes.
17
+ - `tests/unit/NativeModuleHealer.test.ts` — adds a `shared prior heal — multi-subsystem recovery` describe block (4 tests: sync prior-success retry, async prior-success retry, prior-failure still throws, persistent post-retry failure surfaces).
18
+
19
+ Decision point interacted with: exactly one — `NativeModuleHealer` (the authority over native-module rebuilds). The change does NOT alter who rebuilds or when a rebuild runs (still at most once per process); it only stops the guard from spuriously blocking a *cheap re-open* after a *successful* rebuild.
20
+
21
+ ## Decision-point inventory
22
+
23
+ - `NativeModuleHealer.openWithHeal` / `openWithHealSync` (deterministic authority over native-module rebuild) — **modify (narrow)** — the `healAttempted` branch now distinguishes prior-success (retry) from prior-failure (throw). The rebuild gate itself is untouched.
24
+
25
+ ---
26
+
27
+ ## 1. Over-block
28
+
29
+ **What legitimate inputs does this change reject that it shouldn't?**
30
+
31
+ It removes an over-block. Before: a legitimate later open (binding already fixed) was rejected with "(heal previously attempted)". After: that legitimate open succeeds via cache-clear + retry. The prior-failure path is unchanged, so nothing newly legitimate is rejected. No allow/deny surface is added.
32
+
33
+ ---
34
+
35
+ ## 2. Under-block
36
+
37
+ **What failure modes does this still miss?**
38
+
39
+ - **Genuinely-broken binding after a successful prior heal.** If `lastResult.success` is true but THIS subsystem's open still fails (e.g. a different native dep, or a partial rebuild), the retry runs once and the real error surfaces directly (test A4). It is not retried indefinitely and is not swallowed.
40
+ - **First-heal-fails-then-binding-fixed-externally.** If the first heal FAILED, later callers still throw without retry (by design — `success === false`). If an operator manually rebuilds afterward, a process restart is still required. This matches the documented "rebuild fails → visible in heal log + restart" behavior of the original spec; out of scope to auto-detect external repair mid-process.
41
+ - **Non-better-sqlite3 native modules.** Unchanged — the healer only knows better-sqlite3.
42
+
43
+ ---
44
+
45
+ ## 3. Level-of-abstraction fit
46
+
47
+ **Is this at the right layer?**
48
+
49
+ Yes. The bug is in the healer's process-global guard, so the fix belongs in the healer — fixing it once benefits every sqlite consumer (TokenLedger, TopicMemory, MemoryIndex, SemanticMemory) rather than patching each call site. No call site changes.
50
+
51
+ ---
52
+
53
+ ## 4. Signal vs authority compliance
54
+
55
+ The `NativeModuleHealer` remains the sole authority over native-module rebuilds. This change does not move authority and does not add an LLM-backed decision — it is a deterministic refinement of an existing deterministic guard. The expensive action (rebuild) is still gated to once-per-process; only the cheap action (re-require + open) is now permitted after a confirmed-good rebuild. Compliant with `docs/signal-vs-authority.md`.
56
+
57
+ ---
58
+
59
+ ## 5. Interactions
60
+
61
+ - **SemanticMemory / TopicMemory / MemoryIndex** (existing async callers): unaffected on the happy path and on the first-heal path. They gain the same prior-success retry behavior if they happen to open after another subsystem healed — strictly an improvement.
62
+ - **ServerSupervisor.preflightSelfHeal** (supervisor-spawn rebuild): independent path; not touched. If preflight already rebuilt the binding, in-process opens never hit the mismatch at all.
63
+ - **Remediator path (`invokeFromRemediator`, F-8/W-1)**: untouched. It sets `healAttempted`/`lastResult` via its own surface; a subsequent legacy `openWithHeal` after a successful remediator heal now correctly retries instead of throwing — consistent with the fix's intent.
64
+ - **Heal log / `NativeHealDegradationBridge`**: no new event types emitted by the retry path; the prior successful heal event is already logged. Bridge behavior unchanged.
65
+
66
+ ---
67
+
68
+ ## 6. External surfaces
69
+
70
+ No API contract change. No config. No migration. No agent-installed-file change (ships with the npm package, not via `init`/`PostUpdateMigrator`). The observable external effect is positive: on a node-upgraded agent, `/tokens/*` (and any other sqlite-backed endpoint that previously 503'd after another subsystem healed) returns live data once the agent runs the new code.
71
+
72
+ ---
73
+
74
+ ## 7. Rollback cost
75
+
76
+ Revert the two `if (this.healAttempted)` blocks to their unconditional-throw form (one file). No persistent state, no migration, no contract. The reverted-to state is the pre-amendment behavior (later sqlite subsystems dark after the first heals) — exactly the bug being fixed, never a worse state. ~5 minutes detection-to-revert.
77
+
78
+ ---
79
+
80
+ ## Second-pass review
81
+
82
+ **Reviewer concern surfaced:** Could the prior-success retry mask a real ABI problem by retrying into a still-bad binding? No — the retry runs exactly once and any persistent failure is thrown directly (test A4 pins this). The guard against repeated *rebuilds* is preserved, so the pathological-loop protection the original spec relied on is intact.
83
+
84
+ **Concurrence:** The change is mechanically narrow (one branch in two sibling methods), the test matrix covers both sides of the new decision boundary (prior-success → retry; prior-failure → throw) plus the persistent-failure escape, and the rollback is a single-file revert to a strictly-not-worse state. Concurred.
@@ -0,0 +1,61 @@
1
+ # Side-effects review — dedicated mentor topic (mentor.mentorTopicId)
2
+
3
+ **Scope**: Stop the mentor cycle's a2a check-ins from interleaving with the
4
+ human↔mentee conversation topic. Add an optional `mentor.mentorTopicId`; route
5
+ the mentor exchange's `telegramTopicId` to `mentorTopicId ?? menteeTopicId`.
6
+
7
+ **Files touched**:
8
+ - `src/scheduler/MentorOnboardingRunner.ts` — add `mentorTopicId?: number` to
9
+ `MentorConfig`; add pure exported `resolveMentorDeliveryTopic(cfg)` returning
10
+ `cfg.mentorTopicId ?? cfg.menteeTopicId`.
11
+ - `src/server/AgentServer.ts` — `deliverToMentee` now passes
12
+ `telegramTopicId: resolveMentorDeliveryTopic(cfg)` (was `cfg.menteeTopicId`);
13
+ import the helper.
14
+ - `tests/unit/MentorOnboardingRunner.test.ts` — 4 cases for the resolver.
15
+ - `docs/specs/MENTOR-LIVE-READINESS-SPEC.md` — 2026-05-28 amendment.
16
+ - `upgrades/NEXT.md` — release note (patch bump).
17
+
18
+ **Under-block (does it ever route to the WRONG topic / break delivery?)**:
19
+ - Default is unchanged: with `mentorTopicId` unset, `resolveMentorDeliveryTopic`
20
+ returns `menteeTopicId` — byte-identical to prior behavior. No deployed agent
21
+ changes unless it explicitly sets `mentorTopicId`.
22
+ - Uses nullish coalescing (`??`), not `||`, so `mentorTopicId: 0` (the "General"
23
+ forum topic) is honored rather than falling through (the classic zero-is-falsy
24
+ trap; covered by a test).
25
+ - `getConfig()` already spreads the whole `mentor` block, so the new field flows
26
+ through with no plumbing change; no config-schema validator gates the mentor
27
+ block (verified by grep), so the additive field is accepted.
28
+
29
+ **Over-block (does it move something that should stay in the human topic?)**:
30
+ - It moves ONLY the mentor a2a exchange (Echo→mentee prompt + the mentee's
31
+ session/reply binding, which both derive from this one `telegramTopicId`).
32
+ The human's own conversation in `menteeTopicId` is untouched. That's the
33
+ intent: separate automated mentor chatter from human conversation.
34
+
35
+ **Level-of-abstraction fit**: The topic-resolution rule lives as a pure,
36
+ unit-tested helper in `MentorOnboardingRunner` (which owns `MentorConfig`),
37
+ not as an inline `??` buried in the server closure. `AgentServer` just calls it.
38
+
39
+ **Signal vs authority**: No authority change. This is pure routing — which
40
+ topic the existing delivery targets. Delivery success/refusal, the anti-loop
41
+ gate, and the budget gate are all unchanged.
42
+
43
+ **Interactions**:
44
+ - The mentee's REPLY leg uses its own `menteeCfg.replyTopicId` (separate
45
+ config, separate concern) — not changed here. This amendment addresses the
46
+ PROMPT-side pollution (Echo's mentor bot posting role=mentor markers into the
47
+ human topic), which is the observed P3 symptom.
48
+ - Same-machine HTTP a2a remains primary; the Telegram fallback (when HTTP
49
+ fails) now posts to the dedicated topic too, since both read this one id.
50
+
51
+ **Migration parity**: Additive OPTIONAL config field with a backward-compatible
52
+ default (falls back to `menteeTopicId`). Unconfigured agents are unaffected, so
53
+ NO `PostUpdateMigrator` entry is required. New + existing agents pick up the
54
+ capability via the normal `instar` package update; it activates only when an
55
+ operator sets `mentor.mentorTopicId`.
56
+
57
+ **Rollback cost**: Single revert (`MentorOnboardingRunner.ts` field + helper,
58
+ `AgentServer.ts` one-line call). No schema/on-disk/API changes.
59
+
60
+ **Spec**: `MENTOR-LIVE-READINESS-SPEC.md` 2026-05-28 amendment (approved by
61
+ Justin, topic 13435 — Codey-dogfooding "P3 - agreed").
@@ -0,0 +1,84 @@
1
+ # Side-Effects Review — Pre-push smoke base resolver and breadth guard
2
+
3
+ **Version / slug:** `prepush-smoke-base-guard`
4
+ **Date:** `2026-05-29`
5
+ **Author:** `instar-codey`
6
+ **Second-pass reviewer:** `instar-codey second-pass checklist`
7
+
8
+ ## Summary of the change
9
+
10
+ This change moves the pre-push smoke selection logic into `scripts/pre-push-smoke.mjs` and `scripts/lib/pre-push-scope.mjs`, updates `.husky/pre-push` to use that runner, and adds focused unit tests in `tests/unit/scripts/pre-push-scope.test.ts`. The smoke runner now resolves a branch-appropriate base, logs that base plus changed-file count, lists the affected Vitest set, and skips local smoke when the selected set is too broad. CI remains the authoritative merge gate.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `scripts/lib/pre-push-scope.mjs#resolvePrePushBase` — add — chooses which git base local smoke uses for changed-test selection.
15
+ - `scripts/lib/pre-push-scope.mjs#evaluateSmokeBreadth` — add — decides whether local smoke is small enough to run locally.
16
+ - `scripts/pre-push-smoke.mjs` — add — applies the local-only smoke skip decision before invoking Vitest run.
17
+ - `.husky/pre-push` — modify — delegates smoke execution to the runner after best-effort remote fetches.
18
+
19
+ ---
20
+
21
+ ## 1. Over-block
22
+
23
+ The broad-set guard can skip local smoke for a legitimate branch that changes many files or maps to many tests. That does not block the push or merge; it only declines to run a local smoke subset. The hook prints that CI is the authority, and the PR test matrix still runs before merge.
24
+
25
+ The resolver can choose a branch upstream or push remote whose `main` is missing or stale. It falls back through canonical remotes, and the hook performs best-effort fetches for those remotes before running the resolver.
26
+
27
+ ## 2. Under-block
28
+
29
+ This change does not guarantee a local smoke run catches every failure. A branch under the caps may still have failures outside Vitest's changed-test selection. That is an accepted limitation of local smoke; CI remains the full authority.
30
+
31
+ The runner also skips local smoke if changed-file calculation or affected-test listing fails due to local git/Vitest state. This avoids pathological local blocking but leaves the real failure detection to CI.
32
+
33
+ ## 3. Level-of-abstraction fit
34
+
35
+ The resolver is a local git-scope helper. The broad-set guard is a deterministic local ergonomics check, not a product or conversational authority. It operates at the pre-push developer-tooling layer where the question is only whether the optional local smoke run is small enough to be useful.
36
+
37
+ The implementation uses existing git configuration and Vitest changed-test listing rather than adding a new cross-system state source.
38
+
39
+ ## 4. Signal vs authority compliance
40
+
41
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
42
+
43
+ - [ ] No — this change produces a signal consumed by an existing smart gate.
44
+ - [ ] No — this change has no block/allow surface.
45
+ - [ ] Yes — but the logic is a smart gate with full conversational context (LLM-backed with recent history or equivalent).
46
+ - [x] Not applicable to conversational/product judgment — this is a local developer-tooling guard that fails open to CI.
47
+
48
+ This change does hold local skip authority over an optional smoke run, but it does not block user messages, external operations, information flow, or product behavior. It is a fail-open performance guard: when the local set is too broad, it avoids running the local subset and explicitly defers to CI.
49
+
50
+ ## 5. Interactions
51
+
52
+ - **Shadowing:** The broad-set guard runs before `vitest run`. It can shadow local smoke execution, but it cannot shadow CI because CI is separate and unchanged.
53
+ - **Double-fire:** `.husky/pre-push` still wraps `npm run test:smoke` in the existing retry loop. When the runner skips broad smoke, it exits successfully and the retry loop does not repeat.
54
+ - **Races:** The runner reads git refs after the hook performs best-effort fetches. Concurrent branch updates could change remote refs during a push, but that risk already exists for local changed-test selection.
55
+ - **Feedback loops:** No persistent state is written. The hook does not feed its skip decision back into future runs.
56
+
57
+ ## 6. External surfaces
58
+
59
+ This affects developers and agents pushing branches from this repository. It changes local terminal output and can reduce local pre-push runtime for broad affected sets. It does not change CI, GitHub PR checks, Telegram messaging, dashboard behavior, persisted ledgers, or external service APIs.
60
+
61
+ ## 7. Rollback cost
62
+
63
+ Rollback is a pure code revert of `.husky/pre-push`, `package.json`, the new smoke runner/helper files, and tests. No migration, persistent state repair, or user notification is required. The only user-visible regression during rollback would be returning to the older hard-coded local smoke base behavior.
64
+
65
+ ## Conclusion
66
+
67
+ The review found the main risk is local under-testing when a broad affected set is skipped. That is acceptable because local smoke is not the merge authority, and the skip message makes the authority boundary explicit. The change is clear to ship with focused tests and the instar-dev gate.
68
+
69
+ ---
70
+
71
+ ## Second-pass review (if required)
72
+
73
+ **Reviewer:** instar-codey second-pass checklist
74
+ **Independent read of the artifact:** concur
75
+
76
+ The second pass agrees that the guard is local-only, fail-open, and does not introduce a brittle product/message blocking authority. The main risk is documented as reliance on CI for broad changes.
77
+
78
+ ---
79
+
80
+ ## Evidence pointers
81
+
82
+ - `tests/unit/scripts/pre-push-scope.test.ts`
83
+ - `scripts/pre-push-smoke.mjs`
84
+ - `scripts/lib/pre-push-scope.mjs`
@@ -0,0 +1,34 @@
1
+ # TokenLedger scanAllAsync deterministic yield test
2
+
3
+ ## Scope
4
+
5
+ This change touches `TokenLedger` and its unit test for `scanAllAsync`.
6
+
7
+ ## Behavioral Change
8
+
9
+ Production behavior is unchanged by default. `scanAllAsync` still yields with
10
+ `setImmediate` when callers do not provide an override.
11
+
12
+ The new behavior is an optional constructor seam, `asyncYieldFn`, that lets
13
+ tests or specialized callers provide the async yield primitive used by
14
+ `scanAllAsync`.
15
+
16
+ ## Side Effects Review
17
+
18
+ 1. User-facing behavior: none expected. The server's token ledger scanner keeps
19
+ the same default event-loop yielding behavior.
20
+ 2. Data persistence: none. No database schema, ingestion format, offset, or
21
+ summary-query behavior changes.
22
+ 3. Runtime risk: low. The new option is opt-in and defaults to the previous
23
+ implementation.
24
+ 4. Signal versus authority: no new blocking or routing authority is introduced.
25
+ The seam only changes how an async pause is performed.
26
+ 5. Test quality: improved. The unit test now observes the scanner's yield call
27
+ directly instead of depending on wall-clock timer scheduling.
28
+ 6. Rollback: remove the option and restore the prior test. No migration or state
29
+ cleanup is required.
30
+
31
+ ## Verification
32
+
33
+ - Affected test passed 10 consecutive local runs.
34
+ - Full `tests/unit/token-ledger.test.ts` passed locally.