instar 1.1.4 → 1.2.2

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,62 @@
1
+ # Upgrade Guide — v1.2.1 (bareword runtime prompt)
2
+
3
+ <!-- bump: patch -->
4
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
5
+
6
+ ## What Changed
7
+
8
+ **Bareword `npx instar` asks which runtime.**
9
+
10
+ A fresh user typing `npx instar` (no subcommand) is now asked which AI
11
+ runtime they want — Claude Code or Codex CLI — before the wizard
12
+ launches. Previously the bareword silently defaulted to Claude Code and
13
+ exited if Claude wasn't installed, leaving Codex-only users with no
14
+ discoverable path from the headline command.
15
+
16
+ The prompt fires only when stdin is a TTY (interactive terminal), so
17
+ piped invocations and CI runs are unaffected — they keep defaulting to
18
+ Claude Code as before.
19
+
20
+ When exactly one runtime is installed, the prompt skips itself and uses
21
+ the installed one. When both are installed (or neither is), the prompt
22
+ asks; the existing `checkFrameworkPrerequisite` then surfaces a clear
23
+ install URL if the user picked one that isn't on the machine.
24
+
25
+ The explicit subcommand forms continue to work exactly as in v1.1.0:
26
+ `instar init --framework codex-cli --standalone` and
27
+ `instar setup --framework codex-cli` both bypass the prompt and use the
28
+ flag value directly.
29
+
30
+ Spec: `specs/dev-infrastructure/bareword-framework-prompt.md`. ELI16:
31
+ `specs/dev-infrastructure/bareword-framework-prompt.eli16.md`.
32
+ Side-effects review:
33
+ `upgrades/side-effects/feat-bareword-framework-prompt.md`.
34
+
35
+ ## What to Tell Your User
36
+
37
+ Typing the plain instar command now asks which AI runtime you want —
38
+ Claude Code or Codex CLI. Pick whichever you have installed. The
39
+ explicit subcommand forms continue to work exactly as before, so any
40
+ scripts you have written keep working without changes.
41
+
42
+ ## Summary of New Capabilities
43
+
44
+ Closes the final UX gap in the install/wizard portability arc shipped
45
+ in v1.1.0-v1.1.4. The headline command now reaches Codex-only users
46
+ the same way it reaches Claude-only users.
47
+
48
+ ## Evidence
49
+
50
+ Reproduction prior: on a Codex-only host, running `npx instar` exits
51
+ with the "Claude Code is required" message because the bareword defaults
52
+ to Claude Code without asking. After: on a TTY, the bareword presents a
53
+ "Which AI runtime should this agent use?" prompt with the two options
54
+ and an installed/not-installed indicator. Answering "2" routes through
55
+ the Codex setup path; answering "1" or pressing enter routes through
56
+ the Claude path. In a piped or CI environment, the prompt is skipped
57
+ entirely and the default remains Claude Code.
58
+
59
+ Unit verification: `tests/unit/bareword-framework-prompt.test.ts` pins
60
+ the four cases of the pure decision function — both installed prompts,
61
+ only-Claude returns Claude, only-Codex returns Codex, neither installed
62
+ prompts so the user gets to pick which one to install.
@@ -0,0 +1,66 @@
1
+ # Side-effects review — Bareword framework prompt
2
+
3
+ Per L6. Seven dimensions.
4
+
5
+ ## 1. Over-block / under-block
6
+
7
+ Before: UNDER — bareword `npx instar` had no path to Codex-only setup
8
+ even with the underlying machinery in place. After: no over-block.
9
+ Subcommand explicit-flag path unchanged; non-TTY default unchanged. Only
10
+ the bareword on an interactive terminal gains the prompt.
11
+
12
+ ## 2. Level-of-abstraction fit
13
+
14
+ Pure decision in `resolveFrameworkPromptBehavior` (exported, testable);
15
+ side-effectful readline interaction in `promptForFramework` (private to
16
+ setup.ts, only called when the decision says "prompt"). The wizard flow
17
+ itself is untouched; the resolved framework value flows into the same
18
+ existing `checkFrameworkPrerequisite` call.
19
+
20
+ ## 3. Signal vs Authority compliance
21
+
22
+ The `--framework` flag (when present) and the prompt answer (when
23
+ prompted) are SIGNALS of operator intent. The existing
24
+ `checkFrameworkPrerequisite` is the single AUTHORITY for "is the chosen
25
+ runtime's binary present." The prompt's auto-skip-when-only-one-installed
26
+ behavior uses detection results from `detectClaudePath` /
27
+ `detectCodexPath`, which are the existing single source of truth.
28
+
29
+ ## 4. Interactions with adjacent systems
30
+
31
+ - **Explicit `--framework` flag on subcommands** (PRs 3+4 + #276) —
32
+ unchanged. When the flag is set, the prompt is skipped entirely (the
33
+ decision is `opts.framework ?? prompt`).
34
+ - **Non-interactive `runNonInteractiveSetup`** — unchanged. That entry
35
+ point doesn't go through `runSetup` and never triggers the prompt.
36
+ - **`checkFrameworkPrerequisite`** — unchanged. Consumes the resolved
37
+ framework string regardless of whether it came from a flag or the
38
+ prompt.
39
+ - **CI runs (no TTY)** — unchanged. `process.stdin.isTTY` is false in
40
+ CI, so the prompt is bypassed and the default Claude Code behavior
41
+ preserved.
42
+
43
+ ## 5. Rollback cost
44
+
45
+ Trivial. One readline-using function added, one decision helper added,
46
+ one conditional in `runSetup`'s opening lines. `git revert` restores
47
+ the pre-prompt bareword behavior; subcommand and CI flows unaffected.
48
+
49
+ ## 6. Backwards compatibility / drift surface
50
+
51
+ Fully backward-compatible. Default behavior on non-TTY paths is
52
+ unchanged. Subcommand behavior is unchanged. Only interactive bareword
53
+ gets the new UX. Drift surface: none — the decision is centralized in
54
+ one pure exported function.
55
+
56
+ ## 7. Authorization / Trust posture
57
+
58
+ No new authority. The prompt reads stdin and returns a string;
59
+ `checkFrameworkPrerequisite` and the rest of the existing wizard chain
60
+ handle everything downstream as they did before.
61
+
62
+ ## Outcome
63
+
64
+ Ship. Closes the install/wizard portability arc's last UX gap. Pure
65
+ decision logic unit-tested; readline I/O verified by Justin's manual
66
+ end-to-end after merge.
@@ -0,0 +1,64 @@
1
+ # Side-effects review — Secret Drop hardening
2
+
3
+ Per L6. Seven dimensions.
4
+
5
+ ## 1. Over-block / under-block
6
+
7
+ **Before.** UNDER-block: a buggy consumer that called `/secrets/retrieve/<token>` once and dropped the response (parse error, exception in the writer step) lost the secret with no recovery path. The aggressive deletion-on-retrieve looked like a tight one-shot guarantee but actually created a silent-loss footgun, because the same 5-minute cleanup timer was already handling memory hygiene.
8
+
9
+ **After.** No new over-block: the default retrieval shape returns the same value the previous code did. Existing callers that don't pass `?consume=true` simply gain the ability to retry within the cleanup window — a safer state, not a different one. Callers that genuinely need one-shot semantics opt in with the query parameter.
10
+
11
+ The new stuck-consumer event closes the silent-failure gap: a submission that lingers unconsumed for 60s now produces a visible cue to the bound topic. Operators see the failure instead of waiting on a value that's already lost.
12
+
13
+ ## 2. Level-of-abstraction fit
14
+
15
+ Three changes at deliberately different altitudes:
16
+
17
+ - `SecretDrop.ts`: new methods (`peekReceived`, `consumeReceived`, `onStuckConsumer`) and the stuck-timer state machine. Pure logic, unit-tested.
18
+ - `routes.ts`: the route honors `?consume=true`; one default listener wires the stuck-consumer event to the topic-bound session via `injectPasteNotification`. Thin orchestration only.
19
+ - Back-compat alias: `getReceived` is kept as `consumeReceived`'s legacy synonym so no caller has to migrate in this PR. Callers migrate at their own pace by switching to `peekReceived` + `consumeReceived` explicitly.
20
+
21
+ ## 3. Signal vs Authority compliance
22
+
23
+ The Secret Drop request remains the authoritative declaration that this token may receive a submission (operator-issued via `/secrets/request`). The submission, once made, remains the authoritative payload — no agent code can mint a fake submission. The new methods change only the lifecycle of an already-authorized value:
24
+
25
+ - `peekReceived`: read-only, idempotent, returns the existing authority.
26
+ - `consumeReceived`: terminates the authority's lifetime explicitly.
27
+ - `onStuckConsumer`: signal-only emission to listeners; no listener gains write access to the submission.
28
+
29
+ No new authority is created. Existing CSRF, rate-limit, expiry, and one-time submission semantics are unchanged.
30
+
31
+ ## 4. Interactions with adjacent systems
32
+
33
+ - **The `/secrets/retrieve/<token>` route.** Default behavior shifts from destructive to non-destructive. Existing callers that don't add `?consume=true` retain the value across multiple calls — strictly safer; no callers depend on "second call returns 404" except as a footgun.
34
+ - **The `/secrets/drop/<token>` form-side route.** Untouched. Operator-facing flow is byte-identical.
35
+ - **`onReceive` callback on the request.** Untouched. Still fires once on submit.
36
+ - **Existing 5-minute auto-cleanup.** Untouched. Still the safety net.
37
+ - **System-message routing via `injectPasteNotification`.** Reused for the new stuck-consumer system message. Same pattern as `secret-drop-received`.
38
+ - **Existing `SecretDrop.test.ts` suite.** All 25 cases continue to pass. The new test file adds 13 cases for the new surface.
39
+
40
+ ## 5. Rollback cost
41
+
42
+ Low. Three files modified (`SecretDrop.ts`, `routes.ts`, one test file added). `git revert <merge-sha>` restores the pre-change atomic retrieve-and-delete behavior. Callers that adopted `?consume=true` continue to work after a revert (the parameter becomes a no-op against the pre-change atomic-delete path). The stuck-consumer event simply stops firing.
43
+
44
+ No state migration, no deployed-agent impact, no schema change. In-memory only.
45
+
46
+ ## 6. Backwards compatibility / drift surface
47
+
48
+ Fully backwards-compatible at the call-site level:
49
+
50
+ - `getReceived(token)` is preserved as an alias for `consumeReceived(token)`.
51
+ - The route default is non-destructive — strictly safer for existing callers.
52
+ - Callers that depend on one-shot semantics (the bridge script in the parallel telegram-history-backfill PR being one example) opt in via `?consume=true` explicitly.
53
+
54
+ Drift surface: small. Two new methods on `SecretDrop`. One new event interface. One new constant for the grace period. The grace period is a hard-coded constant for v1 ship; a future PR can make it configurable per-request if a real need surfaces.
55
+
56
+ ## 7. Authorization / Trust posture
57
+
58
+ No new authority. The two new methods read/write the same in-memory `received` map the previous code touched. The new listener-registration surface (`onStuckConsumer`) accepts callbacks from the route layer only — there is no external API to register a listener over HTTP. The route layer's default listener routes only to the topic-bound session (the same authority the `secret-drop-received` path already uses).
59
+
60
+ Trust ramifications: the operator gains a new visible signal (the stuck-consumer system message), which strengthens their ability to detect consumer-side failures. The agent gains a new structural primitive for safe-by-default retrieval, which reduces the surface for silent-loss bugs. Both directions of trust move in the safer direction.
61
+
62
+ ## Outcome
63
+
64
+ Ship. Incident-driven, three independent changes, fully unit-tested, fully backwards-compatible at the call-site level. The single highest-impact change is the default-non-destructive retrieve — that alone makes the 2026-05-20 lost-SMS-code failure mode structurally impossible to repeat without an explicit `?consume=true` from the buggy consumer.
@@ -0,0 +1,131 @@
1
+ # Side-effects review — Telegram history backfill (script tier)
2
+
3
+ Per L6. Seven dimensions.
4
+
5
+ ## 1. Over-block / under-block
6
+
7
+ **Before.** Echo had no path to retrieve Telegram history after a local
8
+ topic-memory.db truncation. The Bot API is forward-only, so any data loss
9
+ in the local mirror was permanent absent a Telegram-side export. The
10
+ 2026-05-20 native-module cascade exposed this gap.
11
+
12
+ **After.** The script-tier backfill closes the gap for the post-recovery
13
+ scenario. There is no over-block — the script runs only when invoked
14
+ explicitly (`--chat` flag required), never silently. No new automatic
15
+ behavior is added to any always-on path. Under-block protection: the
16
+ importer's UNIQUE-constraint-guarded INSERT OR IGNORE prevents duplicates
17
+ even under retries, FloodWait recoveries, and manual reruns.
18
+
19
+ ## 2. Level-of-abstraction fit
20
+
21
+ Three modules at deliberately different altitudes:
22
+
23
+ - `topic-memory-importer.mjs` — pure DB logic, unit-tested against the
24
+ real schema. No network, no auth, no gramjs dependency. Trivially testable.
25
+ - `telegram-historian.mjs` — gramjs wrapper. Exposes the minimum surface
26
+ needed (connect, listForumTopics, iterTopicMessages). No business logic.
27
+ - `telegram-history-backfill.mjs` — orchestration only. Parses CLI flags,
28
+ resolves credentials, ties the two libs together.
29
+
30
+ Each module is replaceable without touching the others.
31
+
32
+ ## 3. Signal vs Authority compliance
33
+
34
+ This is the central concern for this PR.
35
+
36
+ `.instar/secrets/...` is the credential signal. The operator's sealed
37
+ file is the authoritative declaration that this account may be accessed
38
+ via MTProto. The script reads the signal and acts on it; it has no
39
+ authority to mint or rotate credentials.
40
+
41
+ The session string, once captured, is the long-lived authority. Storing
42
+ it at mode 0600 outside any git path is the structural enforcement that
43
+ no other process can read it. The operator's Telegram Devices page is
44
+ the only revocation channel — exactly the right placement of authority,
45
+ because the operator alone owns that surface.
46
+
47
+ The script's read-only Telegram posture (iter/get only, no send/edit/
48
+ delete) is the third layer: even if everything else were compromised,
49
+ this code path cannot produce outbound messages or mutations against
50
+ Telegram.
51
+
52
+ ## 4. Interactions with adjacent systems
53
+
54
+ - **instar server (echo's own process).** Holds `topic-memory.db` open
55
+ under WAL mode. SQLite serializes writes across processes, so the
56
+ backfill can write while the server is reading without corruption. The
57
+ FTS triggers maintain the FTS index automatically; tested by
58
+ `tests/unit/topic-memory-importer.test.ts`'s "does not corrupt FTS
59
+ index across re-runs" case.
60
+ - **Bot-API polling (TelegramAdapter).** Unaffected. Live messages
61
+ continue to arrive through the bot. The backfill writes the same
62
+ schema with the same constraints, so the live path's INSERT OR IGNORE
63
+ (if any) and the backfill's INSERT OR IGNORE converge to the same
64
+ final state regardless of ordering.
65
+ - **Coherence gate / external-operation-gate hook.** The script is a
66
+ local Node process; it does not go through the MCP tool path the
67
+ external-operation-gate intercepts. The gate's authority remains over
68
+ agent-initiated network operations from the harness; this script is
69
+ operator-initiated from the CLI.
70
+ - **Migration parity / PostUpdateMigrator.** No agent-installed file is
71
+ modified by this PR. No template change, no hook change, no config
72
+ default change. Existing agents are unaffected by the merge — they
73
+ pick up the script only if they explicitly run it.
74
+
75
+ ## 5. Rollback cost
76
+
77
+ Trivial. Five files, two libraries, one test, one spec, one ELI16, one
78
+ side-effects review. `git revert <merge-sha>` removes the code. The
79
+ sealed credential file and the session string file are outside the repo
80
+ and unaffected by the revert. The `topic-memory.db` is touched only by
81
+ INSERT OR IGNORE, so already-imported rows are correct and stay — no
82
+ data restoration needed.
83
+
84
+ If the operator wants the imported rows undone too, a single SQL command
85
+ removes them: `DELETE FROM messages WHERE timestamp < '<backfill cutoff>'`.
86
+ This is safe because all writes go through the importer, which can be
87
+ disabled by removing the script.
88
+
89
+ ## 6. Backwards compatibility / drift surface
90
+
91
+ Fully backwards-compatible. The script is additive only — no existing
92
+ behavior is modified. The `topic-memory.db` schema is read but never
93
+ altered (no migrations). The CLI entry is a new file; nothing references
94
+ it from existing code paths.
95
+
96
+ Drift surface:
97
+ - The script targets the schema as it exists 2026-05-20 (read live for
98
+ this PR). A future schema change (e.g. adding a `chat_id` column)
99
+ would require updating `topic-memory-importer.mjs`. The narrow surface
100
+ and unit-test coverage make this easy to spot in code review.
101
+ - gramjs (`telegram` package) is a new dependency. Version pin is
102
+ `^2.26.22` — caret range allows non-breaking updates. If gramjs ever
103
+ ships a breaking 3.x, the import surface in `telegram-historian.mjs`
104
+ is small (4 imports, 3 method calls) so adaptation cost is bounded.
105
+
106
+ ## 7. Authorization / Trust posture
107
+
108
+ The single point where new authority is granted is the operator's
109
+ explicit hand-off of `api_id` + `api_hash` (2026-05-20T03:10:18Z via
110
+ Telegram topic 10873) and the live SMS-code exchange during `--auth`.
111
+ The script grants itself nothing — it only reads what the operator has
112
+ already authorized.
113
+
114
+ The session string, once captured, is the durable artifact of that
115
+ authorization. The operator can revoke it via Telegram's Devices UI at
116
+ any time; the script will then error on the next run and require a
117
+ fresh `--auth`. This places revocation entirely in the operator's hands
118
+ with no instar-side dependency.
119
+
120
+ The "read-only on Telegram" posture is enforced by **not exposing any
121
+ write method**, not by a guard the operator must trust. Adding a write
122
+ method would require a spec change AND a fresh operator approval — the
123
+ review-convergence + approved frontmatter tags would have to be
124
+ regenerated, which is the structural enforcement here.
125
+
126
+ ## Outcome
127
+
128
+ Ship. Incident-driven, single-purpose, read-only, idempotent, unit-tested.
129
+ Promotion to a full `src/messaging/TelegramHistorian.ts` feature is a
130
+ separate, follow-up PR after the recovery itself is verified end-to-end
131
+ against the live lifeline supergroup.