Repository navigation
Conversation
Deleting an automation dropped its run history outright. Now delete_automation refuses while any run is queued or running (mirroring delete_terminal_run), moves completed/failed/canceled runs under <root>/archive/<automation_id>/ before removing the live paths, and bounds the archive to the most recent 50 terminal runs per automation. The archive tree sits beside automations/, runs/, and triggers/, so live listings and scheduler scans never see it; list_archived_runs exposes the retained history. Signed-off-by: asto <asto18089@126.com>
|
Stacked consumer: Pinvou/pinvou-agent#653 pins |
|
Fresh full review at head What is solid (verified, not taken on faith)
BlockerB1 — Deterministically breaks an existing test, undisclosed; full MajorM1 — The documented refusal guarantee is not enforced for in-flight enqueues. Both enqueue paths deliberately drop the manager mutex across the task-manager await and persist the run record only afterward ( M2 — The API-surface story overstates what ships; the archive is write-only in this fork. Minor
VerdictUseful, root-caused, faithfully adapted, smuggling-free — but not shippable as-is: the PR knowingly leaves the engine's own endpoint smoke test red without disclosing it (B1), claims a refusal guarantee its own base design can bypass in the exact window it exists for (M1), and presents a write-only, unbounded-at-the-root archive as a bounded API an embedder will delegate to (M2). B1 + the m1 signature change + the M1/M2 rewording are small, well-scoped fixes; happy to re-review on push. |
list_archived_runs returns anyhow Result like every sibling method
instead of flattening to an opaque io::Error, and GET
/v1/automations/{id}/runs?archived=true serves the archive with the
usual limit, porting the upstream runtime-API surface so the retained
history is actually reachable by clients.
Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
Deleting is refused while any persisted run is queued or running; a run still awaiting the task manager is not yet on disk, so deletion in that in-flight window is not refused and the record persists outside the archive as before (upstream closes this with its transaction machinery, which the fork base does not have). Also state the archive's real bounds — per automation only, root never compacted — and the externally-resolved failure modes for corrupt files and task-less active runs. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
Sorting the terminal runs and writing just the newest 50 stops the delete path from writing files that the retention sweep immediately deletes again; the sweep stays so leftovers from an interrupted earlier attempt stay bounded. The rewritten comment now says what the crash windows actually guarantee: rename-atomic writes keep the history across a process crash, and an automation file removed before the live tree leaves that tree orphaned, not dropped. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
The endpoint smoke now exercises the delete-refusal contract (400 naming the reason, automation and run untouched, live automation has no archive) and round-trips a seeded archived record through ?archived=true, which upstream merged red in codewhale-hq#6864. Manager-level tests gain the running refusal arm, the canceled filter arm, exact archived file names (created_at stamping, legacy normalization), and a due-before positive control on the scheduler scan. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
|
Fix wave pushed (4 commits, head B1 (blocker) — red endpoint smoke test: M1 (major) — refusal claim scope: the doc comment and body now say persisted runs block deletion and explicitly disclose the in-flight-enqueue window (the record persists afterward outside the archive — pre-existing behavior pinned by M2 (major) — write-only archive: m3/m2 — comment accuracy and failure modes: the crash comment now states what the windows actually guarantee (rename-atomic writes survive a process crash; an automation removed before the live tree leaves it orphaned, not "dropped"), notes the absence of fsync, and the docs list the externally-resolved failure modes (corrupt/newer-schema file, task-less active run). m5 — retention waste: the delete path sorts terminal runs and writes only the newest-50 slice; the retention sweep remains as the bound on leftovers from an interrupted earlier attempt. m4 — test pins (mutation-checked): the refusal test loops over Queued and Running and asserts the active run survives untouched via Verification at head |
|
Fresh round-2 review at head Verdict: real, complete within its stated scope, faithful to upstream, and bug-free as far as nine lanes plus mutation analysis could establish. Two documentation/test-completeness gaps are being fixed in a follow-up wave below; one copy decision is disclosed for input. 1. Real value and root cause — verified. The premise is real at base: 2. Upstream parity — faithful. The endpoint hunk is byte-identical to codewhale-hq#6864; retention (50), naming, refusal message, and archive helpers are identical. The two structural upstream mechanisms (transaction/claim serialization, suppressed-run visibility) do not exist in the fork base and are disclosed as deliberate follow-ups, not silent drops. The body's "upstream merged with that smoke test red" claim is verified against upstream CI logs: the ubuntu job's only failure is 3. Smuggling — CLEAN. Exactly the 3 described files; each of the 5 commits contains only its claimed scope; hunk-by-hunk classification found zero non-feature changes; no weakened or deleted tests (all base assertions of the replaced test retained verbatim); no debug leftovers, secrets, or personal paths. 4. Bugs — none found. Concurrency: the single shared manager mutex serializes delete against every reader, the scheduler tick phases, and the tool callers; status matching is wildcard-free over the exhaustive 5-variant enum, so a queued/running run can never be silently archived; the archive is provably invisible to Findings
Considered and rejected (recording so they are not re-raised): moving Fix wave incoming: RUNTIME_API.md documentation + the three test pins + the smoke precondition assert, then the full gate set re-run. |
The automations section now names the archived=true query flag, the 50-run archive it reads, the ended_at/created_at ordering with limit, and the DELETE refusal (400) while a persisted run is queued or running — matching the per-parameter provenance style the threads section already uses. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
Three claimed contracts had no pin: the retention sweep's removal loop (pre-seed 52 archived leftovers, delete, assert exactly the newest 50 survive on disk and in the listing), the archived=true limit branch (limit=1 keeps the newest over HTTP, limit=0 is empty), and the fail-closed contract for unreadable run files (a corrupt live file and a newer-schema archive file each fail the delete, naming the file, leaving automation and runs untouched). The smoke also asserts its refusal precondition (the run really is active on disk) and the archived listing's HTTP-level ordering; the manager filename pin now hardcodes one stamp so the format itself is covered, not just the helper's own output. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
|
Fix wave pushed (2 commits, head
Gates at |
Summary
Deleting an automation used to drop its run history outright:
delete_automationremoved the live runs directory, so a user deleting a task lost every record of what it executed. This keeps terminal run history in a bounded archive instead.Pinvou (the embedder this fork serves) ships a richer app-side capability (
ArchivedScheduledTaskSnapshotwith display metadata anddeleted_at). This engine archive is the foundation layer that preserves the run records themselves; the app-side delegation is tracked as follow-up work in Pinvou/pinvou-agent#653 (its Chop D is deliberately not chopped there), since the app contract additionally needs deletion timestamps, display metadata, and an uncapped-history mode that this PR does not attempt.Archive layout and retention
AutomationRunStatusvocabulary) are moved to<automation_root>/archive/<automation_id>/<created_stamp>-<run_id>.jsonbefore the live paths are removed; legacy{run_id}.jsonlive files are normalized to the sortable name. Each record is written with the existingwrite_json_atomichelper (rename-atomic, not fsynced — process-crash safe, not power-loss durable); no schema changes to persisted records.ended_atfalling back tocreated_at(the same recency rule asterminal_run_prune_candidates); only that slice is written, and a retention sweep still bounds any leftovers from an interrupted earlier attempt. Per automation is the only bound: the archive root keeps one capped directory per deleted automation and is never compacted.automations/,runs/, andtriggers/, solist_runs,get_runs_by_ids,terminal_run_prune_candidates, and the scheduler scan (collect_due_runs→list_automations) cannot see it. A deleted automation cannot come back due from its archive.delete_terminal_runrefusal. Scope note: a run whose enqueue is still awaiting the task manager is not yet on disk, so deletion in that in-flight window is not refused; the record is persisted afterward outside the archive — pre-existing behavior, pinned byfinish_scheduled_run_persists_run_when_automation_deleted_mid_enqueue. Upstream enforces this case with its transaction/claim machinery, which the fork base does not have; closing it here is follow-up work, not part of this PR.AutomationDeletedRunsDetail/AutomationDeletePreview, 15 locales); under this change those runs are archived (up to 50) rather than destroyed. Rewriting the copy needs a 15-locale translation pass and is follow-up work; upstream feat(automation): archive terminal runs on delete codewhale-hq/Codewhale#6864 carries the same copy.API surface
AutomationManager::list_archived_runs(automation_id) -> Result<Vec<AutomationRunRecord>>(anyhow, matching every sibling method) — retained archived runs, newest-first byended_at/created_at.GET /v1/automations/{id}/runs?archived=trueserves the archive (honoringlimit), porting the upstream runtime-API surface (feat(automation): archive terminal runs on delete codewhale-hq/Codewhale#6864).Verification
cargo fmt --all -- --check— clean.cargo test -p codewhale-tui --lib— full suite green, including the runtime endpoint smokeworkspace_and_automation_endpoints_workupdated for the refusal contract: DELETE with an active run → 400 naming the reason with the automation and its run untouched; a run-free automation deletes and itsarchived=truelisting round-trips a seeded record. Upstream feat(automation): archive terminal runs on delete codewhale-hq/Codewhale#6864 merged with that smoke test red; fixed here. The manager-level tests now pin both refusal arms (queued, running), the canceled filter arm,ended_at-over-created_atarchive ordering, archived file naming with legacy normalization, and the scheduler's due-before/due-after behavior around the archive.cargo clippy -p codewhale-tui --lib— no warnings for this change.No-Issue: engine foundation for the Pinnou scheduled-task archive; app-side delegation tracked in Pinvou/pinvou-agent#653 (Chop D).