Skip to content

v0.5.1 — workflows become first-class in deletion and the dashboard

Choose a tag to compare

@crAK1644 crAK1644 released this 15 Aug 15:53
· 67 commits to main since this release
a7ba5cd

0.5.0 shipped the workflow layer, but nothing else in the project had been taught that workflows exist. This teaches deletion and the dashboard about them, and fixes the one live defect that gap left behind. Nothing in the 0.5.0 consult, review or workflow tools breaks: no new write surface, no new runtime capability, no new network access.

A workflow-owned review can no longer be deleted out from under a workflow

ReviewStore._delete never looked at workflow_id, and request_delete_all snapshotted every review. Consultations were already protected from exactly this; reviews were not, and workflow_steps.review_id carries no REFERENCES clause, so the dangle was silent.

It was not only a dangle. _open_findings reads the review back through ReviewService.get, which answers a missing row with an error envelope rather than an exception — so the next fix round would have been handed an empty finding list and would have answered from the goal instead of from the review.

Both delete paths now refuse those reviews and point the caller at orchestrator_delete_workflow. request_delete_all excludes them from the snapshot, so the count shown is the count deleted.

Workflows can be deleted

Three tools, advertised only when workflow: is configured: orchestrator_delete_workflow, orchestrator_request_delete_all_workflows, orchestrator_delete_all_workflows. workflow_id is required — an omitted argument never means erase everything.

  • Ids are snapshotted at request time, not re-selected at confirm time, with the same three refusals as the review path (not outstanding / expired / already spent), spent in the statement that reads them.
  • A workflow that is not terminal is refused and pointed at orchestrator_workflow_cancel. A step holding an unexpired lease is refused too: that lease may be a live agent process in another server, and deleting its rows would leave it writing into a workflow that is gone.
  • The whole tree goes in one transaction, and the review teardown is reused rather than copied.

Ownership is reviews.workflow_id and nothing else. parent_review_id is a link, not ownership: a caller's recheck of a workflow's review carries workflow_id = NULL, because orchestrator_review has no workflow_id argument. Walking descendants downward would sweep that recheck and its consultations into a delete no approval ever described, and would widen the set past the ids the token snapshotted — which is the whole reason ids are approved rather than a count. The surviving child's parent link is cleared before its parent row goes, so it lives on as a root of its own.

The dashboard can see workflows

/workflows and /workflows/<id>, with a monitor tile, a step timeline ordered by round_index, attempt, sequence, links from a step to its consultation and its review, and a back-link from a consultation to its workflow. Read-only throughout — deletion stays on the MCP tools, where the confirmation token lives.

Verification

943 tests, offline, against stub executables — no mocks and no network. Every guard has a test proven to fail without it: neutered, confirmed red, restored. That includes the two ownership tests and the invariant that a step's review_id and its review's workflow_id name the same workflow.

Walked by hand against a sqlite3 .backup copy of a real database, never the original, and a throwaway workflow created, cancelled and deleted in a scratch repository with every table checked for leftovers.

Reviewed twice through this project's own orchestrator_review. The first review found the descendant-walk defect that all 939 tests had missed; the recheck confirmed the fix and raised one completeness finding, which is on the record as open and answered with the invariant test rather than with a second definition of ownership.

Known follow-up

workflow_steps.review_id still carries no REFERENCES clause. The invariant holds by construction and is now asserted in tests, but the enforcing constraint is a migration on a shipped table and belongs to its own change.