fix(cli): guard bulk/overwrite CLI ops against partial state and clobbering (#778) - #886
Conversation
…bering (#778) - tasks set --all: an invalid transition no longer aborts the loop mid-way leaving partial state; failures are collected and reported (Updated N / Skipped M / K failed). Single-task mode still errors. - init --generate-config: existing CODEFRAME.md is preserved with a warning; new --force flag overwrites explicitly. - batch follow: resume tailing from the newest event instead of replaying history into progress counters already seeded from batch.results (completed tasks were counted twice). Ambiguous batch-id handling (stop/status/follow) was already fixed by #825 and is regression-tested in tests/core/test_batch_truncation.py.
- batch follow: seed in-flight tasks (BATCH_TASK_STARTED without a terminal result) so the running count and ETA are correct when attaching mid-batch — batch.results only records terminal states. - tasks set --all: exit 1 when every transition failed (total failure must not look like success to scripts); partial success stays 0.
|
Claude finished @frankbria's task in 5m 18s —— View job GLM precision review — bug huntReviewed only the production change in
No defects found. Summary posted via |
Cross-family review (opencode / GLM) — final pre-merge passReviewer: I'll verify both fixes against the actual codebase to confirm correctness.Let me verify the Fix #1 — in-flight task seeding (
Fix #2 — all-fail bulk update exits 1: CORRECT
New findings
Recommend merge. |
|
✅ GLM review: no defects found. Scope: the three production hunks in Notes on the highest-risk spots (all clean):
|
Closes #778.
What changed
Three of the issue's four sub-problems needed fixes; the fourth was already shipped.
tasks set --allno longer aborts mid-loop on an invalid transition. Failures are collected per task and reported (Updated N / Skipped M / K failedwith task ids). Valid transitions are always applied; total failure (nothing updated, failures present) exits 1 so scripts don't read it as success. Single-task mode keeps the loud error (exit 1).init --generate-configno longer clobbers an existingCODEFRAME.md. It warns and preserves the file; the new--forceflag makes the overwrite explicit.batch followno longer double-counts completed tasks. It resumed from the oldest recent batch event (list_recentis newest-first but the code indexed[-1]), replaying history into progress counters already seeded frombatch.results. It now resumes from the newest event, and seeds in-flight tasks (STARTED event, no terminal result) so the running count and ETA are correct when attaching mid-batch.tests/core/test_batch_truncation.py. No change needed; verified in the demo below.Review
followattach (real display regression of the first version of this fix); all-fail bulk update exiting 0. Declined with reasoning: catchingValueErrorfor concurrently-deleted tasks mid-loop (needs a concurrent writer; pre-existing), the microsecond attach race (self-healing — final summary re-reads from disk), erroring on a lone--force(matches repo modifier-flag convention).Demo evidence (per acceptance criterion)
cf tasks set status DONE --allover READY/IN_PROGRESS/BACKLOG tasksUpdated 1 … 2 failed (invalid transition)with task ids;tasks listafterwards shows only IN_PROGRESS→DONE applied, others untouchedcf tasks set status BACKLOG --all(unreachable from all states)Updated 0 … 2 failed, exit code 1--forcecf init . --generate-configover a hand-edited filecatshows content unchanged;--forcere-run regenerates it (front matter shown)cf work batch status aawith twoaa…batchesMultiple batches match 'aa'+ both candidates, exit 1followresumes from newest eventCOMPLETED 2/2event printed — no historical replay, count correctFull narrated demo (Showboat, real command outputs): session scratchpad
demo-778.md.Tests
main'sapp.py.ruffclean, strictmypyclean on changed files.Known limitations
followreconstructs in-flight tasks from the last 100 events; a task started earlier than that window shows as pending until its terminal event (commented at the site).InvalidTransitionError; a task deleted concurrently mid-loop would still abort (pre-existing, needs a concurrent writer).