Cherries workflow UX¶
Status: implementation and target UX, 2026-10-05. The local CAS runtime now
implements ordinary cherries.main(main) capture, active-run asset helpers,
record sealing, archive/restore, metadata merge, review/labels, materialization
leases, lightweight analysis records, rerun, payload-free metadata checkpoints,
and receipt-bound single-machine maintenance. The authoritative implemented
contract is the user documentation. This document still specifies
target work that is not available yet, including automatic environment
reconstruction, remote checkpoint compaction, remote attempt transfer, and
destructive fleet maintenance.
The daily flow is save first, review later. Cherries captures computation evidence, supports lightweight follow-up work, and archives complete records through rclone to a SHA-256 content-addressed store. Successful results start unreviewed. Keeping, reviewing, and using a result are independent actions. Daily execution remains a normal Python command.
Work locations¶
Use a separate experiment Git repository with Apple and Melon as submodules:
experiments/
pyproject.toml uv.lock cherries.toml
libs/apple/ libs/melon/
exp/2026/10/05/mouthopen/
experiment.toml
src/10-run.py
configs/baseline.toml
fixtures/
docs/
analysis/compare-neutral/
analysis.toml
src/10-compare.py
RUN.md
out/ # ignored drafts; select outputs when saving
Authored scripts, configurations, reports, and optional small receipts belong in Git. LFS holds curated fixtures and selected published assets. Local and remote stores share this object, record, and metadata schema:
<storage-root>/objects/sha256/ab/<full-digest> # immutable raw blob, once
<storage-root>/records/<id>/ # record.json, manifest.json, RUN.md
<storage-root>/work/<id>/ # independent/reflinked active copies
<storage-root>/runs/<id>/ # optional ordinary materialized view/cache
<storage-root>/metadata/ # events and checkpoints
work/<id>/inputs and outputs are ordinary writable copies or reflinks, never
writable hardlinks into objects. Records map logical paths to asset IDs; they
are not permanent per-run payload copies. Bundle manifests are themselves
content-addressed sha256-tree inventories of child file hashes.
Run an experiment¶
Initialize the local storage volume once, then execute the script normally:
cherries init --storage /data/cherries/research
python exp/2026/10/05/mouthopen/src/10-run.py --steps 200
# Or use the project's uv environment:
uv run python exp/2026/10/05/mouthopen/src/10-run.py --steps 200
Keep the usual imports, config, and function definitions, with this call at the bottom of the script:
cherries.main(main) is the in-process recording boundary. It allocates a UUID
work folder on the configured data volume, captures the entry script; each
relevant experiment repository and submodule's Git HEAD and combined binary
diff; selected untracked code copied separately; supplied configuration; and
runtime evidence.
It invokes main once. On success it imports retained payloads into canonical
objects, stages, fsyncs, and renames complete local record metadata, appends a
sealed event, then unregisters and reclaims disposable work. There is no default bootstrap, relaunch, sandbox,
daemon, or manual save step.
This is observed provenance at the main boundary: it cannot prove source state
already imported before the call. Record a source-stability flag and never claim
replay_verified. The Git base commit must remain obtainable for replay; the
record does not retain complete environment/build artifacts.
Asset helpers are active only inside main. Config defaults are raw strings or
paths and must not call input, output, or temp during class definition:
class Config(cherries.BaseConfig):
mesh: str = "sha256:<full-digest>"
def main(cfg: Config) -> None:
mesh = cherries.input(cfg.mesh, name="mesh.vtu")
result = cherries.output("solution.npz")
scratch = cherries.temp("solver")
# Read mesh, write result, and use scratch here.
input accepts a full sha256: file digest, a local path/source string, or an
optional run:<id>/path reference. It copies verified bytes to work/inputs.
output returns work/outputs; log_output copies an external result there;
and temp returns disposable work/scratch. A missing required output or a
storage failure yields incomplete, nonzero status, and a retained stage.
On exit zero and a complete recording contract, Cherries imports retained bytes to CAS, publishes the manifest/record and sealed event, then reclaims work. It reports its ID, execution outcome, validation outcome, and unreviewed review state. Stdout/stderr, configuration, source, asset hashes, metrics, and runtime evidence remain in records and objects. Execution failures keep a small receipt and may discard only a stopped, unpinned, never-published, unsealed local leaf payload with no dependent or hold.
cherries show <run-id> --json
cherries read <run-id> RUN.md
cherries path <run-id> outputs/solution.npz
cherries browse --asset sha256:<full-digest> --json
cherries rerun <run-id>
show --json includes retained output paths and their asset_id values.
rerun is a new attempt; it never mutates its source record. A Git commit is
optional navigation: record authored changes and submodule commits deliberately,
then associate it with cherries link <run-id> --git <commit>.
Assets and provenance selection¶
The SHA-256 of file content is its asset_id, stored canonically at
objects/sha256/<2hex>/<full-digest>. Manifests and the catalog index each
asset by run, relative path, digest, and local-or-remote location. Identical
bytes share one object while retaining several producer records.
For a digest or unqualified asset reference, Cherries selects a complete local
producer first and otherwise a remote producer. It verifies bytes, records the
chosen producer run/path and all digests, and registers the parent edge before
the caller reads it. source_run=<id> restricts that choice. An unknown hash is
a visible error; first log or import a local path in a run rather than inventing
a separate asset service or import command.
A plain file digest says nothing about companion files. A bundle ID is
sha256-tree over a canonical inventory of every companion digest; use bundles
for series, meshes, and other multi-file artifacts. Deduplication is exact-byte
identity only; it makes no near-duplicate, block-deduplication, or compression promise.
Make a lightweight view or comparison¶
Create an editable workspace referencing the saved sources:
cherries analysis new exp/2026/10/05/mouthopen/analysis/compare-neutral \
--source <baseline-id> --source <new-id> --name "Neutral comparison"
Write a visualization script, save an application scene, or describe manual
inspection in this folder. Findings go in its RUN.md or other authored
documents. Python, ParaView, and the current library environment are available;
a complete replay environment is optional for this workflow. path materializes
an artifact's declared companion files too, so a ParaView series comes with its
frames. Save the scene/settings and actual displayed source references when
available.
cherries analysis save exp/2026/10/05/mouthopen/analysis/compare-neutral \
--output out/comparison.png --output out/view.pvsm
cherries analysis close exp/2026/10/05/mouthopen/analysis/compare-neutral
Save captures the current scripts/procedure, findings, selected artifacts, and
exact source references as a lightweight record. Repeated saves receive new IDs.
Select outputs with repeated --output options or [save].outputs in
analysis.toml; a missing declared output fails save. Drafts in out/ are not
saved implicitly, and notes-only saves may select no outputs.
The workspace remains editable. Closing releases its workspace hold and local
reader leases; saved analyses retain parent edges and protect their sources.
The saved
computation's own RUN.md remains frozen. Use cherries note <id> --file FILE
for later observations shown alongside it.
Review results later¶
Four distinct controls answer different questions:
| Control | Example | Effect |
|---|---|---|
| Quality review | unreviewed, good, bad, inconclusive | Organizes subjective assessment with findings/history. |
| Free-form labels | promising, baseline, mouthopen | Filters related records; multiple labels are allowed. |
| Important | mark important | Protects logical retention. |
| Keep local | mark keep-local | Preserves this machine's local materialization. |
Execution and scientific validation are displayed alongside these controls. They are not overwritten by a subjective good/bad judgment.
cherries browse --quality unreviewed --json
cherries review <id> --quality good --note "Useful fit for the next comparison"
cherries review <id> --quality bad --note "Poor match; execution was valid"
cherries label add <id> mouthopen promising
cherries label remove <id> promising
cherries mark <id> --important
cherries mark <id> --keep-local
cherries browse --quality good --label mouthopen --json
cherries browse --quality bad --json
The review queue should show available previews and metrics beside name, ID, age, kind, execution/validation, review, labels, importance, meeting uses, and local/remote availability. It loads detailed arrays only when requested. Review and labels can change later, while their history remains available. A bad label does not discard data. Unreviewed results remain saved. A good review does not silently pin a record; use the important control explicitly.
Record weekly meeting use¶
A meeting selection is a lightweight analysis of the results actually used:
cherries analysis new exp/2026/10/05/weekly-meeting \
--source <run-a> --source <run-b> --name "Weekly meeting selections"
# Save meeting notes in RUN.md and select presented figures/tables.
cherries analysis save exp/2026/10/05/weekly-meeting --used-in weekly/2026-10-05
cherries analysis close exp/2026/10/05/weekly-meeting
cherries browse --used-in weekly/2026-10-05 --json
The saved meeting record preserves exact source/artifact references and protects
those runs through parent edges. Its context is searchable from the source run.
An ordinary used-in-weekly-meeting label is convenient categorization; the
registered meeting record is the reference that provides retention protection.
A meeting record can omit plots if it only records findings and referenced runs.
Archive and look back¶
cherries archive <id> [<id>...] --remote main
cherries archive <id> --remote main --evict
cherries restore <id>
cherries browse --remote --quality good --label mouthopen --json
cherries browse --remote --asset sha256:<full-digest> --json
cherries show <id> --json
cherries read <id> RUN.md
cherries path <id> outputs/preview.png
cherries sync --remote main
cherries prune --plan
Archive uses rclone to upload missing canonical objects, reads them back and
verifies SHA-256, then publishes record manifests/control metadata and its
completion marker last. --evict needs that verification plus no local need by
other resident records, active work, reader leases, or this machine's keep-local
flag. Importance protects logical retention, rather than local eviction. It can
free only eligible local bytes because an object may be shared by another record; logical discard never
deletes a blob. Restore pulls missing objects, verifies them, and makes a named
ordinary run view. path resolves a manifest into a verified partial view and
retains a reader lease until analysis close or path --release LEASE.
Rclone alone provides neither portable atomic same-key publication nor SHA-256 proof. The remote must provide documented atomic immutable publication, or Cherries must serialize foreground publishers through supported atomic locking or explicit coordination. Verify the complete object closure; publish the marker last. Never use a marker-file lock or time-based stale-publisher takeover.
Before publishing a complete marker, validate every parent receipt ID and digest, reject self-parents/cycles, and resolve pending parents. Unknown or conflicting parents remain pending and block destructive operations.
Reviews, labels, notes, meeting references, and retention state are preserved in remote metadata. A fresh machine can fetch a compact metadata checkpoint and subsequent events, build an index on demand, and query without retaining a permanent local mirror. If those files were never synced, objects alone cannot invent the missing history. The CLI reports unknown fields explicitly.
Codex uses browse for selection, show for provenance and availability,
read for findings, and path for concrete assets. No persistent browser or
full metadata mirror is required.
Failure and cleanup presentation¶
| Situation | Display and behavior |
|---|---|
| Successful experiment, value uncertain | Saved; unreviewed. |
| Successful execution, failed scientific validation | Saved; validation failed; awaits manual review/discard. |
| Later review says bad | Still saved; appears in cleanup candidates but has no automatic deletion. |
| Execution exits nonzero or confirmed signal failure | Failure receipt/diagnostics preserved; eligible stopped unsealed local payload discarded. |
| Computation finishes, saving/required output fails | Recording incomplete; retain stage for recovery. |
| Process outcome unknown | Incomplete; establish outcome before retirement. |
| Remote upload/verification interrupted | Archive pending; local data retained. |
| Selected remote object fetched | Partial view; no claim of complete restore or replay. |
| Source has an existing dependent or active workspace hold | Discard unavailable; explain the referencing record/hold. |
| A registered machine cannot join deletion maintenance | Collection-wide discard waits for reconciliation. |
Rare cleanup starts from a preview (discard <id> --plan), including dependency,
importance, use, and location information. Logical discard records tombstones
but never deletes canonical blobs. prune is a reviewed CAS mark/sweep over all
live records, pending/active holds, and roots. Local GC freezes every
reference-creating path sharing that store; remote GC freezes all reference-creating
operations in the collection namespace and requires every registered machine paused/synced.
An offline machine blocks it. The command never interprets "bad" as permission
to bypass dependencies, importance, or unsynchronized writers.
Closing a browser does not cancel a computation. The Python process and main wrapper stay foreground
unless another explicitly selected process owner is implemented; this proposal
does not promise that closing its terminal is safe.