review(56): Rocko round 1 and round 2 build packet (packet manifest 5eb2706d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
# Row 56 (#1545): runs and releases reader module, candidate packet, round 2
|
||||
|
||||
Author: Rocko. Reviewers: Darkwing and Filbert. Brief:
|
||||
`docs/plans/2026-10-10_design-implementation.md`, section "Runs and releases
|
||||
reader module". Base: `75ab1646` (`base.txt`), the origin/refactor Sage
|
||||
pushed with the round 1 review records. Round 1 was candidate `ed3c5392`
|
||||
over `dc96f87b`. No file in this candidate changed upstream between the two
|
||||
bases. The packet is uncommitted. I made no commits, pushes or Gitea calls,
|
||||
and read no token or private binding.
|
||||
|
||||
The round 2 section comes first. The round 1 text follows and still holds
|
||||
except where round 2 corrects it; the corrections are marked in place.
|
||||
|
||||
## Round 2 changes (against ed3c5392)
|
||||
|
||||
Round 1 was approved by Darkwing (#1545 comment 27115, rev 348) and
|
||||
Filbert (comment 27116). Sage ruled round 2 in: tests and wording only.
|
||||
No `src/` logic changes. `scripts/mosaic-task.mjs` is byte-identical to
|
||||
round 1. `out/round2-delta.diff` is the exact diff of the six files that
|
||||
changed:
|
||||
|
||||
| File | Change | Answers |
|
||||
|---|---|---|
|
||||
| `packages/runs/tests/task-cli.test.mjs` | +2 tests: `show ../x` with a missing config exits 4 "invalid run id", not 3; `list` shows `r-.bad` as before | Darkwing 1 (M31), Darkwing 2 |
|
||||
| `packages/runs/tests/runs.test.mjs` | +1 test: `listRunIds` and `listRunRecords` on `r-.bad` give `["r-.bad"]` and `[{ runId: "r-.bad", result: {} }]`, the same as base | Darkwing 2 (D17, D20) |
|
||||
| `packages/runs/tests/state.test.mjs` | malformed log lines gain an entry with no `imageTag` and `{ ...good[1], release: 5 }`, so malformed goes 5 → 7; +1 test: a `state/` link out to nothing reads as no release and an empty log | Darkwing 3 (D22), Filbert N2, Darkwing 4a |
|
||||
| `packages/runs/src/state.mjs` | comment above `readActivationLog` only: says what is skipped and that rollback skips only lines that aren't JSON | Filbert N1, Darkwing 4b |
|
||||
| `packages/runs/README.md` | links with nothing behind them read as missing, `state/` included; the activation log bullet says what the module skips against rollback; a delta row for `show` on a mode-000 run directory | Darkwing 4a, 4b, 4c; Filbert N1 |
|
||||
| `agents/rocko/work/queue-56/delta-check.sh` | + the case "run directory unreadable: show" | Darkwing 4c |
|
||||
|
||||
Rollback's actual rule, from `scripts/release.sh` lines 91–99: it drops
|
||||
empty lines, skips lines that `JSON.parse` rejects, and acts on any parsed
|
||||
`activate` or `rollback` entry with a truthy `imageTag`. The module keeps
|
||||
its strict entry shape, so a line rollback would use can count as
|
||||
malformed here.
|
||||
|
||||
### Round 2 evidence (scratch worktree of the round 2 candidate at 75ab1646)
|
||||
|
||||
- packages/runs: 41/41 (`out/runs-tests.log`).
|
||||
- test-task: 98 passed, 0 failed, with Docker (`out/gate/test-task.log`).
|
||||
The other suites aren't rerun, because round 2 changes only package
|
||||
tests, comments, docs and the check script; round 1's results for them
|
||||
stand.
|
||||
- byte-check against a base worktree at 75ab1646: 16 cases, stdout,
|
||||
stderr and exit identical; data root unchanged (`out/byte-check.txt`).
|
||||
- delta-check: 13 cases now (`out/delta-check.txt`). The new one:
|
||||
base `show` printed `run:` and `result.json: (missing or unreadable)`
|
||||
and then crashed in `readdirSync`, exit 1; the candidate refuses with
|
||||
`cannot read …: EACCES`, exit 4.
|
||||
- mutants: 37 (`out/mutants.sh`, `out/mutants.txt`). Round 1's 32, plus
|
||||
Darkwing's D17, D20 and D22, Filbert's N2, and S1 (the dangling `state/`
|
||||
link reading as missing). 36 are killed. Only M04 survives, and it is
|
||||
equivalent (below). Each new kill comes from an assertion in the new
|
||||
tests, not a syntax or reference error.
|
||||
- `build.patch`: `git diff --cached --binary 75ab1646`, 15 files,
|
||||
+1068/−27. It applies to 75ab1646 and the manifest matches
|
||||
(`out/apply-75ab1646.txt`).
|
||||
|
||||
### Correction: M31 was never equivalent
|
||||
|
||||
Round 1 called M31 equivalent: "`readRunRecord` throws the same message".
|
||||
That is wrong. The `isRunId` check in `showRun` is the only id check that
|
||||
runs before `loadConfig`. Without it, `show ../x` with a missing config
|
||||
exits 3 with a configuration problem instead of 4 "invalid run id"
|
||||
(Darkwing's probe P1). The round 1 paragraph even said the check stays "so
|
||||
that an invalid id refuses before the config loads", which contradicted
|
||||
its own verdict. No test covered that case. The new CLI test does, and
|
||||
M31 is killed.
|
||||
|
||||
# Round 1 (candidate ed3c5392 over dc96f87b)
|
||||
|
||||
Round 1 evidence is under `out/round1/`. Paths below that start `out/` mean `out/round1/` in this packet.
|
||||
The round 1 packet files are in `r1/`, unchanged, so the posted hashes still check:
|
||||
`r1/build.patch` dd7b469b…, `r1/candidate-manifest.sha256` ed3c5392… and
|
||||
`r1/packet-manifest.sha256` e0497ad1…. That manifest lists the round 1
|
||||
paths, so it checks against the round 1 tree, not this directory.
|
||||
|
||||
## Files (`files.txt`, 15)
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `packages/runs/package.json` | new: `@mosaic/runs`, private, no dependencies |
|
||||
| `packages/runs/src/errors.mjs` | new: `RunsError(message, exitCode = 4)` |
|
||||
| `packages/runs/src/paths.mjs` | new: `resolveInside` containment, `readJsonObject` |
|
||||
| `packages/runs/src/runs.mjs` | new: `isRunId`, `listRunIds`, `listRunRecords`, `readRunDocument`, `readRunRecord` |
|
||||
| `packages/runs/src/state.mjs` | new: `readActivePointer`, `readActivationLog` |
|
||||
| `packages/runs/src/index.mjs` | new: re-exports |
|
||||
| `packages/runs/README.md` | new: what it reads, the limits, the deliberate deltas |
|
||||
| `packages/runs/tests/*.mjs` | new: 37 tests in round 1 (runs 22, state 10, task-cli 5) plus helpers; 41 in round 2 (runs 23, state 11, task-cli 7) |
|
||||
| `scripts/mosaic-task.mjs` | `list` and `show` read through the module, +18/−27 |
|
||||
| `agents/rocko/work/queue-56/{seed,byte-check,delta-check}.sh` | the byte-identity and delta checks |
|
||||
|
||||
`build.patch` is `git diff --cached --binary dc96f87b` over those files:
|
||||
+1021/−27 in round 1. Round 2's `build.patch` is over 75ab1646, +1068/−27. `candidate-manifest.sha256` hashes the 15 files.
|
||||
|
||||
Q14 freeze: nothing under `packages/bus`, `packages/tasks`, `packages/cli`,
|
||||
`scripts/bus-service.sh` or `scripts/mosaic` changes. Apart from the import,
|
||||
`scripts/mosaic-task.mjs` is the only file under `scripts/` that changes. The
|
||||
module covers the runs view's needs, but the view itself, `release.sh` and
|
||||
pruning are untouched.
|
||||
|
||||
## Design
|
||||
|
||||
- **Containment.** `resolveInside` runs `realpathSync` on the data root and
|
||||
the target, then applies a `path.relative` check:
|
||||
- `..`, `../…` or an absolute relative path counts as outside;
|
||||
- the configured data root is trusted even when it is itself a link;
|
||||
- links that stay inside it are followed.
|
||||
- **Outside paths.**
|
||||
- `runs/`, `state/`, a run directory read by `readRunRecord`, `active.json`
|
||||
and the activation log refuse when they resolve outside.
|
||||
- A run document that resolves outside reads as `null`, the way an
|
||||
unreadable one always has.
|
||||
- **Missing versus broken.** ENOENT and ENOTDIR read as missing. Anything
|
||||
else (ELOOP, EACCES) refuses with the error code instead of reading as
|
||||
empty.
|
||||
- **Documents.** A run document is a JSON object or `null`. Run records are
|
||||
not validated further, because older records carry older shapes.
|
||||
- **Release pointer.** `active.json` is strict: exactly the version 1 shape
|
||||
`release.sh` writes. Bad data exits 2, and a read error exits 4.
|
||||
- **Activation log.** The log is lenient per line, like `release.sh
|
||||
rollback`. It returns `{ entries, malformed }`, and `last` keeps the newest
|
||||
entries.
|
||||
**Corrected in round 2:** not "like rollback". Rollback skips only
|
||||
lines that aren't JSON; this module also counts wrong-shaped entries.
|
||||
- **Errors.** Every refusal is a `RunsError` with exit code 2 or 4.
|
||||
`mosaic-task.mjs` maps it to `fail(exitCode, message)` through
|
||||
`readRuns`.
|
||||
- **Read-only.** No function writes. The "readers write nothing" test and
|
||||
byte-check's before/after snapshot of the data root both check this.
|
||||
|
||||
## Gate (`out/gate/`, sequential, scratch worktree of the candidate)
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| packages/runs tests | 34/34 at gate time; 37/37 after the mutant round (`out/runs-tests-final.log`) |
|
||||
| test-auth | 15 passed, 0 failed |
|
||||
| test-conductor | 17 passed, 0 failed |
|
||||
| test-config | 24 passed, 0 failed |
|
||||
| test-discord | first run 57 passed, 1 failed; rerun 66 passed, 0 failed (below) |
|
||||
| test-extension-package | 18 passed, 0 failed |
|
||||
| test-foundation | 44 passed, 0 failed |
|
||||
| test-queue | 27 passed, 0 failed |
|
||||
| test-release | 14 passed, 0 failed |
|
||||
| test-task | 98 passed, 0 failed |
|
||||
|
||||
**test-discord's first-run failure was environmental.** The failing case was
|
||||
"pi binary present at node_modules/.bin/pi for the extension checks". The
|
||||
scratch worktree has no `node_modules`, and the base worktree lacks it too.
|
||||
I symlinked the checkout's `node_modules` into the scratch tree and reran
|
||||
the suite: 66 passed, 0 failed. The extra 8 cases are the extension checks
|
||||
that the missing binary had skipped. Then I removed the symlink
|
||||
(`out/gate/test-discord-r2.log`). Nothing in this candidate touches
|
||||
packages/discord. Sage's rerun in a tree that has `node_modules` should see
|
||||
66.
|
||||
|
||||
After the gate, the only changes were tests, the README table row and the
|
||||
check scripts. No `src/` or `scripts/mosaic-task.mjs` change came after it.
|
||||
|
||||
## Byte identity (`out/byte-check.txt`)
|
||||
|
||||
`byte-check.sh BASE CAND WORK` seeds a data root with `seed.sh`. The seed
|
||||
holds:
|
||||
|
||||
- runs a–f;
|
||||
- a run that is a file;
|
||||
- an internal link and a dangling link;
|
||||
- `r-zz.weird_name-1`;
|
||||
- a non-`r-` name, `.pruned.log` and a stray file.
|
||||
|
||||
It runs 16 cases from both trees: `list` on seeded, empty and absent roots,
|
||||
and `show` on each run, a missing run, `../escape`, no argument and an
|
||||
absent root. It diffs stdout, stderr and exit codes.
|
||||
|
||||
```
|
||||
byte-check: 16 cases, stdout, stderr and exit identical
|
||||
byte-check: data root unchanged
|
||||
```
|
||||
|
||||
**Correction to my own harness.** My first rerun in this round passed a
|
||||
relative WORK_DIR. Every redirect inside the `cd` subshell failed, both trees
|
||||
recorded exit 1, and the diff still reported "identical": a false pass.
|
||||
Earlier runs used absolute paths and were not affected. Both scripts now
|
||||
make WORK_DIR absolute before doing anything else. `byte-check.sh` also
|
||||
exits 4 if any case leaves no stdout or stderr file. The output above
|
||||
comes from the fixed script.
|
||||
|
||||
## Deliberate deltas (`out/delta-check.txt`)
|
||||
|
||||
These cases fall outside the byte-identity domain: a link out of the data
|
||||
root, a document that is JSON but not an object, a run that is a file, a
|
||||
link loop, and an unreadable directory. Each one changes on purpose. The
|
||||
README table lists them, and `delta-check.sh` prints base against candidate:
|
||||
|
||||
- run directory linked out: `list` shows `unknown`; `show` refuses, exit 4.
|
||||
- `result.json` linked out: `list` shows `unknown`; `show` prints
|
||||
`(missing or unreadable)`.
|
||||
- `runs/` linked out: `list` and `show` refuse, exit 4.
|
||||
- `result.json` is `5`: base `list` crashed (exit 1); base `show` printed
|
||||
`undefined` fields. The candidate gives `unknown` and
|
||||
`(missing or unreadable)`.
|
||||
- `task.json` is `[]`: base printed the snapshot line; the candidate
|
||||
omits it.
|
||||
- run is a regular file: base `show` crashed in `readdirSync`; the
|
||||
candidate gives `run not found`, exit 4.
|
||||
- link loop: base said `run not found`; the candidate refuses with ELOOP,
|
||||
exit 4.
|
||||
- `runs/` unreadable: base `list` printed nothing, exit 0; the candidate
|
||||
refuses with EACCES, exit 4.
|
||||
|
||||
## Mutants (`out/mutants.sh`, `out/mutants.txt`)
|
||||
|
||||
I ran 32 single-line mutants of `src/` and `scripts/mosaic-task.mjs`
|
||||
against the packages/runs tests.
|
||||
|
||||
First round: 26 killed, 4 survived and 2 didn't apply. Three of the
|
||||
survivors were real gaps, and I added a test for each:
|
||||
|
||||
- **M02** dropped the exact `..` check. A link to the data root's parent got
|
||||
through. New test: "a link to the data root's parent is outside it".
|
||||
- **M09** dropped the sort. Node returned this directory already sorted,
|
||||
so no fixture could catch it. New test: it mocks `fs.readdirSync` to
|
||||
return the names reversed.
|
||||
- **M15** read every readdir error on a run directory as missing. New
|
||||
test: "an unreadable run directory refuses instead of reading as
|
||||
missing".
|
||||
|
||||
Final round: 30 killed, 2 survived. Both survivors are equivalent:
|
||||
|
||||
- **M04** dropped `!path.isAbsolute(relative)`. On POSIX, `path.relative`
|
||||
between two absolute paths is never absolute, so the branch is
|
||||
unreachable on Linux. I kept it as a guard for other platforms.
|
||||
- **M31** dropped the CLI's `isRunId` pre-check in `show`. `readRunRecord`
|
||||
throws the same message with exit 4, and `readRuns` maps it to the same
|
||||
`fail`. The pre-check stays so that an invalid id refuses before the
|
||||
config loads, as it did before.
|
||||
|
||||
**Corrected in round 2:** M31 was not equivalent. See "Correction: M31
|
||||
was never equivalent" above; it is killed in round 2.
|
||||
|
||||
## Follow-up (not fixed, out of scope)
|
||||
|
||||
This defect predates the candidate and is unchanged: `list` crashes with
|
||||
a `padEnd` TypeError when `result.json` is an object without a string
|
||||
`status` or `taskId`. Base and candidate behave the same here, so
|
||||
byte-identity holds. A fix would change `list` output and belongs in its
|
||||
own row.
|
||||
|
||||
## Reproduce
|
||||
|
||||
```sh
|
||||
git worktree add --detach /tmp/r56-base 75ab1646
|
||||
git worktree add --detach /tmp/r56-cand 75ab1646
|
||||
(cd /tmp/r56-cand && git apply --index <packet>/build.patch && sha256sum -c <packet>/candidate-manifest.sha256)
|
||||
env -u NODE_TEST_CONTEXT node --test '/tmp/r56-cand/packages/runs/tests/*.test.mjs'
|
||||
bash /tmp/r56-cand/agents/rocko/work/queue-56/byte-check.sh /tmp/r56-base /tmp/r56-cand /tmp/r56-bc
|
||||
bash /tmp/r56-cand/agents/rocko/work/queue-56/delta-check.sh /tmp/r56-base /tmp/r56-cand /tmp/r56-dc
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
75ab164698176178f196009f14f994e0192048a7
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
agents/rocko/work/queue-56/byte-check.sh
|
||||
agents/rocko/work/queue-56/delta-check.sh
|
||||
agents/rocko/work/queue-56/seed.sh
|
||||
packages/runs/package.json
|
||||
packages/runs/README.md
|
||||
packages/runs/src/errors.mjs
|
||||
packages/runs/src/index.mjs
|
||||
packages/runs/src/paths.mjs
|
||||
packages/runs/src/runs.mjs
|
||||
packages/runs/src/state.mjs
|
||||
packages/runs/tests/helpers.mjs
|
||||
packages/runs/tests/runs.test.mjs
|
||||
packages/runs/tests/state.test.mjs
|
||||
packages/runs/tests/task-cli.test.mjs
|
||||
scripts/mosaic-task.mjs
|
||||
@@ -0,0 +1 @@
|
||||
applies to 75ab1646, manifest matches
|
||||
@@ -0,0 +1,3 @@
|
||||
byte-check: 16 cases, stdout, stderr and exit identical
|
||||
byte-check: data root unchanged
|
||||
rc=0
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
== run directory linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== run directory linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 4
|
||||
err| mosaic-task: /mnt/storage/scratch/rocko-r56/dc3/run-link/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/mnt/storage/scratch/rocko-r56/dc3/run-link/data)
|
||||
|
||||
== result.json linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== result.json linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
out| artifacts: result.json
|
||||
|
||||
== runs directory linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 4
|
||||
err| mosaic-task: /mnt/storage/scratch/rocko-r56/dc3/runs-link/data/runs resolves outside the data root (/mnt/storage/scratch/rocko-r56/dc3/runs-link/data)
|
||||
|
||||
== runs directory linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 4
|
||||
err| mosaic-task: /mnt/storage/scratch/rocko-r56/dc3/runs-link/data/runs resolves outside the data root (/mnt/storage/scratch/rocko-r56/dc3/runs-link/data)
|
||||
|
||||
== result.json is 5 and task.json is []: list
|
||||
base: exit 1
|
||||
err| file:///mnt/storage/scratch/rocko-r56/base2/scripts/mosaic-task.mjs:483
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== result.json is 5 and task.json is []: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: undefined
|
||||
out| task: undefined
|
||||
out| adapter: (see config) provider=undefined model=undefined
|
||||
out| request: undefined
|
||||
out| response: undefined
|
||||
out| timing: undefined -> undefined (undefined ms)
|
||||
out| exit: undefined
|
||||
out| task snapshot: task.json present
|
||||
out| artifacts: result.json, task.json
|
||||
cand: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
out| artifacts: result.json, task.json
|
||||
|
||||
== run is a regular file: show
|
||||
base: exit 1
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
err| node:fs:1954
|
||||
cand: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /mnt/storage/scratch/rocko-r56/dc3/run-file/data/runs)
|
||||
|
||||
== run is a link loop: show
|
||||
base: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /mnt/storage/scratch/rocko-r56/dc3/loop/data/runs)
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot resolve /mnt/storage/scratch/rocko-r56/dc3/loop/data/runs/r-20260101T000000Z-aaaaaa: ELOOP
|
||||
|
||||
== runs directory unreadable: list
|
||||
base: exit 0
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot read /mnt/storage/scratch/rocko-r56/dc3/unreadable/data/runs: EACCES
|
||||
|
||||
== runs directory unreadable: show
|
||||
base: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /mnt/storage/scratch/rocko-r56/dc3/unreadable/data/runs)
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot resolve /mnt/storage/scratch/rocko-r56/dc3/unreadable/data/runs/r-20260101T000000Z-aaaaaa: EACCES
|
||||
|
||||
== run directory unreadable: show
|
||||
base: exit 1
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
err| node:fs:1954
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot read /mnt/storage/scratch/rocko-r56/dc3/run-unreadable/data/runs/r-20260101T000000Z-aaaaaa: EACCES
|
||||
rc=0
|
||||
@@ -0,0 +1,102 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
OK mock adapter: gate passes on matching mock response (exit 0)
|
||||
OK mock adapter: expect-mismatch recorded (exit 1)
|
||||
OK mismatch reason recorded
|
||||
OK unknown adapter fails closed (exit 1)
|
||||
OK mission task runs via mock adapter (exit 0)
|
||||
OK mission section injected into generated prompt
|
||||
OK retry of mission run succeeds (exit 0)
|
||||
OK retriedFrom lineage recorded
|
||||
OK mission section present after retry (relative path resolved)
|
||||
OK retry of missing run exits 4 (exit 4)
|
||||
OK skill install bundled ms-tools (exit 0)
|
||||
OK installed to skills-available
|
||||
OK double install refuses (exit 1)
|
||||
OK activate enables skill (exit 0)
|
||||
OK enabled dir populated
|
||||
OK uninstall while enabled refuses (exit 1)
|
||||
OK deactivate moves back to available (exit 0)
|
||||
OK uninstall removes from available (exit 0)
|
||||
OK seat with enabled skill launches
|
||||
OK skill path delivered to adapter
|
||||
OK shipped researcher contract resolves (ceiling + network)
|
||||
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
|
||||
OK resolve-role refuses name/filename mismatch (exit 2)
|
||||
OK resolve-role refuses unknown network declaration (exit 2)
|
||||
OK resolve-role refuses duplicate role tool (exit 2)
|
||||
OK resolve-role refuses unsupported tool (exit 2)
|
||||
OK resolve-role refuses missing contract file (exit 4)
|
||||
OK version 1 role prints no contract line
|
||||
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
|
||||
OK resolve-role accepts a minimal version 2 role (exit 0)
|
||||
OK resolve-role refuses an action outside the vocabulary (exit 2)
|
||||
OK resolve-role refuses a gated-only action in a role file (exit 2)
|
||||
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
|
||||
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
|
||||
OK resolve-role refuses a contract that is a symbolic link (exit 2)
|
||||
OK seat launches under role ceiling
|
||||
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
|
||||
OK narrowing recorded loudly
|
||||
OK missing role contract refuses launch (exit 2)
|
||||
OK missing-contract refusal names the role
|
||||
OK empty ceiling intersection -> tool-free seat
|
||||
OK tool-free outcome recorded loudly
|
||||
OK overridden agents dir without seat definition refuses (exit 4)
|
||||
OK seat-resolution refusal names the seat
|
||||
OK policy: mission only -> mission tools
|
||||
OK policy: task only -> task tools
|
||||
OK policy: both -> intersection (task narrowed)
|
||||
OK policy: empty intersection -> tool-free
|
||||
OK invalid mission capabilities rejected (exit 2)
|
||||
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
|
||||
OK task run with user layer present (exit 0)
|
||||
OK user context dispatched into generated prompt
|
||||
OK workspace+tools task runs via mock (exit 0)
|
||||
OK workspace path + tools delivered to adapter
|
||||
OK persistent workspace created on host
|
||||
OK plain task still runs (no workspace/tools) (exit 0)
|
||||
OK workspace var present but empty when absent
|
||||
OK tools empty when absent
|
||||
OK unknown tool exits 2 (exit 2)
|
||||
OK workspace traversal exits 2 (exit 2)
|
||||
OK live hello task succeeds with exact marker
|
||||
OK result.json written in run dir
|
||||
OK result.json contents are correct
|
||||
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
|
||||
OK each run gets a distinct run dir (no clobber)
|
||||
OK list shows both runs
|
||||
OK fork base: teach succeeds (exit 0)
|
||||
OK fork child recalls ancestor context (exit 0)
|
||||
OK forked child recalled ancestor code word
|
||||
OK ancestor session untouched by fork
|
||||
OK child session dir has its own branch file
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
OK user recall run succeeds
|
||||
OK recalled user name (response: Jason)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 98 passed, 0 failed
|
||||
rc=0
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 56 mutants: each line applies one perl substitution to one file, runs
|
||||
# the packages/runs tests, and expects a failure (killed). Restores after.
|
||||
cd /mnt/storage/scratch/rocko-r56/r2 || exit 4
|
||||
G=/mnt/storage/scratch/rocko-r56/gate2
|
||||
: > "$G/mutants.txt"
|
||||
mut() { # id file perl-expr
|
||||
local id="$1" f="$2" e="$3"
|
||||
cp "$f" "$f.orig"
|
||||
perl -0pi -e "$e" "$f"
|
||||
if cmp -s "$f" "$f.orig"; then echo "$id NOT-APPLIED" >> "$G/mutants.txt"; mv -f "$f.orig" "$f"; return; fi
|
||||
if timeout 300 env -u NODE_TEST_CONTEXT node --test 'packages/runs/tests/*.test.mjs' > "$G/mut-$id.log" 2>&1; then
|
||||
echo "$id SURVIVED" >> "$G/mutants.txt"
|
||||
else
|
||||
echo "$id killed" >> "$G/mutants.txt"
|
||||
fi
|
||||
mv -f "$f.orig" "$f"
|
||||
}
|
||||
P=packages/runs/src/paths.mjs; R=packages/runs/src/runs.mjs; S=packages/runs/src/state.mjs; T=scripts/mosaic-task.mjs
|
||||
mut M01 $P 's/return relative === "" \|\|/return true ||/'
|
||||
mut M02 $P 's/relative !== "\.\." && //'
|
||||
mut M03 $P 's/startsWith\(`\.\.\$\{path\.sep\}`\)/startsWith("..x")/'
|
||||
mut M04 $P 's/ && !path\.isAbsolute\(relative\)\)/)/'
|
||||
mut M05 $P 's/ && !Array\.isArray\(value\) \? value/ ? value/'
|
||||
mut M06 $P 's/error\?\.code === "ENOENT" \|\| error\?\.code === "ENOTDIR"/error?.code === "ENOENT"/'
|
||||
mut M07 $P 's/if \(isMissing\(error\)\) return null;\n throw new RunsError\(`cannot resolve/return null;\n throw new RunsError(`cannot resolve/'
|
||||
mut M08 $P 's/if \(!isInside\(root, real\)\)/if (false)/'
|
||||
mut M09 $R 's/filter\(\(name\) => name\.startsWith\("r-"\)\)\.sort\(\)/filter((name) => name.startsWith("r-"))/'
|
||||
mut M10 $R 's/filter\(\(name\) => name\.startsWith\("r-"\)\)/filter(() => true)/'
|
||||
mut M11 $R 's/if \(isMissing\(error\)\) return \[\];\n throw/return [];\n throw/'
|
||||
mut M12 $R 's/if \(!RUN_DOCUMENTS\.includes\(name\)\) throw/if (false) throw/'
|
||||
mut M13 $R 's/export function readRunRecord\(dataRoot, runId\) \{\n requireRunId\(runId\);/export function readRunRecord(dataRoot, runId) {/'
|
||||
mut M14 $R 's/if \(resolveInside\(dataRoot, RUNS_DIRNAME\) === null\) return null;\n//'
|
||||
mut M15 $R 's/if \(isMissing\(error\)\) return null;\n throw new RunsError\(`cannot read \$\{dir\}/return null;\n throw new RunsError(`cannot read ${dir}/'
|
||||
mut M16 $R 's/RUN_ID_PATTERN = \/\^r-/RUN_ID_PATTERN = \/r-/'
|
||||
mut M17 $S 's/if \(!POINTER_KEYS\.includes\(key\)\) throw/if (false) throw/'
|
||||
mut M18 $S 's/if \(pointer\.pointerVersion !== 1\)/if (false)/'
|
||||
mut M19 $S 's/new RunsError\(`release pointer \$\{why\} \(\$\{state\.file\}\)`, 2\)/new RunsError(`release pointer ${why} (${state.file})`)/'
|
||||
mut M20 $S 's/if \(Object\.keys\(entry\)\.some\(\(key\) => !LOG_KEYS\.includes\(key\)\)\) return null;//'
|
||||
mut M21 $S 's/if \(entry\.note !== undefined && typeof entry\.note !== "string"\) return null;//'
|
||||
mut M22 $S 's/entries\.slice\(-last\)/entries.slice(0, last)/'
|
||||
mut M23 $S 's/if \(line\.trim\(\) === ""\) continue;//'
|
||||
mut M24 $S 's/if \(last !== undefined && \(!Number\.isInteger\(last\) \|\| last < 1\)\)/if (false)/'
|
||||
mut M25 $S 's/ throw new RunsError\(`cannot read \$\{file\}/ return null; throw new RunsError(`cannot read ${file}/'
|
||||
mut M26 $S 's/if \(!isNonEmptyString\(pointer\[key\]\)\)/if (pointer[key] === undefined)/'
|
||||
mut M27 $S 's/\["at", "event", "release", "imageTag"\]\.every/["event"].every/'
|
||||
mut M28 $T 's/ if \(result\) \{\n status = result\.status;/ if (true) {\n status = result.status;/'
|
||||
mut M29 $T 's/ if \(record === null\) \{\n fail\(4, `run not found/ if (false) {\n fail(4, `run not found/'
|
||||
mut M30 $T 's/if \(error instanceof RunsError\) fail\(error\.exitCode, error\.message\);/if (false) fail(error.exitCode, error.message);/'
|
||||
mut M31 $T 's/ if \(!isRunId\(runId\)\) \{\n fail\(4, `invalid run id/ if (false) {\n fail(4, `invalid run id/'
|
||||
mut M32 $T 's/artifacts\.join\(", "\)/artifacts.join(",")/'
|
||||
mut D17 $R 's/result: readJsonObject\(dataRoot, RUNS_DIRNAME, runId, "result\.json"\)/result: readRunDocument(dataRoot, runId, "result.json")/'
|
||||
mut D20 $R 's/names\.filter\(\(name\) => name\.startsWith\("r-"\)\)/names.filter((name) => name.startsWith("r-") \&\& isRunId(name))/'
|
||||
mut D22 $S 's/\["at", "event", "release", "imageTag"\]\.every/["at", "event", "release"].every/'
|
||||
mut N2 $S 's/\["at", "event", "release", "imageTag"\]\.every/["at", "event", "imageTag"].every/'
|
||||
mut S1 $S 's/if \(file === null\) return null;\n try \{\n return \{ file, text/if (file === null) throw new RunsError("x");\n try {\n return { file, text/'
|
||||
echo done >> "$G/mutants.txt"
|
||||
@@ -0,0 +1,38 @@
|
||||
M01 killed
|
||||
M02 killed
|
||||
M03 killed
|
||||
M04 SURVIVED
|
||||
M05 killed
|
||||
M06 killed
|
||||
M07 killed
|
||||
M08 killed
|
||||
M09 killed
|
||||
M10 killed
|
||||
M11 killed
|
||||
M12 killed
|
||||
M13 killed
|
||||
M14 killed
|
||||
M15 killed
|
||||
M16 killed
|
||||
M17 killed
|
||||
M18 killed
|
||||
M19 killed
|
||||
M20 killed
|
||||
M21 killed
|
||||
M22 killed
|
||||
M23 killed
|
||||
M24 killed
|
||||
M25 killed
|
||||
M26 killed
|
||||
M27 killed
|
||||
M28 killed
|
||||
M29 killed
|
||||
M30 killed
|
||||
M31 killed
|
||||
M32 killed
|
||||
D17 killed
|
||||
D20 killed
|
||||
D22 killed
|
||||
N2 killed
|
||||
S1 killed
|
||||
done
|
||||
@@ -0,0 +1 @@
|
||||
applies to 923957e2, manifest matches
|
||||
@@ -0,0 +1 @@
|
||||
applies to dc96f87b, manifest matches
|
||||
@@ -0,0 +1,3 @@
|
||||
byte-check: 16 cases, stdout, stderr and exit identical
|
||||
byte-check: data root unchanged
|
||||
rc=0
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
== run directory linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== run directory linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 4
|
||||
err| mosaic-task: /mnt/storage/scratch/rocko-r56/dc2/run-link/data/runs/r-20260101T000000Z-aaaaaa resolves outside the data root (/mnt/storage/scratch/rocko-r56/dc2/run-link/data)
|
||||
|
||||
== result.json linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== result.json linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
out| artifacts: result.json
|
||||
|
||||
== runs directory linked out of the data root: list
|
||||
base: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa succeeded task=t-out ws=- session=-
|
||||
cand: exit 4
|
||||
err| mosaic-task: /mnt/storage/scratch/rocko-r56/dc2/runs-link/data/runs resolves outside the data root (/mnt/storage/scratch/rocko-r56/dc2/runs-link/data)
|
||||
|
||||
== runs directory linked out of the data root: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: succeeded
|
||||
out| task: t-out
|
||||
out| adapter: (see config) provider=p model=m
|
||||
out| request: "r"
|
||||
out| response: "s"
|
||||
out| timing: a -> b (1 ms)
|
||||
out| exit: 0
|
||||
out| artifacts: result.json
|
||||
cand: exit 4
|
||||
err| mosaic-task: /mnt/storage/scratch/rocko-r56/dc2/runs-link/data/runs resolves outside the data root (/mnt/storage/scratch/rocko-r56/dc2/runs-link/data)
|
||||
|
||||
== result.json is 5 and task.json is []: list
|
||||
base: exit 1
|
||||
err| file:///mnt/storage/scratch/rocko-r56/base/scripts/mosaic-task.mjs:483
|
||||
cand: exit 0
|
||||
out| r-20260101T000000Z-aaaaaa unknown task=- ws=- session=-
|
||||
|
||||
== result.json is 5 and task.json is []: show
|
||||
base: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| status: undefined
|
||||
out| task: undefined
|
||||
out| adapter: (see config) provider=undefined model=undefined
|
||||
out| request: undefined
|
||||
out| response: undefined
|
||||
out| timing: undefined -> undefined (undefined ms)
|
||||
out| exit: undefined
|
||||
out| task snapshot: task.json present
|
||||
out| artifacts: result.json, task.json
|
||||
cand: exit 0
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
out| artifacts: result.json, task.json
|
||||
|
||||
== run is a regular file: show
|
||||
base: exit 1
|
||||
out| run: r-20260101T000000Z-aaaaaa
|
||||
out| result.json: (missing or unreadable)
|
||||
err| node:fs:1954
|
||||
cand: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /mnt/storage/scratch/rocko-r56/dc2/run-file/data/runs)
|
||||
|
||||
== run is a link loop: show
|
||||
base: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /mnt/storage/scratch/rocko-r56/dc2/loop/data/runs)
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot resolve /mnt/storage/scratch/rocko-r56/dc2/loop/data/runs/r-20260101T000000Z-aaaaaa: ELOOP
|
||||
|
||||
== runs directory unreadable: list
|
||||
base: exit 0
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot read /mnt/storage/scratch/rocko-r56/dc2/unreadable/data/runs: EACCES
|
||||
|
||||
== runs directory unreadable: show
|
||||
base: exit 4
|
||||
err| mosaic-task: run not found: r-20260101T000000Z-aaaaaa (under /mnt/storage/scratch/rocko-r56/dc2/unreadable/data/runs)
|
||||
cand: exit 4
|
||||
err| mosaic-task: cannot resolve /mnt/storage/scratch/rocko-r56/dc2/unreadable/data/runs/r-20260101T000000Z-aaaaaa: EACCES
|
||||
rc=0
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 56 gate: packages/runs tests then every scripts/test-*.sh, sequentially.
|
||||
cd /mnt/storage/scratch/rocko-r56/tree || exit 4
|
||||
G=/mnt/storage/scratch/rocko-r56/gate
|
||||
: > "$G/summary.txt"
|
||||
env -u NODE_TEST_CONTEXT node --test 'packages/runs/tests/*.test.mjs' > "$G/runs.log" 2>&1
|
||||
echo "packages/runs rc=$? $(grep -E '^ℹ (pass|fail) ' "$G/runs.log" | tr '\n' ' ')" >> "$G/summary.txt"
|
||||
for s in scripts/test-*.sh; do
|
||||
n="$(basename "$s" .sh)"
|
||||
env -u NODE_TEST_CONTEXT bash "$s" > "$G/$n.log" 2>&1
|
||||
echo "$n rc=$? $(grep -aiE 'pass(ed)?[:=]? *[0-9]+|[0-9]+ *(passed|failed)|PASS|FAIL' "$G/$n.log" | tail -1)" >> "$G/summary.txt"
|
||||
done
|
||||
echo done >> "$G/summary.txt"
|
||||
@@ -0,0 +1,42 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.679687ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.711645ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.711141ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.883213ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.444986ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.167168ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.70133ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.793561ms)
|
||||
✔ readRunDocument reads only the three run documents (0.730363ms)
|
||||
✔ a link inside the data root is followed (1.147751ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.641352ms)
|
||||
✔ a run directory linked out of the data root is never read (0.659011ms)
|
||||
✔ a document linked out of the data root reads as null (1.093445ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.615519ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.563326ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.703948ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.914245ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.587674ms)
|
||||
✔ the readers write nothing (2.658199ms)
|
||||
✔ no pointer is null (2.102149ms)
|
||||
✔ the pointer release.sh writes reads back (1.402897ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.240364ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.676911ms)
|
||||
✔ a state file linked out of the data root refuses (2.216034ms)
|
||||
✔ a state directory linked out of the data root refuses (0.96548ms)
|
||||
✔ a missing log is empty (0.663032ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.490356ms)
|
||||
✔ last must be a positive integer (0.835225ms)
|
||||
✔ the state readers write nothing (1.481171ms)
|
||||
✔ list prints each run in the established format (94.815455ms)
|
||||
✔ show prints the run in the established format (81.715499ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (145.886523ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (171.425218ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (196.821506ms)
|
||||
ℹ tests 34
|
||||
ℹ suites 0
|
||||
ℹ pass 34
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 774.958121
|
||||
@@ -0,0 +1,11 @@
|
||||
packages/runs rc=0 ℹ pass 34 ℹ fail 0
|
||||
test-auth rc=0 selftest: 15 passed, 0 failed
|
||||
test-conductor rc=0 selftest: 17 passed, 0 failed
|
||||
test-config rc=0 selftest: 24 passed, 0 failed
|
||||
test-discord rc=1 discord suite: 57 passed, 1 failed
|
||||
test-extension-package rc=0 extension package selftest: 18 passed, 0 failed
|
||||
test-foundation rc=0 selftest: 44 passed, 0 failed
|
||||
test-queue rc=0 queue suite: 27 passed, 0 failed
|
||||
test-release rc=0 selftest: 14 passed, 0 failed
|
||||
test-task rc=0 selftest: 98 passed, 0 failed
|
||||
done
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,55 @@
|
||||
Note: switching to 'dc96f87b1776cc74d0ea5ba7701db418cc21e1b8'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to 'dc96f87b1776cc74d0ea5ba7701db418cc21e1b8'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,62 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
FAIL pi binary present at node_modules/.bin/pi for the extension checks
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 57 passed, 1 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 561594 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 148
|
||||
ℹ pass 148
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/mnt/storage/scratch/rocko-r56/tree) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,16 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
|
||||
selftest: 14 passed, 0 failed
|
||||
@@ -0,0 +1,101 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
OK mock adapter: gate passes on matching mock response (exit 0)
|
||||
OK mock adapter: expect-mismatch recorded (exit 1)
|
||||
OK mismatch reason recorded
|
||||
OK unknown adapter fails closed (exit 1)
|
||||
OK mission task runs via mock adapter (exit 0)
|
||||
OK mission section injected into generated prompt
|
||||
OK retry of mission run succeeds (exit 0)
|
||||
OK retriedFrom lineage recorded
|
||||
OK mission section present after retry (relative path resolved)
|
||||
OK retry of missing run exits 4 (exit 4)
|
||||
OK skill install bundled ms-tools (exit 0)
|
||||
OK installed to skills-available
|
||||
OK double install refuses (exit 1)
|
||||
OK activate enables skill (exit 0)
|
||||
OK enabled dir populated
|
||||
OK uninstall while enabled refuses (exit 1)
|
||||
OK deactivate moves back to available (exit 0)
|
||||
OK uninstall removes from available (exit 0)
|
||||
OK seat with enabled skill launches
|
||||
OK skill path delivered to adapter
|
||||
OK shipped researcher contract resolves (ceiling + network)
|
||||
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
|
||||
OK resolve-role refuses name/filename mismatch (exit 2)
|
||||
OK resolve-role refuses unknown network declaration (exit 2)
|
||||
OK resolve-role refuses duplicate role tool (exit 2)
|
||||
OK resolve-role refuses unsupported tool (exit 2)
|
||||
OK resolve-role refuses missing contract file (exit 4)
|
||||
OK version 1 role prints no contract line
|
||||
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
|
||||
OK resolve-role accepts a minimal version 2 role (exit 0)
|
||||
OK resolve-role refuses an action outside the vocabulary (exit 2)
|
||||
OK resolve-role refuses a gated-only action in a role file (exit 2)
|
||||
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
|
||||
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
|
||||
OK resolve-role refuses a contract that is a symbolic link (exit 2)
|
||||
OK seat launches under role ceiling
|
||||
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
|
||||
OK narrowing recorded loudly
|
||||
OK missing role contract refuses launch (exit 2)
|
||||
OK missing-contract refusal names the role
|
||||
OK empty ceiling intersection -> tool-free seat
|
||||
OK tool-free outcome recorded loudly
|
||||
OK overridden agents dir without seat definition refuses (exit 4)
|
||||
OK seat-resolution refusal names the seat
|
||||
OK policy: mission only -> mission tools
|
||||
OK policy: task only -> task tools
|
||||
OK policy: both -> intersection (task narrowed)
|
||||
OK policy: empty intersection -> tool-free
|
||||
OK invalid mission capabilities rejected (exit 2)
|
||||
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
|
||||
OK task run with user layer present (exit 0)
|
||||
OK user context dispatched into generated prompt
|
||||
OK workspace+tools task runs via mock (exit 0)
|
||||
OK workspace path + tools delivered to adapter
|
||||
OK persistent workspace created on host
|
||||
OK plain task still runs (no workspace/tools) (exit 0)
|
||||
OK workspace var present but empty when absent
|
||||
OK tools empty when absent
|
||||
OK unknown tool exits 2 (exit 2)
|
||||
OK workspace traversal exits 2 (exit 2)
|
||||
OK live hello task succeeds with exact marker
|
||||
OK result.json written in run dir
|
||||
OK result.json contents are correct
|
||||
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
|
||||
OK each run gets a distinct run dir (no clobber)
|
||||
OK list shows both runs
|
||||
OK fork base: teach succeeds (exit 0)
|
||||
OK fork child recalls ancestor context (exit 0)
|
||||
OK forked child recalled ancestor code word
|
||||
OK ancestor session untouched by fork
|
||||
OK child session dir has its own branch file
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
OK user recall run succeeds
|
||||
OK recalled user name (response: Jason)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 98 passed, 0 failed
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 56 mutants: each line applies one perl substitution to one file, runs
|
||||
# the packages/runs tests, and expects a failure (killed). Restores after.
|
||||
cd /mnt/storage/scratch/rocko-r56/tree || exit 4
|
||||
G=/mnt/storage/scratch/rocko-r56/gate
|
||||
: > "$G/mutants.txt"
|
||||
mut() { # id file perl-expr
|
||||
local id="$1" f="$2" e="$3"
|
||||
cp "$f" "$f.orig"
|
||||
perl -0pi -e "$e" "$f"
|
||||
if cmp -s "$f" "$f.orig"; then echo "$id NOT-APPLIED" >> "$G/mutants.txt"; mv -f "$f.orig" "$f"; return; fi
|
||||
if timeout 300 env -u NODE_TEST_CONTEXT node --test 'packages/runs/tests/*.test.mjs' > "$G/mut-$id.log" 2>&1; then
|
||||
echo "$id SURVIVED" >> "$G/mutants.txt"
|
||||
else
|
||||
echo "$id killed" >> "$G/mutants.txt"
|
||||
fi
|
||||
mv -f "$f.orig" "$f"
|
||||
}
|
||||
P=packages/runs/src/paths.mjs; R=packages/runs/src/runs.mjs; S=packages/runs/src/state.mjs; T=scripts/mosaic-task.mjs
|
||||
mut M01 $P 's/return relative === "" \|\|/return true ||/'
|
||||
mut M02 $P 's/relative !== "\.\." && //'
|
||||
mut M03 $P 's/startsWith\(`\.\.\$\{path\.sep\}`\)/startsWith("..x")/'
|
||||
mut M04 $P 's/ && !path\.isAbsolute\(relative\)\)/)/'
|
||||
mut M05 $P 's/ && !Array\.isArray\(value\) \? value/ ? value/'
|
||||
mut M06 $P 's/error\?\.code === "ENOENT" \|\| error\?\.code === "ENOTDIR"/error?.code === "ENOENT"/'
|
||||
mut M07 $P 's/if \(isMissing\(error\)\) return null;\n throw new RunsError\(`cannot resolve/return null;\n throw new RunsError(`cannot resolve/'
|
||||
mut M08 $P 's/if \(!isInside\(root, real\)\)/if (false)/'
|
||||
mut M09 $R 's/filter\(\(name\) => name\.startsWith\("r-"\)\)\.sort\(\)/filter((name) => name.startsWith("r-"))/'
|
||||
mut M10 $R 's/filter\(\(name\) => name\.startsWith\("r-"\)\)/filter(() => true)/'
|
||||
mut M11 $R 's/if \(isMissing\(error\)\) return \[\];\n throw/return [];\n throw/'
|
||||
mut M12 $R 's/if \(!RUN_DOCUMENTS\.includes\(name\)\) throw/if (false) throw/'
|
||||
mut M13 $R 's/export function readRunRecord\(dataRoot, runId\) \{\n requireRunId\(runId\);/export function readRunRecord(dataRoot, runId) {/'
|
||||
mut M14 $R 's/if \(resolveInside\(dataRoot, RUNS_DIRNAME\) === null\) return null;\n//'
|
||||
mut M15 $R 's/if \(isMissing\(error\)\) return null;\n throw new RunsError\(`cannot read \$\{dir\}/return null;\n throw new RunsError(`cannot read ${dir}/'
|
||||
mut M16 $R 's/RUN_ID_PATTERN = \/\^r-/RUN_ID_PATTERN = \/r-/'
|
||||
mut M17 $S 's/if \(!POINTER_KEYS\.includes\(key\)\) throw/if (false) throw/'
|
||||
mut M18 $S 's/if \(pointer\.pointerVersion !== 1\)/if (false)/'
|
||||
mut M19 $S 's/new RunsError\(`release pointer \$\{why\} \(\$\{state\.file\}\)`, 2\)/new RunsError(`release pointer ${why} (${state.file})`)/'
|
||||
mut M20 $S 's/if \(Object\.keys\(entry\)\.some\(\(key\) => !LOG_KEYS\.includes\(key\)\)\) return null;//'
|
||||
mut M21 $S 's/if \(entry\.note !== undefined && typeof entry\.note !== "string"\) return null;//'
|
||||
mut M22 $S 's/entries\.slice\(-last\)/entries.slice(0, last)/'
|
||||
mut M23 $S 's/if \(line\.trim\(\) === ""\) continue;//'
|
||||
mut M24 $S 's/if \(last !== undefined && \(!Number\.isInteger\(last\) \|\| last < 1\)\)/if (false)/'
|
||||
mut M25 $S 's/ throw new RunsError\(`cannot read \$\{file\}/ return null; throw new RunsError(`cannot read ${file}/'
|
||||
mut M26 $S 's/if \(!isNonEmptyString\(pointer\[key\]\)\)/if (pointer[key] === undefined)/'
|
||||
mut M27 $S 's/\["at", "event", "release", "imageTag"\]\.every/["event"].every/'
|
||||
mut M28 $T 's/ if \(result\) \{\n status = result\.status;/ if (true) {\n status = result.status;/'
|
||||
mut M29 $T 's/ if \(record === null\) \{\n fail\(4, `run not found/ if (false) {\n fail(4, `run not found/'
|
||||
mut M30 $T 's/if \(error instanceof RunsError\) fail\(error\.exitCode, error\.message\);/if (false) fail(error.exitCode, error.message);/'
|
||||
mut M31 $T 's/ if \(!isRunId\(runId\)\) \{\n fail\(4, `invalid run id/ if (false) {\n fail(4, `invalid run id/'
|
||||
mut M32 $T 's/artifacts\.join\(", "\)/artifacts.join(",")/'
|
||||
echo done >> "$G/mutants.txt"
|
||||
@@ -0,0 +1,33 @@
|
||||
M01 killed
|
||||
M02 killed
|
||||
M03 killed
|
||||
M04 SURVIVED
|
||||
M05 killed
|
||||
M06 killed
|
||||
M07 killed
|
||||
M08 killed
|
||||
M09 killed
|
||||
M10 killed
|
||||
M11 killed
|
||||
M12 killed
|
||||
M13 killed
|
||||
M14 killed
|
||||
M15 killed
|
||||
M16 killed
|
||||
M17 killed
|
||||
M18 killed
|
||||
M19 killed
|
||||
M20 killed
|
||||
M21 killed
|
||||
M22 killed
|
||||
M23 killed
|
||||
M24 killed
|
||||
M25 killed
|
||||
M26 killed
|
||||
M27 killed
|
||||
M28 killed
|
||||
M29 killed
|
||||
M30 killed
|
||||
M31 SURVIVED
|
||||
M32 killed
|
||||
done
|
||||
@@ -0,0 +1,46 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (0.965437ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.475094ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.530968ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.699171ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (1.781005ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (0.916419ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.587172ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.652461ms)
|
||||
✔ readRunDocument reads only the three run documents (0.499342ms)
|
||||
✔ a link inside the data root is followed (0.889387ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.553199ms)
|
||||
✔ a run directory linked out of the data root is never read (0.575368ms)
|
||||
✔ a document linked out of the data root reads as null (0.894385ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.51067ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.396113ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.499228ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.684475ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.383259ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.467145ms)
|
||||
✔ a link to the data root's parent is outside it (0.369657ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (0.670714ms)
|
||||
✔ the readers write nothing (1.473542ms)
|
||||
✔ no pointer is null (1.564788ms)
|
||||
✔ the pointer release.sh writes reads back (1.031358ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (1.483582ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.499812ms)
|
||||
✔ a state file linked out of the data root refuses (1.409385ms)
|
||||
✔ a state directory linked out of the data root refuses (0.530763ms)
|
||||
✔ a missing log is empty (0.381369ms)
|
||||
✔ the log reads oldest first and counts malformed lines (0.826565ms)
|
||||
✔ last must be a positive integer (0.441409ms)
|
||||
✔ the state readers write nothing (0.824495ms)
|
||||
✔ list prints each run in the established format (71.57906ms)
|
||||
✔ show prints the run in the established format (64.020206ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (101.737552ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (131.150958ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (134.659276ms)
|
||||
ℹ tests 37
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 547.924201
|
||||
rc=0
|
||||
@@ -0,0 +1,156 @@
|
||||
diff --git tree/agents/rocko/work/queue-56/delta-check.sh r2/agents/rocko/work/queue-56/delta-check.sh
|
||||
index 69224ea..a502d1d 100755
|
||||
--- tree/agents/rocko/work/queue-56/delta-check.sh
|
||||
+++ r2/agents/rocko/work/queue-56/delta-check.sh
|
||||
@@ -59,4 +59,7 @@ if [ "$(id -u)" != 0 ]; then
|
||||
hdr "runs directory unreadable: list"; run_case unreadable list
|
||||
hdr "runs directory unreadable: show"; run_case unreadable show "$A"
|
||||
chmod 755 "$D/runs"
|
||||
+ D="$(root run-unreadable)"; mkdir -p "$D/runs/$A"; printf '%s\n' "$RESULT" > "$D/runs/$A/result.json"; chmod 000 "$D/runs/$A"
|
||||
+ hdr "run directory unreadable: show"; run_case run-unreadable show "$A"
|
||||
+ chmod 755 "$D/runs/$A"
|
||||
fi
|
||||
diff --git tree/packages/runs/README.md r2/packages/runs/README.md
|
||||
index 84230a1..8fd7808 100644
|
||||
--- tree/packages/runs/README.md
|
||||
+++ r2/packages/runs/README.md
|
||||
@@ -40,7 +40,11 @@ printed them.
|
||||
- a run document resolving outside reads as `null`;
|
||||
- `active.json` or `activation-log.jsonl` resolving outside refuses.
|
||||
|
||||
- A link that stays inside the data root is followed.
|
||||
+ 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 a
|
||||
+ `runs/` 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 as `null`. Run records are never validated beyond that,
|
||||
@@ -49,11 +53,14 @@ printed them.
|
||||
shape `scripts/release.sh` writes: exactly `pointerVersion` 1 and
|
||||
non-empty strings `release`, `imageTag` and `activatedAt`. Anything else
|
||||
refuses with exit code 2 rather than being guessed at.
|
||||
-- **The activation log is lenient per line.** An entry needs string `at`,
|
||||
- `event`, `release` and `imageTag`, an optional string `note`, and no
|
||||
- other keys. Other lines are counted in `malformed` and skipped, the way
|
||||
- `release.sh rollback` skips them. Blank lines aren't counted. `last`
|
||||
- must be a positive integer and keeps the newest entries.
|
||||
+- **The activation log is read per line.** One bad line never refuses
|
||||
+ the log. An entry needs string `at`, `event`, `release` and `imageTag`,
|
||||
+ an optional string `note`, and no other keys. A line that isn't JSON,
|
||||
+ or is JSON with another shape, is counted in `malformed` and skipped.
|
||||
+ That is stricter than `release.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. `last` must be a positive integer and
|
||||
+ keeps the newest entries.
|
||||
- **Errors.** Every refusal is a `RunsError` with `exitCode` 2 (invalid
|
||||
data) or 4 (a file or environment problem), matching
|
||||
`mosaic-task.mjs`. A missing data root, `runs/` or `state/` file is not
|
||||
@@ -78,6 +85,7 @@ output changes on purpose:
|
||||
| `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
|
||||
diff --git tree/packages/runs/src/state.mjs r2/packages/runs/src/state.mjs
|
||||
index a92c871..af5cc92 100644
|
||||
--- tree/packages/runs/src/state.mjs
|
||||
+++ r2/packages/runs/src/state.mjs
|
||||
@@ -60,10 +60,11 @@ function logEntry(line) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
-// The activation log as { entries, malformed }, oldest first. Lines that
|
||||
-// aren't a well-formed entry are counted in malformed and skipped, the way
|
||||
-// release.sh rollback skips them. A missing log is empty. With last, only
|
||||
-// the newest last well-formed entries are returned.
|
||||
+// The activation log as { entries, malformed }, oldest first. A line that
|
||||
+// isn't JSON, or is JSON but not the entry shape release.sh writes, is
|
||||
+// counted in malformed and skipped. This is stricter than release.sh
|
||||
+// rollback, which skips only lines that aren't JSON. A missing log is empty.
|
||||
+// With last, only the newest last well-formed entries are returned.
|
||||
export function readActivationLog(dataRoot, { last } = {}) {
|
||||
if (last !== undefined && (!Number.isInteger(last) || last < 1)) {
|
||||
throw new RunsError(`last must be a positive integer (got ${JSON.stringify(last)})`);
|
||||
diff --git tree/packages/runs/tests/runs.test.mjs r2/packages/runs/tests/runs.test.mjs
|
||||
index 85838e5..a652f62 100644
|
||||
--- tree/packages/runs/tests/runs.test.mjs
|
||||
+++ r2/packages/runs/tests/runs.test.mjs
|
||||
@@ -216,6 +216,15 @@ test("listRunIds sorts whatever order the directory returns", (t) => {
|
||||
assert.deepEqual(listRunIds(dataRoot), [A, B]);
|
||||
});
|
||||
|
||||
+// list has always shown any r- name, including ones show refuses as ids.
|
||||
+test("listRunRecords lists an r- name that isn't a valid run id, as before", (t) => {
|
||||
+ const { dataRoot } = scratch(t);
|
||||
+ write(path.join(dataRoot, "runs", "r-.bad", "result.json"), {});
|
||||
+ assert.equal(isRunId("r-.bad"), false);
|
||||
+ assert.deepEqual(listRunIds(dataRoot), ["r-.bad"]);
|
||||
+ assert.deepEqual(listRunRecords(dataRoot), [{ runId: "r-.bad", result: {} }]);
|
||||
+});
|
||||
+
|
||||
test("the readers write nothing", (t) => {
|
||||
const { dataRoot } = scratch(t);
|
||||
const runs = path.join(dataRoot, "runs");
|
||||
diff --git tree/packages/runs/tests/state.test.mjs r2/packages/runs/tests/state.test.mjs
|
||||
index 80ab5ea..4b126f3 100644
|
||||
--- tree/packages/runs/tests/state.test.mjs
|
||||
+++ r2/packages/runs/tests/state.test.mjs
|
||||
@@ -72,6 +72,13 @@ test("a state directory linked out of the data root refuses", (t) => {
|
||||
assert.throws(() => readActivePointer(dataRoot), /state\/active.json resolves outside the data root/);
|
||||
});
|
||||
|
||||
+test("a state directory linked out to nothing reads as no release and an empty log", (t) => {
|
||||
+ const { dataRoot, outside } = scratch(t);
|
||||
+ fs.symlinkSync(path.join(outside, "absent"), path.join(dataRoot, "state"));
|
||||
+ assert.equal(readActivePointer(dataRoot), null);
|
||||
+ assert.deepEqual(readActivationLog(dataRoot), { entries: [], malformed: 0 });
|
||||
+});
|
||||
+
|
||||
test("a missing log is empty", (t) => {
|
||||
const { dataRoot } = scratch(t);
|
||||
assert.deepEqual(readActivationLog(dataRoot), { entries: [], malformed: 0 });
|
||||
@@ -95,11 +102,13 @@ test("the log reads oldest first and counts malformed lines", (t) => {
|
||||
{ ...good[1], extra: true },
|
||||
{ ...good[1], at: 5 },
|
||||
{ ...good[1], note: null },
|
||||
+ { at: good[1].at, event: good[1].event, release: good[1].release },
|
||||
+ { ...good[1], release: 5 },
|
||||
good[2],
|
||||
good[3],
|
||||
]);
|
||||
- assert.deepEqual(readActivationLog(dataRoot), { entries: good, malformed: 5 });
|
||||
- assert.deepEqual(readActivationLog(dataRoot, { last: 2 }), { entries: good.slice(2), malformed: 5 });
|
||||
+ assert.deepEqual(readActivationLog(dataRoot), { entries: good, malformed: 7 });
|
||||
+ assert.deepEqual(readActivationLog(dataRoot, { last: 2 }), { entries: good.slice(2), malformed: 7 });
|
||||
assert.deepEqual(readActivationLog(dataRoot, { last: 99 }).entries, good);
|
||||
});
|
||||
|
||||
diff --git tree/packages/runs/tests/task-cli.test.mjs r2/packages/runs/tests/task-cli.test.mjs
|
||||
index b5fafd4..ed13ecd 100644
|
||||
--- tree/packages/runs/tests/task-cli.test.mjs
|
||||
+++ r2/packages/runs/tests/task-cli.test.mjs
|
||||
@@ -92,3 +92,20 @@ test("show refuses a run linked out of the data root; list reports it unknown",
|
||||
assert.equal(list.status, 0, list.stderr);
|
||||
assert.equal(list.stdout, `${A} unknown task=- ws=- session=-\n`);
|
||||
});
|
||||
+
|
||||
+test("list shows an r- name that isn't a valid run id, as before", (t) => {
|
||||
+ const { dataRoot } = scratch(t);
|
||||
+ write(path.join(dataRoot, "runs", "r-.bad", "result.json"), RESULT);
|
||||
+ const out = task(t, dataRoot, "list");
|
||||
+ assert.equal(out.status, 0, out.stderr);
|
||||
+ assert.equal(out.stdout, "r-.bad succeeded task=t-one ws=- session=-\n");
|
||||
+});
|
||||
+
|
||||
+test("show refuses an invalid id before reading the config", (t) => {
|
||||
+ const { dir } = scratch(t);
|
||||
+ const env = { ...process.env, MOSAIC_CONFIG: path.join(dir, "missing.json") };
|
||||
+ delete env.NODE_TEST_CONTEXT;
|
||||
+ const proc = spawnSync(process.execPath, [path.join(ROOT, "scripts", "mosaic-task.mjs"), "show", "../x"], { cwd: ROOT, env, encoding: "utf8" });
|
||||
+ assert.equal(proc.status, 4, proc.stderr);
|
||||
+ assert.equal(proc.stderr, 'mosaic-task: invalid run id: "../x" (expected r-<id>)\n');
|
||||
+});
|
||||
@@ -0,0 +1,50 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.673322ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.484645ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.904005ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.157425ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.412977ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.628667ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (1.084271ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (1.044359ms)
|
||||
✔ readRunDocument reads only the three run documents (0.837895ms)
|
||||
✔ a link inside the data root is followed (1.571328ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.9115ms)
|
||||
✔ a run directory linked out of the data root is never read (1.032763ms)
|
||||
✔ a document linked out of the data root reads as null (1.651968ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.9743ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.753436ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.869964ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.998801ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.706462ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.727334ms)
|
||||
✔ a link to the data root's parent is outside it (0.619388ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (1.204458ms)
|
||||
✔ listRunRecords lists an r- name that isn't a valid run id, as before (0.759264ms)
|
||||
✔ the readers write nothing (3.124064ms)
|
||||
✔ no pointer is null (2.06006ms)
|
||||
✔ the pointer release.sh writes reads back (1.253317ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.510411ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (1.188753ms)
|
||||
✔ a state file linked out of the data root refuses (1.074534ms)
|
||||
✔ a state directory linked out of the data root refuses (0.628466ms)
|
||||
✔ a state directory linked out to nothing reads as no release and an empty log (0.568961ms)
|
||||
✔ a missing log is empty (0.501754ms)
|
||||
✔ the log reads oldest first and counts malformed lines (1.269789ms)
|
||||
✔ last must be a positive integer (0.577624ms)
|
||||
✔ the state readers write nothing (0.939778ms)
|
||||
✔ list prints each run in the established format (109.147436ms)
|
||||
✔ show prints the run in the established format (100.576139ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (145.04362ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (172.596296ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (169.239482ms)
|
||||
✔ list shows an r- name that isn't a valid run id, as before (86.181593ms)
|
||||
✔ show refuses an invalid id before reading the config (58.252566ms)
|
||||
ℹ tests 41
|
||||
ℹ suites 0
|
||||
ℹ pass 41
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 911.848651
|
||||
rc=0
|
||||
@@ -0,0 +1,42 @@
|
||||
6b8358f91b7512a5514d21e32020d7bb51a526c37c6989be93f9e3d608781189 ./base.txt
|
||||
ad2ee26c16bd13b5d78a5bcedff53b1e5c5e4e11560270cb23d99bc5ca23e289 ./BUILD.md
|
||||
d2fa5fb7f19ab328b51bf0e8a42beb419914ec6f99cb022cc46e88666a0cdca7 ./build.patch
|
||||
1afe07062349b097b955f0391bffb839b083492d02644421def6c39cc19ff4a4 ./byte-check.sh
|
||||
2727198f7e50b935205546a60ea97d8d4f20d87cad2a88e10477148c8d3dc42e ./candidate-manifest.sha256
|
||||
09788e902a54207ef5f94de1c8b7f2f31c049fb028fa68847115da83060d90f9 ./delta-check.sh
|
||||
9bbbf163e4625fc966b4f8368b7a578c752f320a0cf9cb82dc63b49c8a055630 ./files.txt
|
||||
ffeb69dfd69070bc54ac31543737d0f669b5f44f1546919d47370a61f52e2bb9 ./out/apply-75ab1646.txt
|
||||
0bf93f746d1c2fa0cc9fd38ac63ade144d946f0588e9a74add9dc4ac19a1aae4 ./out/byte-check.txt
|
||||
9fe4d344ec31b3a0e7f138f7e1cca4a4d01c7c7b7693386fb87b2573acb29a34 ./out/delta-check.txt
|
||||
422d525e9cdcdfe26156b6c4f9ae8b3bcee0715b2c618b8de459ec634750f87d ./out/gate/test-task.log
|
||||
e00191a5e3c4998ef63061a02f46b3eea8c1e086f1c309a2aaa95191bd7fae1e ./out/mutants.sh
|
||||
486c2f735ceb41ab070d778d742662e7a25a6a7b325a57fbc1ce9aedef0f2e5c ./out/mutants.txt
|
||||
afe2b2f318df2962e739f72ee191e8d214c4f4d5f17d474fce0ae34d0ca306ad ./out/round1/apply-923957e2.txt
|
||||
10c6593fe07f295f7bbe209e608664d8308fbe663a6160353b130d888d39e000 ./out/round1/apply-dc96f87b.txt
|
||||
0bf93f746d1c2fa0cc9fd38ac63ade144d946f0588e9a74add9dc4ac19a1aae4 ./out/round1/byte-check.txt
|
||||
7b1dca80e97d7b0660a38d3daf5f4454380c730a397da652ac9af23dfb3eadb6 ./out/round1/delta-check.txt
|
||||
d81f2ce71f7c9bc3be43f38bb50d4fcea40bc99d099a629c07e6f449ea0e362f ./out/round1/gate/run.sh
|
||||
1f6649145018f4f9b298a98c10b3c0b471214d9cbd0c19a41ce5046a47f41800 ./out/round1/gate/runs.log
|
||||
7278e43ff0a3d60f45c2ed211c25729c105dbf7a9df76eeebd388ea4d73615b6 ./out/round1/gate/summary.txt
|
||||
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 ./out/round1/gate/test-auth.log
|
||||
56c85ba7965e49a10acc80f934874d4be14ae80af81efa72561a293850a2eae2 ./out/round1/gate/test-conductor.log
|
||||
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 ./out/round1/gate/test-config.log
|
||||
c2cdde34b9447351324f2e3e9c160db0c3ff6249057099214218e7737aa7ceb8 ./out/round1/gate/test-discord.log
|
||||
7d5016ded1994048642f8b9ae845e52e8af4a069ba05c056c8f580a4589bacbe ./out/round1/gate/test-discord-r2.log
|
||||
ca044dc45b79206743aba8e22d84d9eecba146c518458dc97a3325cf93eba493 ./out/round1/gate/test-extension-package.log
|
||||
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f ./out/round1/gate/test-foundation.log
|
||||
d86110a3cbaf73c6f8032ae9ff1b737ca8b405281f17673de5b0b8b33a558a14 ./out/round1/gate/test-queue.log
|
||||
fa832794ee35025fb4559d58d01a270f6bff2eb65e863e9075b797f47fa68545 ./out/round1/gate/test-release.log
|
||||
ccea37f4ffde1f160dcd1a1864988083a645d974e892065c956b338d0f8c853a ./out/round1/gate/test-task.log
|
||||
faf3049f543179face1645e81ad64c7172b4d77b40dad0cbc6211bff1630619d ./out/round1/mutants.sh
|
||||
e4bdafcbe4ef48ae1676c5573d340c46b072e1ae285fc917efd03768bd784192 ./out/round1/mutants.txt
|
||||
010e2694eb37248355025ee1c6b7295b184a302991862722713a43f45e31b26c ./out/round1/runs-tests-final.log
|
||||
6462e1c49fa3bde202f453d17ffa9dc32fd2a8d73fb8c83b66e1855d69c4afed ./out/round2-delta.diff
|
||||
9ac76c6c297b07f2e88d330e948eb9a42a19d3103c53c6fe6e95402a35bcea83 ./out/runs-tests.log
|
||||
7a4dae1dff789670061c23adc072504a84db9c8d45847cfa54a6c45501354031 ./r1/base.txt
|
||||
696923cc43617d89c5501ee72961390fed0f8bb6fcef843970b736be3d049758 ./r1/BUILD.md
|
||||
dd7b469b5aed4ae0e7eed39495e5a1677c36391e9fc0d254a49272db5168d8f0 ./r1/build.patch
|
||||
ed3c5392ae43e1c3541c6bab6a2f25826514124f5f68536e5cc330a39cc5d7a3 ./r1/candidate-manifest.sha256
|
||||
9bbbf163e4625fc966b4f8368b7a578c752f320a0cf9cb82dc63b49c8a055630 ./r1/files.txt
|
||||
e0497ad1bf92c41c8eec719e655f0d19840550ca5f0c9271b9c76f81d68ed1c3 ./r1/packet-manifest.sha256
|
||||
15d575cc45313906114f7edda9379b66af7624d9e894bb98812deb7acf5d87ba ./seed.sh
|
||||
@@ -0,0 +1,186 @@
|
||||
# Row 56 (#1545): runs and releases reader module, candidate packet, round 1
|
||||
|
||||
Author: Rocko. Reviewers: Darkwing and Filbert. Brief:
|
||||
`docs/plans/2026-10-10_design-implementation.md`, section "Runs and releases
|
||||
reader module". Base: `dc96f87b` (`base.txt`). `build.patch` also applies
|
||||
cleanly to `923957e2` (origin/refactor at packet time). No file in this
|
||||
candidate changed between the two (`out/apply-*.txt`). The packet is
|
||||
uncommitted. I made no commits, pushes or Gitea calls, and read no token or
|
||||
private binding.
|
||||
|
||||
## Files (`files.txt`, 15)
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `packages/runs/package.json` | new: `@mosaic/runs`, private, no dependencies |
|
||||
| `packages/runs/src/errors.mjs` | new: `RunsError(message, exitCode = 4)` |
|
||||
| `packages/runs/src/paths.mjs` | new: `resolveInside` containment, `readJsonObject` |
|
||||
| `packages/runs/src/runs.mjs` | new: `isRunId`, `listRunIds`, `listRunRecords`, `readRunDocument`, `readRunRecord` |
|
||||
| `packages/runs/src/state.mjs` | new: `readActivePointer`, `readActivationLog` |
|
||||
| `packages/runs/src/index.mjs` | new: re-exports |
|
||||
| `packages/runs/README.md` | new: what it reads, the limits, the deliberate deltas |
|
||||
| `packages/runs/tests/*.mjs` | new: 37 tests (runs 22, state 10, task-cli 5) plus helpers |
|
||||
| `scripts/mosaic-task.mjs` | `list` and `show` read through the module, +18/−27 |
|
||||
| `agents/rocko/work/queue-56/{seed,byte-check,delta-check}.sh` | the byte-identity and delta checks |
|
||||
|
||||
`build.patch` is `git diff --cached --binary dc96f87b` over those files:
|
||||
+1021/−27. `candidate-manifest.sha256` hashes the 15 files.
|
||||
|
||||
Q14 freeze: nothing under `packages/bus`, `packages/tasks`, `packages/cli`,
|
||||
`scripts/bus-service.sh` or `scripts/mosaic` changes. Apart from the import,
|
||||
`scripts/mosaic-task.mjs` is the only file under `scripts/` that changes. The
|
||||
module covers the runs view's needs, but the view itself, `release.sh` and
|
||||
pruning are untouched.
|
||||
|
||||
## Design
|
||||
|
||||
- **Containment.** `resolveInside` runs `realpathSync` on the data root and
|
||||
the target, then applies a `path.relative` check:
|
||||
- `..`, `../…` or an absolute relative path counts as outside;
|
||||
- the configured data root is trusted even when it is itself a link;
|
||||
- links that stay inside it are followed.
|
||||
- **Outside paths.**
|
||||
- `runs/`, `state/`, a run directory read by `readRunRecord`, `active.json`
|
||||
and the activation log refuse when they resolve outside.
|
||||
- A run document that resolves outside reads as `null`, the way an
|
||||
unreadable one always has.
|
||||
- **Missing versus broken.** ENOENT and ENOTDIR read as missing. Anything
|
||||
else (ELOOP, EACCES) refuses with the error code instead of reading as
|
||||
empty.
|
||||
- **Documents.** A run document is a JSON object or `null`. Run records are
|
||||
not validated further, because older records carry older shapes.
|
||||
- **Release pointer.** `active.json` is strict: exactly the version 1 shape
|
||||
`release.sh` writes. Bad data exits 2, and a read error exits 4.
|
||||
- **Activation log.** The log is lenient per line, like `release.sh
|
||||
rollback`. It returns `{ entries, malformed }`, and `last` keeps the newest
|
||||
entries.
|
||||
- **Errors.** Every refusal is a `RunsError` with exit code 2 or 4.
|
||||
`mosaic-task.mjs` maps it to `fail(exitCode, message)` through
|
||||
`readRuns`.
|
||||
- **Read-only.** No function writes. The "readers write nothing" test and
|
||||
byte-check's before/after snapshot of the data root both check this.
|
||||
|
||||
## Gate (`out/gate/`, sequential, scratch worktree of the candidate)
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| packages/runs tests | 34/34 at gate time; 37/37 after the mutant round (`out/runs-tests-final.log`) |
|
||||
| test-auth | 15 passed, 0 failed |
|
||||
| test-conductor | 17 passed, 0 failed |
|
||||
| test-config | 24 passed, 0 failed |
|
||||
| test-discord | first run 57 passed, 1 failed; rerun 66 passed, 0 failed (below) |
|
||||
| test-extension-package | 18 passed, 0 failed |
|
||||
| test-foundation | 44 passed, 0 failed |
|
||||
| test-queue | 27 passed, 0 failed |
|
||||
| test-release | 14 passed, 0 failed |
|
||||
| test-task | 98 passed, 0 failed |
|
||||
|
||||
**test-discord's first-run failure was environmental.** The failing case was
|
||||
"pi binary present at node_modules/.bin/pi for the extension checks". The
|
||||
scratch worktree has no `node_modules`, and the base worktree lacks it too.
|
||||
I symlinked the checkout's `node_modules` into the scratch tree and reran
|
||||
the suite: 66 passed, 0 failed. The extra 8 cases are the extension checks
|
||||
that the missing binary had skipped. Then I removed the symlink
|
||||
(`out/gate/test-discord-r2.log`). Nothing in this candidate touches
|
||||
packages/discord. Sage's rerun in a tree that has `node_modules` should see
|
||||
66.
|
||||
|
||||
After the gate, the only changes were tests, the README table row and the
|
||||
check scripts. No `src/` or `scripts/mosaic-task.mjs` change came after it.
|
||||
|
||||
## Byte identity (`out/byte-check.txt`)
|
||||
|
||||
`byte-check.sh BASE CAND WORK` seeds a data root with `seed.sh`. The seed
|
||||
holds:
|
||||
|
||||
- runs a–f;
|
||||
- a run that is a file;
|
||||
- an internal link and a dangling link;
|
||||
- `r-zz.weird_name-1`;
|
||||
- a non-`r-` name, `.pruned.log` and a stray file.
|
||||
|
||||
It runs 16 cases from both trees: `list` on seeded, empty and absent roots,
|
||||
and `show` on each run, a missing run, `../escape`, no argument and an
|
||||
absent root. It diffs stdout, stderr and exit codes.
|
||||
|
||||
```
|
||||
byte-check: 16 cases, stdout, stderr and exit identical
|
||||
byte-check: data root unchanged
|
||||
```
|
||||
|
||||
**Correction to my own harness.** My first rerun in this round passed a
|
||||
relative WORK_DIR. Every redirect inside the `cd` subshell failed, both trees
|
||||
recorded exit 1, and the diff still reported "identical": a false pass.
|
||||
Earlier runs used absolute paths and were not affected. Both scripts now
|
||||
make WORK_DIR absolute before doing anything else. `byte-check.sh` also
|
||||
exits 4 if any case leaves no stdout or stderr file. The output above
|
||||
comes from the fixed script.
|
||||
|
||||
## Deliberate deltas (`out/delta-check.txt`)
|
||||
|
||||
These cases fall outside the byte-identity domain: a link out of the data
|
||||
root, a document that is JSON but not an object, a run that is a file, a
|
||||
link loop, and an unreadable directory. Each one changes on purpose. The
|
||||
README table lists them, and `delta-check.sh` prints base against candidate:
|
||||
|
||||
- run directory linked out: `list` shows `unknown`; `show` refuses, exit 4.
|
||||
- `result.json` linked out: `list` shows `unknown`; `show` prints
|
||||
`(missing or unreadable)`.
|
||||
- `runs/` linked out: `list` and `show` refuse, exit 4.
|
||||
- `result.json` is `5`: base `list` crashed (exit 1); base `show` printed
|
||||
`undefined` fields. The candidate gives `unknown` and
|
||||
`(missing or unreadable)`.
|
||||
- `task.json` is `[]`: base printed the snapshot line; the candidate
|
||||
omits it.
|
||||
- run is a regular file: base `show` crashed in `readdirSync`; the
|
||||
candidate gives `run not found`, exit 4.
|
||||
- link loop: base said `run not found`; the candidate refuses with ELOOP,
|
||||
exit 4.
|
||||
- `runs/` unreadable: base `list` printed nothing, exit 0; the candidate
|
||||
refuses with EACCES, exit 4.
|
||||
|
||||
## Mutants (`out/mutants.sh`, `out/mutants.txt`)
|
||||
|
||||
I ran 32 single-line mutants of `src/` and `scripts/mosaic-task.mjs`
|
||||
against the packages/runs tests.
|
||||
|
||||
First round: 26 killed, 4 survived and 2 didn't apply. Three of the
|
||||
survivors were real gaps, and I added a test for each:
|
||||
|
||||
- **M02** dropped the exact `..` check. A link to the data root's parent got
|
||||
through. New test: "a link to the data root's parent is outside it".
|
||||
- **M09** dropped the sort. Node returned this directory already sorted,
|
||||
so no fixture could catch it. New test: it mocks `fs.readdirSync` to
|
||||
return the names reversed.
|
||||
- **M15** read every readdir error on a run directory as missing. New
|
||||
test: "an unreadable run directory refuses instead of reading as
|
||||
missing".
|
||||
|
||||
Final round: 30 killed, 2 survived. Both survivors are equivalent:
|
||||
|
||||
- **M04** dropped `!path.isAbsolute(relative)`. On POSIX, `path.relative`
|
||||
between two absolute paths is never absolute, so the branch is
|
||||
unreachable on Linux. I kept it as a guard for other platforms.
|
||||
- **M31** dropped the CLI's `isRunId` pre-check in `show`. `readRunRecord`
|
||||
throws the same message with exit 4, and `readRuns` maps it to the same
|
||||
`fail`. The pre-check stays so that an invalid id refuses before the
|
||||
config loads, as it did before.
|
||||
|
||||
## Follow-up (not fixed, out of scope)
|
||||
|
||||
This defect predates the candidate and is unchanged: `list` crashes with
|
||||
a `padEnd` TypeError when `result.json` is an object without a string
|
||||
`status` or `taskId`. Base and candidate behave the same here, so
|
||||
byte-identity holds. A fix would change `list` output and belongs in its
|
||||
own row.
|
||||
|
||||
## Reproduce
|
||||
|
||||
```sh
|
||||
git worktree add --detach /tmp/r56-base dc96f87b
|
||||
git worktree add --detach /tmp/r56-cand dc96f87b
|
||||
(cd /tmp/r56-cand && git apply --index <packet>/build.patch && sha256sum -c <packet>/candidate-manifest.sha256)
|
||||
env -u NODE_TEST_CONTEXT node --test '/tmp/r56-cand/packages/runs/tests/*.test.mjs'
|
||||
bash /tmp/r56-cand/agents/rocko/work/queue-56/byte-check.sh /tmp/r56-base /tmp/r56-cand /tmp/r56-bc
|
||||
bash /tmp/r56-cand/agents/rocko/work/queue-56/delta-check.sh /tmp/r56-base /tmp/r56-cand /tmp/r56-dc
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
dc96f87b1776cc74d0ea5ba7701db418cc21e1b8
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
1afe07062349b097b955f0391bffb839b083492d02644421def6c39cc19ff4a4 agents/rocko/work/queue-56/byte-check.sh
|
||||
96416ea2eb84490b79e98b9aa0f95673e76070f15ba2876f83dbf1bdaa919f97 agents/rocko/work/queue-56/delta-check.sh
|
||||
15d575cc45313906114f7edda9379b66af7624d9e894bb98812deb7acf5d87ba agents/rocko/work/queue-56/seed.sh
|
||||
146b236a37bf665979e28f1794c4c4437b6e85feb43b892e8d9d697506fc453f packages/runs/package.json
|
||||
594eb42518f73c94608320ce381868026aca69d8f5104711c9685ebdd8df41c9 packages/runs/README.md
|
||||
f686212fa10e8541a1d8055fe30d65558a516245276aa53ecefd97bd7ca0a20c packages/runs/src/errors.mjs
|
||||
eb7063a8ee0417699b45f4bfec69c3ffcde8b3b276956ca4fea266ec0abd6b23 packages/runs/src/index.mjs
|
||||
3f9d025f0615a08edd440bb2aa6cea48f21cafa1f680332fffca3c7ee586cc65 packages/runs/src/paths.mjs
|
||||
ba64e50c3a63e693fc313278ff6cf440234649a6719dd9229bce20272a518ec1 packages/runs/src/runs.mjs
|
||||
c013ed1912bb97cbfb5209170741d9a20c2955a9acbd1fff9e397fac1fab742a packages/runs/src/state.mjs
|
||||
7c941596020b14d8627ccb09b66e0e51195318e86add60eba9d2c4409e6f8dd6 packages/runs/tests/helpers.mjs
|
||||
4125ffc8787f3cd9441bc2a557f42fde6948b884b8b870b503acef6eee6b3913 packages/runs/tests/runs.test.mjs
|
||||
7857d2c8c94011df328dbf5013626dbcdd577945781d104dff533f08b1859130 packages/runs/tests/state.test.mjs
|
||||
6bf3e758c191e23fddec6bcf30d881cdda1bb37d4e3ed1cd4ef47be79fb36bd8 packages/runs/tests/task-cli.test.mjs
|
||||
4873187609897c643e62acf7f1d074fa3c03369fe6702b9b0c7776138e776401 scripts/mosaic-task.mjs
|
||||
@@ -0,0 +1,15 @@
|
||||
agents/rocko/work/queue-56/byte-check.sh
|
||||
agents/rocko/work/queue-56/delta-check.sh
|
||||
agents/rocko/work/queue-56/seed.sh
|
||||
packages/runs/package.json
|
||||
packages/runs/README.md
|
||||
packages/runs/src/errors.mjs
|
||||
packages/runs/src/index.mjs
|
||||
packages/runs/src/paths.mjs
|
||||
packages/runs/src/runs.mjs
|
||||
packages/runs/src/state.mjs
|
||||
packages/runs/tests/helpers.mjs
|
||||
packages/runs/tests/runs.test.mjs
|
||||
packages/runs/tests/state.test.mjs
|
||||
packages/runs/tests/task-cli.test.mjs
|
||||
scripts/mosaic-task.mjs
|
||||
@@ -0,0 +1,28 @@
|
||||
7a4dae1dff789670061c23adc072504a84db9c8d45847cfa54a6c45501354031 ./base.txt
|
||||
696923cc43617d89c5501ee72961390fed0f8bb6fcef843970b736be3d049758 ./BUILD.md
|
||||
dd7b469b5aed4ae0e7eed39495e5a1677c36391e9fc0d254a49272db5168d8f0 ./build.patch
|
||||
1afe07062349b097b955f0391bffb839b083492d02644421def6c39cc19ff4a4 ./byte-check.sh
|
||||
ed3c5392ae43e1c3541c6bab6a2f25826514124f5f68536e5cc330a39cc5d7a3 ./candidate-manifest.sha256
|
||||
96416ea2eb84490b79e98b9aa0f95673e76070f15ba2876f83dbf1bdaa919f97 ./delta-check.sh
|
||||
9bbbf163e4625fc966b4f8368b7a578c752f320a0cf9cb82dc63b49c8a055630 ./files.txt
|
||||
afe2b2f318df2962e739f72ee191e8d214c4f4d5f17d474fce0ae34d0ca306ad ./out/apply-923957e2.txt
|
||||
10c6593fe07f295f7bbe209e608664d8308fbe663a6160353b130d888d39e000 ./out/apply-dc96f87b.txt
|
||||
0bf93f746d1c2fa0cc9fd38ac63ade144d946f0588e9a74add9dc4ac19a1aae4 ./out/byte-check.txt
|
||||
7b1dca80e97d7b0660a38d3daf5f4454380c730a397da652ac9af23dfb3eadb6 ./out/delta-check.txt
|
||||
d81f2ce71f7c9bc3be43f38bb50d4fcea40bc99d099a629c07e6f449ea0e362f ./out/gate/run.sh
|
||||
1f6649145018f4f9b298a98c10b3c0b471214d9cbd0c19a41ce5046a47f41800 ./out/gate/runs.log
|
||||
7278e43ff0a3d60f45c2ed211c25729c105dbf7a9df76eeebd388ea4d73615b6 ./out/gate/summary.txt
|
||||
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 ./out/gate/test-auth.log
|
||||
56c85ba7965e49a10acc80f934874d4be14ae80af81efa72561a293850a2eae2 ./out/gate/test-conductor.log
|
||||
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 ./out/gate/test-config.log
|
||||
c2cdde34b9447351324f2e3e9c160db0c3ff6249057099214218e7737aa7ceb8 ./out/gate/test-discord.log
|
||||
7d5016ded1994048642f8b9ae845e52e8af4a069ba05c056c8f580a4589bacbe ./out/gate/test-discord-r2.log
|
||||
ca044dc45b79206743aba8e22d84d9eecba146c518458dc97a3325cf93eba493 ./out/gate/test-extension-package.log
|
||||
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f ./out/gate/test-foundation.log
|
||||
d86110a3cbaf73c6f8032ae9ff1b737ca8b405281f17673de5b0b8b33a558a14 ./out/gate/test-queue.log
|
||||
fa832794ee35025fb4559d58d01a270f6bff2eb65e863e9075b797f47fa68545 ./out/gate/test-release.log
|
||||
ccea37f4ffde1f160dcd1a1864988083a645d974e892065c956b338d0f8c853a ./out/gate/test-task.log
|
||||
faf3049f543179face1645e81ad64c7172b4d77b40dad0cbc6211bff1630619d ./out/mutants.sh
|
||||
e4bdafcbe4ef48ae1676c5573d340c46b072e1ae285fc917efd03768bd784192 ./out/mutants.txt
|
||||
010e2694eb37248355025ee1c6b7295b184a302991862722713a43f45e31b26c ./out/runs-tests-final.log
|
||||
15d575cc45313906114f7edda9379b66af7624d9e894bb98812deb7acf5d87ba ./seed.sh
|
||||
Reference in New Issue
Block a user