Skip to content

feat: explicit local run lifecycle with list and prune - #32

Merged
EauDoon merged 1 commit into
mainfrom
feat/run-lifecycle
Sep 6, 2026
Merged

feat: explicit local run lifecycle with list and prune#32
EauDoon merged 1 commit into
mainfrom
feat/run-lifecycle

Conversation

@EauDoon

@EauDoon EauDoon commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Capability

aas runs (newest-first with stage status, --json supported) and
aas prune --keep <n> (--dry-run preview, --json supported).
Retention is explicit and non-destructive by default: no automatic
deletion exists, --keep is required, the latest-pointer target is
always kept, and dry runs change nothing.

Verification

  • npm test: 5 new tests (listing order and shape, keep-window pruning
    plus export-after-prune, latest-pointer protection, input validation
    and empty stores, CLI argument handling).
  • Live runs against the dev checkout (41 accumulated runs): listing,
    dry-run, and validation paths exercised.

Devin Review

Run bundles accumulated without bound. `aas runs` lists persisted runs
newest-first with stage status, and `aas prune --keep <n>` removes
oldest runs beyond the window, with `--dry-run` preview. The run the
latest pointer identifies is always kept so exports and downloads never
dangle, and nothing is deleted without an explicit keep count. Missing
stores report empty instead of failing.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 3 potential issues.

2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread bin/aas.mjs
for (const run of runs) {
if (keepSet.has(run.run_id)) continue;
removed.push(run.run_id);
if (!dryRun) rmSync(join(runsDirectory(outputRoot), run.run_id), { recursive: true, force: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Concurrent pruning can erase completed runs

When overlapping demos finish out of order, pruneRuns can delete a published run before its latest pointer is written. persistRunBundle then writes that pointer after deletion. The run is lost, and latest exports fail.

Prompt for agents
Prevent pruneRuns in bin/aas.mjs from racing with persistRunBundle across processes. persistRunBundle publishes the final run directory before updating latest.json, so an older-ID run that completes after a newer-ID run can be deleted during that interval and then become the latest pointer target. Serialize publication and pruning with a shared lock, or introduce a lifecycle marker that makes newly published runs ineligible for deletion until pointer publication completes. Preserve atomic latest-pointer updates and ensure interrupted operations remain recoverable.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread bin/aas.mjs
Comment on lines +1067 to +1068
} catch {
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Unreadable latest pointers disable retention

When latest.json is unreadable, malformed, or invalid, readLatestRunId silently treats it as absent. pruneRuns can then delete its target when that run falls outside the keep window.

Prompt for agents
Make pruneRuns fail closed when latest.json exists but cannot be read, parsed, or validated. Only treat ENOENT as an absent pointer. Validate the pointer schema and run_id before any deletion, and surface other failures so pruning cannot silently drop latest-target protection.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread bin/aas.mjs
} catch {
continue;
}
if (!manifest || typeof manifest !== "object") continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Unrelated run directories can be deleted

A valid-named directory with any JSON object in manifest.json passes listRuns without a matching run ID or schema. pruneRuns can recursively delete that unrelated directory outside retention.

Suggested change
if (!manifest || typeof manifest !== "object") continue;
if (
!manifest
|| typeof manifest !== "object"
|| Array.isArray(manifest)
|| manifest.schema_version !== "agent-action-stack.run/v1"
|| manifest.run_id !== entry.name
) continue;
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@EauDoon
EauDoon merged commit d45d9dc into main Sep 6, 2026
9 checks passed
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