instar 1.3.41 → 1.3.43

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,73 @@
1
+ # Side-Effects Review — mentor migrations (retire dead config + outbox) (PR 3c-3)
2
+
3
+ **Spec:** `docs/specs/MENTOR-LIVE-READINESS-SPEC.md` §Migration parity (the
4
+ "dailySpendCapUsd silent removal is a quiet config break" round-2 adversarial finding +
5
+ the legacy file-outbox cleanup). PR 3 of the staged build, part c-3.
6
+ **Change:** Two new `PostUpdateMigrator` methods that retire the artifacts of the
7
+ file-based mentor delivery design Justin's substrate correction replaced. Both
8
+ **idempotent** via the `_instar_migrations` marker. Registered in the main migration
9
+ list right after `migrateLegacyMaxSessions`.
10
+ **Files:** `src/core/PostUpdateMigrator.ts` (+2 methods + 2 registrations),
11
+ `tests/unit/PostUpdateMigrator-mentor-retire.test.ts` (new, 7).
12
+
13
+ ## What changed
14
+
15
+ 1. **`migrateRetireDeadMentorConfig`** — removes `mentor.dailySpendCapUsd`:
16
+ - field absent → silent skip (mark set so we don't re-check).
17
+ - field present at the default `0.5` → silent delete (operator never changed it).
18
+ - field present at a NON-default value → delete + LOUD `result.upgraded` entry with
19
+ `REVIEW:` prefix explaining the field was decorative (never enforced — Echo runs on
20
+ a Claude subscription) and the future replacement is `mentor.stageBTokenCeiling`.
21
+ This is exactly the "non-silent removal" the spec's round-2 adversarial finding
22
+ required: don't repeat the silent-dead-config bug at migration time.
23
+ 2. **`migrateRetireMentorOutbox`** — removes `{stateDir}/mentor-outbox/` via
24
+ `SafeFsExecutor.safeRmSync`:
25
+ - present → recursive remove + record file-count in `result.upgraded`.
26
+ - absent → mark + skip (so subsequent runs are no-ops).
27
+ - failure → log to `result.errors` + do NOT mark (retry on next run).
28
+
29
+ ## The seven questions
30
+
31
+ 1. **Over-block.** N/A — both are removal-only migrations; nothing is over-blocked.
32
+ 2. **Under-block.** Both are idempotent via the `_instar_migrations` marker; corrupt or
33
+ missing config.json early-exits with a `result.errors`/`result.skipped` entry rather
34
+ than crashing the migration pass.
35
+ 3. **Level-of-abstraction fit.** Each method does ONE thing, modeled exactly on
36
+ `migrateLegacyMaxSessions` (the established pattern for value-patching migrations) +
37
+ uses `SafeFsExecutor.safeRmSync` for the destructive filesystem op (the project's
38
+ established funnel for that). No new infrastructure.
39
+ 4. **Signal vs authority.** N/A.
40
+ 5. **Interactions.** Both run during the normal `PostUpdateMigrator.migrate()` pass
41
+ (between `migrateLegacyMaxSessions` and `migratePrPipelineArtifacts`). They touch
42
+ `config.json` and `{stateDir}/mentor-outbox/` only — both project-owned paths. The
43
+ `_instar_migrations` marker pattern is unchanged. Existing tests for other
44
+ migrations are unaffected.
45
+ 6. **External surfaces.** None new. Both surface their actions via the standard
46
+ `result.upgraded` / `result.skipped` / `result.errors` arrays the migrator already
47
+ uses (visible in the post-update output the operator sees).
48
+ 7. **Rollback cost.** Trivial — revert removes the two methods + their registration.
49
+ The retired data is forward-only; restoring an old `dailySpendCapUsd` or an old
50
+ `mentor-outbox/` directory by hand is possible if absolutely needed (but unlikely —
51
+ neither was actually working).
52
+
53
+ ## Testing
54
+
55
+ 7 new unit tests, all green:
56
+ - **dailySpendCapUsd retirement**: field-absent silent skip + marker; default `0.5`
57
+ silent delete + non-LOUD upgraded entry; **non-default value LOUD `REVIEW:` upgraded
58
+ entry** with the "decorative / never enforced / subscription / token-ceiling
59
+ replacement" explanation (proves the "non-silent removal" invariant); IDEMPOTENT
60
+ (second run is no-op skip).
61
+ - **outbox retirement**: present-directory removed + file-count recorded; IDEMPOTENT
62
+ marker means subsequent runs skip; directory-absent handled gracefully (no error,
63
+ marker set).
64
+
65
+ `tsc --noEmit` clean. Combined mentor-stack tests across the staged build (PRs 1, 2a,
66
+ 2b, 3a, 3b, 3c-1, 3c-2, 3c-3) = **57 tests, all green**.
67
+
68
+ ## Migration parity
69
+
70
+ These ARE the migrations from the spec's §Migration parity. The new MentorConfig fields
71
+ (`botToken`, `menteeBotId`, `menteeChatId`, `menteeTopicId`) added in PR 3c-1 are
72
+ optional + default undefined, so no additive migration is needed for them — they appear
73
+ only when the operator (or PR 3c-4's `/mentor/bot-setup` flow) sets them.
@@ -0,0 +1,34 @@
1
+ # Side-Effects Review — SourceTreeGuard read-tier bypass (extend to readSync)
2
+
3
+ **Driver:** dogfood follow-up to PR #450 (source-tree-guard-data-pull). The narrow `sourceTreeReadOk` opt was added to `execSync` + `spawn` to let `git fetch` work on the agent's own instar checkout. After deploying v1.3.38 to Echo, the next layer of the same gap surfaced: `readSync` also runs source-tree-checks ("defense-in-depth"), and the canonical-ref scan path uses readonly verbs (`rev-parse`, `ls-tree`, `show`, `log`, `merge-base`, `remote -v`) that all go through `readSync` → all blocked on the source tree.
4
+
5
+ ## What changed
6
+
7
+ - **`src/core/SafeGitExecutor.ts`** —
8
+ - `readSync` now honors `opts.sourceTreeReadOk` the same way `execSync` / `spawn` do: when the opt is true AND the verb is in `SOURCE_TREE_READ_TIER_VERBS`, skip `runSourceTreeChecks`; audit the bypass with the `sourceTreeReadOk-bypass` reason.
9
+ - `SOURCE_TREE_READ_TIER_VERBS` extended from `['fetch']` to `['fetch', 'rev-parse', 'ls-tree', 'show', 'log', 'cat-file', 'merge-base', 'remote']`. Still a small closed enumeration; still requires a spec edit to grow. Every verb is read-tier: none mutate the working tree or committed refs.
10
+ - **`src/monitoring/releaseReadinessWiring.ts`** — every `SafeGitExecutor.run` callsite passes `sourceTreeReadOk: true`: `resolveCanonicalRemote` (`remote -v`), `fetchCanonical` (`rev-parse FETCH_HEAD` — fetch already had it), `oldestUnreleasedCommit` (`log`), `isAncestor` (`merge-base --is-ancestor`).
11
+ - **`src/core/featureRolloutScan.ts`** — every `SafeGitExecutor.run` callsite in the canonical scan path passes `sourceTreeReadOk: true`: `rev-parse FETCH_HEAD`, `ls-tree` (spec + trace enumeration), `show` (spec + trace blob reads). `fetch` already had it.
12
+
13
+ ## Side-effects analysis
14
+
15
+ **Why this is safe.** Every verb in the (still-small, still-closed) `SOURCE_TREE_READ_TIER_VERBS` set is read-tier: it does NOT modify the working tree, any committed ref, or any source file. `fetch` writes only to `FETCH_HEAD` + objects (transient). `rev-parse`, `ls-tree`, `show`, `log`, `cat-file`, `merge-base`, `remote -v/-get-url` are pure-read. The bypass is opt-in per-call, audit-logged, and only effective inside the closed verb allowlist.
16
+
17
+ **Why the previous fix wasn't enough.** PR-450 only added the bypass to `execSync` + `spawn`, on the assumption that readonly verbs went through `readSync` which "didn't run source-tree checks." That was wrong: `readSync` runs them as defense-in-depth (catches repo-local aliases that could rebind a "read-only" verb). Every readonly verb on the agent's source tree was therefore still being blocked. This PR closes the gap at the right layer.
18
+
19
+ **Reach.** Two real-code callsites touched (the two new ones added by the spec: Layer B wiring + Layer C scanner). Every existing `SafeGitExecutor.readSync` caller that does NOT pass `sourceTreeReadOk: true` is byte-identically guarded by the prior behavior.
20
+
21
+ **Rollback.** Reverting this PR re-blocks the canonical-ref readSync calls on source trees. Layer B's fail-loud path keeps signaling on the failure; Layer C's local-scan fallback keeps working. No broken state.
22
+
23
+ ## Testing
24
+
25
+ `tests/unit/SafeGitExecutor-sourceTreeReadOk.test.ts` (7 tests, all green):
26
+ - default: fetch on non-source-tree works
27
+ - default: fetch on source-tree blocked
28
+ - `sourceTreeReadOk: true` + fetch on source-tree passes
29
+ - `sourceTreeReadOk: true` does NOT bypass non-allowlist verbs (e.g. `add`)
30
+ - `SOURCE_TREE_READ_TIER_VERBS` is a closed read-tier set — no destructive write verbs (commit/push/reset/checkout/rebase/merge/clean/rm/branch/tag); size ≤ 10
31
+ - **readSync path also honors `sourceTreeReadOk` — `rev-parse FETCH_HEAD` on a source tree passes**
32
+ - **readSync path WITHOUT `sourceTreeReadOk` — `rev-parse FETCH_HEAD` on a source tree STILL blocked**
33
+
34
+ Lint clean.