feat(cli,db): 2.G — interactive human-gate prompt + relavium gate resume#47
Conversation
…xt pickup 2.G PR #46 merged. The ink streaming TUI is shipped — the third RunRenderer over the one event bus (live per-node status + spinners, the active node's streaming tokens, a running cost footer, a persistent final summary), with cooperative Ctrl-C cancel (the native-AbortController + persistent-SIGINT + abort→cancelled chain). Behind ADR-0047 (ink ^6.8.0 + React 19, confined to apps/cli); no new ADR. - phase-2-cli.md: 2.E heading + status header + Remaining-build-order status line all marked ✅ Done (PR #46); the build-order table drops the 2.E row, renumbers (2.G now #1), and flips 2.E → ✓ in the 2.G + chat blocker cells; the gate-closing backbone shrinks to `2.G → 2.I → 2.L` (2.E joins 2.K + 2.H + 2.C as done). - current.md: 2.E added to the Landed list (behind ADR-0047); next pickup → 2.G; date bump (2026-06-24). - CLAUDE.md + README.md: status paragraphs updated (2.E landed; next pickup 2.G). Refs: phase-2-cli.md 2.E, ADR-0047 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…esume
Implement both human-gate surfaces over the engine's single suspend/resume
contract: an inline `@clack/prompts` prompt during `run` on a TTY, and the
out-of-band `relavium gate <runId>` cross-process resume (CI / anywhere).
- db: loadRunSnapshot(db, runId) — status + frozen workflow snapshot + inputs,
the substrate the fresh-process gate command rebuilds the run from (2.H read).
- cli: createHistoryCheckpointer reconstructs the checkpoint from the durable
event log; createCliHost gains a { checkpointer } option. The gate command
reloads the snapshot, reconstructs the checkpoint, calls
engine.resumeFromCheckpoint over the same store, then drives to the terminal.
selectGate auto-fills one pending gate, requires --gate for >1, is idempotent
(terminal / already-resolved → exit 0), and excludes budget gates.
- cli: RunRenderer.suspend/resume seam (ink re-mounts from the retained store);
a framework-free GatePrompter (@Clack confined to one module); a shared
driveRun core both run and gate use — preserving the 2.E Ctrl-C/SIGINT
contract. engine.resume mid-loop is deadlock-free (the bus push never blocks).
- harness: closes 2.K's deferred gate-resume scenario (run → exit 3 → gate →
run:completed) over the offline fixtures.
- deps: @clack/prompts ^1.6.0 (ADR-0047, §9a cooling window); Node floor
20.11→20.12 (its engines floor) across package.json / tech-stack.md / catalog.
- docs: commands.md run + gate notes; regression-harness; deferred-tasks now
tracks the unscheduled `relavium budget resume` follow-up.
Refs: ADR-0047, ADR-0050, ADR-0006, ADR-0028
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…op dead snapshot field
Address the confirmed findings from three independent 2.G reviews (all "ship-ready",
0 blockers); two flagged the same race.
- drive.ts: a Ctrl-C during the inline gate prompt can settle the run before the
decision is applied, so engine.resume throws EngineStateError('run_already_terminal').
Swallow exactly that code and let the loop drain run:cancelled (outcome 'cancelled')
instead of surfacing a generic "unexpected internal error". Deterministic test added.
- db: drop the now-dead `status` field from loadRunSnapshot / RunResumeSnapshot — the
gate command uses the authoritative checkpoint.runStatus (folded fresh from the event
log), and loadRun(runId).status covers any status-by-id need. Leaner read, no dead
projection.
- docs(commands.md): note gate idempotency is per-gate — a blind retry without --gate on
a sequential multi-gate workflow resolves the NEXT gate, so CI retry loops should pin
--gate <gateId>.
Refs: ADR-0047, ADR-0050
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @cemililik, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds interactive human-gate prompting to ChangesInteractive Human-Gate Prompt +
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request implements workstream 2.G, introducing the interactive human-gate prompt and the relavium gate resume command. It consolidates the event-loop driving logic into a shared driveRun driver, which coordinates TUI suspension/resumption, SIGINT handling, and inline gate resolution. Additionally, it integrates @clack/prompts for terminal prompts, bumps the minimum Node.js requirement to >=20.12.0, and establishes the durable resume substrate to reconstruct checkpoints from the SQLite history database. Feedback on the changes suggests simplifying the manual mapping of opts in the gate command registration by directly spreading the object.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
docs/roadmap/deferred-tasks.md (1)
488-493: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep concrete command semantics in
docs/reference/and link from roadmapThis section now restates implementation-level behavior (checkpoint reconstruction and resume semantics). Keep this roadmap item brief and link to the canonical reference spec to avoid drift.
As per coding guidelines, “Specs live in [docs/reference/]; link, don't restate. One canonical home per artifact.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/roadmap/deferred-tasks.md` around lines 488 - 493, This roadmap entry restates implementation details for the deferred command flow instead of pointing to the canonical spec. Shorten the text in the deferred-tasks section so it only summarizes the milestone and add a link to the authoritative command semantics in docs/reference/; keep the detailed behavior around reconstructCheckpointState, CheckpointPendingGate, and engine.resumeFromCheckpoint in the reference doc instead of duplicating it here.Source: Coding guidelines
apps/cli/src/commands/gate.ts (1)
263-269: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace the unsafe cast with a type guard.
The runtime check is already doing the hard work here; the trailing
as Record<string, unknown>just weakens the strict-mode guarantee and violates the repo’s TypeScript rule.Suggested change
+function isRecord(value: unknown): value is Record<string, unknown> { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + function parseInputs(inputJson: string, runId: string): Record<string, unknown> { let parsed: unknown; try { parsed = JSON.parse(inputJson); } catch (err) { @@ - if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { + if (!isRecord(parsed)) { throw new CliError( 'invalid_invocation', `the stored inputs for run ${runId} are not a JSON object`, ); } - return parsed as Record<string, unknown>; + return parsed; }As per coding guidelines,
**/*.{ts,tsx}: TypeScript-first, strict. Noany, no unsafeas.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/cli/src/commands/gate.ts` around lines 263 - 269, The return in the gate command’s parsed-input handling is using an unsafe type cast that bypasses strict TypeScript checks. Update the logic in the parsed JSON validation path in gate.ts so the existing runtime object check is paired with a proper type guard or narrowing function, and return the narrowed value without using the trailing as Record<string, unknown>. Keep the fix local to the parsing/validation flow around the parsed variable and the CliError branch so the function remains strictly typed end to end.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/cli/src/commands/gate.ts`:
- Around line 109-114: The persisted history reconstruction in gate.ts should
convert malformed run_events failures into a typed CLI fault instead of letting
raw errors escape. Update the createHistoryCheckpointer(store) /
checkpointer.load(args.runId) path to catch load/reconstruction exceptions and
rethrow a CliError with invalid_invocation, matching the existing snapshot/input
blob error handling so the command exits cleanly with exit-2 behavior.
In `@docs/roadmap/current.md`:
- Around line 57-59: The roadmap entry still says “Next pickup: 2.G” even though
2.G is already marked as landed in the docs, so update the status wording in the
roadmap to match the landed state and avoid a contradiction. Adjust the text
around the “Next pickup” section in current.md so it reflects the updated 2.G
outcome and stays consistent with the related documentation changes, using the
existing 2.G and status-aware order wording as the anchor points.
---
Nitpick comments:
In `@apps/cli/src/commands/gate.ts`:
- Around line 263-269: The return in the gate command’s parsed-input handling is
using an unsafe type cast that bypasses strict TypeScript checks. Update the
logic in the parsed JSON validation path in gate.ts so the existing runtime
object check is paired with a proper type guard or narrowing function, and
return the narrowed value without using the trailing as Record<string, unknown>.
Keep the fix local to the parsing/validation flow around the parsed variable and
the CliError branch so the function remains strictly typed end to end.
In `@docs/roadmap/deferred-tasks.md`:
- Around line 488-493: This roadmap entry restates implementation details for
the deferred command flow instead of pointing to the canonical spec. Shorten the
text in the deferred-tasks section so it only summarizes the milestone and add a
link to the authoritative command semantics in docs/reference/; keep the
detailed behavior around reconstructCheckpointState, CheckpointPendingGate, and
engine.resumeFromCheckpoint in the reference doc instead of duplicating it here.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bcfb99ec-570a-4c57-9305-bcba1c7c0fbb
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (35)
CLAUDE.mdREADME.mdapps/cli/package.jsonapps/cli/src/commands/drive.test.tsapps/cli/src/commands/drive.tsapps/cli/src/commands/gate.test.tsapps/cli/src/commands/gate.tsapps/cli/src/commands/run.test.tsapps/cli/src/commands/run.tsapps/cli/src/commands/specs.tsapps/cli/src/engine/checkpointer.test.tsapps/cli/src/engine/checkpointer.tsapps/cli/src/engine/host.test.tsapps/cli/src/engine/host.tsapps/cli/src/gate/clack-prompter.test.tsapps/cli/src/gate/clack-prompter.tsapps/cli/src/gate/decision.test.tsapps/cli/src/gate/decision.tsapps/cli/src/gate/prompter.tsapps/cli/src/gate/select-prompter.tsapps/cli/src/harness/regression.e2e.test.tsapps/cli/src/render/renderer.tsapps/cli/src/render/tui/ink-renderer.test.tsapps/cli/src/render/tui/ink-renderer.tsdocs/reference/cli/commands.mddocs/reference/cli/regression-harness.mddocs/roadmap/current.mddocs/roadmap/deferred-tasks.mddocs/roadmap/phases/phase-2-cli.mddocs/tech-stack.mdpackage.jsonpackages/db/src/index.tspackages/db/src/run-history-store.test.tspackages/db/src/run-history-store.tspnpm-workspace.yaml
Address the still-valid PR #47 review findings (verified against the code; the rest skipped with reasons). - gate.ts: wrap checkpointer.load so a malformed run_events row (bad JSON / failed schema parse during the fold) surfaces as a clean exit-2 CliError, matching the snapshot/inputs handling, never a raw escaping error. Test added. - gate.ts: parseInputs uses an isPlainObject type guard instead of an `as` cast (strictly narrowed, no assertion). - drive.ts: drop the redundant `continue` — fold the run:paused break into one condition (Sonar). - specs.ts: the gate action passes `{ runId, ...opts }` (the conditional spread was unnecessary — the spread preserves optionality under exactOptionalPropertyTypes). - gate.test.ts: toHaveLength over `.length).toBe` (Sonar). - deferred-tasks.md: trim the budget-resume entry to a summary + link to the canonical commands.md rather than restating the engine internals. Skipped: current.md "Next pickup: 2.G" — not a contradiction; the roadmap status surfaces flip to Done only after PR #47 merges (the project's Done-after-merge rule, as done for 2.E). Refs: ADR-0047, ADR-0050 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@coderabbitai review all |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/cli/src/commands/drive.ts`:
- Around line 124-129: The prompt flow in drive.ts is losing the real prompt
outcome because renderer.resume?.() in the finally block can throw before the
decision is used, replacing a successful GatePrompter['prompt'] result or the
original prompt error. Update the prompt handling around decision and
renderer.suspend?/resume? so the prompt result is preserved even if resume
fails, and ensure any resume error does not overwrite a successful
approve/reject/input decision or mask the original prompt failure.
In `@apps/cli/src/commands/gate.ts`:
- Around line 127-185: The closed-handle resume race in gate handling is being
treated as a workflow failure instead of an idempotent no-op. Update the `gate`
command flow around `engine.resumeFromCheckpoint()` and `driveRun()` so that
when a run has already been settled and the resumed handle is closed, the
command returns `EXIT_CODES.success` rather than falling through to
`EXIT_CODES.workflowFailed`. Use the existing `selection.kind === 'idempotent'`
path and the `outcome` switch in `gate.ts` to detect this duplicate decision
consistently, and keep the behavior aligned with the comments about
terminal/idempotency decisions.
In `@apps/cli/src/engine/host.ts`:
- Around line 12-19: The host setup allows a Checkpointer to be injected without
ensuring the same durable store is used, which makes resumeFromCheckpoint() read
from one backend and persist through another. Update createCliHost and
CliHostOptions so that when checkpointer is provided, a matching durable store
must also be supplied (or derive both from the same source), and reject the
invalid combination early before resumeFromCheckpoint() calls
host.store.resolveWorkflowId(...) or host.store.persistEvent(...).
In `@apps/cli/src/gate/decision.ts`:
- Around line 45-48: The raw CLI input is being passed through
GateDecision.payload and can be persisted/emitted, so secret-bearing values must
not take the durable decision path. Update the decision flow in decision.ts
around the GateDecision handling to either reject secret-looking --input
payloads or route them through a secret-safe lookup (for example
keychain/gateway-backed storage) before building the decision. Make sure any
path that can reach the human_gate:resumed.payload or --json output never stores
or echoes plaintext secrets.
In `@apps/cli/src/harness/regression.e2e.test.ts`:
- Around line 311-360: The “fresh process” resume path in the regression test is
still using the same SQLite handle, so it never exercises reopening persisted
history. Update the test around openRunStore and gateCommand so the first leg
persists with createRunHistoryStore(client.db, ...) but the second leg opens a
truly new database connection from the same underlying file/state before calling
gateCommand. Keep the assertions on run:started and the resumed event sequence,
but route the resume through a reopened history DB instead of client.db to cover
migration/reload behavior.
In `@docs/reference/cli/commands.md`:
- Around line 170-171: The `--input` contract in the CLI docs needs a security
update: explicitly forbid passing secrets/API keys through `--input` argv and
document the safer alternative path instead. Update the `commands.md` guidance
around the `--input` gate payload behavior to state that sensitive values must
not be provided via command-line text, shell history, or process listings, and
ensure the referenced CLI handling for gate input reflects the prohibition
alongside the existing `--approve` / `--reject` / `--input` rules.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: df84a97d-cab5-4166-af6c-2f80737ac21e
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (35)
CLAUDE.mdREADME.mdapps/cli/package.jsonapps/cli/src/commands/drive.test.tsapps/cli/src/commands/drive.tsapps/cli/src/commands/gate.test.tsapps/cli/src/commands/gate.tsapps/cli/src/commands/run.test.tsapps/cli/src/commands/run.tsapps/cli/src/commands/specs.tsapps/cli/src/engine/checkpointer.test.tsapps/cli/src/engine/checkpointer.tsapps/cli/src/engine/host.test.tsapps/cli/src/engine/host.tsapps/cli/src/gate/clack-prompter.test.tsapps/cli/src/gate/clack-prompter.tsapps/cli/src/gate/decision.test.tsapps/cli/src/gate/decision.tsapps/cli/src/gate/prompter.tsapps/cli/src/gate/select-prompter.tsapps/cli/src/harness/regression.e2e.test.tsapps/cli/src/render/renderer.tsapps/cli/src/render/tui/ink-renderer.test.tsapps/cli/src/render/tui/ink-renderer.tsdocs/reference/cli/commands.mddocs/reference/cli/regression-harness.mddocs/roadmap/current.mddocs/roadmap/deferred-tasks.mddocs/roadmap/phases/phase-2-cli.mddocs/tech-stack.mdpackage.jsonpackages/db/src/index.tspackages/db/src/run-history-store.test.tspackages/db/src/run-history-store.tspnpm-workspace.yaml
…uard + cleanups
Address all confirmed findings from a 10-dimension adversarial review (workflow, 0
blockers, 1 major) plus two external review batches. Verified each against the code;
fixed the still-valid ones, skipped 4 with reasons.
MAJOR — masked-secret cross-process resume (the completeness critic's catch):
runs.input_json persists the MASKED run:started.inputs (a `secret`-typed input is stored
as { secret: true, ref }, never plaintext — ADR-0006/0036), so a fresh-process resume
restored secrets as the masked placeholder, silently diverging from the in-process run.
gate.ts now fails closed (exit 2) when a restored input is a MaskedSecret, with an
actionable message; re-provide-on-resume is a tracked follow-up. + fixture/test + doc.
Code:
- gate.ts: a closed-handle resume (concurrent-settle race) now maps to idempotent exit 0,
not exit 1.
- drive.ts: resolveGateInline's finally catches+logs a renderer.resume() throw so it can't
mask the prompt decision/error; outcomeToExitCode is now a shared helper (run + gate no
longer duplicate the switch).
- ink-renderer.ts: finalize writes the summary in a finally, so a stop() (waitUntilExit)
rejection can't lose it.
- clack-prompter.ts: an input-gate prompt uses a generic label (the card already shows the
message — no double-echo). parseInputs JSDoc reorder; `let handle: RunHandle` typed.
Tests (+15): secret→exit2, closed-handle→exit0, EngineStateError→exit2, empty-log→exit2,
parseInputs[]/corrupt-snapshot x2, --input e2e, sequential multi-gate + prompter wiring,
cancel-on-prompt-throw, resume-throw-doesn't-mask, finalize-summary-survives-stop-reject,
mount-throw-on-resume, all-three-flags, clack review/timeout. The harness gate-resume
scenario now uses a real FILE db with a SEPARATE connection for the resume (true reopen)
and asserts gap-free sequenceNumber continuation across the boundary.
Docs: commands.md --input secrets prohibition + non-secret example + masked-secret-resume
limitation; deferred-tasks re-provide-secret-on-resume follow-up; phase-2-cli.md step
numbers corrected + `gate list` reconciled to 2.I (canonical commands.md).
Skipped: createCliHost checkpointer/store enforcement (no bug — sole caller derives both
from the same store; a runtime same-backend check is impossible, type coupling breaches
the db layering); SIGINT-leak gateCommand test (redundant — contract lives in driveRun,
covered by run.test); redundant `only !== undefined` (TS array-destructure requires it);
driveRun-cancel-on-closed-handle (inert, folded into the closed-handle fix).
Refs: ADR-0047, ADR-0050, ADR-0006
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CodeRabbit (Major): createCliHost(undefined, { checkpointer }) wired a host whose read
side (the checkpointer) and write side (the default in-memory store) point at different
backends, so resumeFromCheckpoint would validate/persist against the wrong store. Reject
that pairing at construction (`store instanceof InMemoryRunStore && checkpointer`) — the
only valid combination is an explicit durable store + a checkpointer reconstructed from
it. + a guard test; the CliHostOptions JSDoc now documents the invariant.
Refs: ADR-0050
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
…xt pickup 2.I 2.G (interactive human-gate prompt + `relavium gate` cross-process resume) merged via PR #47, fully closing 2.K's deferred gate-resume half. Flip the status surfaces: - phase-2-cli.md: §2.G heading → Done; status header + Remaining-build-order status line → add 2.G; the build-order table drops the 2.G row and renumbers (2.I now #1, 2.L #2, 2.S #3, …); the gate-closing backbone shrinks to `2.I → 2.L`; the §2.K deferred gate-resume note → landed. - current.md: 2.G added to Landed; next pickup → 2.I. - CLAUDE.md + README.md: 2.G landed; next pickup 2.I. The structural views (dependency matrix, ordered waves, Mermaid graphs) are the from-scratch plan, not a live tracker, so they keep 2.G as a graph node unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>



Summary
Phase-2 workstream 2.G — implement both human-gate surfaces over the engine's single
suspend/resume contract:
relavium runon a TTY — a@clack/promptscard(approve / reject + comment / input); the run continues without leaving the process.
relavium gate <runId>— the cross-process resume path (CI / anywhere): afresh process reloads the durable snapshot → reconstructs the checkpoint from the persisted
event log → calls
engine.resumeFromCheckpointover the same store → drives the resumed runto its terminal.
Closes 2.K's deferred gate-resume scenario (run → exit 3 →
gate --approve→run:completed).This branch also carries the 2.E Done bookkeeping commit (
5fed500).See phase-2-cli.md §2.G and the canonical
commands.md
relavium gatecontract.What changed
@relavium/db—loadRunSnapshot(db, runId)(additive read): the frozenWorkflowDefinition+ inputs a fresh-process resume rebuilds from (the events don't carry thegraph). Run status is intentionally not returned — the gate command uses the authoritative
checkpoint.runStatus.apps/cliengine —createHistoryCheckpointerreconstructs the checkpoint from the durablelog;
createCliHost(store, { checkpointer })injects it on the resume path.apps/cligate command —selectGateauto-fills one pending gate, requires--gatefor >1(exit 2 listing the ids), is idempotent (terminal / already-resolved → exit 0, never a
double-advance), and excludes budget gates (→ the tracked
relavium budget resumesurface).apps/cliinteractive —RunRenderer.suspend/resumeseam (ink re-mounts from the retainedstore); a framework-free
GatePrompter(@clack/promptsconfined to one module); a shareddriveRuncore bothrunandgateuse — preserving the 2.E Ctrl-C/SIGINT contract. Themid-loop
engine.resumeis deadlock-free (the bus push never blocks; backpressure lives on aseparate pump).
(in-memory) db, then resumes it fresh-process to
run:completed.@clack/prompts ^1.6.0(decided in ADR-0047,added under the §9a cooling window, confined to
apps/cli); Node floor 20.11 → 20.12 (itsown
engines.nodefloor) acrosspackage.json/tech-stack.md/ the catalog.commands.md(run + gate notes, per-gate idempotency footgun),regression-harness.md(gate-resume scenario),
deferred-tasks.md(tracks the unscheduledrelavium budget resume).Reviews
A multi-dimensional adversarial review (6 dimensions) plus three independent external reviews —
all ship-ready, 0 blockers. Confirmed findings fixed in
1630483:engine.resumethrowsrun_already_terminal; now swallowed so the loop drains cleanly torun:cancelledinstead of a generic "internal error" (deterministic test added).statusfield fromloadRunSnapshot.--gatein CI retry loops.Conformance checklist
any, no unsafeas, no@ts-ignore@relavium/llmseam (untouched);packages/corehas zero new platform imports (untouched)@clack/prompts) is ADR-covered (ADR-0047) and confined toapps/cli— engine-deps guard greendecidedBy: 'cli'is a non-PII constant; leakwatch 0pnpm turbo run lint typecheck test buildgreen — 852 core · 444 llm · 105 db · 261 cliprettier --checkclean · engine-deps guard cleanAcceptance criteria (2.G)
3and is later resumed byrelavium gate <runId> --approve--inputpaths both work end-to-enddecidedBy: 'timeout'(engine) and that resolution flows through the event stream; the gate card surfaces the deadline +timeout_action🤖 Generated with Claude Code
Summary by CodeRabbit
relavium gateto resume paused human-gate runs (approve/reject/input) with idempotent handling.relavium runnow supports interactive human-gate prompting in TTYs and continues correctly after decisions.inkTUI supports suspend/remount during prompts.@clack/promptsfor CLI prompting.