Skip to content

feat(cli,db): 2.G — interactive human-gate prompt + relavium gate resume#47

Merged
cemililik merged 6 commits into
mainfrom
development
Jun 24, 2026
Merged

feat(cli,db): 2.G — interactive human-gate prompt + relavium gate resume#47
cemililik merged 6 commits into
mainfrom
development

Conversation

@cemililik

@cemililik cemililik commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase-2 workstream 2.G — implement both human-gate surfaces over the engine's single
suspend/resume contract:

  • Interactive inline prompt during relavium run on a TTY — a @clack/prompts card
    (approve / reject + comment / input); the run continues without leaving the process.
  • Out-of-band relavium gate <runId> — the cross-process resume path (CI / anywhere): a
    fresh process reloads the durable snapshot → reconstructs the checkpoint from the persisted
    event log → calls engine.resumeFromCheckpoint over the same store → drives the resumed run
    to its terminal.

Closes 2.K's deferred gate-resume scenario (run → exit 3 → gate --approverun: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 gate contract.

What changed

  • @relavium/dbloadRunSnapshot(db, runId) (additive read): the frozen
    WorkflowDefinition + inputs a fresh-process resume rebuilds from (the events don't carry the
    graph). Run status is intentionally not returned — the gate command uses the authoritative
    checkpoint.runStatus.
  • apps/cli enginecreateHistoryCheckpointer reconstructs the checkpoint from the durable
    log; createCliHost(store, { checkpointer }) injects it on the resume path.
  • apps/cli gate commandselectGate auto-fills one pending gate, requires --gate for >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 resume surface).
  • apps/cli interactiveRunRenderer.suspend/resume seam (ink re-mounts from the retained
    store); a framework-free GatePrompter (@clack/prompts confined to one module); a shared
    driveRun core both run and gate use — preserving the 2.E Ctrl-C/SIGINT contract. The
    mid-loop engine.resume is deadlock-free (the bus push never blocks; backpressure lives on a
    separate pump).
  • Harness — the regression suite now runs the gate fixture to exit 3 against a durable
    (in-memory) db, then resumes it fresh-process to run:completed.
  • Deps@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 (its
    own engines.node floor) across package.json / tech-stack.md / the catalog.
  • Docscommands.md (run + gate notes, per-gate idempotency footgun), regression-harness.md
    (gate-resume scenario), deferred-tasks.md (tracks the unscheduled relavium 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:

  • A Ctrl-C during the inline prompt can settle the run before the decision applies →
    engine.resume throws run_already_terminal; now swallowed so the loop drains cleanly to
    run:cancelled instead of a generic "internal error" (deterministic test added).
  • Dropped a dead status field from loadRunSnapshot.
  • Documented that gate idempotency is per-gate — pin --gate in CI retry loops.

Conformance checklist

  • Strict TS — no any, no unsafe as, no @ts-ignore
  • No vendor SDK type crosses the @relavium/llm seam (untouched); packages/core has zero new platform imports (untouched)
  • New runtime dependency (@clack/prompts) is ADR-covered (ADR-0047) and confined to apps/cli — engine-deps guard green
  • Secrets: no key in logs / events; decidedBy: 'cli' is a non-PII constant; leakwatch 0
  • Canonical-home docs updated, nothing restated out of place
  • pnpm turbo run lint typecheck test build green — 852 core · 444 llm · 105 db · 261 cli
  • prettier --check clean · engine-deps guard clean

Acceptance criteria (2.G)

  • An interactive run pauses at a gate, prompts, and resumes on approval to completion
  • A non-interactive run exits with the gate-paused code 3 and is later resumed by relavium gate <runId> --approve
  • A doubled decision does not double-advance (idempotent at the gate-state level)
  • Reject and --input paths both work end-to-end
  • A timed-out gate resolves with decidedBy: '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

  • New Features
    • Added relavium gate to resume paused human-gate runs (approve/reject/input) with idempotent handling.
    • relavium run now supports interactive human-gate prompting in TTYs and continues correctly after decisions.
    • The live ink TUI supports suspend/remount during prompts.
  • Bug Fixes
    • Improved reliability for cancellation and prompt/resume edge cases, including correct exit-code mapping.
  • Documentation / Tests
    • Expanded CLI/roadmap/regression-harness docs and added unit + end-to-end coverage for gate resume and checkpoint reconstruction.
  • Chores
    • Bumped Node minimum to >=20.12.0 and added @clack/prompts for CLI prompting.

cemililik and others added 3 commits June 24, 2026 02:51
…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>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @cemililik, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds interactive human-gate prompting to relavium run, implements relavium gate resume from persisted history, extends the ink renderer lifecycle for suspend/resume, and updates supporting docs, tests, and Node version requirements.

Changes

Interactive Human-Gate Prompt + relavium gate Resume

Layer / File(s) Summary
Gate decision contract and prompt selection
apps/cli/src/gate/prompter.ts, apps/cli/src/gate/decision.ts, apps/cli/src/gate/select-prompter.ts, apps/cli/src/gate/clack-prompter.ts, apps/cli/src/gate/*.test.ts, apps/cli/package.json, pnpm-workspace.yaml
Defines gate decision helpers, prompt selection, the GatePrompter contract, and the clack-backed interactive prompter with tests for approval, rejection, input, cancellation, and mode selection.
RunRenderer suspend/resume lifecycle and ink remounting
apps/cli/src/render/renderer.ts, apps/cli/src/render/tui/ink-renderer.ts, apps/cli/src/render/tui/ink-renderer.test.ts
Adds renderer suspend/resume hooks and refactors the ink renderer into lifecycle-controlled start/stop behavior with remount coverage.
driveRun orchestration and runCommand delegation
apps/cli/src/commands/drive.ts, apps/cli/src/commands/run.ts, apps/cli/src/commands/*.test.ts
Adds driveRun for live event handling, inline gate resolution, pause handling, and cleanup, then refactors runCommand to delegate to it. Tests cover inline approval, cancellation, pause behavior, and resume races.
Run snapshot loading and history-backed checkpoint reconstruction
packages/db/src/run-history-store.ts, packages/db/src/index.ts, apps/cli/src/engine/checkpointer.ts, apps/cli/src/engine/host.ts, apps/cli/src/engine/*.test.ts, packages/db/src/run-history-store.test.ts
Adds persisted run snapshot loading, history-backed checkpoint reconstruction, and host injection for checkpointers, with tests for snapshot round-tripping and checkpoint loading.
gateCommand resume flow, registration, and regression coverage
apps/cli/src/commands/gate.ts, apps/cli/src/commands/specs.ts, apps/cli/src/harness/regression.e2e.test.ts, apps/cli/src/commands/gate.test.ts
Adds gateCommand, gate selection and snapshot parsing, command registration, and a durable cross-process resume case.
Docs, roadmap, and Node version floor
README.md, CLAUDE.md, docs/reference/cli/commands.md, docs/reference/cli/regression-harness.md, docs/roadmap/*.md, docs/tech-stack.md, package.json
Updates milestone text, CLI command docs, roadmap status, regression harness docs, and Node engine requirements.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • HodeTech/Relavium#22: Introduced the checkpoint/resume and human-gate semantics that this PR’s CLI resume path relies on.
  • HodeTech/Relavium#40: Added the CLI command-registration foundation that this PR extends with a real gate subcommand.
  • HodeTech/Relavium#46: Added the ink TUI and renderer work that this PR extends with suspend/resume lifecycle hooks.

Poem

🐇 I hopped through gates with clack and glow,
Then paused the stream to let prompts flow.
I bounced back in with resume bright,
And landed clean in terminal night.
A twitch, a nibble, a joyful thump—
This rabbit run did quite the jump.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: interactive human-gate prompting plus relavium gate resume across CLI and DB work.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/cli/src/commands/specs.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
docs/roadmap/deferred-tasks.md (1)

488-493: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep concrete command semantics in docs/reference/ and link from roadmap

This 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 win

Replace 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. No any, no unsafe as.

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between e137c75 and 1630483.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (35)
  • CLAUDE.md
  • README.md
  • apps/cli/package.json
  • apps/cli/src/commands/drive.test.ts
  • apps/cli/src/commands/drive.ts
  • apps/cli/src/commands/gate.test.ts
  • apps/cli/src/commands/gate.ts
  • apps/cli/src/commands/run.test.ts
  • apps/cli/src/commands/run.ts
  • apps/cli/src/commands/specs.ts
  • apps/cli/src/engine/checkpointer.test.ts
  • apps/cli/src/engine/checkpointer.ts
  • apps/cli/src/engine/host.test.ts
  • apps/cli/src/engine/host.ts
  • apps/cli/src/gate/clack-prompter.test.ts
  • apps/cli/src/gate/clack-prompter.ts
  • apps/cli/src/gate/decision.test.ts
  • apps/cli/src/gate/decision.ts
  • apps/cli/src/gate/prompter.ts
  • apps/cli/src/gate/select-prompter.ts
  • apps/cli/src/harness/regression.e2e.test.ts
  • apps/cli/src/render/renderer.ts
  • apps/cli/src/render/tui/ink-renderer.test.ts
  • apps/cli/src/render/tui/ink-renderer.ts
  • docs/reference/cli/commands.md
  • docs/reference/cli/regression-harness.md
  • docs/roadmap/current.md
  • docs/roadmap/deferred-tasks.md
  • docs/roadmap/phases/phase-2-cli.md
  • docs/tech-stack.md
  • package.json
  • packages/db/src/index.ts
  • packages/db/src/run-history-store.test.ts
  • packages/db/src/run-history-store.ts
  • pnpm-workspace.yaml

Comment thread apps/cli/src/commands/gate.ts
Comment thread docs/roadmap/current.md
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>
@cemililik

Copy link
Copy Markdown
Contributor Author

@coderabbitai review all

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

@cemililik Sure, I'll kick off a full review of all the changes in PR #47 right away!

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between e137c75 and d4268d3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (35)
  • CLAUDE.md
  • README.md
  • apps/cli/package.json
  • apps/cli/src/commands/drive.test.ts
  • apps/cli/src/commands/drive.ts
  • apps/cli/src/commands/gate.test.ts
  • apps/cli/src/commands/gate.ts
  • apps/cli/src/commands/run.test.ts
  • apps/cli/src/commands/run.ts
  • apps/cli/src/commands/specs.ts
  • apps/cli/src/engine/checkpointer.test.ts
  • apps/cli/src/engine/checkpointer.ts
  • apps/cli/src/engine/host.test.ts
  • apps/cli/src/engine/host.ts
  • apps/cli/src/gate/clack-prompter.test.ts
  • apps/cli/src/gate/clack-prompter.ts
  • apps/cli/src/gate/decision.test.ts
  • apps/cli/src/gate/decision.ts
  • apps/cli/src/gate/prompter.ts
  • apps/cli/src/gate/select-prompter.ts
  • apps/cli/src/harness/regression.e2e.test.ts
  • apps/cli/src/render/renderer.ts
  • apps/cli/src/render/tui/ink-renderer.test.ts
  • apps/cli/src/render/tui/ink-renderer.ts
  • docs/reference/cli/commands.md
  • docs/reference/cli/regression-harness.md
  • docs/roadmap/current.md
  • docs/roadmap/deferred-tasks.md
  • docs/roadmap/phases/phase-2-cli.md
  • docs/tech-stack.md
  • package.json
  • packages/db/src/index.ts
  • packages/db/src/run-history-store.test.ts
  • packages/db/src/run-history-store.ts
  • pnpm-workspace.yaml

Comment thread apps/cli/src/commands/drive.ts Outdated
Comment thread apps/cli/src/commands/gate.ts Outdated
Comment thread apps/cli/src/engine/host.ts
Comment thread apps/cli/src/gate/decision.ts
Comment thread apps/cli/src/harness/regression.e2e.test.ts Outdated
Comment thread docs/reference/cli/commands.md
cemililik and others added 2 commits June 24, 2026 12:50
…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>
@cemililik
cemililik merged commit 87f6e90 into main Jun 24, 2026
8 of 9 checks passed
@sonarqubecloud

Copy link
Copy Markdown

cemililik added a commit that referenced this pull request Jun 24, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant