Skip to content

feat(loop): drive-session row tagging so ui_review teardown drops exactly the drive-created rows#1333

Merged
mfittko merged 4 commits into
mainfrom
issue-1287
Jul 11, 2026
Merged

feat(loop): drive-session row tagging so ui_review teardown drops exactly the drive-created rows#1333
mfittko merged 4 commits into
mainfrom
issue-1287

Conversation

@mfittko

@mfittko mfittko commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the ui_review Stage-5 teardown dev-DB row-drop real instead of honest-but-inert. Stage 5 ships a side-effect ledger + confirmation gate, but the row-drop could only report may-remain-untagged because the Stage-2 drive never tagged the rows it created. This adds drive-session row tagging so teardown can drop exactly the rows a drive created — dev DB only, fail-closed.

Scope and context

  • packages/core/src/loop/ui-review-drive.mjs + scripts/loop/ui-review-drive.mjs: the drive stamps a per-run drive-session id and emits a rowManifest (one session-stamped record { session, flow, step, action } per mutating step — click/select/upload/dispatch; goto/fill excluded) plus driveSession on its result envelope. It also advertises the session on an X-UI-Review-Drive-Session request header so a cooperating app can tag the dev-DB rows it persists under those requests.
  • scripts/loop/ui-review-teardown.mjs: the previously-inert dropRows seam is now real. On --confirm it resolves the shared session from the --row-manifest and deletes exactly those rows via a project-declared uiReview.run.rowTeardown.deleteCommand, run in the provisioned worktree with the session passed in UI_REVIEW_DRIVE_SESSION (env, not interpolated). The ledger reports dropped with the count. An absent/untagged manifest, a missing recipe, or a non-zero delete fails closed (may-remain-untagged / drop-failed) — never a guess, never a wrong-scope delete.
  • packages/core/src/config/config.mjs: new uiReview.run.rowTeardown.deleteCommand (optional; the operator-provided delete-by-session command).
  • docs/ui-review-recipe-contract.md: documents the session header, the deleteCommand recipe a consuming repo must provide, and the by-session tagging contract.

Fail-closed / trust boundary

The deleteCommand is trusted operator config (like a build command), run only under --confirm in the dev worktree; the session id crosses into it via the UI_REVIEW_DRIVE_SESSION env var (not shell-interpolated), so it cannot inject, and the worktree cwd is POSIX single-quote-escaped before the cd (so a branch-derived path with shell metacharacters can't break out). Dev DB only — no production path. Absent manifest / no recipe / non-zero exit all resolve to a stated ledger reason, not a guess.

Acceptance criteria

Satisfies #1287's ACs (checked off at merge): the Stage-2 drive emits a session-tagged row manifest in its result envelope; Stage-5 teardown drops exactly the manifested rows on --confirm (dev DB only) and the ledger reports dropped with the count instead of may-remain-untagged; the fail-closed paths are distinct — a genuinely absent/empty manifest resolves to may-remain-untagged, while an untagged/mixed-session manifest, a missing deleteCommand recipe, or a non-zero delete resolves to drop-failed (never a guess, never a wrong-scope delete); a test drives a fixture that creates rows and asserts the manifest is emitted and teardown drops exactly those on confirm (no real DB); npm run verify green.

Non-goals

Production row teardown. Migration rollback (the ledger records migrations; reverting is a separate action). Changing unrelated teardown behavior (worktree/app teardown, the artifact-hosting prune).

Validation

  • node --test test/loop/ui-review-drive.test.mjs test/loop/ui-review-teardown.test.mjs — 80 pass (drive emits the session-stamped manifest for mutating steps + a stable envelope shape on the no-recipe early return; teardown drops exactly the manifested rows on --confirm with the count, via an injected run seam — no real DB; absent manifest → may-remain-untagged, and untagged/mixed-session or missing recipe or non-zero delete or malformed worktree path → drop-failed, with the runner never invoked).
  • npm run verify — green (all suites: test:scripts, assets, extension, core, docs, pack, dev-loop); generate-claude-assets --check clean.

Closes #1287

@mfittko mfittko self-assigned this Jul 11, 2026
…ve-created rows

The Stage-2 drive now advertises a per-run drive-session id on the
X-UI-Review-Drive-Session request header and emits a session-stamped row manifest
(one record per mutating step) in its result envelope. Stage-5 teardown consumes
the manifest and, on --confirm, deletes exactly the tagged rows via the project's
uiReview.run.rowTeardown.deleteCommand (dev DB only). An absent/untagged manifest
or a missing recipe still fails closed to a drop failure or may-remain-untagged —
never a guess or a wrong-scope delete.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177JAWNT67FY6d48qQJeDwo
@mfittko

mfittko commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

Gate review: draft_gate

Reviewed head SHA: e611eea0ecd58c69f01bf151a56cb12a8baf355f
Verdict: clean
Execution mode: fanout_fanin

Findings summary: 7 angles reviewed; no findings (see per-angle breakdown below).

  • pr-description → clean
  • scope → clean
  • correctness → clean
  • coverage → clean
  • contract-surface → clean
  • code-conformance → clean
  • input-validation → clean

Next action: Draft gate clean at head e611eea (7 distinct reviewers; scope carried from 40a8b94). Shell-injection fixed + verified. Mark ready; request Copilot review.

@mfittko mfittko marked this pull request as ready for review July 11, 2026 00:55
Copilot AI review requested due to automatic review settings July 11, 2026 00:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes ui_review Stage-5 teardown capable of deleting exactly the dev-DB rows created during a Stage-2 drive run by introducing a per-drive “drive-session” identifier, emitting a session-stamped row manifest from the drive, and wiring the teardown CLI to run a project-provided delete-by-session command on confirmation.

Changes:

  • Stage-2 drive now generates/accepts a drive-session id, advertises it via X-UI-Review-Drive-Session, and emits a session-stamped rowManifest in the result envelope.
  • Stage-5 teardown CLI now resolves uiReview.run.rowTeardown.deleteCommand from config and executes it (confirmed only) with UI_REVIEW_DRIVE_SESSION set, reporting DROPPED with a count.
  • Adds/updates tests and docs to cover/configure the new rowTeardown.deleteCommand contract and validate fail-closed behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/loop/ui-review-teardown.test.mjs Adds end-to-end and CLI-seam tests verifying manifest-driven row drop behavior and fail-closed paths.
test/loop/ui-review-drive.test.mjs Adds tests asserting session-stamped manifest emission and the no-session empty-manifest behavior.
test/docs/ui-review-recipe-doc.test.mjs Updates schema-leaf count to account for the new rowTeardown config key.
scripts/loop/ui-review-teardown.mjs Wires a real dropRows seam: resolves rowTeardown.deleteCommand, extracts a shared session, runs delete command in worktree, and reports dropped count.
scripts/loop/ui-review-drive.mjs Generates a per-run drive-session UUID, sets it as an HTTP header, and passes it into core drive orchestration.
packages/core/src/loop/ui-review-teardown.mjs Updates core documentation to reflect session-tagged manifest model vs. prior “untagged rows” limitation.
packages/core/src/loop/ui-review-drive.mjs Introduces DRIVE_SESSION_HEADER, mutating-step manifest emission, and driveSession/rowManifest on the result envelope.
packages/core/src/config/config.mjs Adds optional uiReview.run.rowTeardown.deleteCommand config schema and resolves it into the run recipe.
docs/ui-review-recipe-contract.md Documents the header + session tagging contract and the new rowTeardown.deleteCommand recipe key.

Comment thread scripts/loop/ui-review-teardown.mjs
sessionFromManifest only collected non-empty sessions, so a manifest with one
tagged row and the rest untagged resolved to a single session and dropped it
while the untagged rows went unaccounted-for but were reported dropped. Any
untagged row now fails closed (refuse, DROP_FAILED), never a partial delete
misreported as complete.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177JAWNT67FY6d48qQJeDwo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread scripts/loop/ui-review-drive.mjs
…y return

The fail-closed return when no uiReview.login recipe is declared omitted the
documented driveSession/rowManifest keys (they were only set once a session was
generated later), so the CLI output shape depended on the failure mode. Emit
driveSession:null + rowManifest:[] there too, and pin it with a runCli test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177JAWNT67FY6d48qQJeDwo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread scripts/loop/ui-review-teardown.mjs Outdated
…he row delete

A present-but-non-string worktreePath signals a corrupted provision result; the
old fallback silently used the primary checkout as the delete cwd, risking a
wrong-scope destructive delete. Resolve a malformed path to null and refuse the
drop (DROP_FAILED) — distinct from an absent/empty path, which still legitimately
falls back to the repo root. The cwd guard runs first so the refusal is explicit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177JAWNT67FY6d48qQJeDwo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

@mfittko

mfittko commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

Gate review: pre_approval_gate

Reviewed head SHA: 9958c529e9d52f02639f51d1444d56c48fc9e9d2
Verdict: clean
Execution mode: fanout_fanin

Findings summary: 5 angles reviewed; 1 finding (see per-angle breakdown below).; Copilot review rounds exhausted (4/2); current head has zero unresolved threads and green or credibly green CI, so pre_approval_gate fallback is allowed without another Copilot re-request.

  • pr-checklist-matrix → clean
  • acceptance-criteria → clean
  • yagni → clean
  • correctness-final → clean
  • contradiction-lens → findings_present
    • [defer] Ledger rows.dropped is the manifested-step count, not confirmed DB rows deleted; disclosed in-code (dropRows detail + DROPPED comment). Observability nuance, not a wrong-scope delete. (scripts/loop/ui-review-teardown.mjs:274) — deferred

Next action: Pre-approval gate clean at head 9958c52 (5 distinct in-pool reviewers; input-validation also re-run clean as defense-in-depth). 1 defer-only (dropped-count observability). CI green; Copilot converged clean (4 rounds of fail-closed hardening). Proceed to merge.

@mfittko mfittko merged commit 3a57ddf into main Jul 11, 2026
13 checks passed
@mfittko mfittko deleted the issue-1287 branch July 11, 2026 01:39
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.

feat(loop): drive-session row tagging so ui_review teardown can drop drive-created rows (follow-up to #1121/#1118)

2 participants