Row 82 round 2, owner Rocko, candidate manifest dbf36e92 on 876f1ffe.
Approved by Filbert (27650); Sage's gate green 02:05-02:09Z.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Runs
Read-only readers for the run records under <dataRoot>/runs/ and the
release state under <dataRoot>/state/ (#1545). scripts/mosaic-task.mjs list and show read through it, and so will the console's runs view.
This README covers what the code does and the limits it accepts.
node --test packages/runs/tests/
What it reads
| Export | Returns |
|---|---|
listRunIds(dataRoot) |
every name under runs/ that starts with r-, sorted, which is oldest first |
listRunRecords(dataRoot) |
[{ runId, result }] for those names; result is null for an incomplete or unreadable record |
readRunRecord(dataRoot, runId) |
{ runId, result, task, mission, artifacts }, or null when the run doesn't exist or isn't a directory |
readRunDocument(dataRoot, runId, name) |
result.json, task.json or mission.json from one run, or null |
readActivePointer(dataRoot) |
state/active.json, or null when no release has been activated |
readActivationLog(dataRoot, { last }) |
{ entries, malformed } from state/activation-log.jsonl, oldest first |
isRunId(value), RUN_ID_PATTERN |
the run id shape mosaic-task.mjs checks: r- then 1 to 64 of [A-Za-z0-9._-], starting with a letter or digit |
The caller passes dataRoot; this package doesn't read the system
config. artifacts are names in directory order, as show has always
printed them.
Limits
-
Nothing writes. No reader creates, changes, prunes or locks anything. Run records stay write-once evidence; pruning stays in
mosaic-task.mjs prune. -
Nothing follows a link out of the data root. The configured data root is trusted as given, even when it is itself a link. Every path below it is resolved, and one that resolves outside it is refused or read as unreadable:
runs/orstate/resolving outside refuses with aRunsError;- a run directory resolving outside:
readRunRecordrefuses, andlistRunRecordsgives that run anullresult; - a run document resolving outside reads as
null; active.jsonoractivation-log.jsonlresolving outside refuses.
Only a path that exists can resolve. A link with nothing behind it reads as missing wherever it points. A
state/link out of the data root to a path that doesn't exist reads as no release and an empty log, and aruns/link like that lists nothing. A link that stays inside the data root is followed. -
Run documents are JSON objects or
null. A document that is missing, unreadable, not JSON, or JSON but not an object (null,5,[]) reads asnull. Run records are never validated beyond that, because older records carry older shapes. -
The release pointer is strict.
active.jsonmust be the version 1 shapescripts/release.shwrites: exactlypointerVersion1 and non-empty stringsrelease,imageTagandactivatedAt. Anything else refuses with exit code 2 rather than being guessed at. -
The activation log is read per line. One bad line never refuses the log. An entry needs string
at,event,releaseandimageTag, an optional stringnote, and no other keys. A line that isn't JSON, or is JSON with another shape, is counted inmalformedand skipped. That is stricter thanrelease.sh rollback, which skips only lines that aren't JSON and would act on an entry with an extra key or a non-string field. Blank lines aren't counted.lastmust be a positive integer and keeps the newest entries. -
Errors. Every refusal is a
RunsErrorwithexitCode2 (invalid data) or 4 (a file or environment problem), matchingmosaic-task.mjs. A missing data root,runs/orstate/file is not an error. A path that can't be resolved for another reason (a link loop, a permission error) or a directory that can't be read refuses instead of reading as empty.
How mosaic-task.mjs uses it
list and show print exactly what they printed before this package
existed, for any data root with no link out of it, no run document that
is JSON but not an object, no result.json with a field of the wrong
type (a status or taskId that isn't a string, or a workspace or
session that is neither a string nor null), and no unreadable
directory.
agents/rocko/work/queue-56/byte-check.sh checks that byte for byte
against a seeded data root. Where those conditions don't hold, the
output changes on purpose:
| Case | Before | Now |
|---|---|---|
run directory or result.json linked out |
read through the link | list: unknown; show: refuses (run directory) or result.json: (missing or unreadable) |
runs/ linked out |
read through the link | list and show refuse, exit 4 |
result.json is 5 or [] |
list crashed; show printed undefined fields |
unknown; (missing or unreadable) |
result.json is an object whose status or taskId isn't a string, or whose workspace or session is neither a string nor null ({}, a numeric status, a null taskId) |
list crashed on a status or taskId that isn't a string, hiding every run after it, and printed a workspace or session that is neither a string nor null through String |
list: unknown for the status, - for each other field, and every later run still lists; show as before |
task.json or mission.json is JSON but not an object |
show printed its snapshot line |
snapshot line omitted |
| run is a regular file | show crashed |
run not found, exit 4 |
runs/ unreadable |
list printed nothing |
refuses, exit 4 |
| run directory unreadable (mode 000) | show printed two lines, then crashed, exit 1 |
refuses with EACCES, exit 4 |
| link loop or a permission error resolving a run | run not found |
refuses with the error code, exit 4 |
agents/rocko/work/queue-56/delta-check.sh prints the before and after
for each case.