Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a32481b780 | ||
|
|
5c70feebde | ||
|
|
a86a881e1b | ||
|
|
fb484acb72 | ||
|
|
f832064731 | ||
|
|
78b69e1e68 | ||
|
|
48b89db33b | ||
|
|
7c8dc20335 | ||
|
|
ce0b6ed411 | ||
|
|
97cb9a7630 | ||
|
|
e908f7ffee | ||
|
|
92df71a982 | ||
|
|
0abec5d2e8 | ||
|
|
20996ecd66 | ||
|
|
78b932feb6 |
No files matched your search
@@ -5583,3 +5583,9 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r
|
||||
|
||||
2026-10-24 — Magica row329 R2 (#1827): B1 ASCII-only management key fix verified with complete-tree refusals and positive auth control; package66/66,30bus readers261/261,49CLI readers726/726,config55/55, no required skip/failure. N1 corrects R1 bus total to1019; N3 stale-lock guidance and N4 34-code inventory corrected. Two over-selected file runs are recorded separately, with no F7 inference; cause unknown/Vikunja625. Old502 test/helper files byte-identical, slot returned to Sage. Evidence agents/magica/work/queue-329/BUILD.md.
|
||||
2026-10-24T23:37:46Z | Sage | Row 329 (#1827, P1 pinned model proxy, Magica) LANDED 61ebf2c34, pushed 23:31:27Z 10-24. Before: refactor at 669cdf42c. After: Magica's 4 commits (94cde633e, c65fb34ee, d1f6f2b19, 61ebf2c34) cherry-picked onto 669cdf42c, diff vs the gated tree limited to drift paths, range-diff in scratch. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r329l/wt (gate.sh 8b4978887 c8a9bcf94), started 23:08:12Z at load 14.35, GATE GREEN 23:29:14Z at load 8.67, no rerun, no test or timeout edit (mos-1634). Host note: two Codex workers cleaned T3 worktrees from about 23:14Z (mos-1639, Vikunja 632). Approvals: Dewey r2 c31643 rev 4047, Boyd r2 c31642 rev 4048. #1827 comment 31650, closed. Vikunja 237 comment 1109 (stays open for P2-P7); C4 P10 red filed as Vikunja 631. Queue: note rev 4063, done rev 4064, committed b24d917ec. DEFERRED: N2 and R2-N1 bound to row 331, N1 bound to P7. Overlap: row 284 (packages/bus/src/broker.mjs) and row 157 (scripts/mosaic-config.mjs) rebase onto 61ebf2c34 before their gates.
|
||||
2026-10-25T00:15:48Z | Sage | Row 284 (#1789, C2-05c library.get scope, Della) LANDED ce0b6ed41, pushed 00:09:52Z 10-25. Before: refactor at 9e2689dff (row 329 landing docs). After: fast-forward to Della's 7 rebased commits 78b932feb..ce0b6ed41; 8 paths, manifest-r4 de505cfd matches the landed tree. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r284l/wt (the 329 gate.sh with D and the compose project renamed), started 23:51:19Z at load 1.62, GATE GREEN 00:09:04Z at load 5.20, no rerun, no test or timeout edit. Counts: bus 1020/0, business 283/0, cli 999 pass 1 allowed skip (FED R7), harness 177/0, library 156/0, seat 218/0, webui 458/0, every scripts/test-*.sh rc 0; queue 34 and task 87/93 skips are the standing ones. Della's whole-file runs on ce0b6ed41: library-lookups 12/0, library-tools 5/0, query 34/0. Approval: Webby round 2 delta c31654 rev 4070 (round 1 rev 4062). Overlap sweep: rows 13, 157, 309, gyro/row310 and fethry/row332-p1 clean. #1789 comment 31655, closed. No Vikunja task for the row. Queue: note rev 4071, done rev 4072, committed 7c8dc2033. DEFERRED: Webby N1 (whole-tree listing per get) bound to the next library read-path row; N2 needs nothing; N3 is this gate.
|
||||
2026-10-25T00:18:35Z | Sage | Follow-up tasks for mos-1647 item 3 (Q109): Vikunja 633 (P7 cutover, CLIProxyAPI 8.0.42 startup Grok-npm and antigravity version refreshes, from row 332 isolated runs) and 634 (row 284 Webby N1, library get lists the whole tree). DEFERRED gains the 633 entry and the 634 number for row 284 N1. Filing script and receipt in agents/sage/work/vikunja-setup/.
|
||||
|
||||
2026-10-24 — Magica row 331 / #1829 BEFORE: P1 has pinned fetch/init and a redacted management client; agent model-key lifecycle, reconciliation and launch preflight are absent. P2 design and acceptance plan in agents/magica/work/queue-331/BUILD.md. Root worktree npm ci passed; no tests run pending Sage CLEAR. No live proxy or real credential access.
|
||||
|
||||
2026-10-25 — Magica row331 / #1829 GREEN attempt on a86a881e1:54 whole files pass; file55 model-keys.test.mjs rc1 (24pass/6fail). All6 new real-bus fixtures lack dataRoot before Store lstat; no old test failed in attempted prefix. Aggregate592tests/586pass/6fail/0skip. Stopped without edits/reruns; remaining207files/test-config/4mutants not run. Slot returned to Sage05:10:48Z. Per-run rc/count/nice/hash receipts: agents/magica/work/queue-331/green-receipts.json. No in-review/completion claim.
|
||||
@@ -0,0 +1,934 @@
|
||||
# Row284 C2-05c — supporting scope and served-tree blob (#1789)
|
||||
|
||||
Owner Della; reviewer Dewey; gate Sage. Build start sage-3658/3660 (10-23).
|
||||
Branch della/row284. Original prerequisite base Honker28346bad7ea3;
|
||||
merge fc05bcbc3 carries22423816cf28 and2164f3fbc25a intact. Only conflict was
|
||||
CLI thread-launch's added footer tests: retained both full test bodies and
|
||||
assertions. No prerequisite code copied. Every prerequisite needs its own
|
||||
review/mutants/landing under mos1351; rebase after283 lands.
|
||||
|
||||
## Execution status
|
||||
|
||||
UNTESTED. No Node, node--check, test or mutant run before Sage CLEAR. First
|
||||
own commit is the authorized old-test migration; the implementation follows.
|
||||
Sage authorizes an immediate untested push. No queue write or shared-log edit.
|
||||
224 stays first;284 is critical to Bubba288. T4 for224 is not a CLEAR yet.
|
||||
|
||||
## Binding readings
|
||||
|
||||
Brief C2-05c at94c33a43c:1737-1789, Q226, sage2918 and mos1330 item3.
|
||||
get defaults canon; explicit playground/supporting opt-in; mismatch null.
|
||||
Sort full outgoing/incoming lists supporting, playground, canon before paging,
|
||||
keep cross-scope links and universe filter. Manuscript stays canon/playground.
|
||||
No all/array scope, write tool, guard, mapping or creative-name-count change.
|
||||
|
||||
## mos-1330 old-test migration (separate commit)
|
||||
|
||||
All paths below are packages/library/tests/query.test.mjs. Baseline file at
|
||||
fc05bcbc3 matches216's file. Source assertions retained; negative calls receive
|
||||
explicit scope too, so universe isolation cannot pass on default-scope denial.
|
||||
|
||||
Call/assertion 348 -> 348:
|
||||
|
||||
```js
|
||||
// OLD
|
||||
const rin = await get(s, { universe: 'idea', path: 'Drafts/New World/Characters/Rin.md' });
|
||||
// NEW
|
||||
const rin = await get(s, { universe: 'idea', path: 'Drafts/New World/Characters/Rin.md', scope: 'playground' });
|
||||
```
|
||||
|
||||
Call/assertion 350 -> 350:
|
||||
|
||||
```js
|
||||
// OLD
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Drafts/New World/Characters/Rin.md' }), null);
|
||||
// NEW
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Drafts/New World/Characters/Rin.md', scope: 'playground' }), null);
|
||||
```
|
||||
|
||||
Call/assertion 366 -> 366:
|
||||
|
||||
```js
|
||||
// OLD
|
||||
assert.equal(await get(src(ref, mapping(WITH_BLEND)), { universe: 'blend', path }), null);
|
||||
// NEW
|
||||
assert.equal(await get(src(ref, mapping(WITH_BLEND)), { universe: 'blend', path, scope: 'playground' }), null);
|
||||
```
|
||||
|
||||
Call/assertion 367 -> 367:
|
||||
|
||||
```js
|
||||
// OLD
|
||||
const own = await get(src(ref), { universe: 'harbor', path });
|
||||
// NEW
|
||||
const own = await get(src(ref), { universe: 'harbor', path, scope: 'playground' });
|
||||
```
|
||||
|
||||
Assertion 349 retained, OLD == NEW:
|
||||
|
||||
```js
|
||||
assert.equal(rin.scope, 'playground');
|
||||
```
|
||||
|
||||
Assertion 368 retained, OLD == NEW:
|
||||
|
||||
```js
|
||||
assert.equal(own.scope, 'playground');
|
||||
```
|
||||
|
||||
The null assertions at350 and366 are the complete OLD/NEW expressions above:
|
||||
expectation remains null. Added one separate no-scope playground-get -> null
|
||||
behavior test. It is unrun and expected to expose old get behavior until the
|
||||
new scope implementation lands. This migration is authorized by mos1330 under
|
||||
mos1272; no check removed or weakened. Dewey checks this list in manifest review.
|
||||
|
||||
## 283 surface and blob choice
|
||||
|
||||
Inspected actual46bad7ea3: optional editor.user is present. reader's parsed
|
||||
file records and index entries still discard tree oid. In owned query.mjs use
|
||||
the existing bounded listTree(repo, servedCommit) to obtain exact path oid,
|
||||
not HEAD and not a user-supplied revision expression. No reader/index edit.
|
||||
Tradeoff: one additional bounded tree listing per get; latency unmeasured.
|
||||
Recheck after rebasing to283's actual landing.
|
||||
|
||||
## Intended owned paths
|
||||
|
||||
query.mjs; broker.mjs lookup branch; tools.mjs lookup schemas/descriptions;
|
||||
library query.test.mjs/query-fixture.mjs; bus library-lookups.test.mjs;
|
||||
harness library-tools.test.mjs; this BUILD.md. Other files are prerequisites.
|
||||
|
||||
## Gate
|
||||
|
||||
Dewey manifest approval, whole library/bus/harness suites and every
|
||||
scripts/test-*.sh on Sage gate. Before review grep all package tests for enum
|
||||
pins and canon/playground pairs. Keep shared logs out; send receipts to Sage.
|
||||
No test or mutant evidence claimed. Manifest/digest follows complete code.
|
||||
|
||||
## Implementation head (UNTESTED, sage-3658)
|
||||
|
||||
- query scope accepts supporting for find/search/get. get defaults canon,
|
||||
mismatch null. Manuscript passes a separate two-value scope allowlist.
|
||||
- get returns blob from listTree(source.repo,index.commit) after scope/universe
|
||||
filtering. The same index.commit supplies the text; source HEAD is irrelevant.
|
||||
- Full outgoing and incoming link lists are stably sorted by supporting,
|
||||
playground,canon before combining/paging. All visible cross-scope links stay;
|
||||
universe/hybrid visibility still applies. Scope metadata isn't added to links.
|
||||
- Broker only adds optional scope to library.get's strict fields. No new verbs.
|
||||
- Typed scope expands find/search/get only; manuscript retains its own two-value
|
||||
enum. Names/M3 counts remain the row224 six/eight, no write tools added.
|
||||
- New tests: book/universe/draft supporting find/search/get; defaults/mismatch;
|
||||
absent supporting prefix; source HEAD advances but each scope's blob equals
|
||||
served:path; 300 outgoing plus300 incoming links ordered across bounded pages,
|
||||
retaining cross-scope links and dropping other-universe links; hybrid source
|
||||
stays canon-only; no all/array scope; manuscript rejects supporting; broker
|
||||
scope/blob forwarding; typed enums and unchanged names. All unrun.
|
||||
|
||||
## Additional old assertion pin (brief Gate)
|
||||
|
||||
harness/tests/library-tools.test.mjs:49 at23816cf28 (line49, unchanged in this head):
|
||||
OLD find scope enum ['canon','playground']; NEW ['canon','playground','supporting'].
|
||||
This is the exact scope-enum pin the brief directs updating. Existing six/eight
|
||||
name snapshots and TYPES equality assertion are unchanged. The new manuscript
|
||||
pin intentionally remains ['canon','playground']. No other two-scope test pin
|
||||
was found by rg over packages/*/tests at this build's prerequisite merge.
|
||||
|
||||
## Proposed mutation evidence (reviewer confirms final set)
|
||||
|
||||
All unrun. Require assertion failures with whole-file runs and pristine restore:
|
||||
M1 remove supporting from query SCOPES -> supporting lookup cases.
|
||||
M2 drop get's requested-scope comparison -> default/mismatch null cases.
|
||||
M3 use source HEAD for blob lookup -> served-tree/advanced-HEAD equality case.
|
||||
M4 remove link rank sort -> multi-page outgoing/incoming expected order.
|
||||
M5 restrict links to requested scope -> cross-scope link counts/order case.
|
||||
M6 drop sees() from visible/link filtering -> second-universe/hybrid cases.
|
||||
M7 allow supporting in manuscript validator -> manuscript refusal case.
|
||||
M8 omit get scope from broker fields -> real broker supporting get case.
|
||||
M9 widen typed manuscript enum -> typed-scope separation case.
|
||||
|
||||
## Inspection and pending gate
|
||||
|
||||
No production imports change in broker/tools, so no new sandbox closure file
|
||||
or mount. query.mjs is host library code and adds existing reader.listTree;
|
||||
no new package/export/copy-path file. rg found the one old find scope pin plus
|
||||
the intentional new manuscript two-scope pin. Whole library/bus/harness and
|
||||
shell gates remain required. CLI integration/merge context is prerequisite224/
|
||||
219, not this row's source delta; any changed gate expectation goes to Sage.
|
||||
Node checks, tests, mutants and elapsed-time evidence remain absent pending
|
||||
CLEAR. The first test-migration commit and this implementation are distinct.
|
||||
|
||||
Sage sweep receipt: Della row284 builds on28346bad7ea3 plus intact224 merge;
|
||||
mos1330 old-test migration separate, supporting scope/blob implementation and
|
||||
fixtures authored, completely UNTESTED. No shared-log/queue write or live action.
|
||||
|
||||
## Current assertion anchors after implementation imports
|
||||
|
||||
The separate migration commit0ed4fbab9 retains its complete OLD/NEW text
|
||||
above. Adding only the fs/path imports in the implementation shifts anchors:
|
||||
|
||||
query.test.mjs:130: `const blendCh2 = await get(src(ref, mapping(WITH_BLEND)), { universe: 'blend', path: 'Book One/Chapters/Chapter 02.md' });`
|
||||
query.test.mjs:350: `const rin = await get(s, { universe: 'idea', path: 'Drafts/New World/Characters/Rin.md', scope: 'playground' });`
|
||||
query.test.mjs:351: `assert.equal(rin.scope, 'playground');`
|
||||
query.test.mjs:352: `assert.equal(await get(s, { universe: 'harbor', path: 'Drafts/New World/Characters/Rin.md', scope: 'playground' }), null);`
|
||||
query.test.mjs:368: `assert.equal(await get(src(ref, mapping(WITH_BLEND)), { universe: 'blend', path, scope: 'playground' }), null);`
|
||||
query.test.mjs:369: `const own = await get(src(ref), { universe: 'harbor', path, scope: 'playground' });`
|
||||
query.test.mjs:370: `assert.equal(own.scope, 'playground');`
|
||||
|
||||
No result/null assertion changed; scope arguments carry the explicit opt-in.
|
||||
|
||||
## Dewey dewey-0941 static review — round2
|
||||
|
||||
Static scope fc05bcbc3..47db04eba CLEAN; not a final verdict or test evidence.
|
||||
M1-M9 accepted as the final set. Optional M10 not added to this gate.
|
||||
N2 taken: keep the existing canon-path supporting-scope mismatch assertion,
|
||||
and ADD get(Book One/Support/Notes/Helper.md,scope:supporting) -> null under
|
||||
the default mapping with no supporting prefix. The supporting-positive test
|
||||
already returns this same path under withSupporting, so the added probe
|
||||
pins the missing-prefix case directly. No assertion removed or weakened;
|
||||
mos1330's separate migration commit and its retained assertions unchanged.
|
||||
|
||||
N1 proposed DEFERRED/follow-up for Sage: retain each tree oid in reader/index
|
||||
entries so get can return blob without cat-file/whole ls-tree -r -l per page.
|
||||
Current get's result is pinned correctly, but per-page cost grows with the
|
||||
library. No performance measurement yet. This optimization needs reader/index
|
||||
ownership and separate acceptance/performance evidence; do not silently expand
|
||||
284's manifest or change its served-snapshot contract for it.
|
||||
|
||||
No Node, tests, mutants or syntax checks run in this round; hold still applies.
|
||||
|
||||
## sage-4281 — rebase onto landed 224/283, 2026-10-24
|
||||
|
||||
New worktree /mnt/storage/scratch/della-row284-r2, branch della/row284-r2,
|
||||
base refactor 95d4991d20a4ef291c824d37c886eaeaa9be7062. Cherry-picked only
|
||||
284-owned 0ed4fbab9, 47db04eba, 5d587a552. Range-diff: all three equal, no
|
||||
conflicts. The separate mos-1330 migration remains its own commit. No 216,
|
||||
224 or 283 prerequisite commits copied. Old della/row284 remains unchanged.
|
||||
|
||||
mos-1584 fixture check: new tests add no businessDoc() with a library. The
|
||||
harness role() uses landed 224's gitea reference for bean; the library-enabled
|
||||
CLI chief fixture fix is also upstream. New broker test uses the direct
|
||||
Broker fixture (no businessDoc/bootConfig), not a new validated organization.
|
||||
No new made-up credential values are introduced in this row; existing short
|
||||
fixture references are reused, so no new credential reaches 24 characters.
|
||||
No credential values were inspected or printed. No fixture edit necessary.
|
||||
|
||||
No Node, npm, syntax check, service, test, mutant or install run in this worktree.
|
||||
No canonical node_modules link. Dependency installation remains a separately
|
||||
cleared local npm ci if needed; tests wait for Sage CLEAR. For future runs use
|
||||
shell mktemp -d /mnt/storage/scratch/tmp/XXXXXX and per-child absolute nice 19.
|
||||
Shared logs unchanged; Sage owns landing receipts.
|
||||
|
||||
## Import-derived whole-file run plan (mos-1612)
|
||||
|
||||
Walked relative static import/re-export edges and literal dynamic imports
|
||||
transitively, from each packages/*/tests/**/*.test.mjs to changed query.mjs,
|
||||
broker.mjs, tools.mjs or query-fixture.mjs. This follows source import edges,
|
||||
not textual mentions. Example CLI/thread-launch -> Broker; CLI/helpers ->
|
||||
Broker; webui/serve -> webui/bus -> Broker; harness/bundle -> tools.
|
||||
Evidence generator and per-test import chains are in
|
||||
/mnt/storage/scratch/della-row284-r2-evidence/import-run-list.py and .json.
|
||||
The import-derived set has 260 files; union with the brief's whole library,
|
||||
bus and harness suites adds files that use subprocesses/source reads instead
|
||||
of direct imports. Excluded case1-accept.test.mjs by sage-4281. Any dynamic
|
||||
nonliteral loading or external shell gate is not claimed covered by import
|
||||
tracing; mandatory package suites and Sage's full script gate remain required.
|
||||
|
||||
First focused whole files: library/query.test.mjs; bus/library-lookups.test.mjs;
|
||||
harness/library-tools.test.mjs. Then the full import-derived/mandatory union
|
||||
below, each file whole (duplicates need not repeat without a change). This
|
||||
is a proposed run list for Sage's CLEAR, not authorization to run now.
|
||||
M1-M9 retain Dewey's agreed specification above; no mutant executed.
|
||||
|
||||
Whole-file union:
|
||||
|
||||
```text
|
||||
packages/bus/tests/access-revoke.test.mjs
|
||||
packages/bus/tests/account-migration.test.mjs
|
||||
packages/bus/tests/address.test.mjs
|
||||
packages/bus/tests/agents.test.mjs
|
||||
packages/bus/tests/approval-binding.test.mjs
|
||||
packages/bus/tests/broker.test.mjs
|
||||
packages/bus/tests/business.test.mjs
|
||||
packages/bus/tests/channel-proof.test.mjs
|
||||
packages/bus/tests/chief-of-staff.test.mjs
|
||||
packages/bus/tests/citation-availability.test.mjs
|
||||
packages/bus/tests/citation-guard.test.mjs
|
||||
packages/bus/tests/citation-runtime.test.mjs
|
||||
packages/bus/tests/comment-cursors.test.mjs
|
||||
packages/bus/tests/comment-migration.test.mjs
|
||||
packages/bus/tests/console-decide.test.mjs
|
||||
packages/bus/tests/creative.test.mjs
|
||||
packages/bus/tests/credential-claim.test.mjs
|
||||
packages/bus/tests/credential-status.test.mjs
|
||||
packages/bus/tests/credential-store.test.mjs
|
||||
packages/bus/tests/credential-verbs.test.mjs
|
||||
packages/bus/tests/credentials.test.mjs
|
||||
packages/bus/tests/csuite-messaging.test.mjs
|
||||
packages/bus/tests/daily-quota-runtime.test.mjs
|
||||
packages/bus/tests/decision-close.test.mjs
|
||||
packages/bus/tests/decision-route.test.mjs
|
||||
packages/bus/tests/decisions.test.mjs
|
||||
packages/bus/tests/end-launch.test.mjs
|
||||
packages/bus/tests/fire-revokes-grants.test.mjs
|
||||
packages/bus/tests/gitea-grant-docker.test.mjs
|
||||
packages/bus/tests/gitea-grant.test.mjs
|
||||
packages/bus/tests/gitea-migration.test.mjs
|
||||
packages/bus/tests/grant-enroll.test.mjs
|
||||
packages/bus/tests/grant-executor.test.mjs
|
||||
packages/bus/tests/grant-host.test.mjs
|
||||
packages/bus/tests/grant-migration.test.mjs
|
||||
packages/bus/tests/grant-scope-runtime.test.mjs
|
||||
packages/bus/tests/grant-scope.test.mjs
|
||||
packages/bus/tests/hard-waits.test.mjs
|
||||
packages/bus/tests/host-run.test.mjs
|
||||
packages/bus/tests/host-tools.test.mjs
|
||||
packages/bus/tests/human.test.mjs
|
||||
packages/bus/tests/instances-verb.test.mjs
|
||||
packages/bus/tests/interpreter.test.mjs
|
||||
packages/bus/tests/land.test.mjs
|
||||
packages/bus/tests/launch-notice.test.mjs
|
||||
packages/bus/tests/launcher-released.test.mjs
|
||||
packages/bus/tests/library-credential.test.mjs
|
||||
packages/bus/tests/library-lookups.test.mjs
|
||||
packages/bus/tests/library-sync.test.mjs
|
||||
packages/bus/tests/library.test.mjs
|
||||
packages/bus/tests/lifecycle-revoke.test.mjs
|
||||
packages/bus/tests/lifecycle-verbs.test.mjs
|
||||
packages/bus/tests/limit-checks.test.mjs
|
||||
packages/bus/tests/limit-stop.test.mjs
|
||||
packages/bus/tests/limits-runtime.test.mjs
|
||||
packages/bus/tests/limits.test.mjs
|
||||
packages/bus/tests/message-citation.test.mjs
|
||||
packages/bus/tests/message-filter.test.mjs
|
||||
packages/bus/tests/messages.test.mjs
|
||||
packages/bus/tests/messaging.test.mjs
|
||||
packages/bus/tests/name-migration.test.mjs
|
||||
packages/bus/tests/names.test.mjs
|
||||
packages/bus/tests/peer-transport.test.mjs
|
||||
packages/bus/tests/peercred.test.mjs
|
||||
packages/bus/tests/plugins-verb.test.mjs
|
||||
packages/bus/tests/principal.test.mjs
|
||||
packages/bus/tests/process.test.mjs
|
||||
packages/bus/tests/processing-runtime.test.mjs
|
||||
packages/bus/tests/processing-timeout.test.mjs
|
||||
packages/bus/tests/processing-web.test.mjs
|
||||
packages/bus/tests/processing.test.mjs
|
||||
packages/bus/tests/project-await.test.mjs
|
||||
packages/bus/tests/project-files.test.mjs
|
||||
packages/bus/tests/project-runtime.test.mjs
|
||||
packages/bus/tests/publish.test.mjs
|
||||
packages/bus/tests/reload.test.mjs
|
||||
packages/bus/tests/replies.test.mjs
|
||||
packages/bus/tests/requirements.test.mjs
|
||||
packages/bus/tests/review.test.mjs
|
||||
packages/bus/tests/revoke-window.test.mjs
|
||||
packages/bus/tests/room-check.test.mjs
|
||||
packages/bus/tests/rooms.test.mjs
|
||||
packages/bus/tests/routes-verb.test.mjs
|
||||
packages/bus/tests/schema.test.mjs
|
||||
packages/bus/tests/session-clear.test.mjs
|
||||
packages/bus/tests/session-compactions.test.mjs
|
||||
packages/bus/tests/sessions.test.mjs
|
||||
packages/bus/tests/single-use.test.mjs
|
||||
packages/bus/tests/spec-migration.test.mjs
|
||||
packages/bus/tests/spec-review.test.mjs
|
||||
packages/bus/tests/spec-socket.test.mjs
|
||||
packages/bus/tests/spec.test.mjs
|
||||
packages/bus/tests/staff-request.test.mjs
|
||||
packages/bus/tests/state.test.mjs
|
||||
packages/bus/tests/store.test.mjs
|
||||
packages/bus/tests/task-page.test.mjs
|
||||
packages/bus/tests/task-read.test.mjs
|
||||
packages/bus/tests/tasks.test.mjs
|
||||
packages/bus/tests/tester-threads.test.mjs
|
||||
packages/bus/tests/tester-writes.test.mjs
|
||||
packages/bus/tests/tester.test.mjs
|
||||
packages/bus/tests/thread-claims.test.mjs
|
||||
packages/bus/tests/thread-migration.test.mjs
|
||||
packages/bus/tests/thread-shares.test.mjs
|
||||
packages/bus/tests/threads.test.mjs
|
||||
packages/bus/tests/transport.test.mjs
|
||||
packages/bus/tests/usage-broker.test.mjs
|
||||
packages/bus/tests/usage-migration.test.mjs
|
||||
packages/bus/tests/usage-runtime.test.mjs
|
||||
packages/bus/tests/usage.test.mjs
|
||||
packages/bus/tests/user-principal.test.mjs
|
||||
packages/bus/tests/viewer.test.mjs
|
||||
packages/bus/tests/web.test.mjs
|
||||
packages/bus/tests/workflow-roles.test.mjs
|
||||
packages/business/tests/case1-business-file.test.mjs
|
||||
packages/business/tests/launch-settings.test.mjs
|
||||
packages/cli/tests/access-boot.test.mjs
|
||||
packages/cli/tests/access.test.mjs
|
||||
packages/cli/tests/app.test.mjs
|
||||
packages/cli/tests/boot-status.test.mjs
|
||||
packages/cli/tests/case1-hostcheck.test.mjs
|
||||
packages/cli/tests/case2-install.test.mjs
|
||||
packages/cli/tests/case2-persona-import.test.mjs
|
||||
packages/cli/tests/case2-unit.test.mjs
|
||||
packages/cli/tests/channel-cli.test.mjs
|
||||
packages/cli/tests/channel-recovery.test.mjs
|
||||
packages/cli/tests/cli.test.mjs
|
||||
packages/cli/tests/compactions-launch.test.mjs
|
||||
packages/cli/tests/config.test.mjs
|
||||
packages/cli/tests/credentials.test.mjs
|
||||
packages/cli/tests/federation-acceptance.test.mjs
|
||||
packages/cli/tests/follow.test.mjs
|
||||
packages/cli/tests/git-socket.test.mjs
|
||||
packages/cli/tests/grants-scope.test.mjs
|
||||
packages/cli/tests/grants.test.mjs
|
||||
packages/cli/tests/host.test.mjs
|
||||
packages/cli/tests/hosts.test.mjs
|
||||
packages/cli/tests/idle-stop-race.test.mjs
|
||||
packages/cli/tests/instances.test.mjs
|
||||
packages/cli/tests/launch-account.test.mjs
|
||||
packages/cli/tests/launch-limit-checks.test.mjs
|
||||
packages/cli/tests/launch-limits.test.mjs
|
||||
packages/cli/tests/launch-routes.test.mjs
|
||||
packages/cli/tests/launcher.test.mjs
|
||||
packages/cli/tests/lifecycle.test.mjs
|
||||
packages/cli/tests/limits-tokens.test.mjs
|
||||
packages/cli/tests/limits.test.mjs
|
||||
packages/cli/tests/mark-read.test.mjs
|
||||
packages/cli/tests/names.test.mjs
|
||||
packages/cli/tests/notifier.test.mjs
|
||||
packages/cli/tests/optional-tracker.test.mjs
|
||||
packages/cli/tests/other-tree.test.mjs
|
||||
packages/cli/tests/processing-cli.test.mjs
|
||||
packages/cli/tests/project-name-repo.test.mjs
|
||||
packages/cli/tests/project.test.mjs
|
||||
packages/cli/tests/publish-socket.test.mjs
|
||||
packages/cli/tests/reply-limits.test.mjs
|
||||
packages/cli/tests/revoke-window.test.mjs
|
||||
packages/cli/tests/session-clear.test.mjs
|
||||
packages/cli/tests/session-cli.test.mjs
|
||||
packages/cli/tests/session-launch.test.mjs
|
||||
packages/cli/tests/stack-copy.test.mjs
|
||||
packages/cli/tests/stack-instances.test.mjs
|
||||
packages/cli/tests/tester-http.test.mjs
|
||||
packages/cli/tests/tester-reads.test.mjs
|
||||
packages/cli/tests/tester-threads.test.mjs
|
||||
packages/cli/tests/tester.test.mjs
|
||||
packages/cli/tests/thread-cli.test.mjs
|
||||
packages/cli/tests/thread-launch.test.mjs
|
||||
packages/cli/tests/thread-revoke.test.mjs
|
||||
packages/cli/tests/trackers-boot.test.mjs
|
||||
packages/cli/tests/transport.test.mjs
|
||||
packages/cli/tests/usage.test.mjs
|
||||
packages/cli/tests/verbs.test.mjs
|
||||
packages/harness/tests/bundle.test.mjs
|
||||
packages/harness/tests/citation-runner.test.mjs
|
||||
packages/harness/tests/claude-gate.test.mjs
|
||||
packages/harness/tests/claude-session.test.mjs
|
||||
packages/harness/tests/creative.test.mjs
|
||||
packages/harness/tests/gate.test.mjs
|
||||
packages/harness/tests/git-tools.test.mjs
|
||||
packages/harness/tests/library-tools.test.mjs
|
||||
packages/harness/tests/mcp-server.test.mjs
|
||||
packages/harness/tests/pi-session.test.mjs
|
||||
packages/harness/tests/raise-domain.test.mjs
|
||||
packages/harness/tests/runner.test.mjs
|
||||
packages/harness/tests/skills.test.mjs
|
||||
packages/harness/tests/spec-tools.test.mjs
|
||||
packages/harness/tests/tools.test.mjs
|
||||
packages/library/tests/guard.test.mjs
|
||||
packages/library/tests/index-build.test.mjs
|
||||
packages/library/tests/links.test.mjs
|
||||
packages/library/tests/mapping.test.mjs
|
||||
packages/library/tests/mirror.test.mjs
|
||||
packages/library/tests/query.test.mjs
|
||||
packages/library/tests/reader.test.mjs
|
||||
packages/library/tests/render.test.mjs
|
||||
packages/library/tests/scope.test.mjs
|
||||
packages/library/tests/supporting.test.mjs
|
||||
packages/library/tests/survey.test.mjs
|
||||
packages/library/tests/sync.test.mjs
|
||||
packages/processing-control/tests/task.test.mjs
|
||||
packages/seat/tests/sandbox.test.mjs
|
||||
packages/seat/tests/session.test.mjs
|
||||
packages/tasks/tests/adapter.test.mjs
|
||||
packages/tasks/tests/comment-restart.test.mjs
|
||||
packages/tasks/tests/fields.test.mjs
|
||||
packages/tasks/tests/processing.test.mjs
|
||||
packages/tasks/tests/shapes.test.mjs
|
||||
packages/tasks/tests/startup.test.mjs
|
||||
packages/tasks/tests/sync.test.mjs
|
||||
packages/tasks/tests/tree.test.mjs
|
||||
packages/tasks/tests/verbs.test.mjs
|
||||
packages/webui/tests/agents-browser.test.mjs
|
||||
packages/webui/tests/agents.test.mjs
|
||||
packages/webui/tests/assistant.test.mjs
|
||||
packages/webui/tests/board-held.test.mjs
|
||||
packages/webui/tests/browser-edge.test.mjs
|
||||
packages/webui/tests/browser.test.mjs
|
||||
packages/webui/tests/bus-browser.test.mjs
|
||||
packages/webui/tests/bus.test.mjs
|
||||
packages/webui/tests/channel-chat.test.mjs
|
||||
packages/webui/tests/chat-browser.test.mjs
|
||||
packages/webui/tests/chat-instances.test.mjs
|
||||
packages/webui/tests/chat-replies-browser.test.mjs
|
||||
packages/webui/tests/chat-sessions-access.test.mjs
|
||||
packages/webui/tests/chat.test.mjs
|
||||
packages/webui/tests/conversation.test.mjs
|
||||
packages/webui/tests/decide-browser.test.mjs
|
||||
packages/webui/tests/decide-sso.test.mjs
|
||||
packages/webui/tests/decision-http.test.mjs
|
||||
packages/webui/tests/discord.test.mjs
|
||||
packages/webui/tests/docs-browser.test.mjs
|
||||
packages/webui/tests/editor-browser.test.mjs
|
||||
packages/webui/tests/editor.test.mjs
|
||||
packages/webui/tests/granted-user.test.mjs
|
||||
packages/webui/tests/history-return-flow.test.mjs
|
||||
packages/webui/tests/instances-browser.test.mjs
|
||||
packages/webui/tests/instances.test.mjs
|
||||
packages/webui/tests/library-cli.test.mjs
|
||||
packages/webui/tests/library-routes.test.mjs
|
||||
packages/webui/tests/library-status.test.mjs
|
||||
packages/webui/tests/limits-browser.test.mjs
|
||||
packages/webui/tests/limits-route.test.mjs
|
||||
packages/webui/tests/messages.test.mjs
|
||||
packages/webui/tests/pinned-copy.test.mjs
|
||||
packages/webui/tests/preview.test.mjs
|
||||
packages/webui/tests/processing-browser.test.mjs
|
||||
packages/webui/tests/processing.test.mjs
|
||||
packages/webui/tests/project-file.test.mjs
|
||||
packages/webui/tests/project-name-repo.test.mjs
|
||||
packages/webui/tests/proof-cli.test.mjs
|
||||
packages/webui/tests/proof.test.mjs
|
||||
packages/webui/tests/reads.test.mjs
|
||||
packages/webui/tests/relaunch.test.mjs
|
||||
packages/webui/tests/return-flow.test.mjs
|
||||
packages/webui/tests/room-browser.test.mjs
|
||||
packages/webui/tests/room-http.test.mjs
|
||||
packages/webui/tests/routes-browser.test.mjs
|
||||
packages/webui/tests/routes.test.mjs
|
||||
packages/webui/tests/send-browser.test.mjs
|
||||
packages/webui/tests/send.test.mjs
|
||||
packages/webui/tests/serve.test.mjs
|
||||
packages/webui/tests/settings-form.test.mjs
|
||||
packages/webui/tests/settings-write.test.mjs
|
||||
packages/webui/tests/shell.test.mjs
|
||||
packages/webui/tests/sso-browser.test.mjs
|
||||
packages/webui/tests/sso.test.mjs
|
||||
packages/webui/tests/tester-browser.test.mjs
|
||||
packages/webui/tests/tester-cli.test.mjs
|
||||
packages/webui/tests/tester-listener.test.mjs
|
||||
packages/webui/tests/tester-socket.test.mjs
|
||||
packages/webui/tests/thread-claims-browser.test.mjs
|
||||
packages/webui/tests/thread-claims.test.mjs
|
||||
packages/webui/tests/thread-sharing.test.mjs
|
||||
packages/webui/tests/usage-browser.test.mjs
|
||||
packages/webui/tests/usage.test.mjs
|
||||
packages/webui/tests/user-option-swap.test.mjs
|
||||
packages/webui/tests/views.test.mjs
|
||||
```
|
||||
|
||||
Queue next on 2026-10-24 names row284 but reports the whole brief differs
|
||||
from its queue pin and asks the lead to re-pin. Sage-4281 names 5e8a720b and
|
||||
says the section is unchanged; awaiting lead resolution of the queue pin
|
||||
before the start move. No queue policy refusal is bypassed.
|
||||
|
||||
## sage-4289 lane B — completed verification, 2026-10-24
|
||||
|
||||
Current reviewer is Webby under mos-1614 / sage-4284, replacing Dewey.
|
||||
Dewey's earlier static readings and agreed M1-M9 remain historical evidence.
|
||||
Row284 claimed in-progress rev4038, op della-284-start-1 after lead re-pin.
|
||||
Candidate tested: 2f1a6bd93e5fc602c46ee5f770d19adecde5fc11. No code,
|
||||
fixture or assertion change during verification. Local npm ci used the exact
|
||||
lockfile, ignore-scripts/no-audit/no-fund, rc0,128 packages; no canonical
|
||||
node_modules link. Installer receipt and log are in the evidence directory.
|
||||
|
||||
Run list sent in full to Sage before starting (della-0058); source is the
|
||||
import walk documented above, union mandatory package suites,279 whole files.
|
||||
Three targeted files first, then M1-M9 with byte-exact restore after each,
|
||||
then remaining276 whole files. Already-passed unchanged targeted files are
|
||||
not repeated. Every Node invocation used fresh shell gXXX TMPDIR,29 bytes,
|
||||
absolute nice19 set in the child before exec, timeout -k10 (900-second cap,
|
||||
shortened automatically near23:45Z hardstop). All timeout nice observations
|
||||
were19. Normal run total1067.962s; mutant total19.485s. Completed22:34Z.
|
||||
|
||||
| Package | Whole files | Pass | Fail | Skip |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| bus | 114 | 1012 | 0 | 0 |
|
||||
| business | 2 | 25 | 0 | 0 |
|
||||
| cli | 58 | 610 | 0 | 1 |
|
||||
| harness | 15 | 177 | 0 | 0 |
|
||||
| library | 12 | 156 | 0 | 0 |
|
||||
| processing-control | 1 | 7 | 0 | 0 |
|
||||
| seat | 2 | 23 | 0 | 0 |
|
||||
| tasks | 9 | 95 | 0 | 0 |
|
||||
| webui | 66 | 396 | 0 | 0 |
|
||||
|
||||
Totals:279 whole files,2501 passed,0 failed,1 skipped,0 cancelled.
|
||||
Sole skip: packages/cli/tests/federation-acceptance.test.mjs,
|
||||
FED R7 scratch two-instance acceptance, directs running
|
||||
scripts/test-federation-acceptance.sh. That acceptance was not exercised;
|
||||
Sage's full shell landing gate is separate. case1-accept.test.mjs excluded
|
||||
as ordered. No unlisted run or extra mutant. Targeted counts: query34/34,
|
||||
broker12/12,typed tools5/5,zero skips.
|
||||
|
||||
M1-M9 all killed with exit1, no syntax/setup failures. M1 and M8 are expected
|
||||
behavior errors when a required capability is removed; the others fail
|
||||
assertions/refusal checks. Killing named tests are recorded below. Each
|
||||
restored original bytes in finally; git diff --stat and status --porcelain
|
||||
empty after every mutant and again after all normal runs, before receipts.
|
||||
No extra post-restore test rerun.
|
||||
|
||||
| Mutant | Killing behavior oracle | Restored |
|
||||
| --- | --- | --- |
|
||||
| M1 | supporting find/search/get | byte-exact, clean |
|
||||
| M2 | get without scope returns null | byte-exact, clean |
|
||||
| M3 | each get blob equals its served tree object | byte-exact, clean |
|
||||
| M4 | full outgoing and incoming links stay in scope order | byte-exact, clean |
|
||||
| M5 | full outgoing and incoming links stay in scope order | byte-exact, clean |
|
||||
| M6 | full outgoing and incoming links stay in scope order | byte-exact, clean |
|
||||
| M7 | manuscript still refuses supporting | byte-exact, clean |
|
||||
| M8 | broker forwards supporting scope | byte-exact, clean |
|
||||
| M9 | scope schemas expand only find/search/get | byte-exact, clean |
|
||||
|
||||
Post-run sweep: current OS user's executable names node/nodejs/chromium/
|
||||
chrome, matching cwd or argv paths under this worktree or any run TMPDIR.
|
||||
Checked289 unique roots;0 matching live processes. No signals sent, no
|
||||
other seat's process inspected beyond process identity/path matching.
|
||||
No full argv, environment or credential values printed. Slot returned to
|
||||
Sage in della-0059 at22:34Z. No further tests.
|
||||
|
||||
Evidence: /mnt/storage/scratch/della-row284-r2-evidence/run-4289 contains
|
||||
install receipt, exact commands, stdout/stderr per whole-file run, mutant
|
||||
edits/restores, summaries and hashed logs. The following plain-text ledger
|
||||
freezes commands and results; raw logs remain available for reviewer checks.
|
||||
Each normal test path below was run whole; hashes are SHA256 of raw logs.
|
||||
|
||||
```text
|
||||
{"label":"target-query","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gUgw","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.889,"mutant":false,"tests":34,"pass":34,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"dc2575e2c597b099c94b7cf254b0cb4d009a0ddb14c0cbd572f93e5ad15f8f72"}
|
||||
{"label":"target-broker","command":["timeout","-k","10","900","node","--test","packages/bus/tests/library-lookups.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDd2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.431,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6729e92f2241a2c2d0b725cb9c6ed4fe4d8b50352fd179cf30f905182eac9160"}
|
||||
{"label":"target-tools","command":["timeout","-k","10","900","node","--test","packages/harness/tests/library-tools.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gmPG","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.169,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"532de351eef39478011d7f84b3b506711c3bafc71676716f623fa29573fc4dec"}
|
||||
{"label":"M1","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gIGt","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.683,"mutant":true,"tests":34,"pass":30,"fail":4,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ec36434e918ee16c7639ec0ec4259be66e0381e2684bac7c6ee5755756bded78"}
|
||||
{"label":"M2","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/geid","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.587,"mutant":true,"tests":34,"pass":31,"fail":3,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e08eadfd606fc685faed21edb06b1d7d00fd11e7942051fddc0b48b83c6bef90"}
|
||||
{"label":"M3","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gs4j","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.538,"mutant":true,"tests":34,"pass":33,"fail":1,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"8abb79847deb7a05940c7b48d5407dde6baaf394fef31334f6ac266d65b3c02f"}
|
||||
{"label":"M4","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gvmZ","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.674,"mutant":true,"tests":34,"pass":33,"fail":1,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"8b8386b876fb85e12335be27216dc5c6a8d5c311c03db0bf49ffbe0639a8693c"}
|
||||
{"label":"M5","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gG6o","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.633,"mutant":true,"tests":34,"pass":32,"fail":2,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"08ff976a29e42f593dce3262e596e75c43da2ae2e3dfe48945989fe98c3061cf"}
|
||||
{"label":"M6","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gWpu","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.347,"mutant":true,"tests":34,"pass":23,"fail":11,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4cd149d0f74e402d2294c2d9ef05aac440ec4c3dc379acad7b9624b7402528bb"}
|
||||
{"label":"M7","command":["timeout","-k","10","900","node","--test","packages/library/tests/query.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g65B","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":2.403,"mutant":true,"tests":34,"pass":33,"fail":1,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"541100f0937dd2e592ed9bee7c48f27ae4e13634d3c0dcf53fc3e8b2f632a000"}
|
||||
{"label":"M8","command":["timeout","-k","10","900","node","--test","packages/bus/tests/library-lookups.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gtX5","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":1.421,"mutant":true,"tests":12,"pass":11,"fail":1,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2adc93e5aaa663621caa9fd762faded3a975f62de918d7c46a1b5b0469001955"}
|
||||
{"label":"M9","command":["timeout","-k","10","900","node","--test","packages/harness/tests/library-tools.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gMtM","start_nice":19,"observed_timeout_nice":19,"exit":1,"seconds":0.199,"mutant":true,"tests":5,"pass":4,"fail":1,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"da8c8c2094732b36526dc803e567d4bce18ff964bf70ff406e1981bb7dd313de"}
|
||||
{"label":"import-001","command":["timeout","-k","10","900","node","--test","packages/bus/tests/access-revoke.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gGcs","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.728,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ee78177b9385025df0b0ce154e9f92f3d84a01920da94eb2b95df7aee533721a"}
|
||||
{"label":"import-002","command":["timeout","-k","10","900","node","--test","packages/bus/tests/account-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/giJX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.456,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"04456ac702b53d59e127eb7ae2d9828b79acbbcc99c8845cc226e88e4569ffb2"}
|
||||
{"label":"import-003","command":["timeout","-k","10","900","node","--test","packages/bus/tests/address.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gLUL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.102,"mutant":false,"tests":15,"pass":15,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c4ff31db588ac88b6492416d3850b123492e29dbd7c6b2aa283dd736e6caa8cc"}
|
||||
{"label":"import-004","command":["timeout","-k","10","900","node","--test","packages/bus/tests/agents.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDuh","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.493,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"424001894b865c63405976f7facc9a9c93c2ac018b6c97f17ebf87001d071d52"}
|
||||
{"label":"import-005","command":["timeout","-k","10","900","node","--test","packages/bus/tests/approval-binding.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gsfL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.305,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f8029477d12d0acbd774ec6fa82d9af47ae93c575e0ada16c31130248280431d"}
|
||||
{"label":"import-006","command":["timeout","-k","10","900","node","--test","packages/bus/tests/broker.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gwdO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.532,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b02528f749bc8367149451f2118cf612f7d46ee6269ad05d9a894d6ef893b727"}
|
||||
{"label":"import-007","command":["timeout","-k","10","900","node","--test","packages/bus/tests/business.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gX5H","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.102,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"3246c77a5431fda5ee0ef798b9850ee8bf05297c7afa272fc88d148582dc14d8"}
|
||||
{"label":"import-008","command":["timeout","-k","10","900","node","--test","packages/bus/tests/channel-proof.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g0t2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.644,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d3dc7af8bd80c5cd391a42468cec38fa2cdf4d7bb8d758e84094a255cf2e8626"}
|
||||
{"label":"import-009","command":["timeout","-k","10","900","node","--test","packages/bus/tests/chief-of-staff.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gAXk","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.744,"mutant":false,"tests":42,"pass":42,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6e355388daee3b3cfe0251abd3f1f1fc56744e26354b8489d8113e4cb4033847"}
|
||||
{"label":"import-010","command":["timeout","-k","10","900","node","--test","packages/bus/tests/citation-availability.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gNQV","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.391,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"fe32f9423c7317c53d0946d6422fb4ea58bcbb0795730b80cba7b5daae47b8ef"}
|
||||
{"label":"import-011","command":["timeout","-k","10","900","node","--test","packages/bus/tests/citation-guard.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gilj","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.258,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1f6c35c535a407a121ec4ee9bb4e9acdc12b5e16e3f8ccf75bcb94a66511b820"}
|
||||
{"label":"import-012","command":["timeout","-k","10","900","node","--test","packages/bus/tests/citation-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gjTW","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.331,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9cb0ab5a64b667f563c827239c242ee77475acb8744e515d8472d839d7190e2e"}
|
||||
{"label":"import-013","command":["timeout","-k","10","900","node","--test","packages/bus/tests/comment-cursors.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQvO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.162,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"898a7bcc0f68f8a54748a9da6c75b702738b07230a48ff4f939206b1c2dccfdf"}
|
||||
{"label":"import-014","command":["timeout","-k","10","900","node","--test","packages/bus/tests/comment-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g4nL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.609,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ade667dca625e36750417b46f3156be89ec5d40fff78511722c3fa671f3bcd25"}
|
||||
{"label":"import-015","command":["timeout","-k","10","900","node","--test","packages/bus/tests/console-decide.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/girM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.893,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"11a0ac7147f0164542442a9fc6e9224e5d36305cd3e999fc03733e289dfc8f4e"}
|
||||
{"label":"import-016","command":["timeout","-k","10","900","node","--test","packages/bus/tests/creative.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gXAL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.447,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"13b107465c7ddecb4fba573b58d4ff42f8807221d1956325b2a0e54eec4581d2"}
|
||||
{"label":"import-017","command":["timeout","-k","10","900","node","--test","packages/bus/tests/credential-claim.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g8Cy","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.157,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a66956b56367cb240de84571fc79e4297bf4bf49c06ce92ac3dd9c5d155b26a5"}
|
||||
{"label":"import-018","command":["timeout","-k","10","900","node","--test","packages/bus/tests/credential-status.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gEDT","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.401,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"8812b6b0962c7b15e437a9c9b5741d502ab81001d9e17c9b5b14729db8f6033d"}
|
||||
{"label":"import-019","command":["timeout","-k","10","900","node","--test","packages/bus/tests/credential-store.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPQx","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.158,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2ead3ac9725024e493121f7aaa8e7cf71a35616ffd57c901d3f425ac50b4a8b7"}
|
||||
{"label":"import-020","command":["timeout","-k","10","900","node","--test","packages/bus/tests/credential-verbs.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g1Wz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.328,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"bf447a8e6566816992f48a92e6821441ec1acd86960251aeee4b03b6e4b6dee3"}
|
||||
{"label":"import-021","command":["timeout","-k","10","900","node","--test","packages/bus/tests/credentials.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/grcF","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.105,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f3f807e470c8ec40f6c1aea188308a1ca183c8f0e98a0eaea996c96577ab471a"}
|
||||
{"label":"import-022","command":["timeout","-k","10","900","node","--test","packages/bus/tests/csuite-messaging.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gFHk","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.779,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1034823ca51c040db2f0bb18416c67761580ed55df0e60636883bc217878afd5"}
|
||||
{"label":"import-023","command":["timeout","-k","10","900","node","--test","packages/bus/tests/daily-quota-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gl0H","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.401,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"95472c3d6b012d38f3db4c790fdd48b710535beee03274c0ea44a2cbd97ed095"}
|
||||
{"label":"import-024","command":["timeout","-k","10","900","node","--test","packages/bus/tests/decision-close.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g2Iz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.34,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"5c855717b19f203797db5b4f2789dd8f8df7d49cbdfee62ae0ffa220e4ed1a4a"}
|
||||
{"label":"import-025","command":["timeout","-k","10","900","node","--test","packages/bus/tests/decision-route.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDI5","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.286,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"485577231e6e797c256db8a832d98aca4bf856e47459e0c30a1940598c60b8aa"}
|
||||
{"label":"import-026","command":["timeout","-k","10","900","node","--test","packages/bus/tests/decisions.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/geqP","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.474,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"aa887a5b2b30390fab4906cbd2f6ffa4ec37cc9e794478b18e7cd47eb354063b"}
|
||||
{"label":"import-027","command":["timeout","-k","10","900","node","--test","packages/bus/tests/end-launch.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g5u4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.414,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e3aced744c09beda07405cba691c54a8c8dca973b7718285f67612cbfdad4ac1"}
|
||||
{"label":"import-028","command":["timeout","-k","10","900","node","--test","packages/bus/tests/fire-revokes-grants.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gD7b","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.034,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0e79b5a7c00f0701c84b76f3e9fd73f1bac2c9343594039df1e8a667e42d6ea4"}
|
||||
{"label":"import-029","command":["timeout","-k","10","900","node","--test","packages/bus/tests/gitea-grant-docker.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gcCX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":25.371,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"5720e4fc3d58350dc4192c7ceb5dff76aba9bf1aff1ac917e016e7295380b325"}
|
||||
{"label":"import-030","command":["timeout","-k","10","900","node","--test","packages/bus/tests/gitea-grant.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gIPd","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":12.386,"mutant":false,"tests":66,"pass":66,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"142962fbea6f3602701e4bcc5e0248cb06b98a2fad8bfafb92143a593e535ee6"}
|
||||
{"label":"import-031","command":["timeout","-k","10","900","node","--test","packages/bus/tests/gitea-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPFr","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.443,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2a3d38fa696805a193460d33bfec2461778463b7087e368b738700c583ad23c3"}
|
||||
{"label":"import-032","command":["timeout","-k","10","900","node","--test","packages/bus/tests/grant-enroll.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gAeL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.838,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"fc5d1d86af5c0689eb5a92d94ee1dc7a0143299523fc010d4a119c34b30ff768"}
|
||||
{"label":"import-033","command":["timeout","-k","10","900","node","--test","packages/bus/tests/grant-executor.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gEJ1","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":22.682,"mutant":false,"tests":42,"pass":42,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"97a66205b109952a7704ff9d5d63efbb0277922a0bf1510a677284f9ccbc9831"}
|
||||
{"label":"import-034","command":["timeout","-k","10","900","node","--test","packages/bus/tests/grant-host.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gYeV","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.433,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0ab7ed5613f9c353c5e6f786ceffc6112c4f851debb799cf60ff73ae2f2bf65d"}
|
||||
{"label":"import-035","command":["timeout","-k","10","900","node","--test","packages/bus/tests/grant-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gcHz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.495,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"690a9c67e3426b8f6dc94c34f1b34ae5e502c0f8fb9ce67c5beab7cf5122557e"}
|
||||
{"label":"import-036","command":["timeout","-k","10","900","node","--test","packages/bus/tests/grant-scope-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gVjH","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.228,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c013731042ef37735fe6648ec299831b127d3ddb351c613d95efd4bc610d6ff2"}
|
||||
{"label":"import-037","command":["timeout","-k","10","900","node","--test","packages/bus/tests/grant-scope.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gwvw","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.35,"mutant":false,"tests":14,"pass":14,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1b48fba6570d0dcef4666725bde8b8c8f95e75eb8abb517d15b226a6ae5b0ea4"}
|
||||
{"label":"import-038","command":["timeout","-k","10","900","node","--test","packages/bus/tests/hard-waits.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gM7r","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.464,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"41d7bdf1b5276a7205b6b3e61ff4ac2f7982d60f5f190c3475e75c1ff4cebefd"}
|
||||
{"label":"import-039","command":["timeout","-k","10","900","node","--test","packages/bus/tests/host-run.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gfCB","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":15.268,"mutant":false,"tests":26,"pass":26,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7f5c5ab5e5f654c1826e84369739616606de89763879d96ffaa9cc196db05d72"}
|
||||
{"label":"import-040","command":["timeout","-k","10","900","node","--test","packages/bus/tests/host-tools.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbtM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.726,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"dfd0a799f300925eb291c8047adc0f9359bf05927f0b1c129c6b420fcf0bfcc0"}
|
||||
{"label":"import-041","command":["timeout","-k","10","900","node","--test","packages/bus/tests/human.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gnLK","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.102,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"09791081c562afc5c9fa6da5b6df4e37935797147fc6d4c3f4cd8be2dd3d56ae"}
|
||||
{"label":"import-042","command":["timeout","-k","10","900","node","--test","packages/bus/tests/instances-verb.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g46h","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.305,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d41d14dd33492e34f21452550fed8d0ca0d3b1847cca0fb86c53ad13f8cb4fb7"}
|
||||
{"label":"import-043","command":["timeout","-k","10","900","node","--test","packages/bus/tests/interpreter.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gTjS","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.178,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c3be5eace9c994767d451fcd0d99ddfff2a647a8f7e74f21306c95ac503c229c"}
|
||||
{"label":"import-044","command":["timeout","-k","10","900","node","--test","packages/bus/tests/land.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gOEa","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.156,"mutant":false,"tests":18,"pass":18,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b59e6b60f597b4b1d02d4ced63344dbcb820946148a11a4ddd8e29ac422ef0f8"}
|
||||
{"label":"import-045","command":["timeout","-k","10","900","node","--test","packages/bus/tests/launch-notice.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gv3V","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.543,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4979af0184d2b8fe0cfc7048da37828e2f58a8bb433b589399be4d8a7798371d"}
|
||||
{"label":"import-046","command":["timeout","-k","10","900","node","--test","packages/bus/tests/launcher-released.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gEQl","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.365,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9e4890aeaa7e21398fb140c469c606cb8a5cec2b564826232ade66f171fed937"}
|
||||
{"label":"import-047","command":["timeout","-k","10","900","node","--test","packages/bus/tests/library-credential.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gJlk","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.11,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ca7c89d815a3bc281b406089a04dbaf94d04927c0b6b0ae840cae34cd8e858f5"}
|
||||
{"label":"import-049","command":["timeout","-k","10","900","node","--test","packages/bus/tests/library-sync.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/geyX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":7.525,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"835bea6e1730fb67ab7d7ef02701ace657b0b3a839d300713ca16d0a32713828"}
|
||||
{"label":"import-050","command":["timeout","-k","10","900","node","--test","packages/bus/tests/library.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gejb","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.736,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"70633e1f2e5662e511239ada1bb47e1dc197b79e2c9ecf005308b270f713a41c"}
|
||||
{"label":"import-051","command":["timeout","-k","10","900","node","--test","packages/bus/tests/lifecycle-revoke.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gA7g","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.815,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"190fd253cefb4d669245531043a93225d37ae66ba694635a3e8bffd86c9e4264"}
|
||||
{"label":"import-052","command":["timeout","-k","10","900","node","--test","packages/bus/tests/lifecycle-verbs.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gYLp","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.62,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"071bed270aff7f251d954b557e090230ee6e224535938b85b93e8f885d3fb944"}
|
||||
{"label":"import-053","command":["timeout","-k","10","900","node","--test","packages/bus/tests/limit-checks.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gt3p","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.347,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"cb634a9e2819a161ec128a250586e4086591696309f00c9e6a62d7c54c6b9382"}
|
||||
{"label":"import-054","command":["timeout","-k","10","900","node","--test","packages/bus/tests/limit-stop.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g1Vs","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.362,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"93504cdb3c5a9e5295bd72b8224ec69c43bbd344529a97538376c5a09e47328e"}
|
||||
{"label":"import-055","command":["timeout","-k","10","900","node","--test","packages/bus/tests/limits-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/giXl","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.454,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a18ac489d4eaa62e0bdb40444d70722379b0f4dc172f28e71f8600307ce7c3b6"}
|
||||
{"label":"import-056","command":["timeout","-k","10","900","node","--test","packages/bus/tests/limits.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gZog","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.424,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"072dfdd9f45f609d52910d3619d4253bd05b54ec36e1a8599db17e17b472e99a"}
|
||||
{"label":"import-057","command":["timeout","-k","10","900","node","--test","packages/bus/tests/message-citation.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gCEq","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.244,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d53489b437b4adc8ac10bc52d0f1e6557b6fd187786008a6ad13a264207be034"}
|
||||
{"label":"import-058","command":["timeout","-k","10","900","node","--test","packages/bus/tests/message-filter.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gsfe","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.58,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6038a839d1b12b29d8cdbc0ea341ae22197e19444be5743ca327cb188cd1af59"}
|
||||
{"label":"import-059","command":["timeout","-k","10","900","node","--test","packages/bus/tests/messages.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gkz2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.501,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e507905469453069f469a1366bd4cbd446c6b7e2e8ba5234c3d0bee83d1172ba"}
|
||||
{"label":"import-060","command":["timeout","-k","10","900","node","--test","packages/bus/tests/messaging.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPZR","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.502,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"434390031617151891ecb63a788da353c0d43051a4ea60db2e5727b149c9de4e"}
|
||||
{"label":"import-061","command":["timeout","-k","10","900","node","--test","packages/bus/tests/name-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gsLH","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.192,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1979f0b1dd32162d42acace08a4b4306249e1096f5bbadb47038681af6b375d5"}
|
||||
{"label":"import-062","command":["timeout","-k","10","900","node","--test","packages/bus/tests/names.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/grPm","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.313,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1d35ce65a08ec74388a7a65af8bfb7a87d341fab5795360fd553e65aa10d4999"}
|
||||
{"label":"import-063","command":["timeout","-k","10","900","node","--test","packages/bus/tests/peer-transport.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g2ja","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.194,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"579cce4a4581e991e6cad80666cd40bfb10d1346699f1233a44dab5d17ee1583"}
|
||||
{"label":"import-064","command":["timeout","-k","10","900","node","--test","packages/bus/tests/peercred.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g35b","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.499,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c6d9c4a534f8aab5c73e6920346674eb243a19439eaaa09436ff4c2bc9b68fd7"}
|
||||
{"label":"import-065","command":["timeout","-k","10","900","node","--test","packages/bus/tests/plugins-verb.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gGrj","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.331,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4677c69292a7fe1961239e40ecd5848b1e9355e6d9326f1c935dd077605cc36b"}
|
||||
{"label":"import-066","command":["timeout","-k","10","900","node","--test","packages/bus/tests/principal.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g5U9","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.102,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"efac2a82f133ae63ba26543832b6704df573f345c7699b48b928d4329467f03e"}
|
||||
{"label":"import-067","command":["timeout","-k","10","900","node","--test","packages/bus/tests/process.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/go8U","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.448,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e26c9f5eb045dd7f651f453875fc1daac5dafa5f2060a69c3edcf37c79433e24"}
|
||||
{"label":"import-068","command":["timeout","-k","10","900","node","--test","packages/bus/tests/processing-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gmrO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.392,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"624f60b78329dbf8db5b5ce17e9f218148f7e417a46bcace45666b10193ea4c4"}
|
||||
{"label":"import-069","command":["timeout","-k","10","900","node","--test","packages/bus/tests/processing-timeout.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gt14","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.263,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"30a4c425941c5732bb0afa09ff8cf2229f11ec6031c7cd3ae26932cf55dc2552"}
|
||||
{"label":"import-070","command":["timeout","-k","10","900","node","--test","packages/bus/tests/processing-web.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gWj9","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.126,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"dbe9bc036efc99fc38b2dea8824cb7a64fc39e49fcd3a1ef07c60298b0d8e69f"}
|
||||
{"label":"import-071","command":["timeout","-k","10","900","node","--test","packages/bus/tests/processing.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gP4m","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.381,"mutant":false,"tests":14,"pass":14,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d2204029f1dbc5836a759b43d421f2790ca32b97a1cc1fc6cb3f098fa4d7ab5d"}
|
||||
{"label":"import-072","command":["timeout","-k","10","900","node","--test","packages/bus/tests/project-await.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/grrE","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.177,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"55cb0e215ff4f1e3265322c3336b3861d37fc3ba3cac53a31aa3dcc9ee0cab46"}
|
||||
{"label":"import-073","command":["timeout","-k","10","900","node","--test","packages/bus/tests/project-files.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gZZ4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.894,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b3395652052ccd06de8df2a1ce1aaac185dab50e854fb712a33aef8319d17fdc"}
|
||||
{"label":"import-074","command":["timeout","-k","10","900","node","--test","packages/bus/tests/project-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gYyK","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.404,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b139c3c9cea5fc9bd86ac64f33c4af23f568bee13f9271fef40a07d76ed51cf2"}
|
||||
{"label":"import-075","command":["timeout","-k","10","900","node","--test","packages/bus/tests/publish.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gUl2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.831,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0adf717babd8ac6229a115e1c796071fd68c1524f140d4596c9f1ee8c528b69f"}
|
||||
{"label":"import-076","command":["timeout","-k","10","900","node","--test","packages/bus/tests/reload.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gZPr","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.003,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2ce6a9bd3ea3eeccf1689e1b6335b72bba2d440ae27feeec3dc727567fa140ff"}
|
||||
{"label":"import-077","command":["timeout","-k","10","900","node","--test","packages/bus/tests/replies.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbjz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.374,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6db5c1588c83f2df4be469f06857569df7f4cfc47c18b15a8f9e741d4d739c4f"}
|
||||
{"label":"import-078","command":["timeout","-k","10","900","node","--test","packages/bus/tests/requirements.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g1jX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.192,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"32b876110fc8a35246f6ab16343dae9ea6da42ef556a99e1e5356e077474a96f"}
|
||||
{"label":"import-079","command":["timeout","-k","10","900","node","--test","packages/bus/tests/review.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gVJz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.427,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"333b7d84cf72b95ba2cdb1d3824bcb8043623c5f0810c68ed22738210977c297"}
|
||||
{"label":"import-080","command":["timeout","-k","10","900","node","--test","packages/bus/tests/revoke-window.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gRZs","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.305,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7e759b3330537c0c566b55b2c405adc6f5f48c3b229c4450d938aaec3280fc09"}
|
||||
{"label":"import-081","command":["timeout","-k","10","900","node","--test","packages/bus/tests/room-check.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gzW9","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.225,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"300f1ea96ccfa38c9405ef163e531cfeec3c7eb8f46986f077669ca95b8f72f3"}
|
||||
{"label":"import-082","command":["timeout","-k","10","900","node","--test","packages/bus/tests/rooms.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gEx0","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.033,"mutant":false,"tests":18,"pass":18,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"fbcac36a4495da1def5dd650d8aea5758bf25d3e7473148a2b8961bb97b57239"}
|
||||
{"label":"import-083","command":["timeout","-k","10","900","node","--test","packages/bus/tests/routes-verb.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g0LO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.292,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4fef276b76a79518faa03b81d119962ab8880af3010fa33ead93f393dca89f02"}
|
||||
{"label":"import-084","command":["timeout","-k","10","900","node","--test","packages/bus/tests/schema.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gZFQ","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.169,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e71271414564fa851dbf25a155b44dcf481770c7f3d4dafe5ce44a09c49526b1"}
|
||||
{"label":"import-085","command":["timeout","-k","10","900","node","--test","packages/bus/tests/session-clear.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gfrg","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.773,"mutant":false,"tests":19,"pass":19,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c8097880d5e9a396fb907bc1f35f342821971b545fea7f1e4ddcb2b9f2fb322a"}
|
||||
{"label":"import-086","command":["timeout","-k","10","900","node","--test","packages/bus/tests/session-compactions.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gfRL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.179,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0a2f0ef4d7f6588e2aaeb5a5d8e200650ae573c4468640130140eb83430261b3"}
|
||||
{"label":"import-087","command":["timeout","-k","10","900","node","--test","packages/bus/tests/sessions.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gaNb","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.654,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"11f26dc0d082b49ce7839d22aca75067dcc2f978d6c319da3d2800adcc0a9ffc"}
|
||||
{"label":"import-088","command":["timeout","-k","10","900","node","--test","packages/bus/tests/single-use.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gSe3","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.416,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7a0e2e7a4a6ab79b85462ad1a39955c5402dab8a9236534c857cef88d08ecc87"}
|
||||
{"label":"import-089","command":["timeout","-k","10","900","node","--test","packages/bus/tests/spec-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPmw","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.215,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b9831356a0d65a037796a66764dcf7dcc267d8512e70087b364cbe390a8c85dc"}
|
||||
{"label":"import-090","command":["timeout","-k","10","900","node","--test","packages/bus/tests/spec-review.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gqKv","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.034,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"8a70a550b2b056c35f2e85bdd4a155bf7d02332d89596aeed40e16bec765221c"}
|
||||
{"label":"import-091","command":["timeout","-k","10","900","node","--test","packages/bus/tests/spec-socket.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gY6G","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.787,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d58670526310fa5106ab9b3d724d817910283b76cfa8bd79278b0428e42b73c3"}
|
||||
{"label":"import-092","command":["timeout","-k","10","900","node","--test","packages/bus/tests/spec.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gnTM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.538,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d04ac45ad25906322cbbd50b703c316c4eeefad1d40f901eeaf2c34224fc5958"}
|
||||
{"label":"import-093","command":["timeout","-k","10","900","node","--test","packages/bus/tests/staff-request.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g07B","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.544,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e0e379d2e0871ecbbae9a5e92ee7217342dc31cf69c80373aeef72f78ea27fd9"}
|
||||
{"label":"import-094","command":["timeout","-k","10","900","node","--test","packages/bus/tests/state.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gx6m","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.289,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"63ac32064c6220c89204a8c34f13ce45ebb76f65a6e10e4173608740e1b54887"}
|
||||
{"label":"import-095","command":["timeout","-k","10","900","node","--test","packages/bus/tests/store.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g5mj","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.286,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"329377b361787f86ffb5bda9210559d3d073e8ed9cfacc43e341156de63af707"}
|
||||
{"label":"import-096","command":["timeout","-k","10","900","node","--test","packages/bus/tests/task-page.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gErK","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.203,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0e48322586d18073df7d9efbc0700b7f9e2e45a7a61eb32ffa13ab9d9695de54"}
|
||||
{"label":"import-097","command":["timeout","-k","10","900","node","--test","packages/bus/tests/task-read.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g7B0","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.237,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"3aa73723d9410d5c6851352f6b3d9c0f671f9b008b8a2355021488f2a071c22b"}
|
||||
{"label":"import-098","command":["timeout","-k","10","900","node","--test","packages/bus/tests/tasks.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gqcQ","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.037,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"60e43ca791f0ee0e5a91c0a112ad0d971f38d28e18d6feb7a2d33132aec934fe"}
|
||||
{"label":"import-099","command":["timeout","-k","10","900","node","--test","packages/bus/tests/tester-threads.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbPX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.486,"mutant":false,"tests":15,"pass":15,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"101fcf483990278f3a997ed1cff44b6191b1766147083ddf21341a4480eb602b"}
|
||||
{"label":"import-100","command":["timeout","-k","10","900","node","--test","packages/bus/tests/tester-writes.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gMT4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.609,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d5618314574cf7e64ac877e29787712f5fd68903228e6e1001c37cdd9b54a9e4"}
|
||||
{"label":"import-101","command":["timeout","-k","10","900","node","--test","packages/bus/tests/tester.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gl5m","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.337,"mutant":false,"tests":14,"pass":14,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c44885e9d43e6ed5969af2f87ecc17fe00e1f1895435d86f5ddbe399c945642e"}
|
||||
{"label":"import-102","command":["timeout","-k","10","900","node","--test","packages/bus/tests/thread-claims.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gmfF","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.354,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"bd053d20e52d1859ea6ed13f316e7ed27dac3ba3a8f1be5b3b7adef2fa15e356"}
|
||||
{"label":"import-103","command":["timeout","-k","10","900","node","--test","packages/bus/tests/thread-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gUt3","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.513,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ee94081c476ab12b1c1b73eb2efb6138c22deee6dda92aa015fd2b2cc4cc405e"}
|
||||
{"label":"import-104","command":["timeout","-k","10","900","node","--test","packages/bus/tests/thread-shares.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g6KY","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.966,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e2c37691510cc5acfecfcd6d5be612b701fa72a06c544c265b027a381eb577ae"}
|
||||
{"label":"import-105","command":["timeout","-k","10","900","node","--test","packages/bus/tests/threads.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gqdT","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.848,"mutant":false,"tests":20,"pass":20,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0e25845dfd5e6c68c206ef19b6ab74b5e00d01cf2ce6cd8843b7f0abe4229d60"}
|
||||
{"label":"import-106","command":["timeout","-k","10","900","node","--test","packages/bus/tests/transport.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gG0Z","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.355,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"177ad5f143bb47dc7d65ef89785b19170ed7f5a25d1dd78079c73367b7d34bb5"}
|
||||
{"label":"import-107","command":["timeout","-k","10","900","node","--test","packages/bus/tests/usage-broker.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gYVM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.179,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"91d3b9ff3e854f2afb6d635a0994a4b2d1af482804067c2e6f78930babf25378"}
|
||||
{"label":"import-108","command":["timeout","-k","10","900","node","--test","packages/bus/tests/usage-migration.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gJIX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.232,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"35dae9048409bf16982b1554b0d3bf53de51293c012b428cfa95e3876c353d1b"}
|
||||
{"label":"import-109","command":["timeout","-k","10","900","node","--test","packages/bus/tests/usage-runtime.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gzcI","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.3,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1b260c414d98caf272852cc1c6a0928a79f84b05baad356f954fabb8befed9f4"}
|
||||
{"label":"import-110","command":["timeout","-k","10","900","node","--test","packages/bus/tests/usage.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g9hD","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.341,"mutant":false,"tests":15,"pass":15,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4b7a7719685b54f0bafcd0d6ca0eff8da3c35e8c438b69fb63826120c444646d"}
|
||||
{"label":"import-111","command":["timeout","-k","10","900","node","--test","packages/bus/tests/user-principal.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gCyo","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.843,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7e5d8e391b6d683905f7654276e74e56cd2e0bb3019d8882fb51f5e96e8bb4e2"}
|
||||
{"label":"import-112","command":["timeout","-k","10","900","node","--test","packages/bus/tests/viewer.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gckq","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.495,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0088c69c83ad21b0c09c5b74986f7d1b5609f6155d40d6230cb755914506b209"}
|
||||
{"label":"import-113","command":["timeout","-k","10","900","node","--test","packages/bus/tests/web.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gr8J","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.209,"mutant":false,"tests":19,"pass":19,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"81db5367857555ba39abf0d45a2b4837a60c796109af4039b43f3dceaaf9a3e4"}
|
||||
{"label":"import-114","command":["timeout","-k","10","900","node","--test","packages/bus/tests/workflow-roles.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gObE","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.265,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4a3d911233067a9623aacbe0f856296e7c80d616d1099a30a9ce253506791bb2"}
|
||||
{"label":"import-115","command":["timeout","-k","10","900","node","--test","packages/business/tests/case1-business-file.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gKGR","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.839,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2dd7e08774e88449fc90beffa7a1097772a4b04906e62548cd34b31f1cf541b3"}
|
||||
{"label":"import-116","command":["timeout","-k","10","900","node","--test","packages/business/tests/launch-settings.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gaiJ","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.451,"mutant":false,"tests":18,"pass":18,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"849eeebfaaf47c96f22db45752fb5f00143663fb4ed0aa2d22d74f8af9ea3905"}
|
||||
{"label":"import-117","command":["timeout","-k","10","900","node","--test","packages/cli/tests/access-boot.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gU7B","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.64,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1698f3f93b14d11b330e559f19a580edae78b4182042df0e7da1cc9d4cbb53be"}
|
||||
{"label":"import-118","command":["timeout","-k","10","900","node","--test","packages/cli/tests/access.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gHaW","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":7.364,"mutant":false,"tests":34,"pass":34,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e42f5491e4db8370ad6f6c2f09a8008c9559ecd572f0985f766f0ca11b1d7b60"}
|
||||
{"label":"import-119","command":["timeout","-k","10","900","node","--test","packages/cli/tests/app.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gmHw","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":16.086,"mutant":false,"tests":30,"pass":30,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d65043a7cf3a1d67b99bb47de8efa029c44b90b9fcc0cc7cb8d8ed54403bba81"}
|
||||
{"label":"import-120","command":["timeout","-k","10","900","node","--test","packages/cli/tests/boot-status.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gYv3","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.916,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f6753a454d658737099bb6da76219aeb3c6e92bce76b78c53f2d9dae29f5d777"}
|
||||
{"label":"import-121","command":["timeout","-k","10","900","node","--test","packages/cli/tests/case1-hostcheck.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gvaK","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.735,"mutant":false,"tests":19,"pass":19,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"aeb1e23bf951d5859d7adb54fe8f3c6464ba4bf368e9316dce8e5142e097565c"}
|
||||
{"label":"import-122","command":["timeout","-k","10","900","node","--test","packages/cli/tests/case2-install.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gLWb","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":11.37,"mutant":false,"tests":27,"pass":27,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"06166332e80fd57cd9ae5e5ef2f167260adb413cd6c799b9c1834255cbf3ed63"}
|
||||
{"label":"import-123","command":["timeout","-k","10","900","node","--test","packages/cli/tests/case2-persona-import.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g955","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.606,"mutant":false,"tests":14,"pass":14,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6005b321161cba438dc94ee6f58a05a05919e099da0d5b43b16b0438214b128e"}
|
||||
{"label":"import-124","command":["timeout","-k","10","900","node","--test","packages/cli/tests/case2-unit.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPC5","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":104.569,"mutant":false,"tests":47,"pass":47,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b9c64e1eea4e389dfc10b9e1ddf88e47d903c7abb1f175b0f73b0a6adf5e8a3b"}
|
||||
{"label":"import-125","command":["timeout","-k","10","900","node","--test","packages/cli/tests/channel-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gCxf","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.604,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"69ae3003d282a5f998613498ca1a696b684a9e4c03f5b006409d1fb15402c3c6"}
|
||||
{"label":"import-126","command":["timeout","-k","10","900","node","--test","packages/cli/tests/channel-recovery.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gy7b","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":7.039,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0df2d0dce24f2c7e9e70c1db3da115d848ee778a3b77c30f50db5fa5b70b15a8"}
|
||||
{"label":"import-127","command":["timeout","-k","10","900","node","--test","packages/cli/tests/cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g4YA","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.986,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9e9f75b810bdce44a7e288a0f03572a824642a0e615c3912113d8882fba57f20"}
|
||||
{"label":"import-128","command":["timeout","-k","10","900","node","--test","packages/cli/tests/compactions-launch.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gH0o","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":18.141,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7ab789f5de01f4d9fb2c2933fc906d657bb3c867ebb7e8545b4ae8b2f07e348b"}
|
||||
{"label":"import-129","command":["timeout","-k","10","900","node","--test","packages/cli/tests/config.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gvdv","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.989,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"46976c51d867431031baeb7dfb03518e111f88327f99f24fafa727c987037710"}
|
||||
{"label":"import-130","command":["timeout","-k","10","900","node","--test","packages/cli/tests/credentials.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gW4m","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.558,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1633748b05e927193646fd8eaeb9cc64092553734a972a33ac9b26baee4390e1"}
|
||||
{"label":"import-131","command":["timeout","-k","10","900","node","--test","packages/cli/tests/federation-acceptance.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gEeW","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.281,"mutant":false,"tests":1,"pass":0,"fail":0,"cancelled":0,"skipped":1,"todo":0,"log_sha256":"76dec3f040a21cad59757b69487247ebb3b6a8b27b015f0615585b3be477942a"}
|
||||
{"label":"import-132","command":["timeout","-k","10","900","node","--test","packages/cli/tests/follow.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gxPz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.084,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"db3463b8998634a487878c02c81b31eb196077dc45ac102de56f6f06a7bd26ee"}
|
||||
{"label":"import-133","command":["timeout","-k","10","900","node","--test","packages/cli/tests/git-socket.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gcHM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":27.936,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c1a6247751b05dcaad81d9a42e93c923200f2971ef17622924d00a07c8292735"}
|
||||
{"label":"import-134","command":["timeout","-k","10","900","node","--test","packages/cli/tests/grants-scope.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gRr9","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.298,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2f9222ce2ac41443cb24c69bffea46fd96c53af469670f642dcc2319d162ffc5"}
|
||||
{"label":"import-135","command":["timeout","-k","10","900","node","--test","packages/cli/tests/grants.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbr2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.418,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"018fba785221ec997b369739f1fa39ff927f7144b6276f071338a64ba331e555"}
|
||||
{"label":"import-136","command":["timeout","-k","10","900","node","--test","packages/cli/tests/host.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gIQc","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":20.522,"mutant":false,"tests":43,"pass":43,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"98d2bb8ac91e143fd16fb02b29d877b2df4397a1d8857687707d0bfebb3459be"}
|
||||
{"label":"import-137","command":["timeout","-k","10","900","node","--test","packages/cli/tests/hosts.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gB9t","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.5,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"376d8a698c39bc25b9e43ee63f762718ab2d2d2dbfab36da61971e3982f98353"}
|
||||
{"label":"import-138","command":["timeout","-k","10","900","node","--test","packages/cli/tests/idle-stop-race.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gt4N","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.246,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4ee49db59177a6cedcc98642ad2b4215e1489c8720e3ab27c88a1d3513b4384f"}
|
||||
{"label":"import-139","command":["timeout","-k","10","900","node","--test","packages/cli/tests/instances.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/ga2n","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.158,"mutant":false,"tests":15,"pass":15,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"125780e39af442913bf056f71a57b663c954d8eb5a96fb245db8063237ccb1e1"}
|
||||
{"label":"import-140","command":["timeout","-k","10","900","node","--test","packages/cli/tests/launch-account.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gEgp","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.427,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1a2de9540358023f6acb51ab8cdfe0f56e21331cca06ecef672fe7e9b1e0b43a"}
|
||||
{"label":"import-141","command":["timeout","-k","10","900","node","--test","packages/cli/tests/launch-limit-checks.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gxqf","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.398,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"027fb83eb3ef68f01fde4e1a272faf569516372f194e3e8404973756f9ffb974"}
|
||||
{"label":"import-142","command":["timeout","-k","10","900","node","--test","packages/cli/tests/launch-limits.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g5N5","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.255,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a3c26a3f092d8e7dd96fe1addfcd77b89d726d027e9f988340a1442823644c3c"}
|
||||
{"label":"import-143","command":["timeout","-k","10","900","node","--test","packages/cli/tests/launch-routes.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gMhQ","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.514,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ba0335e2efd7de694b51ca89b427ffb06b652f6d9757df1997f9c1c7703ac74a"}
|
||||
{"label":"import-144","command":["timeout","-k","10","900","node","--test","packages/cli/tests/launcher.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gqhv","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":45.769,"mutant":false,"tests":34,"pass":34,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"5e5374884901f29432961198070f3ede2f87fcc41a818423c3cb90c919dfd7d0"}
|
||||
{"label":"import-145","command":["timeout","-k","10","900","node","--test","packages/cli/tests/lifecycle.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g76b","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":16.885,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"37c3dcc1555bc527fe0f853c1a08bae993bd541143c3b6f7edc360c0c3c26d8d"}
|
||||
{"label":"import-146","command":["timeout","-k","10","900","node","--test","packages/cli/tests/limits-tokens.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g7dV","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.224,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6dae4cd3607f44b07df6a5e6764276a2ef152b892f050340d6defd14a83b731e"}
|
||||
{"label":"import-147","command":["timeout","-k","10","900","node","--test","packages/cli/tests/limits.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gLVw","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.442,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"efe051216bb6347423f556f8951c6b6c4adf9c266d8fde6fedfbebb3e678c17f"}
|
||||
{"label":"import-148","command":["timeout","-k","10","900","node","--test","packages/cli/tests/mark-read.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gVQ4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.266,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b90e024010c9a6fbded17306d01b78b2f5748167f9dd4fee20bc5775ef72de81"}
|
||||
{"label":"import-149","command":["timeout","-k","10","900","node","--test","packages/cli/tests/names.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gJL7","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.297,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b8d4005cb4e0f1acbe89bc6427b952459b2b876f54c1ed031a558789b535e87e"}
|
||||
{"label":"import-150","command":["timeout","-k","10","900","node","--test","packages/cli/tests/notifier.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gSos","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.033,"mutant":false,"tests":27,"pass":27,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c7156a9c99f99c3b51da2c73b728349f348dac6a0b0530bda96212a848014f7c"}
|
||||
{"label":"import-151","command":["timeout","-k","10","900","node","--test","packages/cli/tests/optional-tracker.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/ggn7","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.413,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d09457d3034c683bc98639f04d83d5348815571d46d5c30cbe055aed5ceef3a7"}
|
||||
{"label":"import-152","command":["timeout","-k","10","900","node","--test","packages/cli/tests/other-tree.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gclm","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.3,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"aa7be88cd823a44c8743159006f1d9b61072ebaa04935339d4a875d0997f903e"}
|
||||
{"label":"import-153","command":["timeout","-k","10","900","node","--test","packages/cli/tests/processing-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gvyI","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.367,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"55d67187192edfd5c929713af6a8f40025259429f36a444eb886fd26013a3135"}
|
||||
{"label":"import-154","command":["timeout","-k","10","900","node","--test","packages/cli/tests/project-name-repo.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g3qR","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.431,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9e8364d68d732dbcb9e518ff8dc1cb957d0d254c0a3821711df20e34f6427f07"}
|
||||
{"label":"import-155","command":["timeout","-k","10","900","node","--test","packages/cli/tests/project.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gJK9","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.908,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7014e206c70053c2eaeb89fa59b38781802e65d2982fee3855772dd9721285a6"}
|
||||
{"label":"import-156","command":["timeout","-k","10","900","node","--test","packages/cli/tests/publish-socket.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQ5m","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":11.807,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e20a380f9e0e0070d2598bd3ae85679f70f59c92e6f8cf1c7c840a0975c253b7"}
|
||||
{"label":"import-157","command":["timeout","-k","10","900","node","--test","packages/cli/tests/reply-limits.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gR4L","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.167,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9339298bbd5ebfb0e033d2b992c69f62c168e124e741bfc0de500c35b8ffc092"}
|
||||
{"label":"import-158","command":["timeout","-k","10","900","node","--test","packages/cli/tests/revoke-window.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gHpa","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.868,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"774c1211e7dadeab526d34d50b143c1141ee9f85e3799b644734eeb78c8e9b6f"}
|
||||
{"label":"import-159","command":["timeout","-k","10","900","node","--test","packages/cli/tests/session-clear.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gd9q","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":13.609,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a47caf45da848c1bf1a8456eb89c213df981229dc0765362573ec82e6701b11c"}
|
||||
{"label":"import-160","command":["timeout","-k","10","900","node","--test","packages/cli/tests/session-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gXLB","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.323,"mutant":false,"tests":14,"pass":14,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"3d260701a0c4afd1d21effb5ca1db6cdb47803281bde2536853926f56a236955"}
|
||||
{"label":"import-161","command":["timeout","-k","10","900","node","--test","packages/cli/tests/session-launch.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPoX","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":12.34,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9d077d5e2956b1f40de6abba79ebed6504cc61a3821754526a032231e8697271"}
|
||||
{"label":"import-162","command":["timeout","-k","10","900","node","--test","packages/cli/tests/stack-copy.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gb3j","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":32.255,"mutant":false,"tests":18,"pass":18,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a31fda44310b53fcbfbfb5ca037db8bf9209a0235ed951a911358c61c5f4d13b"}
|
||||
{"label":"import-163","command":["timeout","-k","10","900","node","--test","packages/cli/tests/stack-instances.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gVU0","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.223,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"baf388164839b10a5ea8c3e019d6b5095d7951657ee638784701848b69a160cd"}
|
||||
{"label":"import-164","command":["timeout","-k","10","900","node","--test","packages/cli/tests/tester-http.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/geku","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.28,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d06f4abf2476d898d85533318fee23bdfe3fdc6012c40e809ae4f21e1d6e74bb"}
|
||||
{"label":"import-165","command":["timeout","-k","10","900","node","--test","packages/cli/tests/tester-reads.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g7PM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.141,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2f293f7b23b679733870b5d5d2f666570b56fa1335d5b94590046447433225c8"}
|
||||
{"label":"import-166","command":["timeout","-k","10","900","node","--test","packages/cli/tests/tester-threads.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gAdD","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":12.373,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6505ae90432cfcf6c2b74ed4c22ebe6ee1b26055b41f3fdada4e40c54e1124f9"}
|
||||
{"label":"import-167","command":["timeout","-k","10","900","node","--test","packages/cli/tests/tester.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gx0E","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.628,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"abadf2326205ea5ba1e617fd174018995de824d1552f308815e2bc9c3f71e4bc"}
|
||||
{"label":"import-168","command":["timeout","-k","10","900","node","--test","packages/cli/tests/thread-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbFa","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.076,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"fc7f5fadded9b52bcebcf80af32a54bfcb85c2a58608e261d839e8c91d473ce4"}
|
||||
{"label":"import-169","command":["timeout","-k","10","900","node","--test","packages/cli/tests/thread-launch.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQNn","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":36.824,"mutant":false,"tests":15,"pass":15,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f4052bf907e300dae2a7d44f3e8b370849e879abf1de9f84e0c74138f41f2640"}
|
||||
{"label":"import-170","command":["timeout","-k","10","900","node","--test","packages/cli/tests/thread-revoke.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gNtT","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.273,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2f66b678922916e9856a6ade3a4524c420d981f39548a6687df77cb95c660d40"}
|
||||
{"label":"import-171","command":["timeout","-k","10","900","node","--test","packages/cli/tests/trackers-boot.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/giDk","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.097,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"dbb78cab0545fe2d18089c4510092562aa5bc16cdc3f1f95784719d8e2dda241"}
|
||||
{"label":"import-172","command":["timeout","-k","10","900","node","--test","packages/cli/tests/transport.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g6h3","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.447,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7b7a41c111670e869d5156d3714a33c7a4e7a675893cff4767c505b658ac0dc8"}
|
||||
{"label":"import-173","command":["timeout","-k","10","900","node","--test","packages/cli/tests/usage.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gERb","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.44,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d39ae2e64f3e78a0d4bd7fc8d4c6f8f087ac80f0ba47d5dbd199d627d826e86f"}
|
||||
{"label":"import-174","command":["timeout","-k","10","900","node","--test","packages/cli/tests/verbs.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gaL4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.562,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6eafbcea30d6c03a2f8dd925ca6b801fb7f2a8475f3b70715ad8a5060f47f0f0"}
|
||||
{"label":"import-175","command":["timeout","-k","10","900","node","--test","packages/harness/tests/bundle.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g8v2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.276,"mutant":false,"tests":22,"pass":22,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"232cba86e5cf956c07e7ac3bce916899fd288db2ef252bf33041c54fa29f6e1b"}
|
||||
{"label":"import-176","command":["timeout","-k","10","900","node","--test","packages/harness/tests/citation-runner.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g6h4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":6.327,"mutant":false,"tests":21,"pass":21,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ef0a3e5f582524e0f03a3a39b518356af2d180cefcaa64439c828dbfc9ab0555"}
|
||||
{"label":"import-177","command":["timeout","-k","10","900","node","--test","packages/harness/tests/claude-gate.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gmnt","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.62,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2b7867d549382bd5943f9f828f7132743abd724b9d6a26f7e7647e5deac1086c"}
|
||||
{"label":"import-178","command":["timeout","-k","10","900","node","--test","packages/harness/tests/claude-session.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gWex","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.983,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e97d2c71fb63346d736d85b37e1bae38708041a7e09b15cc5d64f44d5f87e592"}
|
||||
{"label":"import-179","command":["timeout","-k","10","900","node","--test","packages/harness/tests/creative.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g7fD","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.349,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0a08749c464113ccbc5207cc63ad3cb33ccd7d7af3ebdcb614106f14a38fa8af"}
|
||||
{"label":"import-180","command":["timeout","-k","10","900","node","--test","packages/harness/tests/gate.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gLqE","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.177,"mutant":false,"tests":12,"pass":12,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"8d3cf0837ecb7d8c5be2a02c67fbe1fb1fbf0a7d75b8e79029a6a3530870353f"}
|
||||
{"label":"import-181","command":["timeout","-k","10","900","node","--test","packages/harness/tests/git-tools.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gcPM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.824,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"170299b738f35a5cbce8336dbf37ddd340045c9f12b0d86716dc9764ce692656"}
|
||||
{"label":"import-183","command":["timeout","-k","10","900","node","--test","packages/harness/tests/mcp-server.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gruQ","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.352,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"fcbb8ff07441b7458fad73e8f0d2e8c81a37a184244403c46f0db51bcb579d6c"}
|
||||
{"label":"import-184","command":["timeout","-k","10","900","node","--test","packages/harness/tests/pi-session.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQR8","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":7.664,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"21ef864f6c0608e398001beed596c711eaa16df9830be6e9948d23adaf7ae19b"}
|
||||
{"label":"import-185","command":["timeout","-k","10","900","node","--test","packages/harness/tests/raise-domain.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gkno","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.289,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"258b03d95708255aaf9c53af130e80349d3fb7a15c412f5e99a552d38d273cf4"}
|
||||
{"label":"import-186","command":["timeout","-k","10","900","node","--test","packages/harness/tests/runner.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gHpN","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":14.264,"mutant":false,"tests":40,"pass":40,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6b2ed91f2cc0af529b15da7dd1ca38fc4f9024b8838f1ae948bb9fb61f52b1d4"}
|
||||
{"label":"import-187","command":["timeout","-k","10","900","node","--test","packages/harness/tests/skills.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/ge6l","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.322,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a5eb44bb14feab35f8afff44467dd1ab4b6ccd95f0c20e99bd8165e02ab11e94"}
|
||||
{"label":"import-188","command":["timeout","-k","10","900","node","--test","packages/harness/tests/spec-tools.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gqeF","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.392,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"25bef2391ffce43b2ad8c6159fb801bf371064b48998f5212b45043b78a65ab4"}
|
||||
{"label":"import-189","command":["timeout","-k","10","900","node","--test","packages/harness/tests/tools.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gh6I","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.254,"mutant":false,"tests":23,"pass":23,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"032cd4df77581f65cc3d1e3487b542cc2568e40461f34cf345c9e3ccf224d2c0"}
|
||||
{"label":"import-190","command":["timeout","-k","10","900","node","--test","packages/library/tests/guard.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gX5T","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.7,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c5c5e1daabdcbc4830da5472dc584f3095874effa6ed00a5d752ef5a792f433a"}
|
||||
{"label":"import-191","command":["timeout","-k","10","900","node","--test","packages/library/tests/index-build.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDCo","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.794,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"6122712a2a10cd4fc7ad0f29e7581dc27e6d1e5daddb92dc3b9831ccd26c06b5"}
|
||||
{"label":"import-192","command":["timeout","-k","10","900","node","--test","packages/library/tests/links.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gNyj","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.128,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"933818b92437600be0591b784675887f9240915167a6218d9bd5546737c0de1d"}
|
||||
{"label":"import-193","command":["timeout","-k","10","900","node","--test","packages/library/tests/mapping.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gBnG","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.16,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b97b350865814827ad8998f9a24ea1f65749b882dd10531eb79052268769f9d0"}
|
||||
{"label":"import-194","command":["timeout","-k","10","900","node","--test","packages/library/tests/mirror.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gfP9","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":6.763,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ba91652dd7daa182d771e5f8d093769fa009494d0f4080fd697a423b8c8aff4c"}
|
||||
{"label":"import-196","command":["timeout","-k","10","900","node","--test","packages/library/tests/reader.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gvmz","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.028,"mutant":false,"tests":14,"pass":14,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"5b335d4472ccc9aead596840a67909ccf96270c7ccf88e4ad2141eb5953de74c"}
|
||||
{"label":"import-197","command":["timeout","-k","10","900","node","--test","packages/library/tests/render.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gfUA","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.211,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f95a831fa8ef95d90f50861d7ebf05fdedc4d3d68d1ea3dec1e17e9392befab9"}
|
||||
{"label":"import-198","command":["timeout","-k","10","900","node","--test","packages/library/tests/scope.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gxAY","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.368,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"3935e31b852d0be272395f8d2af9816e8b8ee3bf75a088be39559e413d58fd1e"}
|
||||
{"label":"import-199","command":["timeout","-k","10","900","node","--test","packages/library/tests/supporting.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gT0U","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.25,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"cfd46a3aaa0167b3adda75bb69095f6328de4cfd43bdb26ae4952e6867c6bf81"}
|
||||
{"label":"import-200","command":["timeout","-k","10","900","node","--test","packages/library/tests/survey.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQVP","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.764,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a20f9ea4cd570def995d73d389f0610621f0198499e60c8f27ada2e952b22bb8"}
|
||||
{"label":"import-201","command":["timeout","-k","10","900","node","--test","packages/library/tests/sync.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gGSr","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":6.838,"mutant":false,"tests":24,"pass":24,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"683e7b2ec581c2d72a6a3edc68913a7092a49c3ac5c15fe9dfa3062fc3d59fe9"}
|
||||
{"label":"import-202","command":["timeout","-k","10","900","node","--test","packages/processing-control/tests/task.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gAOM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.776,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a9c5e79fa8bddcd222359c6b5fb5634ad170ad56babfe3971f22d8f1fbcaceb9"}
|
||||
{"label":"import-203","command":["timeout","-k","10","900","node","--test","packages/seat/tests/sandbox.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gcfc","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":7.076,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9d9e237b00482d14c1173fcdf2ba0770d87c2e77e0a2d7f8b3631531d1fa5da2"}
|
||||
{"label":"import-204","command":["timeout","-k","10","900","node","--test","packages/seat/tests/session.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gpBq","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.478,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2ffb0ee5961f4611d6afde99fd99270dee783b7abde51c9038ee2087dc426c22"}
|
||||
{"label":"import-205","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/adapter.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/guN4","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.512,"mutant":false,"tests":8,"pass":8,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b0441401e2ab0e9593e6d55cca661366e7ee8b51850ac934bfca23de45d2a35a"}
|
||||
{"label":"import-206","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/comment-restart.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gyAw","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.45,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"599ad621c9d89dc1f200ad0e87eb1573e5ae7bfcc6ff9cddb9048855a9f8613f"}
|
||||
{"label":"import-207","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/fields.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gOij","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.617,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2d83b920f5f716537a69555f6bead6b99be1b3c64751305ec8de1a71cadeb56f"}
|
||||
{"label":"import-208","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/processing.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g72u","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.32,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"5d740844a654055345b9e7c69ed4d2a247cd932ad2a5a6fcf329c7801cbd0ad7"}
|
||||
{"label":"import-209","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/shapes.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gJAm","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.518,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"3811bcc866161d152d9460cb8bfef2faa9cd3e533de96f1b3f014403dd7fb51e"}
|
||||
{"label":"import-210","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/startup.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gGTN","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.541,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b2c08b276e60a676346a7a7a96423939fc50fece24e84f7d31a141414ae9c65e"}
|
||||
{"label":"import-211","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/sync.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gccq","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.994,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7521b57b037d5dfd35e4e7f8b3e0f772e6f09e9a7f218eaa55a95b40798fb3cb"}
|
||||
{"label":"import-212","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/tree.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gKai","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.129,"mutant":false,"tests":19,"pass":19,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"035eba7993242c2797c236e2c7f5d41f9248d872984cbfd785bb596bd323721f"}
|
||||
{"label":"import-213","command":["timeout","-k","10","900","node","--test","packages/tasks/tests/verbs.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gcgn","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.767,"mutant":false,"tests":31,"pass":31,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"27cd7e0bb4607ec39ededdc89e4afcb363698eee6dd31ddd5d5a6b2cbabd847b"}
|
||||
{"label":"import-214","command":["timeout","-k","10","900","node","--test","packages/webui/tests/agents-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gtNR","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":17.433,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"e2c213130dbf80c125011ba8d45c016bfb347ebe1d21f1c64d1756e0db82d6f9"}
|
||||
{"label":"import-215","command":["timeout","-k","10","900","node","--test","packages/webui/tests/agents.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQRO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.233,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c94baa2f42b463dee93e00cf1f3b1e60b287f109debf3a5a5133a8363b992d3a"}
|
||||
{"label":"import-216","command":["timeout","-k","10","900","node","--test","packages/webui/tests/assistant.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gCyc","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.233,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1a32bdca8137c6aff24e9eee5c19dcadfef1e3a5f0aab5a45c600cf39c29e56e"}
|
||||
{"label":"import-217","command":["timeout","-k","10","900","node","--test","packages/webui/tests/board-held.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gPDU","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":10.78,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"fb532481a6401e7097d1b7fd8c858885988e37529779c7e989885dd0de21f7e9"}
|
||||
{"label":"import-218","command":["timeout","-k","10","900","node","--test","packages/webui/tests/browser-edge.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gw0B","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.748,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f23507b59a89ff056564e71cda5e73b275ebc4de3130956b9cf69615af5aebe0"}
|
||||
{"label":"import-219","command":["timeout","-k","10","900","node","--test","packages/webui/tests/browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gZfS","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.398,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"08f605ea6cfe1ec7b0f9d1a951a359e2e0106892dc56567152ed569548bff757"}
|
||||
{"label":"import-220","command":["timeout","-k","10","900","node","--test","packages/webui/tests/bus-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gjO2","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.795,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c03e5eaab52ea76d3b5ac1ccfbae6c25a2fab9f592aa45cd29bc6e22f3f45c3f"}
|
||||
{"label":"import-221","command":["timeout","-k","10","900","node","--test","packages/webui/tests/bus.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g766","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.937,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"703208aaab3751971784b6266b335c54f41048735650e92f40b6e8bd37ea9a6a"}
|
||||
{"label":"import-222","command":["timeout","-k","10","900","node","--test","packages/webui/tests/channel-chat.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gyou","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.526,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9dd425b57cb427c25265f7e1b4266cc9d9d09892d2de5ddf4c916635b66515c1"}
|
||||
{"label":"import-223","command":["timeout","-k","10","900","node","--test","packages/webui/tests/chat-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g5ri","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":40.593,"mutant":false,"tests":19,"pass":19,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4a751781c8e1b9c4e1bbe9e1767d5d897c3cbb09ac4db39ea25d13c276e947cd"}
|
||||
{"label":"import-224","command":["timeout","-k","10","900","node","--test","packages/webui/tests/chat-instances.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gypj","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.248,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c43f61b93bbf092d23c0bfe9c6e85b76e4ded17b18d973c4a37832764df71f72"}
|
||||
{"label":"import-225","command":["timeout","-k","10","900","node","--test","packages/webui/tests/chat-replies-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDNY","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.751,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f919fe31839c0bc40d4860c4f4412480a1016267c00b292fead32b36098031d5"}
|
||||
{"label":"import-226","command":["timeout","-k","10","900","node","--test","packages/webui/tests/chat-sessions-access.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g9LK","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.741,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a4c4116cfd2a752d7efd2897430a9b2b052b6acf868c1ca8ce26b81ba9585812"}
|
||||
{"label":"import-227","command":["timeout","-k","10","900","node","--test","packages/webui/tests/chat.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/go2w","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.849,"mutant":false,"tests":30,"pass":30,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"77d6c675d0a4534e84f4b2ce4eddf2135908dc5014469e74b87b6fdd3f63c130"}
|
||||
{"label":"import-228","command":["timeout","-k","10","900","node","--test","packages/webui/tests/conversation.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/ga85","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.852,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"35d962e50a5a143e4dd8b816414c3044096d8907e807f50ca72eef81df9531ee"}
|
||||
{"label":"import-229","command":["timeout","-k","10","900","node","--test","packages/webui/tests/decide-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gd1n","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":12.017,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"559586deeaa79f7f28185318bf29e7ea28b4536c60bddbfdcdd337850104820a"}
|
||||
{"label":"import-230","command":["timeout","-k","10","900","node","--test","packages/webui/tests/decide-sso.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gTYk","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.998,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f277b7937f563610c111f75d55917ba1a6474b0cbf7edcc417f66ca1db9e77a8"}
|
||||
{"label":"import-231","command":["timeout","-k","10","900","node","--test","packages/webui/tests/decision-http.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gesP","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.149,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1ae4314867f3da3d96bb8bd99bb9e4bb120610e4b4f76f266a4394d2c4cdac3d"}
|
||||
{"label":"import-232","command":["timeout","-k","10","900","node","--test","packages/webui/tests/discord.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gROh","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.126,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d1da3a8a99ae68e4e2c704b993c9f4fac1502f60dc21502abadad75584e212f1"}
|
||||
{"label":"import-233","command":["timeout","-k","10","900","node","--test","packages/webui/tests/docs-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gZtM","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.892,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b126c48278cbe8276b5a1459b14cf00d1047ec686a80a82dfe509169e4d8aa7c"}
|
||||
{"label":"import-234","command":["timeout","-k","10","900","node","--test","packages/webui/tests/editor-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gJnO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":23.444,"mutant":false,"tests":16,"pass":16,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1053f032713f4a82d05003347cb02c6db54f6c554d3d549608615e3dcc835c83"}
|
||||
{"label":"import-235","command":["timeout","-k","10","900","node","--test","packages/webui/tests/editor.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gLje","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":6.017,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"a1b78738684b0e0049cc5731f55b67cc21d83c0bd117a20ee094efcefbc926b9"}
|
||||
{"label":"import-236","command":["timeout","-k","10","900","node","--test","packages/webui/tests/granted-user.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gH8T","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":6.001,"mutant":false,"tests":19,"pass":19,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"7e778058c0b4dfc5a44b1c95d146e3628f1401f18e5ca68d0eb5a2af2357fe16"}
|
||||
{"label":"import-237","command":["timeout","-k","10","900","node","--test","packages/webui/tests/history-return-flow.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gOZs","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":56.725,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1e74c166baa23c019edac72a0ce2735e7804112369ef6f6d12b4b30e0ed1a7a1"}
|
||||
{"label":"import-238","command":["timeout","-k","10","900","node","--test","packages/webui/tests/instances-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/goix","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.608,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"20da30c92fc5b9149e84cd2547aceac366f5509e8efbeb346b700d6f8cacc18b"}
|
||||
{"label":"import-239","command":["timeout","-k","10","900","node","--test","packages/webui/tests/instances.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gr2y","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.265,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"0b0782580df886f98f4428ad6f94d33fa2d37d89c58d12c152964b26786d48fe"}
|
||||
{"label":"import-240","command":["timeout","-k","10","900","node","--test","packages/webui/tests/library-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gGV8","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.868,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1e3617d918b4d7474e1acf9b5ad32732305aa86f8ad3044d488aac21f551cb4e"}
|
||||
{"label":"import-241","command":["timeout","-k","10","900","node","--test","packages/webui/tests/library-routes.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gWxL","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.532,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d9729aef19f819e8190e6fdc9dbb1d7c4061f2311598f3785678cc81fbaea30e"}
|
||||
{"label":"import-242","command":["timeout","-k","10","900","node","--test","packages/webui/tests/library-status.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gurC","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.265,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"99e68ba123769f5df8b60d9d12546d113f9f1f18f27b26a82738f4ac670fc18f"}
|
||||
{"label":"import-243","command":["timeout","-k","10","900","node","--test","packages/webui/tests/limits-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gq0e","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.598,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4d3b961b33557106779e969bdfb9aa959e01462a521d5368d9e7e560a4bab19b"}
|
||||
{"label":"import-244","command":["timeout","-k","10","900","node","--test","packages/webui/tests/limits-route.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gK8P","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.561,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2283f78868a29c6ebf37b0aaabc2fdfd84d0a381b3fd046d27902adf2710bfae"}
|
||||
{"label":"import-245","command":["timeout","-k","10","900","node","--test","packages/webui/tests/messages.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gaaW","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":6.474,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"c2e137bf0cc8d12832a07cdb1f70f95dd3cc8ce34a500e6b0479f9d94bf4b300"}
|
||||
{"label":"import-246","command":["timeout","-k","10","900","node","--test","packages/webui/tests/pinned-copy.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gG1M","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":7.925,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"ebeca41fa26f042b9e877ddc3315254a60f98b947fb3eb9ccf098c4d455117b4"}
|
||||
{"label":"import-247","command":["timeout","-k","10","900","node","--test","packages/webui/tests/preview.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gHU1","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.186,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4f0064f793bb36dbdf294fe77d67530afc0aea22f01b847aa1d00c3844469119"}
|
||||
{"label":"import-248","command":["timeout","-k","10","900","node","--test","packages/webui/tests/processing-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gkgO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.863,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"816a885bb9c45578982d7e43326cb77215e8cb29f557aa06e0efc39a26297e05"}
|
||||
{"label":"import-249","command":["timeout","-k","10","900","node","--test","packages/webui/tests/processing.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gMEO","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.36,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"d0d8b5325152e1919b5838bfd1b83c4551b52d69c60f07d0921eb95af6391222"}
|
||||
{"label":"import-250","command":["timeout","-k","10","900","node","--test","packages/webui/tests/project-file.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g0K1","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.316,"mutant":false,"tests":10,"pass":10,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"52754ce30568e4d818370c3558342ab23d1292a67f692c1ded6a4d4b4121a029"}
|
||||
{"label":"import-251","command":["timeout","-k","10","900","node","--test","packages/webui/tests/project-name-repo.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gRNU","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.132,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"324df0e70027674e99303723c75b02402d6ae43aefc73192e6c723644952fe98"}
|
||||
{"label":"import-252","command":["timeout","-k","10","900","node","--test","packages/webui/tests/proof-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gUya","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.557,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9d0379c0dac670ea3b54331afee0d9da9c4c76a9c62315bd7cdf33458708b9b1"}
|
||||
{"label":"import-253","command":["timeout","-k","10","900","node","--test","packages/webui/tests/proof.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gw3P","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.23,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"4b564ec494d8631518bd5e0a9dafce3492cc3655e90a82f12bc994fb0f48f017"}
|
||||
{"label":"import-254","command":["timeout","-k","10","900","node","--test","packages/webui/tests/reads.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDRG","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.251,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"acd0a107da0270ad86ea4e75d1af8089f2541e1aa1c1613c10c35f47fcc38dea"}
|
||||
{"label":"import-255","command":["timeout","-k","10","900","node","--test","packages/webui/tests/relaunch.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gDNA","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.062,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b717bc82d68a804092878b6f68c73d970519d5dde6d9170a5cc3f417cd108c66"}
|
||||
{"label":"import-256","command":["timeout","-k","10","900","node","--test","packages/webui/tests/return-flow.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/go3N","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":21.066,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"61fa95c4b8c802668f2b144954e8785c1a5d6e5f8d511646c61ae51abfcf899c"}
|
||||
{"label":"import-257","command":["timeout","-k","10","900","node","--test","packages/webui/tests/room-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/geyH","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.015,"mutant":false,"tests":9,"pass":9,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"65cd71916ae9b8b6a8d47a0175d5cca96ed6c3d93d65ea334338cc651c3529e7"}
|
||||
{"label":"import-258","command":["timeout","-k","10","900","node","--test","packages/webui/tests/room-http.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gfgT","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.284,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"32d8f63c7cbf6a67265e3ebb08eeda185061a94d2674c9e79da3e5ec9348ffaa"}
|
||||
{"label":"import-259","command":["timeout","-k","10","900","node","--test","packages/webui/tests/routes-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gqwx","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.226,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"915d721ef9ebb84e963f021957955ce02a7603a048fde2ba61bd7f87e70137ae"}
|
||||
{"label":"import-260","command":["timeout","-k","10","900","node","--test","packages/webui/tests/routes.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gULI","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.499,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"f5cb0cbb1428122e4a826ebf11a304123af5e33696dc7c3c6e9a40105a7f59b5"}
|
||||
{"label":"import-261","command":["timeout","-k","10","900","node","--test","packages/webui/tests/send-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gV7T","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":14.209,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b76ab6c25710827dc2e6c7231b3aa82259cce1a88174a33f83a45c251ff4e8cf"}
|
||||
{"label":"import-262","command":["timeout","-k","10","900","node","--test","packages/webui/tests/send.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbPv","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.077,"mutant":false,"tests":13,"pass":13,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"51a759891344d96c63d9d554c0b0a0cb541acca3d3d139170805723a90f83201"}
|
||||
{"label":"import-263","command":["timeout","-k","10","900","node","--test","packages/webui/tests/serve.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g9AU","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.835,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"38c0c60b3b161bcd2cc3642f551fafd755e7ff57aa3a1f9cfc4329f0333ade9d"}
|
||||
{"label":"import-264","command":["timeout","-k","10","900","node","--test","packages/webui/tests/settings-form.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gp5I","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.17,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"305e2081fd87ffad6bae81ef0ea0a7732f9208a0fcd6ea73c8604de13af86210"}
|
||||
{"label":"import-265","command":["timeout","-k","10","900","node","--test","packages/webui/tests/settings-write.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gQmT","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.2,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"604a5dfa84401b569a004ca35182684d0aefda4da8a34c833953c15d8047c238"}
|
||||
{"label":"import-266","command":["timeout","-k","10","900","node","--test","packages/webui/tests/shell.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/goIy","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.597,"mutant":false,"tests":5,"pass":5,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"aa8226cc8ad92136f875f3a976de37dfc1b0ab30a3c24f2861c34fcd6355d002"}
|
||||
{"label":"import-267","command":["timeout","-k","10","900","node","--test","packages/webui/tests/sso-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gxVE","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.875,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"37b3e8ed9a38b6a66600fee9386b5a414d6179a8630349c6f695075c1fb29806"}
|
||||
{"label":"import-268","command":["timeout","-k","10","900","node","--test","packages/webui/tests/sso.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gsiS","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":8.274,"mutant":false,"tests":28,"pass":28,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2f286ac4644f8789c2ede5261362f4c57bcf02b84671e9b6c2f5c2994368bfbe"}
|
||||
{"label":"import-269","command":["timeout","-k","10","900","node","--test","packages/webui/tests/tester-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g7am","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":4.164,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"92727a4aee749943dc100d1b843f848ef75bbeeda29d693217882c71368c4d42"}
|
||||
{"label":"import-270","command":["timeout","-k","10","900","node","--test","packages/webui/tests/tester-cli.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gz1T","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":11.668,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"1c952ee8767467b1d1d4306e566b9b0f863d68215d30ae8ed3ab46d75ef46e8a"}
|
||||
{"label":"import-271","command":["timeout","-k","10","900","node","--test","packages/webui/tests/tester-listener.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g99a","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":2.294,"mutant":false,"tests":11,"pass":11,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"b07648c44f522c5fa789c03634f248f63e8d312e9b93b7edd90d7951145d5419"}
|
||||
{"label":"import-272","command":["timeout","-k","10","900","node","--test","packages/webui/tests/tester-socket.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gbnC","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.19,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"61704160b72bcd47d1e4166371e3ff65b148b7ef02b785f9427ef133d5ecdfb6"}
|
||||
{"label":"import-273","command":["timeout","-k","10","900","node","--test","packages/webui/tests/thread-claims-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gGv0","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.008,"mutant":false,"tests":6,"pass":6,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9d3d03d3fc2a108a5e1da2c8e153db2a21bc1e08b207698e9c8eb242dff9905a"}
|
||||
{"label":"import-274","command":["timeout","-k","10","900","node","--test","packages/webui/tests/thread-claims.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g09J","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.423,"mutant":false,"tests":4,"pass":4,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"2b031497a6d9d4b18d215d28bc04de28fe26a800c5a7d54af8052aab35eee3be"}
|
||||
{"label":"import-275","command":["timeout","-k","10","900","node","--test","packages/webui/tests/thread-sharing.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gmhU","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":3.736,"mutant":false,"tests":7,"pass":7,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"9f7b307edc31c95fac9ce71f39ea02466a40a6d80c70d9db91f433a07e19adb7"}
|
||||
{"label":"import-276","command":["timeout","-k","10","900","node","--test","packages/webui/tests/usage-browser.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/g1rW","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":1.032,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"819697b7c5cb3918c007b9fe57dd3f101c79afb9b5656905e5e1e1b0bc82de9f"}
|
||||
{"label":"import-277","command":["timeout","-k","10","900","node","--test","packages/webui/tests/usage.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gHnS","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.238,"mutant":false,"tests":2,"pass":2,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"555da950dacca5dc85db7172b50aaef5e50e726c6e30bb66c770c0c38d58ab76"}
|
||||
{"label":"import-278","command":["timeout","-k","10","900","node","--test","packages/webui/tests/user-option-swap.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gx24","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":0.38,"mutant":false,"tests":1,"pass":1,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"20bcc3985f5808a2d7f8daa02c5438fbac81c5163c117fde07bea3f46e8fa5b3"}
|
||||
{"label":"import-279","command":["timeout","-k","10","900","node","--test","packages/webui/tests/views.test.mjs"],"TMPDIR":"/mnt/storage/scratch/tmp/gHJv","start_nice":19,"observed_timeout_nice":19,"exit":0,"seconds":5.483,"mutant":false,"tests":3,"pass":3,"fail":0,"cancelled":0,"skipped":0,"todo":0,"log_sha256":"dd69767c233e8eaeeb85ddaaf274380bbb6df73ec6cc7aaaadc6336053e4ddd8"}
|
||||
```
|
||||
|
||||
Exact mutant edits (temporary only, all restored):
|
||||
|
||||
```text
|
||||
{"mutant":"M1","edits":[["const SCOPES = Object.freeze(['canon', 'playground', 'supporting']);","const SCOPES = Object.freeze(['canon', 'playground']);"]],"path":"packages/library/src/query.mjs","oracle":"supporting find/search/get","killed":true,"restored_clean":true}
|
||||
{"mutant":"M2","edits":[["file.scope !== requestedScope || ",""]],"path":"packages/library/src/query.mjs","oracle":"get without scope returns null","killed":true,"restored_clean":true}
|
||||
{"mutant":"M3","edits":[["import { listTree } from './reader.mjs';","import { listTree, headCommit } from './reader.mjs';"],["listTree(source.repo, index.commit)","listTree(source.repo, await headCommit(source.repo))"]],"path":"packages/library/src/query.mjs","oracle":"each get blob equals its served tree object","killed":true,"restored_clean":true}
|
||||
{"mutant":"M4","edits":[["}).sort((a, b) => SCOPE_RANK[index.files.get(a.path).scope] - SCOPE_RANK[index.files.get(b.path).scope]);","});"]],"path":"packages/library/src/query.mjs","oracle":"full outgoing and incoming links stay in scope order","killed":true,"restored_clean":true}
|
||||
{"mutant":"M5","edits":[["return other !== undefined && sees(index.mapping, universe, other);","return other !== undefined && other.scope === requestedScope && sees(index.mapping, universe, other);"]],"path":"packages/library/src/query.mjs","oracle":"full outgoing and incoming links stay in scope order","killed":true,"restored_clean":true}
|
||||
{"mutant":"M6","edits":[["file.scope === scope && sees(index.mapping, universe, file)","file.scope === scope"],["other !== undefined && sees(index.mapping, universe, other)","other !== undefined"]],"path":"packages/library/src/query.mjs","oracle":"full outgoing and incoming links stay in scope order","killed":true,"restored_clean":true}
|
||||
{"mutant":"M7","edits":[["scopeArg(scope, MANUSCRIPT_SCOPES)","scopeArg(scope, SCOPES)"]],"path":"packages/library/src/query.mjs","oracle":"manuscript still refuses supporting","killed":true,"restored_clean":true}
|
||||
{"mutant":"M8","edits":[["'library.get': { method: 'get', fields: ['universe', 'path', 'scope', 'from', 'links_from']","'library.get': { method: 'get', fields: ['universe', 'path', 'from', 'links_from']"]],"path":"packages/bus/src/broker.mjs","oracle":"broker forwards supporting scope","killed":true,"restored_clean":true}
|
||||
{"mutant":"M9","edits":[["const manuscriptScope = str(\"canon by default; playground is opt-in\", { enum: [\"canon\", \"playground\"] });","const manuscriptScope = str(\"canon by default; playground is opt-in\", { enum: [\"canon\", \"playground\", \"supporting\"] });"]],"path":"packages/harness/src/tools.mjs","oracle":"scope schemas expand only find/search/get","killed":true,"restored_clean":true}
|
||||
```
|
||||
|
||||
Sweep summary:
|
||||
|
||||
```text
|
||||
{
|
||||
"root_count": 289,
|
||||
"matches": []
|
||||
}
|
||||
```
|
||||
|
||||
## sage-4310 — row329 landed-base rebase, 2026-10-24
|
||||
|
||||
Branch della/row284-r4 rebased the six commits in 95d4991d2..315e9c84f
|
||||
onto origin/refactor 9e2689dffb24c9881724b67db982bc87d527157f. Old branch
|
||||
della/row284-r2 and its crash handoff remain unchanged. No conflict hunks
|
||||
occurred: Git replayed all six commits automatically; range-diff marks all
|
||||
six patches equal. Thus no manual conflict resolution was required.
|
||||
|
||||
Inspected broker.mjs against the new base: the sole row284 delta adds scope
|
||||
to library.get fields. Row329 proxy verbs and all other base broker code
|
||||
are retained unchanged. No optimization or review-note behavior change added.
|
||||
|
||||
The new eight-path seal is stored outside Git in
|
||||
/mnt/storage/scratch/della-row284-r2-evidence/manifest-r4.txt with sibling
|
||||
manifest-r4.blobs.txt and range-diff-r4.txt. Base, final candidate tip and
|
||||
manifest digest are in seal-r4.txt beside them (avoids a self-referential
|
||||
BUILD hash). Prior test/mutant results above apply to the prior base only;
|
||||
no test, mutant, Node, syntax check, dependency install or queue command
|
||||
was run for this rebase. Explicit sage-4310 authorizes this untested push.
|
||||
Worker runs and Sage landing gate wait for CLEAR after Webby delta review.
|
||||
@@ -0,0 +1,374 @@
|
||||
# Row 331 / P2 / #1829
|
||||
|
||||
## 2026-10-24 — design before implementation
|
||||
|
||||
Base: 78b69e1e6, branch magica/row331. Brief blob fd4325c05a7f,
|
||||
`2026-10-24_model-proxy-rows.md` P2 and Rules for every row; gate rules in BRIEF-TEMPLATE.
|
||||
Root `npm ci --ignore-scripts --no-audit --no-fund` passed (128 packages).
|
||||
No tests run: Sage holds the slot until CLEAR. Test-driven development applies;
|
||||
write tests before fixes and observe the first RED only after CLEAR.
|
||||
|
||||
### Integration points (line numbers at base)
|
||||
|
||||
- `packages/model-proxy/src/client.mjs`: management path :6; getKeys/putKeys :63-64;
|
||||
trusted loader :68. GET/PUT already redact receipts and pin numeric loopback.
|
||||
- `packages/model-proxy/src/settings.mjs`: instance tag :77 (sha256(configDir), first 12 hex),
|
||||
sentinel :78. Reuse that tag, never inspect proxy/config.yaml.
|
||||
- `packages/bus/src/credentials.mjs`: issue :291, revoke :383, remove :400,
|
||||
useFor :417, status :474. Store root comes from runtime :178.
|
||||
- `packages/bus/src/runtime.mjs`: writer lock precedes management client :179-182;
|
||||
Credentials construction :201; revoke :213-240; instance writer :243;
|
||||
settings callback :390-415; credentialStatus :633; returned modelProxy :686.
|
||||
- `packages/business/src/instance-settings.mjs`: hire :354-383; fire :386-405;
|
||||
commit :431-466. `packages/business/src/organization.mjs`: writer :226,
|
||||
beforeWrite :252-255, lock release :170-175.
|
||||
- `packages/cli/src/launcher.mjs`: common launch :439, credentialStatus :453,
|
||||
spawn :595, binding :616, registry :622, exit :635 onward.
|
||||
- `packages/bus/src/process.mjs`: trusted launch IPC allowlist :5 and dispatch :78.
|
||||
- Shared predicate already exported: model-proxy/src/common.mjs :32;
|
||||
missing callers broker.mjs :4827 and cli/src/model-proxy.mjs :57.
|
||||
|
||||
### Planned behavior
|
||||
|
||||
Store: `<configDir>/secrets/agents/<business>/<agent>/model`, mode 0600;
|
||||
all directories 0700. Implicit model kind in proxy mode only, no role-file or
|
||||
business credentials.model reference. Persist key id, issue time, rotation due
|
||||
and retiring-key metadata durably so restart cannot forget an overlap window.
|
||||
Values stay in private credential storage; public status and receipts expose ids only.
|
||||
Key format is msk-<sha256(configDir)[0:12]>-<32 random bytes base64url>.
|
||||
|
||||
Management API for all mutations: `GET /v8/management/config/access/api-keys`,
|
||||
`PUT /v8/management/config/access/api-keys` with a JSON string array, confirming GET.
|
||||
Never DELETE. Serialize read/modify/write operations inside the one locked broker;
|
||||
Store's data-root writer lock precedes creation of the management client.
|
||||
|
||||
Issue: require own sentinel, GET/add/PUT/confirm GET before any credential-file write;
|
||||
any failure => proxy-key-issue-failed, no issued file. Used at hire and first launch.
|
||||
Rotate: same sequence adding new beside old, then durable credential update;
|
||||
new launches take new key, runs retain key ids. Remove old when its runs exit or
|
||||
120-minute grace expires, stopping surviving runs first. Default rotateBy 30 days.
|
||||
Revoke: GET/filter exact key/PUT/confirm GET absence, then poll
|
||||
`GET /v1/models` with removed key until 401. Proposed bound: 5 seconds total,
|
||||
25 ms spacing, each HTTP request bounded by remaining deadline. Neither management
|
||||
absence alone nor one successful PUT proves revocation. Delete credential file only
|
||||
after 401; failure => credential-revoke-failed and no delisting.
|
||||
Reconcile: initial GET once, require own sentinel, add recorded missing keys and
|
||||
remove own-prefixed unknown keys in one PUT, confirm GET; poll each removed key to
|
||||
401 with the same deadline. Preserve sentinel and every foreign/manual key.
|
||||
Receipt contains added/removed key ids only. Reconcile failure blocks launches.
|
||||
|
||||
Preflight: exactly one `GET /v1/models` using agent key per launch; 401 =>
|
||||
proxy-key-refused, connection failure => proxy-down; missing own sentinel => proxy-open.
|
||||
Other HTTP failures must refuse too (code to be fixed before tests are finalized).
|
||||
No first output within default 120 seconds => stop and log proxy-stall.
|
||||
Trusted harness answer keeps `{ baseUrl, key }`; P3 owns harness config/env.
|
||||
|
||||
Important integration constraint: existing organization beforeWrite is synchronous.
|
||||
Preserve the revision check, validation and writer lock across HTTP confirmation;
|
||||
never move revoke ahead of validation or release the lock before 401. Investigating
|
||||
an async-capable writer path that leaves existing synchronous callers unchanged.
|
||||
|
||||
### P1 deferrals
|
||||
|
||||
N2: choose outside-agent guard for fetch, before config/network/disk access, matching
|
||||
init's refusalInsideAgent + tester rejection. Fetch does not need a management
|
||||
credential prompt or bus human proof: its sole effects are verified pinned download
|
||||
and install. This prevents managed agents causing unasked fetches.
|
||||
R2-N1: import validManagementKey into broker and CLI, replacing local predicates;
|
||||
retain initializeProxy and ManagementClient uses. Tests at each caller reject
|
||||
Latin-1, leading/trailing/internal spaces before callbacks/writes and never echo input.
|
||||
|
||||
Item 7: 8.0.42 rejects per-key model objects with 422 (P1 probe, carried by Sage).
|
||||
No per-key model restriction exists in the tested API; bundle model choice remains
|
||||
the guard. No policy expansion or invented model filtering.
|
||||
|
||||
### Acceptance tests and named mutants
|
||||
|
||||
Fake loopback management/client server in fresh temp roots; never real instance
|
||||
config, management key, dataRoot/proxy or port 8317. Generated test key values never
|
||||
appear in assertion diffs, logs or snapshots. Literal fake credentials <24 characters.
|
||||
|
||||
- model store mode, private directories, implicit registration, status ids/issue/state,
|
||||
reload persistence, no-proxy regression, no credential.model role requirement;
|
||||
- issue exact GET/PUT/GET order, no file on each failure, hire and first-launch paths;
|
||||
- rotate overlap/new-session selection, old-run exit cleanup, grace deadline stop,
|
||||
restart during grace and human-only rotation setting;
|
||||
- revoke confirming GET and delayed 401 (first response 200), deadline failure keeps
|
||||
record and fire membership, successful fire observes 401 before delisting;
|
||||
- reconcile missing recorded key, own unknown removal, foreign/manual preservation,
|
||||
sentinel preserved, proxy-open test, receipts contain ids and no values;
|
||||
- preflight accepted key, proxy-key-refused (401), proxy-down (connection failure),
|
||||
unexpected status fails closed; first output cancels stall, silence stops proxy-stall;
|
||||
- fetch outside-agent/tester guard before any fetch; predicate test at broker, CLI and
|
||||
model-proxy initializer; secret-free errors/status/receipts across failure paths.
|
||||
|
||||
Required mutants: (1) revoke skips confirming GET; (2) reconcile removes foreign key;
|
||||
(3) preflight treats 401 as success. Also test delayed revocation to kill
|
||||
management-list-only confirmation, fire-before-revoke, wrong agent key and leaked values.
|
||||
|
||||
Whole-file run list will be computed from parsed imports (including transitive local
|
||||
imports and literal dynamic imports), reported to Sage before any run, with explicit
|
||||
exclusion of packages/cli/tests/case1-accept.test.mjs unless Mos approves. No text grep
|
||||
will select files. Exact list pending final touched-module set.
|
||||
|
||||
## 2026-10-24 — Sage accepted design, writer hold
|
||||
|
||||
Sage sage-4345 accepted the design. Poll constants are **REVOKE_TIMEOUT_MS = 5000**
|
||||
and **REVOKE_POLL_MS = 25**. Tests will pin the observable 5-second total bound and
|
||||
25ms interval; per-request timeout cannot extend the total bound.
|
||||
Hold: no edits to organization.mjs until Sage sees the options below.
|
||||
|
||||
### Writer options
|
||||
|
||||
A. Make existing write/withLock asynchronous throughout. Reject: touches every
|
||||
synchronous caller and changes their return contract. Direct static importers,
|
||||
parsed with vm.SourceTextModule.dependencySpecifiers (not grep):
|
||||
`bus/src/runtime.mjs`, `business/src/instance-settings.mjs`,
|
||||
`business/src/launch-settings.mjs`, `business/src/plugin-settings.mjs`, plus tests
|
||||
`bus/tests/citation-guard`, `business/tests/library-projects-protected`,
|
||||
`business/tests/lifecycle-settings`, `business/tests/plugin-settings`,
|
||||
`business/tests/reply-limits`. Dynamic-import callers also include
|
||||
bus/tests/limits and business/tests/launch-settings. This is unnecessarily broad.
|
||||
|
||||
B. Add an optional asynchronous write entry point sharing validation/publication
|
||||
and retaining writer lock through HTTP; old write contract stays synchronous.
|
||||
Correct ordering but edits shared writer internals, so a larger review and regression
|
||||
surface even when existing caller code remains unchanged. Slow HTTP holds the lock
|
||||
for up to 5s per bounded revoke; failure keeps revision and model file unchanged.
|
||||
|
||||
C. Recommended: prepare/revoke HTTP outside the existing writer lock, leave
|
||||
organization.mjs and every existing synchronous caller unchanged. Add proxy-mode
|
||||
preparation only in bus orchestration. Capture exact organization revision and
|
||||
agent/model generation after current eligibility checks. Mark target unavailable
|
||||
for launches while preparation runs. Remote revoke confirms GET and 401 but does
|
||||
NOT remove the credential record yet. Then call the current synchronous writer;
|
||||
its existing beforeWrite callback checks that exact captured revision and model
|
||||
generation, then removes the private file synchronously and performs existing
|
||||
credential revokes. The ordinary writer rechecks revision under lock before
|
||||
beforeWrite as today. On slow/failed HTTP: revision/membership/store are unchanged,
|
||||
launches held. On revision change: refuse revision-conflict, keep private record,
|
||||
remote key may already be revoked; receipt marks remote revocation, and retry can
|
||||
complete or reconciliation can restore it only under an explicit lifecycle-state
|
||||
rule. This side effect must be visible; never silently report a clean no-op.
|
||||
|
||||
C's tradeoff: remote revocation can precede a later revision-conflict refusal;
|
||||
local file removal and delisting still cannot precede proof of remote refusal.
|
||||
Need to ensure reconciliation cannot restore a key during pending fire and that a
|
||||
restart resumes/refuses pending revocation rather than undoing it. A durable
|
||||
revocation intent keyed by business/agent/generation resolves that window.
|
||||
Existing synchronous APIs stay byte-for-byte unchanged. Only bus proxy-mode
|
||||
branches become promise-aware, including chief lifecycle and its grant job path.
|
||||
No writer edit made. Await Sage's choice before implementing the integration.
|
||||
|
||||
Prepared RED tests (unrun): new CLI model-proxy-guards.test.mjs and added broker
|
||||
predicate cases in bus/tests/model-proxy.test.mjs. These expose old behavior before
|
||||
any implementation change. Existing initializer tests cover the third caller.
|
||||
|
||||
## 2026-10-24 — writer option C approved (sage-4354)
|
||||
|
||||
Writer and existing synchronous callers remain unchanged. Revoke intent belongs to
|
||||
model credential's metadata status only: state `revoking`, key id and time, never
|
||||
value in status/receipt. No new store, file kind or public schema. The model store
|
||||
file may hold its own private model record (value + metadata); only sanitized
|
||||
metadata is exposed. No extra intent file.
|
||||
|
||||
Boot reconcile must finish `revoking` with confirm GET and poll-to-401, never PUT
|
||||
that key back. Add named mutant: **reconcile ignores revoking**.
|
||||
Launch hold refusal: **proxy-key-revoking**, before any preflight HTTP call.
|
||||
Revision race after successful remote revoke: **proxy-revoke-revision-conflict**;
|
||||
receipt carries keyIds, remoteState `revoked`, localState `kept`. Local metadata
|
||||
remains revoking; next reconcile completes cleanup, without restoring access.
|
||||
Each code has its own test. Call this section out explicitly to Boyd.
|
||||
|
||||
### Initial RED run request (magica-331-0005, unrun)
|
||||
|
||||
Parsed static imports with `vm.SourceTextModule.dependencySpecifiers`:
|
||||
|
||||
- packages/model-proxy/tests/client-probe.test.mjs → ../src/client.mjs
|
||||
- packages/bus/tests/model-credential-store.test.mjs → ../src/credentials.mjs
|
||||
- packages/cli/tests/model-proxy-guards.test.mjs → ../src/model-proxy.mjs, ./helpers.mjs
|
||||
- packages/bus/tests/model-proxy.test.mjs → ../src/broker.mjs, ../src/store.mjs
|
||||
|
||||
These four whole files only, serially. Missing probe/model-store APIs, missing fetch
|
||||
guard and divergent CLI/broker predicates should cause new-case RED. Old-case RED
|
||||
must be reported before changing anything. case1-accept.test.mjs excluded.
|
||||
Request sent before execution; waiting for CLEAR, not yet granted.
|
||||
|
||||
## RED run — sage-4374, 2026-10-24 04:11–04:14Z
|
||||
|
||||
Four whole files, serial absolute paths, short TMPDIR (red-tmpdir.txt), each nice19.
|
||||
- client-probe: rc1, 0 pass / 6 fail: probeKey is absent in every new case
|
||||
(200,401,503,connection,timeout,redirect).
|
||||
- model-credential-store: rc1, 1 pass / 4 fail: implicit status absent; writeModel
|
||||
absent for persistence, revoking persistence and wrong-mode checks. Nonproxy passes.
|
||||
- CLI model-proxy-guards: rc1, 1 pass / 6 fail: agent fetch reaches config rather than
|
||||
human-required (two cases); four invalid management-key inputs accepted. Tester passes.
|
||||
- bus model-proxy: rc1, 8 pass / 4 fail: every original test passes; four new bad-key
|
||||
cases fail missing synchronous rejection. Their unconsumed init promises outlive
|
||||
teardown and cause storage-unavailable rejections. Consume those promises in cleanup
|
||||
while retaining the synchronous-rejection assertion before the GREEN run.
|
||||
|
||||
No old-test failures, no skips, no reruns. Logs red-*.log. Slot returned in
|
||||
magica-331-0007. GREEN/mutants require separate CLEAR.
|
||||
Correction: initial probe run started immediately after a busy check showing queue
|
||||
writes, contrary to wait instruction. Paused remaining runs and waited for clear
|
||||
before continuing; reported promptly in magica-331-0006. No live proxy access.
|
||||
No production implementation was written before these RED results.
|
||||
|
||||
## 2026-10-24 — integration rulings and candidate implementation
|
||||
|
||||
Sage sage-4381 explicitly authorizes candidate commits and pushes on magica/row331
|
||||
before GREEN: the seat branch is not a landing. GREEN and mutants remain held.
|
||||
Only syntax parsing and git diff whitespace checks have run since RED; no test rerun.
|
||||
New lifecycle/settings/stop/marker tests were authored before their corresponding
|
||||
integration changes; their first execution is held with GREEN per the slot ruling.
|
||||
|
||||
Sage sage-4388 approved:
|
||||
- stopSession only; MODEL_STOP_TIMEOUT_MS = 5000, identity checked against agent,
|
||||
run, key id, pid/start time before signalling, then stopSession verifies the runner.
|
||||
Failed/timed-out stop => proxy-stop-failed and keeps old key + retiring record;
|
||||
the next sweep retries. Proxy receipt reason is proxy-rotation or proxy-stall,
|
||||
with key ids. The session's normal launcher close/endRun path still records
|
||||
session.ended and the normal exit/signal result; no run evidence is rewritten.
|
||||
- proxy.json optional rotateGraceMinutes (1..1440) and firstOutputSeconds (1..3600),
|
||||
absent defaults 120/120. Own refusal codes: proxy-rotate-grace-invalid and
|
||||
proxy-first-output-invalid. Human-only init flags --rotate-grace-minutes and
|
||||
--first-output-seconds. Existing sidecars keep their two-field shape.
|
||||
- No per-agent override verb/setter in P2. Thirty-day default only. DEFERRED entry
|
||||
'Row 331: human-only per-agent model rotation override — P7' binds the follow-up.
|
||||
- Marker file `<run>/proxy-first-output`: fixed `output` token + newline, mode0600,
|
||||
emitted from runner.mjs's child.stdout data listener through first-output.mjs.
|
||||
No stdout contents or key values enter the marker or proxy receipts. Collection
|
||||
of stdout for RESULT is unchanged. No P3 env/config change.
|
||||
|
||||
P3 handoff contract: trusted launcher IPC op `modelPreflight` takes business/role
|
||||
and returns exactly `{baseUrl,key}` (null outside proxy mode). The launcher passes
|
||||
that object as `proxy` into buildBundle; P3 owns consuming it. modelKeyId is separate
|
||||
metadata in the host's session registry/bind message, stripped before Broker.bindLaunch
|
||||
so the broker's public launch-record schema is unchanged. No socket verb returns keys.
|
||||
|
||||
Organization option C is implemented as modelOrganization's adapter in model-keys.mjs.
|
||||
Existing business/src/organization.mjs and business settings callers are unmodified.
|
||||
The adapter supplies captured revision to the existing locked synchronous write.
|
||||
Runtime's revoke callback requires matching pending intent and revision before local
|
||||
cleanup. A race after remote success produces proxy-revoke-revision-conflict and keeps
|
||||
revoking metadata, for next boot reconcile. Generic Credentials.remove cannot bypass
|
||||
remote confirmation for a live model record.
|
||||
|
||||
### Candidate validation plan (awaiting separate CLEAR)
|
||||
|
||||
Root npm ci and packages/webui npm ci both ran in this worktree (128 and 2 packages,
|
||||
respectively); canonical node_modules was not read/linked/modified. Static syntax
|
||||
checks parsed all 26 changed JavaScript modules; git diff --check and bash -n passed.
|
||||
These are static checks, not GREEN evidence. No tests have run since the RED slot.
|
||||
|
||||
`import-list.mjs` generated green-files.txt: 262 whole files selected from actual
|
||||
parsed imports/transitive edges of 15 changed production modules, plus changed tests.
|
||||
It uses Node's module parser for static imports and tokenizes literal dynamic imports
|
||||
outside comments/string/template/regex text. No fixture-text grep. The JSON manifest
|
||||
records edges and has zero parse failures. Explicit exclusions: queue and
|
||||
packages/cli/tests/case1-accept.test.mjs (mos-1612; no Mos approval). The additional
|
||||
shell run is scripts/test-config.sh because mosaic-config.mjs changed; timeout -k10
|
||||
1800 and absolute nice19. green.sh executes whole files serially, one short TMPDIR
|
||||
per run with fixture HOME/config, logs the observed shell nice, and stops at first RED.
|
||||
|
||||
Mutants (mutants.mjs; separate CLEAR only), each with the entire model-keys.test file:
|
||||
1. revoke skips confirming GET: mutate removeKeys only, retain PUT + client 401 poll;
|
||||
the delayed-revoke test must fail its GET/PUT/GET ordering assertion.
|
||||
2. reconcile removes foreign key: remove the own-prefix condition; the foreign/manual
|
||||
preservation test must fail.
|
||||
3. preflight treats401 as success: bypass401 refusal; the HTTP401 preflight test must fail.
|
||||
4. reconcile ignores revoking (sage-4354): classify revoking records as retained and
|
||||
omit their cleanup; the restart/revoking test must fail.
|
||||
A kill requires nonzero status AND the corresponding named test's not-ok TAP record;
|
||||
loader/syntax failures cannot count as kills. Original production bytes restore in
|
||||
finally. These mutants have not run.
|
||||
|
||||
First-output emission site: packages/harness/src/runner.mjs:351 calls the marker in
|
||||
its existing stdout listener, before the unchanged RESULT accumulator. File content
|
||||
is fixed by packages/harness/src/first-output.mjs, never by the stdout chunk.
|
||||
|
||||
Remaining gates: GREEN, four mutants, Boyd first review, Dewey landing review,
|
||||
Sage's full landing gate. No landing/completion claim.
|
||||
|
||||
### Evidence whitespace correction
|
||||
|
||||
The first candidate commit f83206473 preserved TAP formatter whitespace. The staged
|
||||
whitespace check warned about space-only diagnostic lines in three RED logs; the
|
||||
commit proceeded because that shell was not fail-fast. Normalize trailing spaces
|
||||
only, without changing the recorded tests/results. No test rerun. Original raw bytes
|
||||
remain in f83206473; hashes below make the presentation change reviewable.
|
||||
|
||||
- red-bus-proxy.log: original SHA256 b8eacf3c025c63289702a8a2a5630f694f74e9cc8f02dfa04b983e22f40466ae; normalized SHA256 b8eacf3c025c63289702a8a2a5630f694f74e9cc8f02dfa04b983e22f40466ae
|
||||
- red-cli-guards.log: original SHA256 45d6f847556259d00511904dbe933272adcae251e6d0bf539d19eb03f93bfcad; normalized SHA256 9645a7c408249ecbacbfaa7d631a5b98c380916a2aa37985061b27a6ed05e1b0
|
||||
- red-client-probe.log: original SHA256 3c3bc989a00e2d3860a44712b31a93d80cf8395c9260777f5a94c09ff45f42e1; normalized SHA256 e72318cbbc3fb0803770e4bb8e8ded9725fb4c4ebee6ea4bec365bceaa68250a
|
||||
- red-model-store.log: original SHA256 5d46867eacee5ed014ed71cc45120de9c79167fc08c8583276eaec5fc5e5bfcc; normalized SHA256 1797d611f72f417612e6b7b24f3119ae0f079dc0b9a12acba087ab5c8b2cec59
|
||||
|
||||
### Base comparison and direct handoffs — sage-4409
|
||||
|
||||
Compared `git diff --name-only 78b69e1e6 775bbe236` against all 262 GREEN files and
|
||||
all 26 changed .mjs modules. Both intersections are empty (base-overlap.json).
|
||||
BUILD-LOG.md and docs/SESSIONS.md overlap only as append-only tracking files;
|
||||
no test/source rebase trigger under Sage's condition. Published implementation
|
||||
candidate remains fb484acb7. This follow-up records metadata only.
|
||||
|
||||
Fethry (14f80e3c-ace7-4793-a577-e1275ca09b0b) received the exact trusted P3 interface
|
||||
in magica-331-0010. Boyd (48a3ae38-83aa-4d3f-9064-64e911ec4547) received the build
|
||||
notice with option C review flag in magica-331-0011. Sage copied in -0012.
|
||||
Both notices say GREEN/mutants are pending and do not request a test slot.
|
||||
Sage ordered 331 GREEN/mutants after Bubba's 288 and before198; awaiting CLEAR.
|
||||
|
||||
## 2026-10-25 — GREEN attempt stopped on new fixture RED (sage-4420)
|
||||
|
||||
Candidate a86a881e1. Ran approved whole files serially, each with a fresh short TMPDIR
|
||||
and a nice19 wrapper, no test/source/timeout edits and no reruns. Driver stopped on
|
||||
file55, model-keys.test.mjs: 30 tests,24pass,6fail,0skip/cancel,rc1. All54 preceding
|
||||
files passed, including the seven model-credential-store cases. Aggregate attempted:
|
||||
592tests/586pass/6fail/0skip/0cancel. The remaining207 files, scripts/test-config.sh,
|
||||
all4 mutants and restored whole-file positive controls did not run.
|
||||
|
||||
All6 failures share one fixture defect: bootBusiness never creates `<temp>/data`
|
||||
before startBroker; Store at store.mjs:163 refuses its missing dataRoot with ENOENT.
|
||||
Affected new cases: fire-before-delist, failed-PUT fire, revision-race fire, hire issue,
|
||||
failed-issue hire and newly-hired preflight. None reached the intended real-bus
|
||||
behavior. The24 controller cases passed; no old test failed in the attempted prefix.
|
||||
Recommended correction (not made): create that fixture data directory at0700 before
|
||||
startBroker, preserving all assertions. Await Sage before any test edit or rerun.
|
||||
|
||||
Reported first RED in magica-331-0014. Slot returned in -0015 at05:10:48Z, with no
|
||||
gate process remaining; Sage's288 landing gate can proceed. Do not move in-review
|
||||
on this RED result. Complete per-run rc/counts/nice/hash receipts are green-receipts.json.
|
||||
Raw text logs are retained off-repo under /mnt/storage/scratch/tmp/JT6SN5/ with SHA256
|
||||
pins in that receipt. Archiving copies verified bytes across filesystems before
|
||||
removing the untracked worktree copies; it did not change any log contents.
|
||||
|
||||
## 2026-10-25 — r2 fixture-only correction authorized (sage-4422)
|
||||
|
||||
Added exactly `fs.mkdirSync(join(f.root, 'data'), { mode: 0o700 })` inside
|
||||
bootBusiness before startBroker in packages/bus/tests/model-keys.test.mjs. No
|
||||
production code, assertion, timeout or other test file changed. No test run.
|
||||
|
||||
There was no build-time whole-file run of model-keys.test.mjs. The original RED
|
||||
slot (sage-4374) covered only client-probe, model-credential-store, CLI guards and
|
||||
bus model-proxy; this integration file was authored afterward, with execution
|
||||
held for separate GREEN CLEAR. Its first whole-file run was file55 of sage-4420
|
||||
on a86a881e1:24 controller cases passed;6 integration fixtures failed in Store's
|
||||
dataRoot lstat before their intended assertions. That explains why the six errors
|
||||
were absent from the build-time evidence. It was not a prior passing run.
|
||||
|
||||
No intended fail-closed refusal path is removed by this correction. None of the
|
||||
six cases asserted ENOENT or deliberately omitted dataRoot: each failed during
|
||||
shared fixture setup. Store's missing-directory refusal is unchanged production
|
||||
code. The credential-revoke-failed, proxy-revoke-revision-conflict and
|
||||
proxy-key-issue-failed cases retain their original assertions and remote fault
|
||||
injection. They can first reach those paths in r2. The successful fire/hire and
|
||||
newly-hired-preflight assertions also first become reachable in r2; another RED
|
||||
is possible. No separate test by a new refusal code applies to this fixture
|
||||
precondition repair (no refusal code or production guard was added/changed).
|
||||
|
||||
r2 request: restart full GREEN from file1, all262 whole files, test-config.sh,
|
||||
all4 mutants, each restored with a whole-file positive control; stop first RED.
|
||||
No tests until Sage CLEAR after288 landing and Poe's330 focused block.
|
||||
Frozen run-list SHA256:512d0c1c8823ff32a1c3308bd08950595e19ac87668fef3569be9c7aac3db674.
|
||||
Frozen import-manifest SHA256:a026f5637caf13bfac121b3fd6637e7052d1f90fa1a4114a94e59edf60344875.
|
||||
The import edges and262 file list are unchanged by this mkdir; case1-accept remains excluded.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"from": "78b69e1e6",
|
||||
"to": "775bbe236",
|
||||
"greenFiles": [],
|
||||
"changedModules": [],
|
||||
"ownDocs": [
|
||||
"BUILD-LOG.md",
|
||||
"docs/SESSIONS.md"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
packages/bus/tests/access-revoke.test.mjs
|
||||
packages/bus/tests/agents.test.mjs
|
||||
packages/bus/tests/approval-binding.test.mjs
|
||||
packages/bus/tests/broker.test.mjs
|
||||
packages/bus/tests/business.test.mjs
|
||||
packages/bus/tests/channel-proof.test.mjs
|
||||
packages/bus/tests/chief-of-staff.test.mjs
|
||||
packages/bus/tests/citation-availability.test.mjs
|
||||
packages/bus/tests/citation-guard.test.mjs
|
||||
packages/bus/tests/citation-runtime.test.mjs
|
||||
packages/bus/tests/comment-cursors.test.mjs
|
||||
packages/bus/tests/console-decide.test.mjs
|
||||
packages/bus/tests/creative.test.mjs
|
||||
packages/bus/tests/credential-claim.test.mjs
|
||||
packages/bus/tests/credential-status.test.mjs
|
||||
packages/bus/tests/credential-store.test.mjs
|
||||
packages/bus/tests/credential-verbs.test.mjs
|
||||
packages/bus/tests/credentials.test.mjs
|
||||
packages/bus/tests/csuite-messaging.test.mjs
|
||||
packages/bus/tests/daily-quota-runtime.test.mjs
|
||||
packages/bus/tests/decision-close.test.mjs
|
||||
packages/bus/tests/decision-route.test.mjs
|
||||
packages/bus/tests/decisions.test.mjs
|
||||
packages/bus/tests/end-launch.test.mjs
|
||||
packages/bus/tests/fire-revokes-grants.test.mjs
|
||||
packages/bus/tests/gitea-grant-docker.test.mjs
|
||||
packages/bus/tests/gitea-grant.test.mjs
|
||||
packages/bus/tests/grant-enroll.test.mjs
|
||||
packages/bus/tests/grant-executor.test.mjs
|
||||
packages/bus/tests/grant-host.test.mjs
|
||||
packages/bus/tests/grant-migration.test.mjs
|
||||
packages/bus/tests/grant-scope-runtime.test.mjs
|
||||
packages/bus/tests/grant-scope.test.mjs
|
||||
packages/bus/tests/hard-waits.test.mjs
|
||||
packages/bus/tests/host-run.test.mjs
|
||||
packages/bus/tests/human.test.mjs
|
||||
packages/bus/tests/instances-verb.test.mjs
|
||||
packages/bus/tests/land.test.mjs
|
||||
packages/bus/tests/launch-notice.test.mjs
|
||||
packages/bus/tests/launcher-released.test.mjs
|
||||
packages/bus/tests/library-credential.test.mjs
|
||||
packages/bus/tests/library-lookups.test.mjs
|
||||
packages/bus/tests/library-sync.test.mjs
|
||||
packages/bus/tests/library.test.mjs
|
||||
packages/bus/tests/lifecycle-revoke.test.mjs
|
||||
packages/bus/tests/lifecycle-verbs.test.mjs
|
||||
packages/bus/tests/limit-stop.test.mjs
|
||||
packages/bus/tests/limits-runtime.test.mjs
|
||||
packages/bus/tests/limits.test.mjs
|
||||
packages/bus/tests/message-citation.test.mjs
|
||||
packages/bus/tests/message-filter.test.mjs
|
||||
packages/bus/tests/messages.test.mjs
|
||||
packages/bus/tests/messaging.test.mjs
|
||||
packages/bus/tests/model-credential-store.test.mjs
|
||||
packages/bus/tests/model-keys.test.mjs
|
||||
packages/bus/tests/model-proxy.test.mjs
|
||||
packages/bus/tests/model-stop.test.mjs
|
||||
packages/bus/tests/names.test.mjs
|
||||
packages/bus/tests/peer-transport.test.mjs
|
||||
packages/bus/tests/peercred.test.mjs
|
||||
packages/bus/tests/plugins-verb.test.mjs
|
||||
packages/bus/tests/principal.test.mjs
|
||||
packages/bus/tests/process.test.mjs
|
||||
packages/bus/tests/processing-runtime.test.mjs
|
||||
packages/bus/tests/processing-timeout.test.mjs
|
||||
packages/bus/tests/processing.test.mjs
|
||||
packages/bus/tests/project-await.test.mjs
|
||||
packages/bus/tests/project-files.test.mjs
|
||||
packages/bus/tests/project-runtime.test.mjs
|
||||
packages/bus/tests/publish.test.mjs
|
||||
packages/bus/tests/reload.test.mjs
|
||||
packages/bus/tests/replies.test.mjs
|
||||
packages/bus/tests/requirements.test.mjs
|
||||
packages/bus/tests/review.test.mjs
|
||||
packages/bus/tests/revoke-window.test.mjs
|
||||
packages/bus/tests/room-check.test.mjs
|
||||
packages/bus/tests/rooms.test.mjs
|
||||
packages/bus/tests/routes-verb.test.mjs
|
||||
packages/bus/tests/session-clear.test.mjs
|
||||
packages/bus/tests/session-compactions.test.mjs
|
||||
packages/bus/tests/sessions.test.mjs
|
||||
packages/bus/tests/single-use.test.mjs
|
||||
packages/bus/tests/spec-review.test.mjs
|
||||
packages/bus/tests/spec-socket.test.mjs
|
||||
packages/bus/tests/spec.test.mjs
|
||||
packages/bus/tests/staff-request.test.mjs
|
||||
packages/bus/tests/state.test.mjs
|
||||
packages/bus/tests/task-page.test.mjs
|
||||
packages/bus/tests/task-read.test.mjs
|
||||
packages/bus/tests/tasks.test.mjs
|
||||
packages/bus/tests/tester-threads.test.mjs
|
||||
packages/bus/tests/tester-writes.test.mjs
|
||||
packages/bus/tests/tester.test.mjs
|
||||
packages/bus/tests/thread-claims.test.mjs
|
||||
packages/bus/tests/thread-migration.test.mjs
|
||||
packages/bus/tests/thread-shares.test.mjs
|
||||
packages/bus/tests/threads.test.mjs
|
||||
packages/bus/tests/transport.test.mjs
|
||||
packages/bus/tests/usage-broker.test.mjs
|
||||
packages/bus/tests/usage-runtime.test.mjs
|
||||
packages/bus/tests/user-principal.test.mjs
|
||||
packages/bus/tests/viewer.test.mjs
|
||||
packages/bus/tests/web.test.mjs
|
||||
packages/bus/tests/workflow-roles.test.mjs
|
||||
packages/business/tests/case1-business-file.test.mjs
|
||||
packages/cli/tests/access-boot.test.mjs
|
||||
packages/cli/tests/access.test.mjs
|
||||
packages/cli/tests/app.test.mjs
|
||||
packages/cli/tests/boot-status.test.mjs
|
||||
packages/cli/tests/case1-hostcheck.test.mjs
|
||||
packages/cli/tests/case2-install.test.mjs
|
||||
packages/cli/tests/case2-persona-import.test.mjs
|
||||
packages/cli/tests/case2-unit.test.mjs
|
||||
packages/cli/tests/channel-cli.test.mjs
|
||||
packages/cli/tests/channel-recovery.test.mjs
|
||||
packages/cli/tests/cli.test.mjs
|
||||
packages/cli/tests/compactions-launch.test.mjs
|
||||
packages/cli/tests/config.test.mjs
|
||||
packages/cli/tests/credentials.test.mjs
|
||||
packages/cli/tests/federation-acceptance.test.mjs
|
||||
packages/cli/tests/follow.test.mjs
|
||||
packages/cli/tests/git-socket.test.mjs
|
||||
packages/cli/tests/grants-scope.test.mjs
|
||||
packages/cli/tests/grants.test.mjs
|
||||
packages/cli/tests/host.test.mjs
|
||||
packages/cli/tests/hosts.test.mjs
|
||||
packages/cli/tests/idle-stop-race.test.mjs
|
||||
packages/cli/tests/instances.test.mjs
|
||||
packages/cli/tests/launch-account.test.mjs
|
||||
packages/cli/tests/launch-limit-checks.test.mjs
|
||||
packages/cli/tests/launch-limits.test.mjs
|
||||
packages/cli/tests/launch-routes.test.mjs
|
||||
packages/cli/tests/launcher.test.mjs
|
||||
packages/cli/tests/lifecycle.test.mjs
|
||||
packages/cli/tests/limits-tokens.test.mjs
|
||||
packages/cli/tests/limits.test.mjs
|
||||
packages/cli/tests/mark-read.test.mjs
|
||||
packages/cli/tests/model-proxy-guards.test.mjs
|
||||
packages/cli/tests/model-proxy.test.mjs
|
||||
packages/cli/tests/model-stall.test.mjs
|
||||
packages/cli/tests/names.test.mjs
|
||||
packages/cli/tests/notifier.test.mjs
|
||||
packages/cli/tests/optional-tracker.test.mjs
|
||||
packages/cli/tests/other-tree.test.mjs
|
||||
packages/cli/tests/processing-cli.test.mjs
|
||||
packages/cli/tests/project-name-repo.test.mjs
|
||||
packages/cli/tests/project.test.mjs
|
||||
packages/cli/tests/publish-socket.test.mjs
|
||||
packages/cli/tests/reply-limits.test.mjs
|
||||
packages/cli/tests/revoke-window.test.mjs
|
||||
packages/cli/tests/session-clear.test.mjs
|
||||
packages/cli/tests/session-cli.test.mjs
|
||||
packages/cli/tests/session-launch.test.mjs
|
||||
packages/cli/tests/stack-copy.test.mjs
|
||||
packages/cli/tests/stack-instances.test.mjs
|
||||
packages/cli/tests/tester-http.test.mjs
|
||||
packages/cli/tests/tester-reads.test.mjs
|
||||
packages/cli/tests/tester-threads.test.mjs
|
||||
packages/cli/tests/tester.test.mjs
|
||||
packages/cli/tests/thread-cli.test.mjs
|
||||
packages/cli/tests/thread-launch.test.mjs
|
||||
packages/cli/tests/thread-revoke.test.mjs
|
||||
packages/cli/tests/trackers-boot.test.mjs
|
||||
packages/cli/tests/transport.test.mjs
|
||||
packages/cli/tests/usage.test.mjs
|
||||
packages/cli/tests/verbs.test.mjs
|
||||
packages/harness/tests/citation-runner.test.mjs
|
||||
packages/harness/tests/claude-session.test.mjs
|
||||
packages/harness/tests/creative.test.mjs
|
||||
packages/harness/tests/first-output.test.mjs
|
||||
packages/harness/tests/pi-session.test.mjs
|
||||
packages/harness/tests/raise-domain.test.mjs
|
||||
packages/harness/tests/runner.test.mjs
|
||||
packages/harness/tests/skills.test.mjs
|
||||
packages/harness/tests/spec-tools.test.mjs
|
||||
packages/harness/tests/tools.test.mjs
|
||||
packages/model-proxy/tests/client-probe.test.mjs
|
||||
packages/model-proxy/tests/client.test.mjs
|
||||
packages/model-proxy/tests/config-unit.test.mjs
|
||||
packages/model-proxy/tests/fetch.test.mjs
|
||||
packages/model-proxy/tests/management-key-encoding.test.mjs
|
||||
packages/model-proxy/tests/p2-settings.test.mjs
|
||||
packages/model-proxy/tests/runner.test.mjs
|
||||
packages/model-proxy/tests/settings.test.mjs
|
||||
packages/processing-control/tests/task.test.mjs
|
||||
packages/seat/tests/sandbox.test.mjs
|
||||
packages/seat/tests/session.test.mjs
|
||||
packages/tasks/tests/adapter.test.mjs
|
||||
packages/tasks/tests/comment-restart.test.mjs
|
||||
packages/tasks/tests/fields.test.mjs
|
||||
packages/tasks/tests/processing.test.mjs
|
||||
packages/tasks/tests/shapes.test.mjs
|
||||
packages/tasks/tests/startup.test.mjs
|
||||
packages/tasks/tests/sync.test.mjs
|
||||
packages/tasks/tests/tree.test.mjs
|
||||
packages/tasks/tests/verbs.test.mjs
|
||||
packages/webui/tests/agents-browser.test.mjs
|
||||
packages/webui/tests/agents.test.mjs
|
||||
packages/webui/tests/assistant.test.mjs
|
||||
packages/webui/tests/board-held.test.mjs
|
||||
packages/webui/tests/browser-edge.test.mjs
|
||||
packages/webui/tests/browser.test.mjs
|
||||
packages/webui/tests/bus-browser.test.mjs
|
||||
packages/webui/tests/bus.test.mjs
|
||||
packages/webui/tests/channel-chat.test.mjs
|
||||
packages/webui/tests/chat-browser.test.mjs
|
||||
packages/webui/tests/chat-instances.test.mjs
|
||||
packages/webui/tests/chat-replies-browser.test.mjs
|
||||
packages/webui/tests/chat-sessions-access.test.mjs
|
||||
packages/webui/tests/chat.test.mjs
|
||||
packages/webui/tests/conversation.test.mjs
|
||||
packages/webui/tests/decide-browser.test.mjs
|
||||
packages/webui/tests/decide-sso.test.mjs
|
||||
packages/webui/tests/decision-http.test.mjs
|
||||
packages/webui/tests/discord.test.mjs
|
||||
packages/webui/tests/docs-browser.test.mjs
|
||||
packages/webui/tests/editor-browser.test.mjs
|
||||
packages/webui/tests/editor.test.mjs
|
||||
packages/webui/tests/granted-user.test.mjs
|
||||
packages/webui/tests/history-return-flow.test.mjs
|
||||
packages/webui/tests/instances-browser.test.mjs
|
||||
packages/webui/tests/instances.test.mjs
|
||||
packages/webui/tests/library-cli.test.mjs
|
||||
packages/webui/tests/library-routes.test.mjs
|
||||
packages/webui/tests/library-status.test.mjs
|
||||
packages/webui/tests/limits-browser.test.mjs
|
||||
packages/webui/tests/limits-route.test.mjs
|
||||
packages/webui/tests/messages.test.mjs
|
||||
packages/webui/tests/pinned-copy.test.mjs
|
||||
packages/webui/tests/preview.test.mjs
|
||||
packages/webui/tests/processing-browser.test.mjs
|
||||
packages/webui/tests/processing.test.mjs
|
||||
packages/webui/tests/project-file.test.mjs
|
||||
packages/webui/tests/project-name-repo.test.mjs
|
||||
packages/webui/tests/proof-cli.test.mjs
|
||||
packages/webui/tests/proof.test.mjs
|
||||
packages/webui/tests/reads.test.mjs
|
||||
packages/webui/tests/relaunch.test.mjs
|
||||
packages/webui/tests/return-flow.test.mjs
|
||||
packages/webui/tests/room-browser.test.mjs
|
||||
packages/webui/tests/room-http.test.mjs
|
||||
packages/webui/tests/routes-browser.test.mjs
|
||||
packages/webui/tests/routes.test.mjs
|
||||
packages/webui/tests/send-browser.test.mjs
|
||||
packages/webui/tests/send.test.mjs
|
||||
packages/webui/tests/serve.test.mjs
|
||||
packages/webui/tests/settings-form.test.mjs
|
||||
packages/webui/tests/settings-write.test.mjs
|
||||
packages/webui/tests/shell.test.mjs
|
||||
packages/webui/tests/sso-browser.test.mjs
|
||||
packages/webui/tests/sso.test.mjs
|
||||
packages/webui/tests/tester-browser.test.mjs
|
||||
packages/webui/tests/tester-cli.test.mjs
|
||||
packages/webui/tests/tester-listener.test.mjs
|
||||
packages/webui/tests/tester-socket.test.mjs
|
||||
packages/webui/tests/thread-claims-browser.test.mjs
|
||||
packages/webui/tests/thread-claims.test.mjs
|
||||
packages/webui/tests/thread-sharing.test.mjs
|
||||
packages/webui/tests/usage-browser.test.mjs
|
||||
packages/webui/tests/usage.test.mjs
|
||||
packages/webui/tests/user-option-swap.test.mjs
|
||||
packages/webui/tests/views.test.mjs
|
||||
@@ -0,0 +1,929 @@
|
||||
{
|
||||
"candidate": "a86a881e1b2eb38bd0f1dd25fa094f1ea60df2ca",
|
||||
"clear": "sage-4420",
|
||||
"driverRc": 1,
|
||||
"runs": [
|
||||
{
|
||||
"file": "packages/bus/tests/access-revoke.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 10,
|
||||
"pass": 10,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_access-revoke.test.mjs.log",
|
||||
"sha256": "7185f6dd41807d8f9644ce60eb7c92d551d053e82ba7600ee57d3145df1a24a1",
|
||||
"bytes": 2680
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/agents.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 11,
|
||||
"pass": 11,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_agents.test.mjs.log",
|
||||
"sha256": "25e44548c0d06bdf3ee0d6fad285e2ca9b647f9f76e74a48953373fef0ad0b60",
|
||||
"bytes": 2726
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/approval-binding.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_approval-binding.test.mjs.log",
|
||||
"sha256": "ded96e9c324eba0ef99e8786c63dc11318948ee05ee89ce4d30eb5a73d11dd23",
|
||||
"bytes": 1728
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/broker.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 16,
|
||||
"pass": 16,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_broker.test.mjs.log",
|
||||
"sha256": "7df7961cd813cb4d6dcf58cde1cb646f4171ff10cea666ca9f387a35c048cbb0",
|
||||
"bytes": 4146
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/business.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 1,
|
||||
"pass": 1,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_business.test.mjs.log",
|
||||
"sha256": "a868ae6324a84660a3481dcfe6cb4baa0093c95385a6e3622b4a3c0d23f274bd",
|
||||
"bytes": 379
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/channel-proof.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 2,
|
||||
"pass": 2,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_channel-proof.test.mjs.log",
|
||||
"sha256": "42f6b4ef333e32f3a0cf7e4270a2725ab0841023c5c1708d3cb5d8ceb6dd5b34",
|
||||
"bytes": 665
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/chief-of-staff.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 42,
|
||||
"pass": 42,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_chief-of-staff.test.mjs.log",
|
||||
"sha256": "821cb17676c594edfc80be45f7a2de7ef561483d54f46d7567c0afc6820547e8",
|
||||
"bytes": 13022
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/citation-availability.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 3,
|
||||
"pass": 3,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_citation-availability.test.mjs.log",
|
||||
"sha256": "b916b86842f9813af41986b7ecc18d81ada8dd9e90e1966eb63d51bcebab10bd",
|
||||
"bytes": 838
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/citation-guard.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 13,
|
||||
"pass": 13,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_citation-guard.test.mjs.log",
|
||||
"sha256": "574e56ff2094b869b1b595a88a2ddf2a4076357808b530d8a50f05ea6cf8b6e0",
|
||||
"bytes": 3139
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/citation-runtime.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 1,
|
||||
"pass": 1,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_citation-runtime.test.mjs.log",
|
||||
"sha256": "7181a9160812df7ba580e7dd0daa1a7e56dd410d2dcc6b76643d9109a1de77bc",
|
||||
"bytes": 348
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/comment-cursors.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 2,
|
||||
"pass": 2,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_comment-cursors.test.mjs.log",
|
||||
"sha256": "0c23b7caf9b593f9081f09ad753faba419ff0812e3c99e0fe695ad8d3f15f86c",
|
||||
"bytes": 575
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/console-decide.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 12,
|
||||
"pass": 12,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_console-decide.test.mjs.log",
|
||||
"sha256": "66862154416e869dcc9651335ddc8c8a9c2b6703da6d602b76353a8a848f6d97",
|
||||
"bytes": 3057
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/creative.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 4,
|
||||
"pass": 4,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_creative.test.mjs.log",
|
||||
"sha256": "3ff582af06543dd057416c374a9f1f607028ccbae6cb8ba7d6c2c2b4557a8dae",
|
||||
"bytes": 1394
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/credential-claim.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 8,
|
||||
"pass": 8,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_credential-claim.test.mjs.log",
|
||||
"sha256": "c8061939ad10d2762466434e3bc223f1114b70e79bb2a0d0ba1c856bd6a1e029",
|
||||
"bytes": 1916
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/credential-status.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 4,
|
||||
"pass": 4,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_credential-status.test.mjs.log",
|
||||
"sha256": "27204c46e50b28ec847f5e17dd3ecbed6f55a200a37c7fd97a3606d3ab459523",
|
||||
"bytes": 1076
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/credential-store.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 12,
|
||||
"pass": 12,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_credential-store.test.mjs.log",
|
||||
"sha256": "5b51807f0dcc87fe16a172f2960a273fc7321c450ce4070d3d683975f5dd1528",
|
||||
"bytes": 2845
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/credential-verbs.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 2,
|
||||
"pass": 2,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_credential-verbs.test.mjs.log",
|
||||
"sha256": "059f81fc3d74a0628b6b92c2c2eaee7b73268c39a5ca4b412a53755dfe023ce4",
|
||||
"bytes": 701
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/credentials.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 5,
|
||||
"pass": 5,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_credentials.test.mjs.log",
|
||||
"sha256": "18041b027496a1a21bd8e849abba5c41e6c9b5073788cd105f3d0505461faf0c",
|
||||
"bytes": 1275
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/csuite-messaging.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 16,
|
||||
"pass": 16,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_csuite-messaging.test.mjs.log",
|
||||
"sha256": "cdc3a39827c6e5d47722b5b4b3fe440458280335c5a3c773cf8b664f067a95c9",
|
||||
"bytes": 4336
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/daily-quota-runtime.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 4,
|
||||
"pass": 4,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_daily-quota-runtime.test.mjs.log",
|
||||
"sha256": "7c235a488ec9fb43dc3dec36b8c9ef1638dfae0c726ac11552dc638d0afb961f",
|
||||
"bytes": 2132
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/decision-close.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 8,
|
||||
"pass": 8,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_decision-close.test.mjs.log",
|
||||
"sha256": "f5451dee1427c69c965bd062a90a2db382f5ae1241e9e06b375d33bddb712441",
|
||||
"bytes": 1970
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/decision-route.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 5,
|
||||
"pass": 5,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_decision-route.test.mjs.log",
|
||||
"sha256": "f47709376bbc68c82037bee60e7b58fa3d99d807341716fc4ecc958517babade",
|
||||
"bytes": 1184
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/decisions.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 7,
|
||||
"pass": 7,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_decisions.test.mjs.log",
|
||||
"sha256": "2e0f2cc696b3db69fec6341134fc6568e9767eb52712f7e7eaeb8fb8841a7022",
|
||||
"bytes": 1576
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/end-launch.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 7,
|
||||
"pass": 7,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_end-launch.test.mjs.log",
|
||||
"sha256": "3e03479aaad4bd7d7cd792bb2497d22e975cf664dc7c521818e133c2c347b1ad",
|
||||
"bytes": 1648
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/fire-revokes-grants.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 24,
|
||||
"pass": 24,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_fire-revokes-grants.test.mjs.log",
|
||||
"sha256": "289a308d5e7e2cd47ed7409f3ac670263712f33e430a4b508af85a21010db810",
|
||||
"bytes": 7270
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/gitea-grant-docker.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 2,
|
||||
"pass": 2,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_gitea-grant-docker.test.mjs.log",
|
||||
"sha256": "4aaf1b93ccb237997cc08cb4ccfe5e1513cfdc546a9780035ec38e19fc7888d0",
|
||||
"bytes": 1460
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/gitea-grant.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 66,
|
||||
"pass": 66,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_gitea-grant.test.mjs.log",
|
||||
"sha256": "efe0b05e956ebc132ea23371fb051e78901515710724482c3242c86ab60aa88c",
|
||||
"bytes": 16582
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/grant-enroll.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 24,
|
||||
"pass": 24,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_grant-enroll.test.mjs.log",
|
||||
"sha256": "47ee11ac91ce1ccb33c81c96cec299d67651d77f374d32823650d062d41e51c5",
|
||||
"bytes": 6364
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/grant-executor.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 42,
|
||||
"pass": 42,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_grant-executor.test.mjs.log",
|
||||
"sha256": "f99eabf74f2225504c93e9e1c604f553ab7f5429c45c1da38987bc52589db208",
|
||||
"bytes": 11755
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/grant-host.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 10,
|
||||
"pass": 10,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_grant-host.test.mjs.log",
|
||||
"sha256": "a852e12076fabea11eba97121036cbfb2d445a66fc75317db81f6f9a7330ac93",
|
||||
"bytes": 2850
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/grant-migration.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_grant-migration.test.mjs.log",
|
||||
"sha256": "9c6fc5c4e8c70171b6e14247c45f2bfd2bb36e3194b27b09e37e8b6d157e74ab",
|
||||
"bytes": 1730
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/grant-scope-runtime.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 1,
|
||||
"pass": 1,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_grant-scope-runtime.test.mjs.log",
|
||||
"sha256": "ce14284e1b8084ce976033c4ee820fc64d7124785a2a842079565a4b71902b0a",
|
||||
"bytes": 472
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/grant-scope.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 14,
|
||||
"pass": 14,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_grant-scope.test.mjs.log",
|
||||
"sha256": "3c788044749f391c7f804f4c58879f0a0b6d168466a6a17af9d8ee3f36f648e1",
|
||||
"bytes": 3755
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/hard-waits.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 13,
|
||||
"pass": 13,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_hard-waits.test.mjs.log",
|
||||
"sha256": "d0ba56411e81744c5f00e42f4dad73038fe5dd131a88d26114b147f2baafe3ea",
|
||||
"bytes": 3755
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/host-run.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 26,
|
||||
"pass": 26,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_host-run.test.mjs.log",
|
||||
"sha256": "8a8e8bc2f8fa4c2bf8f85bf17cee17526c4b7206f7569f69093d730cbaa626c8",
|
||||
"bytes": 6222
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/human.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 4,
|
||||
"pass": 4,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_human.test.mjs.log",
|
||||
"sha256": "d03a9c57f3905bd79875038e9fd55151e8d5721f8962ea3da2bcb2814fbeb5f4",
|
||||
"bytes": 1055
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/instances-verb.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 3,
|
||||
"pass": 3,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_instances-verb.test.mjs.log",
|
||||
"sha256": "89303a2faae0d17d4a3caa064c1270a520c2f3efbb472cc3637e8151ca1b4456",
|
||||
"bytes": 1350
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/land.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 18,
|
||||
"pass": 18,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_land.test.mjs.log",
|
||||
"sha256": "a8abb96b31ec84c7552994568e96ef6a782843e4790972b89c5f73d9a9420494",
|
||||
"bytes": 5269
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/launch-notice.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 8,
|
||||
"pass": 8,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_launch-notice.test.mjs.log",
|
||||
"sha256": "c3083725d9c5189ded3a35f64f54e084a2e4e7c425ebce834b039a597fbbc194",
|
||||
"bytes": 2033
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/launcher-released.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 3,
|
||||
"pass": 3,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_launcher-released.test.mjs.log",
|
||||
"sha256": "2ee2f6121f7bf0e6895f22758fbddf180107525915dfbc1e6b2ec1cacbc3755f",
|
||||
"bytes": 973
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/library-credential.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_library-credential.test.mjs.log",
|
||||
"sha256": "ff6c80cb96a3c014b603dc977818e4ba9ce5fbd72216073fafe64bae6b72c177",
|
||||
"bytes": 1778
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/library-lookups.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 12,
|
||||
"pass": 12,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_library-lookups.test.mjs.log",
|
||||
"sha256": "de782424a01ef8eff1cc3984ea337a27674e56d768e2d3fe9934263746c6078e",
|
||||
"bytes": 2868
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/library-sync.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_library-sync.test.mjs.log",
|
||||
"sha256": "5db561e52f7eb383be8fe2cce00e4fb87ee38baea85fc2e7f896b7aeefe498ba",
|
||||
"bytes": 1705
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/library.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_library.test.mjs.log",
|
||||
"sha256": "de4d1a49b3cfd8eae0f541c22f19e6cdefe86832ac31b0146d0ca9441758fd40",
|
||||
"bytes": 1588
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/lifecycle-revoke.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 8,
|
||||
"pass": 8,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_lifecycle-revoke.test.mjs.log",
|
||||
"sha256": "9ee07e863a8cd32aa798f2e4da196679d2d9e5c9875df63668b4bfa8c8bbe527",
|
||||
"bytes": 2584
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/lifecycle-verbs.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 13,
|
||||
"pass": 13,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_lifecycle-verbs.test.mjs.log",
|
||||
"sha256": "afecc3369f572380277b67b6721a59b2e782ff692423d1163e73980fd44cd514",
|
||||
"bytes": 4523
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/limit-stop.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 5,
|
||||
"pass": 5,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_limit-stop.test.mjs.log",
|
||||
"sha256": "ea5396ffabfc01eedbae049c2f7f6fc38ab3fd49a8b09466bd44888d285ae039",
|
||||
"bytes": 1461
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/limits-runtime.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 5,
|
||||
"pass": 5,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_limits-runtime.test.mjs.log",
|
||||
"sha256": "3d7eeed3cc199962d0d03e97205c5a5f8abfcc2ad726514d9666a4bd1d0cd208",
|
||||
"bytes": 2607
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/limits.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 3,
|
||||
"pass": 3,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_limits.test.mjs.log",
|
||||
"sha256": "117bbb47d775f74796ec9bde6c961b0750816da4f5eaf9d25c465821ab47a8d5",
|
||||
"bytes": 1033
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/message-citation.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 3,
|
||||
"pass": 3,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_message-citation.test.mjs.log",
|
||||
"sha256": "deecc45e1f633aaeb881c75ef26887e03f7c95acf174951c168776dbe884f042",
|
||||
"bytes": 838
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/message-filter.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_message-filter.test.mjs.log",
|
||||
"sha256": "9aa7fc406400e8ceac0b12990350d51c921a349a72f59b4412c437e17516d222",
|
||||
"bytes": 1732
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/messages.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 4,
|
||||
"pass": 4,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_messages.test.mjs.log",
|
||||
"sha256": "f226ddef0348557b84e0235a91d72533950fb7967d3db979ef22be674f0882a3",
|
||||
"bytes": 977
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/messaging.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 13,
|
||||
"pass": 13,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_messaging.test.mjs.log",
|
||||
"sha256": "9f71d7d8f9dad4bd1142dd64e3e96aa5cfa4c92de013af62c7f4cc3eb81ac2ff",
|
||||
"bytes": 2951
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/model-credential-store.test.mjs",
|
||||
"rc": 0,
|
||||
"nice": 19,
|
||||
"tests": 7,
|
||||
"pass": 7,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_model-credential-store.test.mjs.log",
|
||||
"sha256": "edd92bfb70c55f3f67acd783f793914f0f3a017bca5f54c656c50190936864c5",
|
||||
"bytes": 1673
|
||||
}
|
||||
},
|
||||
{
|
||||
"file": "packages/bus/tests/model-keys.test.mjs",
|
||||
"rc": 1,
|
||||
"nice": 19,
|
||||
"tests": 30,
|
||||
"pass": 24,
|
||||
"fail": 6,
|
||||
"cancelled": 0,
|
||||
"skipped": 0,
|
||||
"failedNames": [
|
||||
"bus fire proves remote 401 before delisting the agent and deleting model file",
|
||||
"credential-revoke-failed: bus fire on failed remote PUT keeps membership and model record",
|
||||
"proxy-revoke-revision-conflict: racing organization write keeps record, next boot finishes intent",
|
||||
"bus hire issues and confirms implicit model key before publishing new membership",
|
||||
"proxy-key-issue-failed: bus hire failure publishes neither membership nor model file",
|
||||
"newly hired role can use runtime preflight without a broker restart"
|
||||
],
|
||||
"log": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-packages_bus_tests_model-keys.test.mjs.log",
|
||||
"sha256": "e664374e7c66183d0b8eb723eb7f27c7c49c344548f7e44dbe8f1c28d01c0c33",
|
||||
"bytes": 13026
|
||||
}
|
||||
}
|
||||
],
|
||||
"totals": {
|
||||
"tests": 592,
|
||||
"pass": 586,
|
||||
"fail": 6,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"notRun": {
|
||||
"remainingWholeFiles": 207,
|
||||
"testConfig": true,
|
||||
"mutants": [
|
||||
"revoke-skips-confirming-GET",
|
||||
"reconcile-removes-foreign",
|
||||
"preflight-accepts-401",
|
||||
"reconcile-ignores-revoking"
|
||||
],
|
||||
"positiveControls": true
|
||||
},
|
||||
"archive": "/mnt/storage/scratch/tmp/JT6SN5",
|
||||
"driverLog": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-driver.log",
|
||||
"sha256": "ff6366c16fd081250434875f6740492ec2a359e9c9a2565dfad2e4f88149280b",
|
||||
"bytes": 2613
|
||||
},
|
||||
"runManifest": {
|
||||
"path": "/mnt/storage/scratch/tmp/JT6SN5/green-run-manifest.txt",
|
||||
"sha256": "31fb96199ad87d10abc1ada4104cbb3a1db1c493a5282a3a6db0d717cdcc9b94",
|
||||
"bytes": 4454
|
||||
},
|
||||
"rootCause": {
|
||||
"code": "ENOENT",
|
||||
"component": "Store dataRoot lstat",
|
||||
"fixture": "packages/bus/tests/model-keys.test.mjs:175",
|
||||
"summary": "bootBusiness does not create its fresh data directory before startBroker"
|
||||
}
|
||||
}
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run only after Sage CLEAR. Every selected file runs whole and serially.
|
||||
set -euo pipefail
|
||||
repo=$(git rev-parse --show-toplevel)
|
||||
evidence="$repo/agents/magica/work/queue-331"
|
||||
while IFS= read -r test_file; do
|
||||
[[ -n "$test_file" ]] || continue
|
||||
[[ "$test_file" != packages/cli/tests/case1-accept.test.mjs ]] || exit 2
|
||||
case_tmp=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)
|
||||
mkdir -p "$case_tmp/home" "$case_tmp/config"
|
||||
name=${test_file//\//_}
|
||||
printf '%s TMPDIR=%s\n' "$test_file" "$case_tmp" >> "$evidence/green-run-manifest.txt"
|
||||
nice -n $((19 - $(nice))) bash -c '
|
||||
printf "nice="; nice
|
||||
exec env -u OPENAI_API_KEY -u ANTHROPIC_API_KEY -u ANTHROPIC_AUTH_TOKEN -u CLAUDE_CODE_OAUTH_TOKEN -u OPENROUTER_API_KEY -u ZAI_API_KEY -u MOSAIC_BUS_CAP TMPDIR="$1" HOME="$1/home" MOSAIC_CONFIG="$1/config/config.json" node --test --test-reporter=tap "$2"
|
||||
' _ "$case_tmp" "$repo/$test_file" > "$evidence/green-$name.log" 2>&1 || {
|
||||
printf 'RED %s; stop before rerun or changes\n' "$test_file"
|
||||
exit 1
|
||||
}
|
||||
printf 'PASS %s\n' "$test_file"
|
||||
done < "$evidence/green-files.txt"
|
||||
export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/XXXXXX)
|
||||
nice -n $((19 - $(nice))) timeout -k 10 1800 "$repo/scripts/test-config.sh" > "$evidence/green-test-config.log" 2>&1
|
||||
@@ -0,0 +1,70 @@
|
||||
// Read-only import manifest; parse static requests with Node's module parser.
|
||||
// Literal dynamic imports are tokenized outside comments/strings/regex/template text.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import vm from 'node:vm';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
const root = process.cwd();
|
||||
const files = dir => fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => e.isDirectory() && !['node_modules', '.git', 'dist', 'build'].includes(e.name)
|
||||
? files(path.join(dir, e.name)) : e.isFile() && e.name.endsWith('.mjs') ? [path.join(dir, e.name)] : []);
|
||||
const changed = [...new Set(execFileSync('git', ['ls-files', '--modified', '--others', '--exclude-standard'], { encoding: 'utf8' }).trim().split('\n'))];
|
||||
const sources = new Set(changed.filter(p => /^(packages\/[^/]+\/src\/|scripts\/).*\.mjs$/.test(p)));
|
||||
function dynamicImports(source) {
|
||||
const tokens = []; let i = 0, previous = '';
|
||||
while (i < source.length) {
|
||||
const c = source[i];
|
||||
if (/\s/.test(c)) { i++; continue; }
|
||||
if (source.startsWith('//', i)) { i = source.indexOf('\n', i); if (i < 0) break; continue; }
|
||||
if (source.startsWith('/*', i)) { const end = source.indexOf('*/', i + 2); i = end < 0 ? source.length : end + 2; continue; }
|
||||
if (c === '"' || c === "'" || c === '`') {
|
||||
const quote = c; let text = '', literal = true; i++;
|
||||
while (i < source.length && source[i] !== quote) {
|
||||
if (source[i] === '\\') { text += source[i++] + (source[i++] ?? ''); continue; }
|
||||
if (quote === '`' && source.startsWith('${', i)) literal = false;
|
||||
text += source[i++];
|
||||
}
|
||||
i++; tokens.push({ kind: literal ? 'string' : 'template', value: text }); previous = 'value'; continue;
|
||||
}
|
||||
if (c === '/' && !/^(value|\)|\])$/.test(previous)) {
|
||||
i++; let inClass = false;
|
||||
while (i < source.length) { if (source[i] === '\\') { i += 2; continue; } if (source[i] === '[') inClass = true; if (source[i] === ']') inClass = false; if (source[i++] === '/' && !inClass) break; }
|
||||
while (/[A-Za-z]/.test(source[i] ?? '')) i++;
|
||||
tokens.push({ kind: 'regex' }); previous = 'value'; continue;
|
||||
}
|
||||
if (/[A-Za-z_$]/.test(c)) {
|
||||
const start = i++; while (/[A-Za-z0-9_$]/.test(source[i] ?? '')) i++;
|
||||
const value = source.slice(start, i); tokens.push({ kind: 'id', value }); previous = ['return', 'throw', 'case', 'typeof', 'void', 'new', 'yield', 'await'].includes(value) ? value : 'value'; continue;
|
||||
}
|
||||
tokens.push({ kind: 'punct', value: c }); previous = c; i++;
|
||||
}
|
||||
const imports = [];
|
||||
for (let n = 0; n < tokens.length - 3; n++) if (tokens[n].kind === 'id' && tokens[n].value === 'import'
|
||||
&& tokens[n + 1].value === '(' && tokens[n + 2].kind === 'string' && [')', ','].includes(tokens[n + 3].value)) imports.push(tokens[n + 2].value);
|
||||
return imports;
|
||||
}
|
||||
const graph = new Map(), parseFailures = [];
|
||||
function imports(file) {
|
||||
if (graph.has(file)) return graph.get(file);
|
||||
if (!fs.existsSync(file)) return [];
|
||||
const source = fs.readFileSync(file, 'utf8'); let requests;
|
||||
try { requests = new vm.SourceTextModule(source).dependencySpecifiers; }
|
||||
catch { parseFailures.push(path.relative(root, file)); return []; }
|
||||
const dependencies = [...new Set([...requests, ...dynamicImports(source)])].filter(s => s.startsWith('.')).map(s => path.resolve(path.dirname(file), s));
|
||||
graph.set(file, dependencies); return dependencies;
|
||||
}
|
||||
const tests = files(path.join(root, 'packages')).filter(p => p.endsWith('.test.mjs'));
|
||||
function reaches(file, seen = new Set()) {
|
||||
if (seen.has(file)) return false; seen.add(file);
|
||||
if (sources.has(path.relative(root, file))) return true;
|
||||
return imports(file).some(dependency => reaches(dependency, seen));
|
||||
}
|
||||
const excluded = 'packages/cli/tests/case1-accept.test.mjs';
|
||||
const selected = tests.filter(file => {
|
||||
const relative = path.relative(root, file);
|
||||
return relative !== excluded && !relative.startsWith('packages/queue/') && (changed.includes(relative) || reaches(file));
|
||||
}).map(p => path.relative(root, p)).sort();
|
||||
const home = 'agents/magica/work/queue-331/';
|
||||
fs.writeFileSync(home + 'green-files.txt', selected.join('\n') + '\n');
|
||||
fs.writeFileSync(home + 'import-manifest.json', JSON.stringify({ sources: [...sources].sort(), tests: selected, excluded, parseFailures,
|
||||
edges: Object.fromEntries([...graph].map(([file, deps]) => [path.relative(root, file), deps.map(p => path.relative(root, p))])) }, null, 2) + '\n');
|
||||
process.stdout.write(JSON.stringify({ tests: selected.length, sources: sources.size, excluded, parseFailures }) + '\n');
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,35 @@
|
||||
// Run only with Sage's separate mutant CLEAR. Each source mutation restores in finally.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
const root = process.cwd(), home = path.join(root, 'agents/magica/work/queue-331');
|
||||
const file = path.join(root, 'packages/bus/src/model-keys.mjs'), original = fs.readFileSync(file, 'utf8');
|
||||
const cases = [
|
||||
{ name: 'revoke-skips-confirming-GET', from: "await this.#replace(before, before.filter(k => !keys.includes(k)));", to: "this.#sentinel(before); await this.#client.putKeys(before.filter(k => !keys.includes(k)));", failure: 'revoke confirms absence then polls delayed client refusal before local removal' },
|
||||
{ name: 'reconcile-removes-foreign', from: "const removed = before.filter(k => k.startsWith(this.#prefix) && !k.startsWith(this.#prefix + 'sentinel-') && !retained.includes(k));", to: "const removed = before.filter(k => !k.startsWith(this.#prefix + 'sentinel-') && !retained.includes(k));", failure: 'reconcile restores missing recorded keys, removes own unknown, and preserves foreign and sentinel' },
|
||||
{ name: 'preflight-accepts-401', from: "if (status < 200 || status >= 300) fail('proxy-key-refused');", to: "if (status !== 401 && (status < 200 || status >= 300)) fail('proxy-key-refused');", failure: 'proxy-key-refused: preflight rejects HTTP 401 with exactly one probe' },
|
||||
{ name: 'reconcile-ignores-revoking', from: "const retained = records.filter(e => e.record.state !== 'revoking').flatMap(e => [e.record.key, ...e.record.retiring.map(r => r.key)]);\n const revoking = records.filter(e => e.record.state === 'revoking');", to: "const retained = records.flatMap(e => [e.record.key, ...e.record.retiring.map(r => r.key)]);\n const revoking = [];", failure: 'reconcile revoking never restores key and completes delayed revocation after restart' },
|
||||
];
|
||||
os.setPriority(0, 19);
|
||||
const baseEnv = { ...process.env };
|
||||
for (const name of Object.keys(baseEnv)) if (/(TOKEN|SECRET|PASSWORD|API_KEY|AUTH_SOCK|BUS_CAP)/.test(name)) delete baseEnv[name];
|
||||
try {
|
||||
for (const mutant of cases) {
|
||||
if (original.split(mutant.from).length !== 2) throw Error('mutation anchor not unique: ' + mutant.name);
|
||||
fs.writeFileSync(file, original.replace(mutant.from, mutant.to));
|
||||
const scratch = spawnSync('mktemp', ['-d', '/mnt/storage/scratch/tmp/XXXXXX'], { encoding: 'utf8' }).stdout.trim();
|
||||
if (!/^\/mnt\/storage\/scratch\/tmp\/[A-Za-z0-9]{6}$/.test(scratch)) throw Error('short scratch creation failed');
|
||||
fs.mkdirSync(path.join(scratch, 'home')); fs.mkdirSync(path.join(scratch, 'config'));
|
||||
const log = spawnSync(process.execPath, ['--test', '--test-reporter=tap', path.join(root, 'packages/bus/tests/model-keys.test.mjs')], {
|
||||
cwd: root, encoding: 'utf8', timeout: 180000, maxBuffer: 8 * 1024 * 1024,
|
||||
env: { ...baseEnv, TMPDIR: scratch, HOME: path.join(scratch, 'home'), MOSAIC_CONFIG: path.join(scratch, 'config/config.json') },
|
||||
});
|
||||
const output = (log.stdout ?? '') + (log.stderr ?? '');
|
||||
fs.writeFileSync(path.join(home, 'mutant-' + mutant.name + '.log'), output);
|
||||
const namedFailure = output.split('\n').some(line => /^not ok /.test(line) && line.includes(mutant.failure));
|
||||
if (log.status === 0 || !namedFailure || log.error) throw Error('mutant did not fail its named behavior: ' + mutant.name);
|
||||
process.stdout.write(mutant.name + ' killed; nice=' + os.getPriority(0) + '\n');
|
||||
fs.writeFileSync(file, original);
|
||||
}
|
||||
} finally { fs.writeFileSync(file, original); }
|
||||
@@ -0,0 +1,99 @@
|
||||
nice=19
|
||||
✔ proxy.init only accepts path-bound outside-agent human; refuses agent/tester/reader (21.226848ms)
|
||||
✔ proxy.init proxy-key-invalid (13.453809ms)
|
||||
✔ proxy.init proxy-config-invalid (12.565655ms)
|
||||
✔ proxy.init invalid-request (12.106821ms)
|
||||
✔ proxy.init not-configured (12.315024ms)
|
||||
✔ proxy.init callback error redacts key and maps outcome to named refusal (11.717939ms)
|
||||
✔ socket awaits proxy.init and does not return a Promise object or retain human cap (32.004316ms)
|
||||
✔ proxy-init-failed: extra callback fields cannot return a management key (12.922711ms)
|
||||
✖ proxy-key-invalid: broker rejects Latin-1 before initializer (11.977229ms)
|
||||
✖ proxy-key-invalid: broker rejects leading space before initializer (11.442742ms)
|
||||
✖ proxy-key-invalid: broker rejects trailing space before initializer (11.788604ms)
|
||||
✖ proxy-key-invalid: broker rejects internal space before initializer (11.359554ms)
|
||||
ℹ Error: Test "proxy-key-invalid: broker rejects Latin-1 before initializer" at packages/bus/tests/model-proxy.test.mjs:20:3 generated asynchronous activity after the test ended. This activity created the error "Error: storage-unavailable" and would have caused the test to fail, but instead triggered an unhandledRejection event.
|
||||
ℹ Error: Test "proxy-key-invalid: broker rejects leading space before initializer" at packages/bus/tests/model-proxy.test.mjs:20:3 generated asynchronous activity after the test ended. This activity created the error "Error: storage-unavailable" and would have caused the test to fail, but instead triggered an unhandledRejection event.
|
||||
ℹ Error: Test "proxy-key-invalid: broker rejects trailing space before initializer" at packages/bus/tests/model-proxy.test.mjs:20:3 generated asynchronous activity after the test ended. This activity created the error "Error: storage-unavailable" and would have caused the test to fail, but instead triggered an unhandledRejection event.
|
||||
ℹ Error: Test "proxy-key-invalid: broker rejects internal space before initializer" at packages/bus/tests/model-proxy.test.mjs:20:3 generated asynchronous activity after the test ended. This activity created the error "Error: storage-unavailable" and would have caused the test to fail, but instead triggered an unhandledRejection event.
|
||||
ℹ tests 12
|
||||
ℹ suites 0
|
||||
ℹ pass 8
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 256.218396
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/model-proxy.test.mjs:20:3
|
||||
✖ proxy-key-invalid: broker rejects Latin-1 before initializer (11.977229ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-proxy.test.mjs:23:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/model-proxy.test.mjs:20:3
|
||||
✖ proxy-key-invalid: broker rejects leading space before initializer (11.442742ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-proxy.test.mjs:23:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/model-proxy.test.mjs:20:3
|
||||
✖ proxy-key-invalid: broker rejects trailing space before initializer (11.788604ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-proxy.test.mjs:23:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/model-proxy.test.mjs:20:3
|
||||
✖ proxy-key-invalid: broker rejects internal space before initializer (11.359554ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-proxy.test.mjs:23:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
nice=19
|
||||
✖ human-required: proxy fetch refuses CODEX_THREAD_ID before config or fetch (1.450492ms)
|
||||
✖ human-required: proxy fetch refuses MOSAIC_AGENT_RUN before config or fetch (0.227362ms)
|
||||
✔ human-required: proxy fetch tester refuses before config or fetch (0.461104ms)
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (12) (0.486647ms)
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (12) (0.3288ms)
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (12) (0.281426ms)
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (11) (0.321288ms)
|
||||
ℹ tests 7
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 6
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 120.753461
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/cli/tests/model-proxy-guards.test.mjs:8:3
|
||||
✖ human-required: proxy fetch refuses CODEX_THREAD_ID before config or fetch (1.450492ms)
|
||||
AssertionError [ERR_ASSERTION]: The validation function is expected to return "true". Received false
|
||||
|
||||
Caught error:
|
||||
|
||||
CliError: proxy-fetch-failed
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:11:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: CliError: proxy-fetch-failed
|
||||
at failure (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/src/model-proxy.mjs:4:55)
|
||||
at proxyCommand (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/src/model-proxy.mjs:62:11)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:11:26)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17),
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/model-proxy-guards.test.mjs:8:3
|
||||
✖ human-required: proxy fetch refuses MOSAIC_AGENT_RUN before config or fetch (0.227362ms)
|
||||
AssertionError [ERR_ASSERTION]: The validation function is expected to return "true". Received false
|
||||
|
||||
Caught error:
|
||||
|
||||
CliError: proxy-fetch-failed
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:11:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: CliError: proxy-fetch-failed
|
||||
at failure (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/src/model-proxy.mjs:4:55)
|
||||
at proxyCommand (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/src/model-proxy.mjs:62:11)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:11:26)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3),
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/model-proxy-guards.test.mjs:22:3
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (12) (0.486647ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:25:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/model-proxy-guards.test.mjs:22:3
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (12) (0.3288ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:25:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/model-proxy-guards.test.mjs:22:3
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (12) (0.281426ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:25:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/cli/tests/model-proxy-guards.test.mjs:22:3
|
||||
✖ proxy-key-invalid: CLI rejects non-management-key input (11) (0.321288ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/cli/tests/model-proxy-guards.test.mjs:25:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
nice=19
|
||||
✖ probeKey observes 200 with one agent-authenticated GET /v1/models (9.151708ms)
|
||||
✖ probeKey observes 401 with one agent-authenticated GET /v1/models (1.636636ms)
|
||||
✖ probeKey observes 503 with one agent-authenticated GET /v1/models (0.696746ms)
|
||||
✖ proxy-down: agent-key probe connection failure is sanitized (1.651552ms)
|
||||
✖ proxy-down: silent listener cannot extend probe request deadline (0.813308ms)
|
||||
✖ agent-key probe returns redirect status without following Location or exposing key (0.763418ms)
|
||||
ℹ tests 6
|
||||
ℹ suites 0
|
||||
ℹ pass 0
|
||||
ℹ fail 6
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 99.280118
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/model-proxy/tests/client-probe.test.mjs:16:3
|
||||
✖ probeKey observes 200 with one agent-authenticated GET /v1/models (9.151708ms)
|
||||
AssertionError [ERR_ASSERTION]: client must support bounded agent-key probe
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/model-proxy/tests/client-probe.test.mjs:22:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/model-proxy/tests/client-probe.test.mjs:16:3
|
||||
✖ probeKey observes 401 with one agent-authenticated GET /v1/models (1.636636ms)
|
||||
AssertionError [ERR_ASSERTION]: client must support bounded agent-key probe
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/model-proxy/tests/client-probe.test.mjs:22:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/model-proxy/tests/client-probe.test.mjs:16:3
|
||||
✖ probeKey observes 503 with one agent-authenticated GET /v1/models (0.696746ms)
|
||||
AssertionError [ERR_ASSERTION]: client must support bounded agent-key probe
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/model-proxy/tests/client-probe.test.mjs:22:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/model-proxy/tests/client-probe.test.mjs:30:1
|
||||
✖ proxy-down: agent-key probe connection failure is sanitized (1.651552ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/model-proxy/tests/client-probe.test.mjs:33:10)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/model-proxy/tests/client-probe.test.mjs:36:1
|
||||
✖ proxy-down: silent listener cannot extend probe request deadline (0.813308ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/model-proxy/tests/client-probe.test.mjs:38:10)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/model-proxy/tests/client-probe.test.mjs:43:1
|
||||
✖ agent-key probe returns redirect status without following Location or exposing key (0.763418ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/model-proxy/tests/client-probe.test.mjs:46:10)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
nice=19
|
||||
✖ implicit model kind starts not-issued without a business or role reference (2.1449ms)
|
||||
✖ model record persists at 0600 in 0700 dirs and reloads ids/issue time/state only in status (0.854684ms)
|
||||
✖ revoking model metadata survives restart without losing the private record (0.575987ms)
|
||||
✖ model record wrong file mode refuses startup rather than inventing a replacement (0.540917ms)
|
||||
✔ non-proxy Credentials has no implicit model kind (0.805283ms)
|
||||
ℹ tests 5
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 69.204058
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/bus/tests/model-credential-store.test.mjs:23:1
|
||||
✖ implicit model kind starts not-issued without a business or role reference (2.1449ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ undefined
|
||||
- 'not-issued'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-credential-store.test.mjs:26:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: 'not-issued',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/model-credential-store.test.mjs:29:1
|
||||
✖ model record persists at 0600 in 0700 dirs and reloads ids/issue time/state only in status (0.854684ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-credential-store.test.mjs:31:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/model-credential-store.test.mjs:44:1
|
||||
✖ revoking model metadata survives restart without losing the private record (0.575987ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-credential-store.test.mjs:46:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/bus/tests/model-credential-store.test.mjs:53:1
|
||||
✖ model record wrong file mode refuses startup rather than inventing a replacement (0.540917ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'undefined'
|
||||
- 'function'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/magica-row331/packages/bus/tests/model-credential-store.test.mjs:55:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'undefined',
|
||||
expected: 'function',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/mnt/storage/scratch/tmp/X0S193
|
||||
@@ -0,0 +1,71 @@
|
||||
// Sage, 2026-10-25: files two follow-ups as P19 tasks (mos-1647 item 3, Q109): the CLIProxyAPI startup version refreshes
|
||||
// seen in row 332's isolated runs (a P7 cutover item) and Webby's row 284 N1 (library read path). No relation, labels,
|
||||
// assignees, due dates or bucket moves. Sage's own key (bot user 13); the sync key reads back. Refuses if a title exists.
|
||||
// Prints no secret.
|
||||
// Usage: node file-1025-followups.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const SIG = "Created by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (sig, xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(sig)}</p>`;
|
||||
const TASKS = [
|
||||
{
|
||||
title: "Stack P7 cutover: CLIProxyAPI 8.0.42 makes outbound version-refresh calls at startup (Grok CLI via npm, antigravity)",
|
||||
description: ps(SIG, [
|
||||
"Filed by Mos's ruling mos-1647 item 3. Seen by Fethry in row 332 (P3) isolated runs on 2026-10-24, pinned CLIProxyAPI 8.0.42 (commit bc064340, PIN binary sha256 74661f8c), run under unshare -rn with only lo, empty HOME and auth, panel auto-update disabled.",
|
||||
"At startup the binary logged xai_version.go:96 'failed to fetch latest Grok CLI version from npm' for https://registry.npmjs.org/@xai-official/grok/latest (network unreachable inside the namespace), then xai_version.go:79 'periodic Grok CLI version refresh started (interval=3h0m0s)'. It also logged antigravity_version.go:71 'periodic antigravity version refresh started (interval=3h0m0s)'. Evidence: origin/fethry/row332-p1 agents/fethry/work/queue-332/BUILD.md:251-252 and evidence/offline.txt.",
|
||||
"On the live host these calls would leave the machine every 3 hours, which P1's loopback-only design doesn't account for. The proxy kept working with the fallback version, so the failure is harmless to requests.",
|
||||
"Needed before or at P7 cutover, with Jason present: find whether 8.0.42 has a config switch for these refreshes; if so set it in P1's initial config with a test, otherwise decide deliberately to allow or block the egress (unit-level network policy) and record the choice in P7's receipt. Also recorded in docs/plans/DEFERRED.md. Umbrella: 237.",
|
||||
]),
|
||||
},
|
||||
{
|
||||
title: "Stack: library get lists the whole served tree per read (row 284 N1, Webby)",
|
||||
description: ps(SIG, [
|
||||
"Filed by Mos's ruling mos-1647 item 3. Webby's round 1 note N1 on row 284 (#1789, landed ce0b6ed41 on 2026-10-25), carried in c31654. Not a correctness issue.",
|
||||
"Each read that resolves one file walks the whole served tree with listTree (git ls-tree -r -l) and then finds the one entry: packages/library/src/mirror.mjs:257 and packages/library/src/query.mjs:142, both via packages/library/src/reader.mjs:28. On a large library that is one full tree listing per read.",
|
||||
"Fix options: git rev-parse <commit>:<path> for the one oid, or a per-commit listing cache. listTree also bounds and checks the tree and its object IDs, so a replacement must keep those checks.",
|
||||
"Bound to the next row that edits the library read path: that row measures a get on a library of realistic size and records the choice in its BUILD.md. Also recorded in docs/plans/DEFERRED.md.",
|
||||
]),
|
||||
},
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
async function existing() {
|
||||
const out = [];
|
||||
for (let page = 1; page < 20; page++) {
|
||||
const r = await api("GET", `/api/v2/projects/${PROJECT}/tasks?per_page=50&page=${page}`, undefined, sync);
|
||||
if (r.status !== 200) throw new Error(`list tasks: ${r.status}`);
|
||||
out.push(...list(r.json));
|
||||
if (list(r.json).length < 50) return out;
|
||||
}
|
||||
throw new Error("more than 19 pages");
|
||||
}
|
||||
const rec = { at: new Date().toISOString(), project: PROJECT, created: null, problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
const have = await existing();
|
||||
for (const T of TASKS) if (have.some((t) => t.title === T.title)) throw new Error("title exists; refusing");
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
rec.created = [];
|
||||
for (const T of TASKS) {
|
||||
const r = await api("PUT", `/api/v1/projects/${PROJECT}/tasks`, { title: T.title, description: T.description }, key);
|
||||
const c = { status: r.status, id: r.json?.id ?? null };
|
||||
rec.created.push(c);
|
||||
if (r.status >= 300 || !Number.isSafeInteger(r.json?.id)) { rec.problems.push(`create: ${r.status} ${r.json?.message ?? ""}`); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${r.json.id}`, undefined, sync);
|
||||
Object.assign(c, { readBack: back.status, done: back.json?.done });
|
||||
if (back.status !== 200 || back.json?.title !== T.title || back.json?.done) rec.problems.push(`read-back differs for ${r.json.id}`);
|
||||
}
|
||||
save();
|
||||
console.log(JSON.stringify(rec));
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"at": "2026-10-25T00:18:17.480Z",
|
||||
"project": 19,
|
||||
"created": [
|
||||
{
|
||||
"status": 201,
|
||||
"id": 633,
|
||||
"readBack": 200,
|
||||
"done": false
|
||||
},
|
||||
{
|
||||
"status": 201,
|
||||
"id": 634,
|
||||
"readBack": 200,
|
||||
"done": false
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -1693,3 +1693,6 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-24 | magica | row329 round2 #1827 | In-progress rev4031; B1 ASCII-only key fix/new inventory tests and N1/N3/N4 docs, cleared reader verification running, old tests byte-identical.
|
||||
2026-10-24 | magica | row329 R2 #1827 | ASCII management-key fix and docs verified: package66/66,bus readers261/261,CLI readers726/726,config55/55; no required failures/skips, slot returned. Preparing authorized ff review candidate; old tests unchanged.
|
||||
2026-10-24 | Sage 1ef1e4f8 | row329 landing #1827 (mos-1634) | Gate GREEN 23:29:14Z unchanged, landed 61ebf2c34 pushed 23:31:27Z; #1827 closed c31650; queue note 4063, done 4064 (b24d917ec); DEFERRED N2/R2-N1 to 331, N1 to P7; Vikunja 631 filed, 237 c1109; 284 and 157 must rebase.
|
||||
2026-10-25 | Sage 1ef1e4f8 | row284 landing #1789 | Gate GREEN 00:09:04Z unchanged, landed ce0b6ed41 pushed 00:09:52Z; #1789 closed c31655; queue note 4071, done 4072 (7c8dc2033); DEFERRED Webby N1 to the next library read-path row; row 332 PROXY_CODEX_API ruled openai-responses (sage-4331), P3 holds for row 331.
|
||||
2026-10-24 | magica | row 331 P2 #1829, magica/row331 at 78b69e1e6 | registered; design investigation and local npm ci complete; tests held for Sage CLEAR
|
||||
2026-10-25 | magica | row331 GREEN on a86a881e1 under sage-4420 | stopped first RED:54 files pass,6 new integration cases fail missing fixture dataRoot; no edits/reruns; slot returned to Sage05:10:48Z
|
||||
@@ -796,3 +796,35 @@ Sage, for Dewey's round 1 N1 on row 329 (c31639). The acceptance netns had only
|
||||
but never saw the real binary refuse a non-loopback client.
|
||||
Bound to P7: at cutover, with Jason present, confirm the listener is on 127.0.0.1 only (`ss -ltnp`), and
|
||||
record it in P7's receipt.
|
||||
|
||||
## Row 284 N1: library.get lists the whole served tree per read
|
||||
|
||||
Sage, for Webby's round 1 N1 on row 284 (c31654 carries it forward). Each library get runs `listTree`
|
||||
(`git ls-tree -r -l`) over the whole served tree to find one blob oid. On a large library that is one full
|
||||
tree listing per read. `git rev-parse <commit>:<path>`, or a per-commit cache, would avoid it. Not a
|
||||
correctness issue. Bound to the next row that edits the library read path; that row measures a get on a
|
||||
library of realistic size and records the choice in its BUILD.md.
|
||||
|
||||
## Row 284 N1: Vikunja task
|
||||
|
||||
Sage, for mos-1647 item 3 (Q109). The row 284 N1 entry above is tracked as Vikunja 634. No open task
|
||||
covered the library read path.
|
||||
|
||||
## Row 332 finding: CLIProxyAPI startup version refreshes reach outside the host
|
||||
|
||||
Sage, for Fethry's row 332 isolated runs (2026-10-24, pinned 8.0.42, `unshare -rn` with only `lo`). At
|
||||
startup the binary tried `https://registry.npmjs.org/@xai-official/grok/latest` (xai_version.go:96, blocked
|
||||
by the namespace) and then started a 3-hour Grok CLI version refresh and a 3-hour antigravity version
|
||||
refresh. The proxy kept serving with its fallback version. Evidence: origin/fethry/row332-p1
|
||||
`agents/fethry/work/queue-332/BUILD.md`:251-252 and `evidence/offline.txt`. Bound to P7: before or at
|
||||
cutover, find a config switch that turns these off and set it in P1's initial config with a test, or
|
||||
decide on purpose to allow or block that egress and record it in P7's receipt. Tracked as Vikunja 633
|
||||
(umbrella 237).
|
||||
|
||||
## Row 331: human-only per-agent model rotation override — P7
|
||||
|
||||
Sage ruling sage-4388 (2026-10-24) limits P2 to the 30-day model-key rotation default,
|
||||
with no setter and no `proxy.rotate --agent --rotate-by` verb. A human-only
|
||||
per-agent rotation override is bound to P7, with Jason's cutover/settings review.
|
||||
It must preserve D20/Q222: no agent or tester can change the period. Row 331 ships
|
||||
only the default and the approved proxy.json grace/first-output settings.
|
||||
+2
-2
@@ -33,7 +33,7 @@ Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
|
||||
|
||||
<!-- mosaic-queue:begin -->
|
||||
|
||||
Generated from `docs/plans/queue.json` revision 4064 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
Generated from `docs/plans/queue.json` revision 4072 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
|
||||
| # | Piece | Owner | Issues | State | After | Gate | Brief | Note |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
@@ -320,7 +320,7 @@ Generated from `docs/plans/queue.json` revision 4064 by `queue render`. Do not e
|
||||
| 281 | A fire or a delete revokes the instance's host and Gitea grants (194/236 follow-up, MVP, mos-1271) | megavolt; reviewers dewey | #1786 | done | 236 done | Dewey approves the design, then the manifest. Bus, business, cli and every touched test file run whole, plus the Docker gitea-grant test and every scripts/test-*.sh, green on Sage's gate. M1-M4 killed. Lands before .53 Case 1 hires a granted instance. Red goes to Sage, then Mos. (sage) | `docs/plans/2026-10-22_fire-revokes-grants.md` § Brief: no grant outlives its instance | LANDED e479521d3 ff on 385a7194c (17 commits, manifest b73d4c81). Slot on 7979fb41e green: bus 819, business 231, cli 837, dewey-bus 76 with gitea-grant-docker run, shells green under 3x limits, M1-M22 killed. M1 hang fixed in b4a (mos-1273). Skips: test-task row 244 live-provider pair, test-queue canonical root. DEFERRED: N1 host-unknown hint verb, N2 fire tool text, to the follow-up row. Dewey APPROVE c30614. |
|
||||
| 282 | PRD REQ-LIB-6 rewritten to Q223 and Q226 (mos-1320 item 8) | gosalyn; reviewers webby | #1787 | done | — | Webby approves against the ruling. requirements.test.mjs and every scripts/test-*.sh that reads the PRD or requirements.json are green on Sage's gate. Jason gets a report. (sage) | `docs/plans/2026-10-20_case2-rows.md` § PRD REQ-LIB-6 rewritten to Q223 and Q226 (mos-1320 item 8) | Landed 647424442 on 86a665bdf: 1841f7d4e and 6b5b06819 rebased, patch-ids equal (4e50b4b6, 16a7a02f), range-diff =, tree 4f30ed10a = Gosalyn's gated trial tree. Webby APPROVE rev 3308 c30764 on 6b5b06819. Gate 19:06Z on the tip tree: bus+tasks requirements and tasks verbs 37/0/0, 0 skips; no scripts/test-*.sh reads the PRD (control ok). PRD 1.15. 1.16 is row 291, waits for Jason. |
|
||||
| 283 | Case 2 C2-01d supporting folders, the supporting scope, the editor field and supportingPath (M12) | honker; reviewers webby | #1788 | done | 268 done, 282 done | Webby approves the manifest. The library and business suites are green on Sage's gate, along with every scripts/test-*.sh. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-01d supporting folders, the supporting scope, the editor field and supportingPath (M12) | LANDED 8d2856ee3 (replay of 0bf308dba onto 59226f878, 13 patch-ids equal) plus DEFERRED N1 93694c518. Webby APPROVE r1 c31338 rev 3677, r2 c31342 rev 3683 (mos-1528 G1 mapping.test:52). Sage full gate GREEN 20:37:16Z: all package suites but queue plus every scripts/test-*.sh, 0 fail, library 98/0, webui npm ci, editor build required. supporting.test first-run red placed (mos-1272, 54d9e07bb). #1788 closed c31358. Live unchanged 45dca69f6. |
|
||||
| 284 | Case 2 C2-05c the supporting scope and blob in the lookups | della; reviewers webby | #1789 | in-review, round 1, request posted | 224 done, 283 done | Dewey approves the manifest. Library, bus and harness suites and every scripts/test-*.sh green on Sage's gate. Grep packages/*/tests for the scope enum pin and ['canon', 'playground'] before in-review; update each. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-05c the supporting scope and blob in the lookups (sage-2833) | — |
|
||||
| 284 | Case 2 C2-05c the supporting scope and blob in the lookups | della; reviewers webby | #1789 | done | 224 done, 283 done | Dewey approves the manifest. Library, bus and harness suites and every scripts/test-*.sh green on Sage's gate. Grep packages/*/tests for the scope enum pin and ['canon', 'playground'] before in-review; update each. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-05c the supporting scope and blob in the lookups (sage-2833) | LANDED ce0b6ed41 (fast-forward of Della r4 on 9e2689dff), pushed 00:09:52Z 10-25. Sage gate GREEN 00:09:04Z on ce0b6ed41: every package suite but queue (cli 999/0 + allowed FED R7 skip) and every scripts/test-*.sh rc 0. Webby APPROVE r2 c31654 rev 4070 on manifest-r4 de505cfd; landed tree matches. Della whole-file runs 51/0. #1789 closed with c31655. |
|
||||
| 285 | Case 2 C2-08b role changes for agent writes and the chief's read grant | magica; reviewers poe, webby | #1790 | done | 213 done, 291 done | Webby approves the manifest. Business suite, test-config.sh and every scripts/test-*.sh green on Sage's gate. Grep for CREATIVE_KINDS and 64-hex pins of changed role files. Sage checks it against mos-1309 item 5. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-08b role changes for agent writes and the chief's read grant (mos-1309 item 5) | LANDED 1f3d0350f 13:33:42Z: candidate 2e4503dc7 (14 commits) cherry-picked onto 25874bc86. Logs kept both sides; otherwise the tree equals the candidate except on 7 base paths. Poe c31568 rev 3919, Webby c31569 rev 3920. Sage gate GREEN 13:28:46Z with its own npm ci: 4236 package tests, 0 fail, 1 allowed FED R7 skip; 12 scripts rc 0. No CREATIVE_KINDS, no role-file hash pins. Roles match mos-1309 item 5. 286 rebases as r5. |
|
||||
| 286 | Case 2 C2-03b push and per-seat credentials for the library mirror (with R4, mos-1323) | rockerduck; reviewers boyd, dewey | #1791 | done | 222 done | Dewey approves the manifest. Library and bus suites and every scripts/test-*.sh green on Sage's gate, mutant killed. A search of the run output for the fixture token finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-03b push and per-seat credentials for the library mirror | LANDED 5d7801dc1 14:37:02Z: r6 75f6d56b1 (19 commits) picked onto d55944edb; logs kept both sides, code equals the candidate. Boyd c31587 rev 3942, Dewey c31588 rev 3943 (r2 c31584/c31585). Sage gate GREEN 14:36:42Z, own npm ci: 4251 tests, 0 fail, 1 allowed FED R7 skip; 12 scripts rc 0; fixture values in 0 of 29 outputs; B285-M1 kills T7/T9. r6 shortened two fixtures after push-check STOP secrets (sage-4112). Chief seed per sage-4100. |
|
||||
| 287 | Case 2 C2-21 the grant executor for a library project | steelbeak; reviewers boyd, dewey | #1792 | done | 226 done, 285 done | Dewey approves the manifest. Bus suite, Gitea Docker test, install test and every scripts/test-*.sh green on Sage's gate. A search of the run output for the fixture admin and seat tokens finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-21 the grant executor for a library project | LANDED 0e935776e 16:12:35Z 10-24 (cand c2338ed58 + a5370b198 on 9b6345466). Boyd APPROVE c31602 rev 3967, Dewey APPROVE c31603 rev 3968. Sage gate GREEN 16:11:38Z: bus 998/0 (Gitea Docker F-A/F-B + X-G3, minted 4 hits 0), business 283/0, cli 989 +1 allowed FED R7 skip, webui 442/0, 12 scripts rc 0. F-C negative record-only follow-up. Dewey N1-N3 non-blocking. #1792 closed c31606. No P19 task. |
|
||||
|
||||
+248
-9
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"canonicalRoot": "/mnt/storage/src/mosaic-stack",
|
||||
"revision": 4064,
|
||||
"revision": 4072,
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
@@ -30162,7 +30162,7 @@
|
||||
"closes": [
|
||||
1789
|
||||
],
|
||||
"state": "in-review",
|
||||
"state": "done",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -30241,18 +30241,69 @@
|
||||
"candidate": "c403f7e3e4496c8eabfe29cce00582267c6dc5a5140b74fcb4ba4c2ec031ca65"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"n": 2,
|
||||
"op": "della-284-r2-review",
|
||||
"by": "della",
|
||||
"at": "2026-10-24T23:43:28.297Z",
|
||||
"issue": 1789,
|
||||
"candidate": {
|
||||
"kind": "manifest",
|
||||
"digest": "de505cfd18c82f66ff6cc5faf00789e81c66d148e07e1972c5d664031ee0f727",
|
||||
"text": "0cf1506f848cc1734e34625f101b60561ecdc8505e33b6a9d16e430b3cb2f68e agents/della/work/queue-284/BUILD.md\n591a412ed4243288da29c406269ff089a58bbcfcb0a2a0b3536b87bfa2e89eab packages/bus/src/broker.mjs\n9b974b53c7fde08b795e1a26c75ad1e6dd664fda72b79a656900ec96f1603d03 packages/bus/tests/library-lookups.test.mjs\n145ede2d2f5c9c776cc2a6eb5f48975e96b09e78c495401d5aaae435974496a4 packages/harness/src/tools.mjs\n21e78b4424ae0e8337aaf548e880abf297407301cb0ae41711bd794e99344276 packages/harness/tests/library-tools.test.mjs\nbafe113bee70f17bd249876d6bdd1c08fa051223c2111955e5d5f275bd76bba0 packages/library/src/query.mjs\n766b80f53f4565588ac48bd613f6099b1f7ca1c933994779fe6c6cb9eae23a29 packages/library/tests/query-fixture.mjs\nd99111d1a4f7ca42881ba699cbc55d2dad12f6c75e7b8e1bac44f05c7df56dec packages/library/tests/query.test.mjs\n"
|
||||
},
|
||||
"request": "comment",
|
||||
"attempts": [
|
||||
{
|
||||
"op": "della-284-r2-review",
|
||||
"by": "della",
|
||||
"at": "2026-10-24T23:43:28.297Z",
|
||||
"state": "failed",
|
||||
"comment": null,
|
||||
"transport": {
|
||||
"outcome": "failed",
|
||||
"status": null,
|
||||
"comment": null,
|
||||
"detail": "pre-send: the credential or account check failed"
|
||||
},
|
||||
"resolutions": []
|
||||
},
|
||||
{
|
||||
"op": "sage-284-r2-request",
|
||||
"by": "sage",
|
||||
"at": "2026-10-24T23:46:18.731Z",
|
||||
"state": "posted",
|
||||
"comment": 31653,
|
||||
"transport": {
|
||||
"outcome": "posted",
|
||||
"status": 201,
|
||||
"comment": 31653,
|
||||
"detail": "created"
|
||||
},
|
||||
"resolutions": []
|
||||
}
|
||||
],
|
||||
"duplicateRisk": false,
|
||||
"receipts": [
|
||||
{
|
||||
"reviewer": "webby",
|
||||
"op": "webby-284-verdict-r2",
|
||||
"at": "2026-10-24T23:48:52.791Z",
|
||||
"verdict": "approve",
|
||||
"comment": 31654,
|
||||
"candidate": "de505cfd18c82f66ff6cc5faf00789e81c66d148e07e1972c5d664031ee0f727"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"claim": {
|
||||
"seat": "della",
|
||||
"op": "della-284-start-1"
|
||||
},
|
||||
"note": null,
|
||||
"claim": null,
|
||||
"note": "LANDED ce0b6ed41 (fast-forward of Della r4 on 9e2689dff), pushed 00:09:52Z 10-25. Sage gate GREEN 00:09:04Z on ce0b6ed41: every package suite but queue (cli 999/0 + allowed FED R7 skip) and every scripts/test-*.sh rc 0. Webby APPROVE r2 c31654 rev 4070 on manifest-r4 de505cfd; landed tree matches. Della whole-file runs 51/0. #1789 closed with c31655.",
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-22T18:28:04.590Z",
|
||||
"updatedAt": "2026-10-24T23:25:20.620Z",
|
||||
"updatedBy": "webby"
|
||||
"updatedAt": "2026-10-25T00:12:13.878Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
"id": 285,
|
||||
@@ -140751,6 +140802,194 @@
|
||||
"receipt": "ok sage-329-done-landed rev 4064 row 329 in-review→done round 2"
|
||||
},
|
||||
"viewSha": "cf64a27b66b579121eb5f4dba03d2b4a3e88ff1aedd95ffe2c3d713591edf8b4"
|
||||
},
|
||||
{
|
||||
"rev": 4065,
|
||||
"op": "della-284-r2-reopen",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "della",
|
||||
"at": "2026-10-24T23:42:25.130Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"from": "in-review",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok della-284-r2-reopen rev 4065 row 284 in-review→in-progress"
|
||||
},
|
||||
"viewSha": "1ca33a4b5b66adffafabf5096cc182b7dd13cd71cdab6186fba9a0d3974ead96"
|
||||
},
|
||||
{
|
||||
"rev": 4066,
|
||||
"op": "della-284-r2-review",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"to": "in-review",
|
||||
"reason": null,
|
||||
"candidate": "/mnt/storage/scratch/della-row284-r2-evidence/manifest-r4.txt",
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "della",
|
||||
"at": "2026-10-24T23:43:28.297Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"from": "in-progress",
|
||||
"to": "in-review",
|
||||
"round": 2,
|
||||
"issue": 1789,
|
||||
"candidate": {
|
||||
"kind": "manifest",
|
||||
"digest": "de505cfd18c82f66ff6cc5faf00789e81c66d148e07e1972c5d664031ee0f727",
|
||||
"text": "0cf1506f848cc1734e34625f101b60561ecdc8505e33b6a9d16e430b3cb2f68e agents/della/work/queue-284/BUILD.md\n591a412ed4243288da29c406269ff089a58bbcfcb0a2a0b3536b87bfa2e89eab packages/bus/src/broker.mjs\n9b974b53c7fde08b795e1a26c75ad1e6dd664fda72b79a656900ec96f1603d03 packages/bus/tests/library-lookups.test.mjs\n145ede2d2f5c9c776cc2a6eb5f48975e96b09e78c495401d5aaae435974496a4 packages/harness/src/tools.mjs\n21e78b4424ae0e8337aaf548e880abf297407301cb0ae41711bd794e99344276 packages/harness/tests/library-tools.test.mjs\nbafe113bee70f17bd249876d6bdd1c08fa051223c2111955e5d5f275bd76bba0 packages/library/src/query.mjs\n766b80f53f4565588ac48bd613f6099b1f7ca1c933994779fe6c6cb9eae23a29 packages/library/tests/query-fixture.mjs\nd99111d1a4f7ca42881ba699cbc55d2dad12f6c75e7b8e1bac44f05c7df56dec packages/library/tests/query.test.mjs\n"
|
||||
},
|
||||
"receipt": "ok della-284-r2-review rev 4066 row 284 in-progress→in-review round 2 on #1789; request della-284-r2-review requesting"
|
||||
},
|
||||
"viewSha": "06c4188500095cb6d4a33ed66cf0ca641c422791578fd716de241899f8e6cd9a"
|
||||
},
|
||||
{
|
||||
"rev": 4067,
|
||||
"op": "della-284-r2-review.outcome",
|
||||
"verb": "review-outcome",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"attempt": "della-284-r2-review",
|
||||
"outcome": "failed",
|
||||
"status": null,
|
||||
"comment": null,
|
||||
"detail": "pre-send: the credential or account check failed"
|
||||
},
|
||||
"by": "della",
|
||||
"at": "2026-10-24T23:44:28.236Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"round": 2,
|
||||
"attempt": "della-284-r2-review",
|
||||
"state": "failed",
|
||||
"receipt": "ok della-284-r2-review.outcome rev 4067 row 284 round 2 request della-284-r2-review failed"
|
||||
},
|
||||
"viewSha": "c5746412794db34382b1445e1cf6653bd9bc5d18ced39eb7530f3d8b85f9dc71"
|
||||
},
|
||||
{
|
||||
"rev": 4068,
|
||||
"op": "sage-284-r2-request",
|
||||
"verb": "review-request",
|
||||
"args": {
|
||||
"id": 284
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-24T23:46:18.731Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"round": 2,
|
||||
"attempt": "sage-284-r2-request",
|
||||
"state": "requesting",
|
||||
"receipt": "ok sage-284-r2-request rev 4068 row 284 round 2 request sage-284-r2-request requesting"
|
||||
},
|
||||
"viewSha": "d88a0b75a5e83fc52e67f38060a10beb206122bd3c7b4c8f6e5abc59ec5db2cd"
|
||||
},
|
||||
{
|
||||
"rev": 4069,
|
||||
"op": "sage-284-r2-request.outcome",
|
||||
"verb": "review-outcome",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"attempt": "sage-284-r2-request",
|
||||
"outcome": "posted",
|
||||
"status": 201,
|
||||
"comment": 31653,
|
||||
"detail": "created"
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-24T23:47:19.540Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"round": 2,
|
||||
"attempt": "sage-284-r2-request",
|
||||
"state": "posted",
|
||||
"receipt": "ok sage-284-r2-request.outcome rev 4069 row 284 round 2 request sage-284-r2-request posted comment 31653"
|
||||
},
|
||||
"viewSha": "afadc7eb51a8de8bc7ea9e231718958e058ad4b34f355396d589e5b90d994ab0"
|
||||
},
|
||||
{
|
||||
"rev": 4070,
|
||||
"op": "webby-284-verdict-r2",
|
||||
"verb": "review-record",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"verdict": "approve",
|
||||
"comment": 31654,
|
||||
"candidate": "de505cfd18c82f66ff6cc5faf00789e81c66d148e07e1972c5d664031ee0f727"
|
||||
},
|
||||
"by": "webby",
|
||||
"at": "2026-10-24T23:48:52.791Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"round": 2,
|
||||
"verdict": "approve",
|
||||
"receipt": "ok webby-284-verdict-r2 rev 4070 row 284 round 2 approve by webby (comment 31654)"
|
||||
},
|
||||
"viewSha": "fc73cc2acac68aad3f22e65bd531f278572839f3134073aafd3cbf1e495411a3"
|
||||
},
|
||||
{
|
||||
"rev": 4071,
|
||||
"op": "sage-284-note-landed",
|
||||
"verb": "note",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"text": "LANDED ce0b6ed41 (fast-forward of Della r4 on 9e2689dff), pushed 00:09:52Z 10-25. Sage gate GREEN 00:09:04Z on ce0b6ed41: every package suite but queue (cli 999/0 + allowed FED R7 skip) and every scripts/test-*.sh rc 0. Webby APPROVE r2 c31654 rev 4070 on manifest-r4 de505cfd; landed tree matches. Della whole-file runs 51/0. #1789 closed with c31655."
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T00:11:07.895Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"field": "note",
|
||||
"receipt": "ok sage-284-note-landed rev 4071 row 284 note"
|
||||
},
|
||||
"viewSha": "c02f6437f45a2ef9181cd3b05d90b331d0468534a294200a55e38dcc316d3ac9"
|
||||
},
|
||||
{
|
||||
"rev": 4072,
|
||||
"op": "sage-284-done-landed",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 284,
|
||||
"to": "done",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T00:12:13.878Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 284,
|
||||
"from": "in-review",
|
||||
"to": "done",
|
||||
"round": 2,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok sage-284-done-landed rev 4072 row 284 in-review→done round 2"
|
||||
},
|
||||
"viewSha": "546679f234a78b2d4582ed5a50b90c7427bf82cf2bd75243eeccad1f5730fa79"
|
||||
}
|
||||
]
|
||||
}
|
||||
+30
-15
@@ -1,4 +1,5 @@
|
||||
import { randomUUID, randomBytes, createHash } from 'node:crypto';
|
||||
import { validManagementKey, proxyTiming } from '../../model-proxy/src/common.mjs';
|
||||
import { parseAddress } from './address.mjs';
|
||||
import { isAbsolute, dirname } from 'node:path';
|
||||
import { initializeControl, processingStatus } from '../../processing-control/src/state.mjs';
|
||||
@@ -105,7 +106,7 @@ export const PROJECT_FILE_VERBS = Object.freeze(['project.files', 'project.file'
|
||||
// here also makes server.mjs await their host effects instead of serializing a Promise as {}.
|
||||
const LIBRARY_LOOKUPS = Object.freeze({
|
||||
'library.find': { method: 'find', fields: ['universe', 'query', 'type', 'book', 'scope', 'offset'], required: ['universe', 'query'] },
|
||||
'library.get': { method: 'get', fields: ['universe', 'path', 'from', 'links_from'], required: ['universe', 'path'] },
|
||||
'library.get': { method: 'get', fields: ['universe', 'path', 'scope', 'from', 'links_from'], required: ['universe', 'path'] },
|
||||
'library.search': { method: 'search', fields: ['universe', 'text', 'book', 'scope', 'offset'], required: ['universe', 'text'] },
|
||||
'library.manuscript': { method: 'manuscript', fields: ['universe', 'book', 'scope', 'chapter', 'from', 'to'], required: ['universe', 'book'] },
|
||||
'library.universe': { method: 'universeInfo', fields: ['universe'], required: ['universe'] },
|
||||
@@ -2587,7 +2588,7 @@ export class Broker {
|
||||
}
|
||||
let step = 'B';
|
||||
try {
|
||||
const result = write('instance.fire');
|
||||
const result = await write('instance.fire');
|
||||
step = 'C';
|
||||
this.#store.transaction(() => {
|
||||
this.#recordAuthority(sc, 'role.fire', authority, { reason: args.reason, transaction: result.transaction ?? null });
|
||||
@@ -4593,7 +4594,8 @@ export class Broker {
|
||||
this.#organizationFail(e, 'instances.set');
|
||||
}
|
||||
// sage-1610 (b): a deleted agent's Vikunja bot is named, so the human removes it in Vikunja.
|
||||
return { transaction: out.transaction, retry: false, ...(out.tracker ? { bot: out.tracker.bot } : {}) };
|
||||
const finish = out => ({ transaction: out.transaction, retry: false, ...(out.tracker ? { bot: out.tracker.bot } : {}) });
|
||||
return out?.then ? out.then(finish, error => this.#organizationFail(error, 'instances.set')) : finish(out);
|
||||
}
|
||||
// The close of a taken request, with the run it started, the delete's transaction or the code
|
||||
// that stopped it. A delete also names the agent's Vikunja bot (sage-1610 b). A fire's end
|
||||
@@ -4823,14 +4825,16 @@ export class Broker {
|
||||
if (request.verb === 'proxy.init') {
|
||||
this.#human(s);
|
||||
if (!this.#proxyInit) fail('not-configured');
|
||||
keys(args, ['key', 'port'], ['key']);
|
||||
if (typeof args.key !== 'string' || !args.key.trim() || args.key.length > 4096 || /[\x00-\x1f\x7f]/.test(args.key)) fail('proxy-key-invalid');
|
||||
keys(args, ['key', 'port', 'rotateGraceMinutes', 'firstOutputSeconds'], ['key']);
|
||||
if (!validManagementKey(args.key)) fail('proxy-key-invalid');
|
||||
if (Object.hasOwn(args, 'port') && (!Number.isInteger(args.port) || args.port < 1024 || args.port > 65535 || args.port === 8317)) fail('proxy-config-invalid');
|
||||
try { proxyTiming(args); } catch (error) { fail(error.code); }
|
||||
// Async initialization is outside the store transaction. Refusals retain codes only.
|
||||
return Promise.resolve().then(() => this.#proxyInit(args)).then(result => {
|
||||
if (!object(result) || Object.keys(result).sort().join(',') !== 'binary,port'
|
||||
if (!object(result) || Object.keys(result).some(k => !['binary', 'port', 'rotateGraceMinutes', 'firstOutputSeconds'].includes(k))
|
||||
|| !Number.isInteger(result.port) || result.port < 1024 || result.port > 65535 || result.port === 8317
|
||||
|| typeof result.binary !== 'string' || !result.binary.startsWith('/')) fail('proxy-init-failed');
|
||||
try { proxyTiming(result); } catch (error) { fail(error.code); }
|
||||
this.#store.transaction(() => this.#event(s, 'action.allowed', { call: 'proxy.init', port: result.port }, null));
|
||||
return result;
|
||||
}).catch(error => { throw this.#refused(s, new BusError(/^proxy-[a-z-]+$/.test(error.code ?? '') ? error.code : 'proxy-init-failed'), request); });
|
||||
@@ -4882,7 +4886,10 @@ export class Broker {
|
||||
}
|
||||
if (CREDENTIAL_VERBS.includes(request.verb)) return this.#requestCredential(s, request.verb, structuredClone(args));
|
||||
if (request.verb === 'instance.hire' || request.verb === 'instance.fire')
|
||||
return this.#chiefLifecycle(s, request.verb, structuredClone(args));
|
||||
{
|
||||
const result = this.#chiefLifecycle(s, request.verb, structuredClone(args));
|
||||
return result?.then ? result.catch(error => { throw this.#refused(s, error, request); }) : result;
|
||||
}
|
||||
if (request.verb === 'grants.scope' || request.verb === 'grants.scope.set')
|
||||
return this.#grantScopeVerb(s, request.verb, structuredClone(args));
|
||||
if (["limits", "limits.set", "organization.set", "organization.revision", "plugins", "plugins.set", "instances", "instances.set", "routes", "routes.set", "veto.set", "library.limits.set"].includes(request.verb)) {
|
||||
@@ -5083,7 +5090,8 @@ export class Broker {
|
||||
const { reason: _, ...written } = args;
|
||||
const write = (op) => {
|
||||
try {
|
||||
return this.#organization(s.business, op, written, actor, context);
|
||||
const result = this.#organization(s.business, op, written, actor, context);
|
||||
return result?.then ? result.catch(error => this.#organizationFail(error, verb)) : result;
|
||||
} catch (e) {
|
||||
this.#organizationFail(e, verb);
|
||||
}
|
||||
@@ -5091,8 +5099,11 @@ export class Broker {
|
||||
if (hire) {
|
||||
this.#hireable(s, args.definition);
|
||||
const result = write('instance.hire');
|
||||
this.#store.transaction(() => this.#consumeAuthority(s, action, authority, { reason: args.reason, transaction: result.transaction }));
|
||||
return result;
|
||||
const finish = result => {
|
||||
this.#store.transaction(() => this.#consumeAuthority(s, action, authority, { reason: args.reason, transaction: result.transaction }));
|
||||
return result;
|
||||
};
|
||||
return result?.then ? result.then(finish) : finish(result);
|
||||
}
|
||||
if (!Object.hasOwn(b.roles, args.name)) fail('unknown-role');
|
||||
if (END_PROTECTED.includes(b.roles[args.name].definition)) fail('organization-definition-refused');
|
||||
@@ -5142,11 +5153,14 @@ export class Broker {
|
||||
// Step B: the delist. An instance already delisted counts as done.
|
||||
const result = write('instance.fire');
|
||||
// Step C: log the fire and record it done. A failure at B or C leaves I firing until a repeat.
|
||||
this.#store.transaction(() => {
|
||||
this.#consumeAuthority(s, action, authority, { reason: args.reason, transaction: result.transaction ?? null });
|
||||
this.#event(s, 'action.allowed', { action: 'fire.done', transaction: result.transaction ?? null }, args.name);
|
||||
});
|
||||
return { ...result, ...stopped };
|
||||
const finish = result => {
|
||||
this.#store.transaction(() => {
|
||||
this.#consumeAuthority(s, action, authority, { reason: args.reason, transaction: result.transaction ?? null });
|
||||
this.#event(s, 'action.allowed', { action: 'fire.done', transaction: result.transaction ?? null }, args.name);
|
||||
});
|
||||
return { ...result, ...stopped };
|
||||
};
|
||||
return result?.then ? result.then(finish) : finish(result);
|
||||
}
|
||||
// The shape of hire and fire arguments.
|
||||
#hireFireArgs(action, args) {
|
||||
@@ -5179,6 +5193,7 @@ export class Broker {
|
||||
// The bus code for an organization writer's refusal. Anything the writer didn't refuse by name is
|
||||
// outcome-unknown: the write may have committed.
|
||||
#organizationFail(e, verb) {
|
||||
if (['proxy-revoke-revision-conflict', 'proxy-key-issue-failed', 'proxy-key-revoking', 'proxy-key-refused', 'proxy-down', 'proxy-open'].includes(e.code)) fail(e.code);
|
||||
// Row 239 (#1745): the writer marks an error raised after a fire's or a delete's revoke ran, so
|
||||
// the human reads that the tokens may already be revoked. Without the mark, today's code.
|
||||
if (e.revoked) fail(e.code === "revision-conflict" ? "revision-conflict-revoked" : "outcome-unknown-revoked");
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { BusError } from './broker.mjs';
|
||||
import { credentialKind, isHostKind } from '../../business/src/vocabulary.mjs';
|
||||
import { credentialKind, isHostKind, ID_PATTERN } from '../../business/src/vocabulary.mjs';
|
||||
const deny = (code) => {
|
||||
throw new BusError(code);
|
||||
};
|
||||
@@ -125,7 +125,7 @@ export class Credentials {
|
||||
// storeRoot is <configDir>/secrets/agents (design D1); a reference with store: true must name the
|
||||
// path under it for its own business, agent and service. `held` lists "instance:service" keys whose
|
||||
// reference loads and whose file doesn't (#436b section 5).
|
||||
constructor({ references = {}, repoRoots = [], dataRoot, env = process.env, storeRoot = null, held = [] }) {
|
||||
constructor({ references = {}, repoRoots = [], dataRoot, env = process.env, storeRoot = null, held = [], modelAgents = [] }) {
|
||||
this.#storeRoot = storeRoot;
|
||||
this.#repoRoots = repoRoots;
|
||||
this.#dataRoot = dataRoot;
|
||||
@@ -139,6 +139,7 @@ export class Credentials {
|
||||
const value = this.#read(ref, env, service);
|
||||
if (value !== null) this.#tokens.set(instance + ':' + service, { ...value, date });
|
||||
}
|
||||
for (const instance of modelAgents) this.registerModel(instance);
|
||||
} catch (e) {
|
||||
this.close();
|
||||
throw e;
|
||||
@@ -146,7 +147,7 @@ export class Credentials {
|
||||
}
|
||||
// One reference's checks, for the constructor and register (row 235, r6 X4). Returns its date.
|
||||
#checkRef(instance, service, ref) {
|
||||
if (!credentialKind(service) || !ref || typeof ref !== 'object') deny('credential-reference');
|
||||
if (!(credentialKind(service) || service === 'model') || !ref || typeof ref !== 'object') deny('credential-reference');
|
||||
const date = service === 'vikunja' ? ref.expires : ref.rotateBy;
|
||||
if (!day(date)) deny('credential-date');
|
||||
if (
|
||||
@@ -177,6 +178,34 @@ export class Credentials {
|
||||
this.#refs.delete(k);
|
||||
this.#revoked.delete(k);
|
||||
}
|
||||
// Host-only implicit kind. It is deliberately absent from role service vocabulary:
|
||||
// neither role authority nor an agent's credential reference can opt into it.
|
||||
registerModel(instance) {
|
||||
const parts = typeof instance === 'string' ? instance.split('/') : [];
|
||||
if (parts.length !== 2 || parts.some(p => !ID_PATTERN.test(p))) deny('credential-reference');
|
||||
const k = instance + ':model';
|
||||
if (this.#refs.has(k)) return;
|
||||
const file = this.#storePath(instance, 'model');
|
||||
const ref = { store: true, file, rotateBy: '9999-12-31' };
|
||||
this.#refs.set(k, { ref, date: ref.rotateBy });
|
||||
// An instance may have no credential directory before its first hire/launch.
|
||||
if (!privateDir(this.#storeRoot, { missing: true })) return;
|
||||
const value = this.#read(ref, {}, 'model');
|
||||
if (value) {
|
||||
this.#tokens.set(k, { ...value, date: value.model.rotateBy });
|
||||
this.#refs.set(k, { ref: { ...ref, rotateBy: value.model.rotateBy }, date: value.model.rotateBy });
|
||||
}
|
||||
}
|
||||
// Trusted lifecycle use only; no socket verb returns this private record.
|
||||
modelRecord(instance) {
|
||||
const entry = this.#tokens.get(instance + ':model');
|
||||
return entry?.model ? structuredClone(entry.model) : null;
|
||||
}
|
||||
writeModel(instance, record) {
|
||||
this.registerModel(instance);
|
||||
this.issue(instance, 'model', JSON.stringify(record), record?.rotateBy);
|
||||
return this.status().find(e => e.instance === instance && e.service === 'model');
|
||||
}
|
||||
// r6 X5: the executor holds the entry's #inflight slot for its steps, so a host.run refuses host-busy
|
||||
// and revoke's abort() reaches the executor. A slot already held refuses host-busy. Returns the signal.
|
||||
claim(instance, service) {
|
||||
@@ -271,6 +300,20 @@ export class Credentials {
|
||||
}
|
||||
// { token } for gitea and vikunja, { token, key } for host.<id> (design D2).
|
||||
static #format(service, token) {
|
||||
if (service === 'model') {
|
||||
let m;
|
||||
try { m = JSON.parse(token); } catch { deny('credential-format'); }
|
||||
const keyOk = k => typeof k === 'string' && /^msk-[a-z0-9]+-[A-Za-z0-9_-]{43}$/.test(k);
|
||||
const keyId = k => createHash('sha256').update(k).digest('hex').slice(0, 12);
|
||||
const instant = s => typeof s === 'string' && !Number.isNaN(Date.parse(s)) && new Date(s).toISOString() === s;
|
||||
if (!m || Array.isArray(m) || Object.keys(m).some(k => !['key', 'keyId', 'issuedAt', 'rotateBy', 'state', 'revokingAt', 'retiring'].includes(k))
|
||||
|| !keyOk(m.key) || m.keyId !== keyId(m.key) || !instant(m.issuedAt) || !day(m.rotateBy)
|
||||
|| !['valid', 'revoking'].includes(m.state) || (m.state === 'revoking' ? !instant(m.revokingAt) : m.revokingAt !== undefined)
|
||||
|| !Array.isArray(m.retiring) || m.retiring.length > 64 || m.retiring.some(r => !r || Array.isArray(r)
|
||||
|| Object.keys(r).sort().join(',') !== 'issuedAt,key,keyId,retireAt'
|
||||
|| !keyOk(r.key) || r.keyId !== keyId(r.key) || !instant(r.issuedAt) || !instant(r.retireAt))) deny('credential-format');
|
||||
return { token: m.key, model: m };
|
||||
}
|
||||
if (isHostKind(service)) {
|
||||
const key = parseHostKey(token);
|
||||
return { token: key.text, key };
|
||||
@@ -309,7 +352,7 @@ export class Credentials {
|
||||
let fd;
|
||||
try {
|
||||
fd = openSync(tmp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
||||
writeSync(fd, value.key ? value.token : value.token + '\n');
|
||||
writeSync(fd, service === 'model' ? JSON.stringify(value.model) + '\n' : value.key ? value.token : value.token + '\n');
|
||||
fsyncSync(fd);
|
||||
} finally {
|
||||
if (fd !== undefined) closeSync(fd);
|
||||
@@ -380,8 +423,9 @@ export class Credentials {
|
||||
}
|
||||
// Memory first, then the store file (design section 6, D12). A file or env reference outside the
|
||||
// store is never unlinked; the caller turns its reference into store, so a restart finds nothing.
|
||||
revoke(instance, service) {
|
||||
revoke(instance, service, { modelConfirmed = false } = {}) {
|
||||
const { ref } = this.#known(instance, service);
|
||||
if (service === 'model' && this.#tokens.has(instance + ':model') && !modelConfirmed) deny('credential-revoke-failed');
|
||||
this.#tokens.delete(instance + ':' + service);
|
||||
this.#revoked.add(instance + ':' + service);
|
||||
this.#held.delete(instance + ':' + service);
|
||||
@@ -426,7 +470,8 @@ export class Credentials {
|
||||
} catch {
|
||||
deny('invalid-request');
|
||||
}
|
||||
for (const { token } of this.#tokens.values()) if (encoded?.includes(token)) deny('credential-leak');
|
||||
for (const { token, model } of this.#tokens.values())
|
||||
if ([token, ...(model?.retiring ?? []).map(r => r.key)].some(k => encoded?.includes(k))) deny('credential-leak');
|
||||
}
|
||||
// Row 210 (design D5): whether a host.run for this agent and host is in flight.
|
||||
busy(instance, service) {
|
||||
@@ -438,6 +483,7 @@ export class Credentials {
|
||||
const k = instance + ':' + service;
|
||||
const entry = this.#tokens.get(k);
|
||||
if (!entry) deny('credential-unavailable');
|
||||
if (entry.model?.state === 'revoking') deny('proxy-key-revoking');
|
||||
if (service === 'vikunja' && Date.now() >= Date.parse(entry.date + 'T00:00:00Z'))
|
||||
deny('credential-expired');
|
||||
if (!entry.key) {
|
||||
@@ -474,6 +520,7 @@ export class Credentials {
|
||||
status() {
|
||||
return [...this.#refs.entries()].map(([key, { date }]) => {
|
||||
const hostKey = this.#tokens.get(key)?.key;
|
||||
const model = this.#tokens.get(key)?.model;
|
||||
const i = key.lastIndexOf(':'),
|
||||
service = key.slice(i + 1),
|
||||
left = Date.parse(date + 'T00:00:00Z') - Date.now();
|
||||
@@ -482,13 +529,16 @@ export class Credentials {
|
||||
service,
|
||||
date,
|
||||
...(hostKey ? { fingerprint: hostKey.fingerprint, publicKey: hostKey.publicLine } : {}),
|
||||
...(model ? { keyId: model.keyId, issuedAt: model.issuedAt,
|
||||
...(model.revokingAt ? { revokingAt: model.revokingAt } : {}),
|
||||
retiring: model.retiring.map(({ keyId, issuedAt, retireAt }) => ({ keyId, issuedAt, retireAt })) } : {}),
|
||||
state: !this.#tokens.has(key)
|
||||
? this.#held.has(key)
|
||||
? 'held'
|
||||
: this.#revoked.has(key)
|
||||
? 'revoked'
|
||||
: 'not-issued'
|
||||
: service !== 'vikunja'
|
||||
: model?.state === 'revoking' ? 'revoking' : service !== 'vikunja'
|
||||
? left <= 0
|
||||
? 'rotation-due'
|
||||
: 'valid'
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
// P2: one broker holds this lifecycle controller after Store acquired its writer lock.
|
||||
// Every management-list read/modify/write is serialized. Only Credentials owns files.
|
||||
import { randomBytes, createHash } from 'node:crypto';
|
||||
import { performance } from 'node:perf_hooks';
|
||||
import { BusError } from './broker.mjs';
|
||||
export const REVOKE_TIMEOUT_MS = 5000;
|
||||
export const REVOKE_POLL_MS = 25;
|
||||
export const MODEL_STOP_TIMEOUT_MS = 5000;
|
||||
const keyId = key => createHash('sha256').update(key).digest('hex').slice(0, 12);
|
||||
const fail = code => { throw new BusError(code); };
|
||||
export class ModelKeys {
|
||||
#credentials; #client; #baseUrl; #prefix; #receipt; #now; #runs; #stopRuns; #grace;
|
||||
#tail = Promise.resolve(); #ready = false; #failure = 'proxy-down'; #confirmed = new Map();
|
||||
constructor({ credentials, client, configDir, baseUrl, receipt = () => {}, now = () => Date.now(),
|
||||
runs = () => [], stopRuns = async () => {}, rotateGraceMinutes = 120 }) {
|
||||
this.#credentials = credentials; this.#client = client; this.#baseUrl = baseUrl;
|
||||
this.#prefix = `msk-${keyId(configDir)}-`; this.#receipt = receipt; this.#now = now;
|
||||
this.#runs = runs; this.#stopRuns = stopRuns; this.#grace = rotateGraceMinutes * 60_000;
|
||||
}
|
||||
#exclusive(operation) {
|
||||
const result = this.#tail.then(operation);
|
||||
this.#tail = result.catch(() => {});
|
||||
return result;
|
||||
}
|
||||
#instances() { return this.#credentials.status().filter(s => s.service === 'model').map(s => s.instance); }
|
||||
#record(instance) { return this.#credentials.modelRecord(instance); }
|
||||
hasRecord(instance) { return this.#record(instance) !== null; }
|
||||
assertLaunchable(instance) { this.#launchable(instance); }
|
||||
#sentinel(keys) {
|
||||
if (!keys.some(k => k.startsWith(this.#prefix + 'sentinel-') && /^[A-Za-z0-9_-]{43}$/.test(k.slice((this.#prefix + 'sentinel-').length)))) {
|
||||
this.#ready = false; this.#failure = 'proxy-open'; fail('proxy-open');
|
||||
}
|
||||
}
|
||||
#launchable(instance) {
|
||||
if (this.#record(instance)?.state === 'revoking') fail('proxy-key-revoking');
|
||||
if (!this.#ready) fail(this.#failure);
|
||||
}
|
||||
async #replace(before, after) {
|
||||
this.#sentinel(before); this.#sentinel(after);
|
||||
await this.#client.putKeys(after);
|
||||
const confirmed = await this.#client.getKeys();
|
||||
this.#sentinel(confirmed);
|
||||
const removed = before.filter(k => !after.includes(k));
|
||||
if (after.some(k => !confirmed.includes(k)) || removed.some(k => confirmed.includes(k))) fail('proxy-client-response');
|
||||
}
|
||||
async #pollRevoked(keys) {
|
||||
const deadline = performance.now() + REVOKE_TIMEOUT_MS;
|
||||
for (const key of keys) {
|
||||
for (;;) {
|
||||
const remaining = deadline - performance.now();
|
||||
if (remaining <= 0) fail('credential-revoke-failed');
|
||||
let status;
|
||||
try { status = await this.#client.probeKey(key, { timeout: remaining }); }
|
||||
catch { status = 0; }
|
||||
if (status === 401) break;
|
||||
const left = deadline - performance.now();
|
||||
if (left <= 0) fail('credential-revoke-failed');
|
||||
await new Promise(resolve => setTimeout(resolve, Math.min(REVOKE_POLL_MS, left)));
|
||||
}
|
||||
}
|
||||
}
|
||||
async #removeKeys(keys) {
|
||||
const before = await this.#client.getKeys();
|
||||
await this.#replace(before, before.filter(k => !keys.includes(k)));
|
||||
await this.#pollRevoked(keys);
|
||||
}
|
||||
async reconcile() {
|
||||
return this.#exclusive(async () => {
|
||||
this.#ready = false;
|
||||
try {
|
||||
const before = await this.#client.getKeys(); this.#sentinel(before);
|
||||
const records = this.#instances().map(instance => ({ instance, record: this.#record(instance) })).filter(e => e.record);
|
||||
const retained = records.filter(e => e.record.state !== 'revoking').flatMap(e => [e.record.key, ...e.record.retiring.map(r => r.key)]);
|
||||
const revoking = records.filter(e => e.record.state === 'revoking');
|
||||
const intended = revoking.flatMap(e => [e.record.key, ...e.record.retiring.map(r => r.key)]);
|
||||
const removed = before.filter(k => k.startsWith(this.#prefix) && !k.startsWith(this.#prefix + 'sentinel-') && !retained.includes(k));
|
||||
const after = [...new Set([...before.filter(k => !removed.includes(k) && !intended.includes(k)), ...retained])];
|
||||
const added = after.filter(k => !before.includes(k));
|
||||
if (added.length || removed.length || intended.length) await this.#replace(before, after);
|
||||
await this.#pollRevoked([...new Set([...removed, ...intended])]);
|
||||
for (const { instance, record } of revoking) {
|
||||
this.#confirmed.set(instance, record.keyId);
|
||||
this.completeRevoke(instance);
|
||||
}
|
||||
this.#ready = true;
|
||||
const receipt = { action: 'proxy.reconcile', addedKeyIds: added.map(keyId), removedKeyIds: [...new Set([...removed, ...intended])].map(keyId) };
|
||||
this.#receipt(receipt); return receipt;
|
||||
} catch (error) {
|
||||
this.#failure = error.code === 'proxy-open' ? 'proxy-open' : 'proxy-down';
|
||||
this.#receipt({ action: 'proxy.reconcile', code: this.#failure });
|
||||
fail(this.#failure);
|
||||
}
|
||||
});
|
||||
}
|
||||
async #issue(instance, rotate = false, currentKeys = null) {
|
||||
this.#launchable(instance); this.#credentials.registerModel(instance);
|
||||
const old = this.#record(instance);
|
||||
if (old && !rotate) return { baseUrl: this.#baseUrl, key: old.key };
|
||||
const key = this.#prefix + randomBytes(32).toString('base64url');
|
||||
const now = this.#now(), issuedAt = new Date(now).toISOString();
|
||||
const date = new Date(now + 30 * 86400_000).toISOString().slice(0, 10);
|
||||
const retiring = old ? [...old.retiring, { key: old.key, keyId: old.keyId, issuedAt: old.issuedAt, retireAt: new Date(now + this.#grace).toISOString() }] : [];
|
||||
try {
|
||||
const before = currentKeys ?? await this.#client.getKeys();
|
||||
await this.#replace(before, [...new Set([...before, key])]);
|
||||
this.#credentials.writeModel(instance, { key, keyId: keyId(key), issuedAt, rotateBy: date, state: 'valid', retiring });
|
||||
this.#receipt({ action: rotate ? 'proxy.key.rotated' : 'proxy.key.issued', keyIds: [keyId(key)], instance });
|
||||
return { baseUrl: this.#baseUrl, key };
|
||||
} catch (error) {
|
||||
if (error.code === 'proxy-open') throw error;
|
||||
fail('proxy-key-issue-failed');
|
||||
}
|
||||
}
|
||||
ensure(instance) { return this.#exclusive(() => this.#issue(instance)); }
|
||||
rotate(instance) { return this.#exclusive(() => this.#issue(instance, true)); }
|
||||
async preflight(instance) {
|
||||
// The revoking check must run before even an issue/preflight request.
|
||||
this.#launchable(instance);
|
||||
const proxy = await this.#exclusive(async () => {
|
||||
this.#launchable(instance);
|
||||
let keys;
|
||||
try { keys = await this.#client.getKeys(); } catch { fail('proxy-down'); }
|
||||
this.#sentinel(keys);
|
||||
return this.#issue(instance, false, keys);
|
||||
});
|
||||
let status;
|
||||
try { status = await this.#client.probeKey(proxy.key); }
|
||||
catch { fail('proxy-down'); }
|
||||
if (status < 200 || status >= 300) fail('proxy-key-refused');
|
||||
return proxy;
|
||||
}
|
||||
prepareRevoke(instance) {
|
||||
return this.#exclusive(async () => {
|
||||
const record = this.#record(instance);
|
||||
if (!record) return;
|
||||
if (record.state !== 'revoking') this.#credentials.writeModel(instance, { ...record, state: 'revoking', revokingAt: new Date(this.#now()).toISOString() });
|
||||
try {
|
||||
await this.#removeKeys([record.key, ...record.retiring.map(r => r.key)]);
|
||||
this.#confirmed.set(instance, record.keyId);
|
||||
this.#receipt({ action: 'proxy.key.revoked', keyIds: [record.keyId, ...record.retiring.map(r => r.keyId)], remoteState: 'revoked', localState: 'kept' });
|
||||
} catch { fail('credential-revoke-failed'); }
|
||||
});
|
||||
}
|
||||
completeRevoke(instance) {
|
||||
const record = this.#record(instance);
|
||||
if (!record) return;
|
||||
if (record.state !== 'revoking' || this.#confirmed.get(instance) !== record.keyId) fail('credential-revoke-failed');
|
||||
this.#credentials.revoke(instance, 'model', { modelConfirmed: true }); this.#confirmed.delete(instance);
|
||||
}
|
||||
revisionConflict(instance) {
|
||||
const record = this.#record(instance);
|
||||
this.#receipt({ action: 'proxy.key.revoke-conflict', keyIds: record ? [record.keyId] : [], remoteState: 'revoked', localState: 'kept' });
|
||||
fail('proxy-revoke-revision-conflict');
|
||||
}
|
||||
sweep() {
|
||||
return this.#exclusive(async () => {
|
||||
for (const instance of this.#instances()) {
|
||||
let record = this.#record(instance);
|
||||
if (!record || record.state === 'revoking') continue;
|
||||
if (this.#now() >= Date.parse(record.rotateBy + 'T00:00:00Z')) {
|
||||
await this.#issue(instance, true); record = this.#record(instance);
|
||||
}
|
||||
for (const old of record.retiring) {
|
||||
const runs = this.#runs(instance).filter(r => r.keyId === old.keyId);
|
||||
if (runs.length && this.#now() < Date.parse(old.retireAt)) continue;
|
||||
if (runs.length) {
|
||||
try { await this.#stopRuns(runs); } catch { fail('proxy-stop-failed'); }
|
||||
}
|
||||
if (this.#runs(instance).some(r => r.keyId === old.keyId)) fail('proxy-stop-failed');
|
||||
await this.#removeKeys([old.key]);
|
||||
const current = this.#record(instance);
|
||||
this.#credentials.writeModel(instance, { ...current, retiring: current.retiring.filter(r => r.keyId !== old.keyId) });
|
||||
this.#receipt({ action: 'proxy.key.retired', keyIds: [old.keyId] });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// No new processing-control stop reason/schema: stopSession owns bounded SIGTERM
|
||||
// and start-time verification. This layer additionally checks agent/key ownership.
|
||||
export async function stopModelRuns({ runs, sessions, stop, receipt, reason = 'proxy-rotation', timeoutMs = MODEL_STOP_TIMEOUT_MS }) {
|
||||
for (const run of runs) {
|
||||
let timer;
|
||||
const ids = typeof run.keyId === 'string' && /^[a-f0-9]{12}$/.test(run.keyId) ? [run.keyId] : [];
|
||||
try {
|
||||
if (!ids.length) fail('proxy-stop-failed');
|
||||
const entry = sessions().find(s => s.business === run.business && s.run === run.run);
|
||||
if (!entry || entry.instance !== run.role || entry.modelKeyId !== run.keyId
|
||||
|| entry.pid !== run.pid || entry.startTime !== run.startTime) fail('proxy-stop-failed');
|
||||
if (entry.live) {
|
||||
const result = await Promise.race([
|
||||
stop(run.run),
|
||||
new Promise((_, reject) => { timer = setTimeout(() => reject(new BusError('proxy-stop-failed')), timeoutMs); }),
|
||||
]);
|
||||
if (!result?.stopped || sessions().some(s => s.business === run.business && s.run === run.run && s.live)) fail('proxy-stop-failed');
|
||||
}
|
||||
receipt({ action: reason, keyIds: ids, business: run.business, agent: run.role, run: run.run, outcome: 'stopped' });
|
||||
} catch {
|
||||
receipt({ action: reason, code: 'proxy-stop-failed', keyIds: ids, run: run.run });
|
||||
fail('proxy-stop-failed');
|
||||
} finally { clearTimeout(timer); }
|
||||
}
|
||||
}
|
||||
|
||||
// Option C: HTTP is outside the organization lock. This adapter preserves write's
|
||||
// synchronous contract and passes the captured revision into its existing lock check.
|
||||
// Neither organization.mjs nor its synchronous settings callers needs modification.
|
||||
export function modelOrganization({ modelKeys, writer }) {
|
||||
const pending = new Map();
|
||||
const adapter = {
|
||||
...writer,
|
||||
write(business, document, options) {
|
||||
const prepare = pending.get(business);
|
||||
return writer.write(business, document, prepare ? { ...options, revision: prepare.revision } : options);
|
||||
},
|
||||
};
|
||||
const revoke = original => (business, agent, how, services) => {
|
||||
const prepare = pending.get(business), instance = business + '/' + agent;
|
||||
if (!prepare || prepare.instance !== instance) fail('credential-revoke-failed');
|
||||
// For an already-delisted repeat there is no write callback/lock, but the
|
||||
// revision still must match before completing the model credential cleanup.
|
||||
if (writer.read(business).revision !== prepare.revision) modelKeys.revisionConflict(instance);
|
||||
modelKeys.completeRevoke(instance);
|
||||
return original(business, agent, how, services);
|
||||
};
|
||||
const wrap = invoke => (business, verb, args, actor, context) => {
|
||||
const isRevoke = verb === 'instance.fire' || (verb === 'instances.set' && args.op === 'delete');
|
||||
if (!isRevoke && verb !== 'instance.hire') return invoke(business, verb, args, actor, context);
|
||||
return (async () => {
|
||||
if (pending.has(business)) fail('organization-busy');
|
||||
const agent = args.name ?? args.instance, instance = business + '/' + agent;
|
||||
if (verb === 'instance.fire') invoke(business, 'instance.fireCheck', { name: agent }, actor, context);
|
||||
if (verb === 'instances.set') invoke(business, 'instances.check', { instance: agent }, actor, context);
|
||||
const { revision } = writer.read(business);
|
||||
if (args.revision !== undefined && args.revision !== revision) fail('revision-conflict');
|
||||
const prepare = { instance, revision, remoteRevoked: false };
|
||||
pending.set(business, prepare);
|
||||
const existed = modelKeys.hasRecord(instance);
|
||||
try {
|
||||
if (isRevoke) { await modelKeys.prepareRevoke(instance); prepare.remoteRevoked = existed; }
|
||||
else await modelKeys.ensure(instance);
|
||||
return invoke(business, verb, args, actor, context);
|
||||
} catch (error) {
|
||||
if (isRevoke && prepare.remoteRevoked && (error.code === 'revision-conflict' || error.code === 'revision-conflict-revoked')) modelKeys.revisionConflict(instance);
|
||||
// A failed hire must not leave a newly issued credential usable. Preserve
|
||||
// a revoking intent if remote cleanup fails; boot reconciliation finishes it.
|
||||
if (!isRevoke && !existed && modelKeys.hasRecord(instance)) {
|
||||
try { await modelKeys.prepareRevoke(instance); modelKeys.completeRevoke(instance); } catch {}
|
||||
}
|
||||
throw error;
|
||||
} finally { pending.delete(business); }
|
||||
})();
|
||||
};
|
||||
return { writer: adapter, revoke, wrap };
|
||||
}
|
||||
@@ -8,6 +8,8 @@ const LAUNCH_OPS = new Set([
|
||||
'refuse',
|
||||
'endLaunch',
|
||||
'credentialStatus',
|
||||
'modelPreflight',
|
||||
'modelStall',
|
||||
'checkPush',
|
||||
'recordPush',
|
||||
'reviewFetch',
|
||||
@@ -76,6 +78,8 @@ function launchOp(m) {
|
||||
if (m.op === 'sessionClearRetire') return runtime.sessionClearRetire(m.record);
|
||||
if (m.op === 'sessionClearStart') return runtime.sessionClearStart(m.record);
|
||||
if (m.op === 'credentialStatus') return runtime.credentialStatus(m.business, m.role);
|
||||
if (m.op === 'modelPreflight') return runtime.modelPreflight(m);
|
||||
if (m.op === 'modelStall') return runtime.modelStall(m);
|
||||
// Every LAUNCH_OPS name has its line above. A missing one used to answer as credentialStatus,
|
||||
// whose [] for an unknown role read as "no holders" for landHolders (row 156 mutant P02).
|
||||
throw new BusError('launch-op-refused');
|
||||
|
||||
@@ -8,13 +8,14 @@ import { Broker, BusError, LIBRARY_FETCH_FIX } from './broker.mjs';
|
||||
import { Credentials } from './credentials.mjs';
|
||||
import { serve } from './server.mjs';
|
||||
import { launchSettings } from '../../business/src/launch-settings.mjs';
|
||||
import { readSessions } from '../../seat/src/session.mjs';
|
||||
import { readSessions, stopSession } from '../../seat/src/session.mjs';
|
||||
import { compactionCount, storeDir } from '../../seat/src/compactions.mjs';
|
||||
import { publishRemote, workingRepo, WorkingError } from '../../seat/src/working.mjs';
|
||||
import { createLoader, hasModule } from '../../plugins/src/loader.mjs';
|
||||
import { pluginSettings } from '../../business/src/plugin-settings.mjs';
|
||||
import { loadManagementClient } from '../../model-proxy/src/client.mjs';
|
||||
import { initializeProxy } from '../../model-proxy/src/settings.mjs';
|
||||
import { ModelKeys, modelOrganization, stopModelRuns, MODEL_STOP_TIMEOUT_MS } from './model-keys.mjs';
|
||||
import { instanceSettings } from '../../business/src/instance-settings.mjs';
|
||||
import { organizationWriter, OrganizationError, setReplyLimits } from '../../business/src/organization.mjs';
|
||||
import { credentialVerbs } from './credential-verbs.mjs';
|
||||
@@ -159,6 +160,8 @@ export async function startBroker({
|
||||
|| (Object.hasOwn(librarySync, 'configDir') && typeof librarySync.configDir !== 'string')))
|
||||
throw new BusError('invalid-host-request');
|
||||
let modelProxyClient = null;
|
||||
let modelKeys = null, modelTimer = null, modelSweep = null;
|
||||
const modelRuns = new Map();
|
||||
let store, credentials, server, adapter, processing, syncTimer = null, syncPass = null, accessTimer = null;
|
||||
try {
|
||||
const proxyReceipt = entry => process.stderr.write(`model-proxy: ${JSON.stringify(entry)}\n`);
|
||||
@@ -198,7 +201,19 @@ export async function startBroker({
|
||||
// Row 236 (#1742, #436b r5 section 5): each gitea grant record gives its agent a store reference to
|
||||
// gitea, and `held` names the files memory must not load until reconcile or a revoke.
|
||||
const { held } = storeRoot ? giteaStart(store, storeRoot, businesses, references) : { held: [] };
|
||||
credentials = new Credentials({ references, repoRoots: protectedRoots, dataRoot, storeRoot, held });
|
||||
credentials = new Credentials({ references, repoRoots: protectedRoots, dataRoot, storeRoot, held,
|
||||
modelAgents: proxy ? Object.entries(businesses).flatMap(([id, b]) => Object.keys(b.roles).map(role => id + '/' + role)) : [] });
|
||||
if (proxy) {
|
||||
modelKeys = new ModelKeys({ credentials, client: modelProxyClient, configDir: organization.dir,
|
||||
baseUrl: `http://127.0.0.1:${proxy.port}`, receipt: proxyReceipt, rotateGraceMinutes: proxy.rotateGraceMinutes ?? 120,
|
||||
runs: instance => [...modelRuns.values()].filter(r => r.instance === instance),
|
||||
stopRuns: async runs => {
|
||||
await stopModelRuns({ runs, sessions: () => readSessions(dataRoot), stop: run => stopSession(dataRoot, run, { timeoutMs: MODEL_STOP_TIMEOUT_MS }), receipt: proxyReceipt });
|
||||
for (const r of runs) modelRuns.delete(r.business + ':' + r.run);
|
||||
} });
|
||||
// Keep the control/human surface available, but refuse every launch on failed reconcile.
|
||||
try { await modelKeys.reconcile(); } catch (error) { proxyReceipt({ action: 'proxy.boot-refused', code: error.code }); }
|
||||
}
|
||||
// vikunja:19/305: the plug-in switch through the same human-only path as the limits: the verbs
|
||||
// plugins and plugins.set, logged in changes.jsonl with the actor, a missing module refused.
|
||||
const plugins=organization?pluginSettings({dir:organization.dir,rolesDir:organization.rolesDir,exists:hasModule}):null;
|
||||
@@ -240,7 +255,9 @@ export async function startBroker({
|
||||
return { revoked: services, outsideStore: how === 'fire' ? outside : [] };
|
||||
}
|
||||
// Row 165 (#1662): the human adds and changes role instances through instances and instances.set.
|
||||
const instances=organization?instanceSettings({dir:organization.dir,rolesDir:organization.rolesDir,system:organization.system,levels,revoke}):null;
|
||||
const proxyOrganization = modelKeys ? modelOrganization({ modelKeys, writer: organizationWriter({ dir: organization.dir, rolesDir: organization.rolesDir }) }) : null;
|
||||
const instances=organization?instanceSettings({dir:organization.dir,rolesDir:organization.rolesDir,system:organization.system,levels,revoke,
|
||||
...(proxyOrganization ? { writer: proxyOrganization.writer, revoke: proxyOrganization.revoke(revoke) } : {})}):null;
|
||||
const usage = await createUsage({ store, dataRoot, businesses,
|
||||
log: entry => process.stderr.write(`usage: ${JSON.stringify(entry)}\n`) });
|
||||
// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): the limit checks read the organization file at
|
||||
@@ -378,7 +395,7 @@ export async function startBroker({
|
||||
},
|
||||
// limits and limits.set (organization.set is limits.set) carry each token limit's count; a
|
||||
// plugins.set reloads the business's limit checks in the background.
|
||||
organization:settings?(id,verb,args,actor,context)=>{
|
||||
organization:settings?(proxyOrganization?.wrap ?? (operation => operation))((id,verb,args,actor,context)=>{
|
||||
if(verb==='limits')return withTokens(id,settings.read(id));
|
||||
if(verb==='plugins')return plugins.read(id);
|
||||
// Row 256 (#1761): the actor is the reader, so endProtected is the delete check for that reader.
|
||||
@@ -413,7 +430,7 @@ export async function startBroker({
|
||||
// took, so a refused one (credentials, roots, ...) shows the restart line for a keep or a
|
||||
// hire too. The other writers' answers carry no restart field.
|
||||
return INSTANCE_VERBS.has(verb)?{...result,restart:!live}:result;
|
||||
}:null,
|
||||
}):null,
|
||||
// Row 194 (D2.3): the role files as they stand, for the hire checks.
|
||||
roleFile: organization ? (name) => loadRole(organization.rolesDir, name) : null,
|
||||
secretCheck: (value) => credentials.assertClean(value),
|
||||
@@ -472,8 +489,15 @@ export async function startBroker({
|
||||
!/^\d+$/.test(r.startTime)
|
||||
)
|
||||
throw new BusError('invalid-launch-process');
|
||||
const cap = broker.bindLaunch(r);
|
||||
const { modelKeyId, ...record } = r;
|
||||
if (modelKeys) {
|
||||
modelKeys.assertLaunchable(r.business + '/' + r.role);
|
||||
const m = credentials.modelRecord(r.business + '/' + r.role);
|
||||
if (!m || ![m.keyId, ...m.retiring.map(k => k.keyId)].includes(modelKeyId)) throw new BusError('proxy-key-refused');
|
||||
}
|
||||
const cap = broker.bindLaunch(record);
|
||||
launchRecords.push({ ...r });
|
||||
if (modelKeys) modelRuns.set(r.business + ':' + r.run, { ...r, instance: r.business + '/' + r.role, keyId: modelKeyId });
|
||||
return { business: r.business, run: r.run, cap };
|
||||
}
|
||||
const bound = launches.map(bindLaunch);
|
||||
@@ -634,7 +658,7 @@ export async function startBroker({
|
||||
return credentials
|
||||
.status()
|
||||
.filter((c) => c.instance === business + '/' + role)
|
||||
.map(({ service, date, state }) => ({ service, date, state }));
|
||||
.map(({ service, date, state, keyId, issuedAt }) => ({ service, date, state, ...(service === 'model' ? { keyId, issuedAt } : {}) }));
|
||||
}
|
||||
// C2-03a: each library entry with a sync user fetches once at start, then every 60 s. A pass
|
||||
// still running skips the tick. Only codes are logged, never git's output.
|
||||
@@ -679,6 +703,13 @@ export async function startBroker({
|
||||
}, access.intervalMs ?? ACCESS_SWEEP_MS);
|
||||
accessTimer.unref();
|
||||
}
|
||||
if (modelKeys) {
|
||||
modelTimer = setInterval(() => {
|
||||
if (modelSweep) return;
|
||||
modelSweep = modelKeys.sweep().catch(error => proxyReceipt({ action: 'proxy.sweep-refused', code: error.code ?? 'proxy-key-issue-failed' })).finally(() => { modelSweep = null; });
|
||||
}, 1000);
|
||||
modelTimer.unref();
|
||||
}
|
||||
return {
|
||||
broker,
|
||||
credentials,
|
||||
@@ -686,7 +717,20 @@ export async function startBroker({
|
||||
get modelProxy() { return modelProxyClient; },
|
||||
path,
|
||||
bindLaunch,
|
||||
endLaunch: (r) => broker.endLaunch(r),
|
||||
endLaunch: (r) => { const out = broker.endLaunch(r); modelRuns.delete(r.business + ':' + r.run); return out; },
|
||||
modelPreflight: async ({ business, role }) => {
|
||||
const current = settings ? settings.current(business) : businesses[business];
|
||||
if (!Object.hasOwn(current?.roles ?? {}, role)) throw new BusError('unknown-role');
|
||||
return modelKeys ? modelKeys.preflight(business + '/' + role) : null;
|
||||
},
|
||||
modelStall: async ({ business, role, run, keyId }) => {
|
||||
const record = modelRuns.get(business + ':' + run);
|
||||
if (!record || record.role !== role || record.keyId !== keyId) throw new BusError('proxy-stop-failed');
|
||||
await stopModelRuns({ runs: [record], reason: 'proxy-stall', sessions: () => readSessions(dataRoot),
|
||||
stop: id => stopSession(dataRoot, id, { timeoutMs: MODEL_STOP_TIMEOUT_MS }), receipt: proxyReceipt });
|
||||
modelRuns.delete(business + ':' + run);
|
||||
return { stopped: true };
|
||||
},
|
||||
threadsState: (business) => broker.threadsState(business),
|
||||
threadIdle: (r) => broker.threadIdle(r),
|
||||
// Row 125 (#1619, REQ-LIMIT-4): the launcher asks the limit checks before every launch.
|
||||
@@ -728,6 +772,8 @@ export async function startBroker({
|
||||
launches: bound,
|
||||
readers: readCaps,
|
||||
async close() {
|
||||
clearInterval(modelTimer);
|
||||
await modelSweep;
|
||||
clearInterval(accessTimer);
|
||||
await stopSync();
|
||||
await server.close();
|
||||
@@ -738,6 +784,7 @@ export async function startBroker({
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
clearInterval(modelTimer);
|
||||
clearInterval(accessTimer);
|
||||
await server?.close();
|
||||
await adapter?.close?.();
|
||||
|
||||
@@ -27,8 +27,8 @@ const TEXTS = {
|
||||
'Second World/Characters/Elsewhere.md': note('character', 'Elsewhere', 'Other ferry.'),
|
||||
};
|
||||
|
||||
async function world(t, { projects, adapter, claimed = true, now = () => NOW } = {}) {
|
||||
const f = scratchRepo(t, TEXTS), dataRoot = join(f.root, 'data');
|
||||
async function world(t, { projects, adapter, claimed = true, now = () => NOW, files = TEXTS } = {}) {
|
||||
const f = scratchRepo(t, files), dataRoot = join(f.root, 'data');
|
||||
mkdirSync(dataRoot);
|
||||
const business = { id: 'acme', human: 'jason', vars: { 'bus.tester': true },
|
||||
projects: projects ?? { books: mappingEntry() }, arbiters: { technical: 'coder', delivery: 'coder' },
|
||||
@@ -235,3 +235,19 @@ test('runtime actually injects the lookup adapter, without exposing human librar
|
||||
await assert.rejects(runtime.broker.request(cap, { verb: 'library.file', args: { project: 'books', path: GET.path } }),
|
||||
{ code: 'human-required' });
|
||||
});
|
||||
|
||||
test('C2-05c broker forwards supporting scope and returns the served blob without adding write verbs', async t => {
|
||||
const projects = { books: mappingEntry() };
|
||||
projects.books.universes.harbor.supporting = ['Book One/Support/'];
|
||||
const path = 'Book One/Support/Helper.md';
|
||||
const f = await world(t, { projects, files: { ...TEXTS, [path]: note('summary', 'Helper', 'Supporting fixture.') } });
|
||||
assert.deepEqual((await f.call('library.find', { universe: 'harbor', query: '', scope: 'supporting' })).results.map(x => x.path), [path]);
|
||||
assert.deepEqual((await f.call('library.search', { universe: 'harbor', text: 'Supporting fixture', scope: 'supporting' })).results.map(x => x.path), [path]);
|
||||
assert.equal(await f.call('library.get', { universe: 'harbor', path }), null);
|
||||
const result = await f.call('library.get', { universe: 'harbor', path, scope: 'supporting' });
|
||||
assert.equal(result.scope, 'supporting');
|
||||
assert.equal(result.commit, f.commit);
|
||||
assert.equal(result.blob, git(f.mirror, 'rev-parse', `${f.commit}:${path}`));
|
||||
assert.equal(await f.call('library.get', { universe: 'second', path, scope: 'supporting' }), null);
|
||||
await assert.rejects(f.call('library.manuscript', { universe: 'harbor', book: 'Book One', scope: 'supporting' }), { code: 'query-argument-invalid' });
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
// P2 RED: model is implicit, durable/private, and status never exposes current/retiring values.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { randomBytes, createHash } from 'node:crypto';
|
||||
import { Credentials } from '../src/credentials.mjs';
|
||||
|
||||
function fixture(t) {
|
||||
const root = fs.mkdtempSync(join(tmpdir(), 'p2-store-'));
|
||||
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
||||
const storeRoot = join(root, 'config', 'secrets', 'agents');
|
||||
fs.mkdirSync(storeRoot, { recursive: true, mode: 0o700 });
|
||||
const open = () => new Credentials({ storeRoot, dataRoot: join(root, 'data'), modelAgents: ['demo/coder'] });
|
||||
const file = join(storeRoot, 'demo', 'coder', 'model');
|
||||
return { root, storeRoot, open, file };
|
||||
}
|
||||
const key = () => 'msk-fixture-' + randomBytes(32).toString('base64url');
|
||||
const id = value => createHash('sha256').update(value).digest('hex').slice(0, 12);
|
||||
const record = value => ({ key: value, keyId: id(value), issuedAt: '2026-10-24T00:00:00.000Z', rotateBy: '2026-11-23', state: 'valid', retiring: [] });
|
||||
|
||||
test('implicit model kind starts not-issued without a business or role reference', t => {
|
||||
const f = fixture(t), c = f.open(); t.after(() => c.close());
|
||||
const status = c.status().find(s => s.instance === 'demo/coder' && s.service === 'model');
|
||||
assert.equal(status?.state, 'not-issued');
|
||||
assert.equal(fs.existsSync(f.file), false);
|
||||
});
|
||||
test('model record persists at 0600 in 0700 dirs and reloads ids/issue time/state only in status', t => {
|
||||
const f = fixture(t), c = f.open(), value = key(); t.after(() => c.close());
|
||||
assert.equal(typeof c.writeModel, 'function');
|
||||
c.writeModel('demo/coder', record(value));
|
||||
assert.equal(fs.statSync(f.file).mode & 0o777, 0o600);
|
||||
for (const dir of [f.storeRoot, join(f.storeRoot, 'demo'), join(f.storeRoot, 'demo', 'coder')]) assert.equal(fs.statSync(dir).mode & 0o777, 0o700);
|
||||
c.close();
|
||||
const again = f.open(); t.after(() => again.close());
|
||||
const status = again.status().find(s => s.service === 'model');
|
||||
assert.equal(status.keyId === id(value), true);
|
||||
assert.equal(status.issuedAt, '2026-10-24T00:00:00.000Z');
|
||||
assert.equal(status.state, 'valid');
|
||||
assert.equal(JSON.stringify(again.status()).includes(value), false);
|
||||
assert.equal(again.modelRecord('demo/coder').key === value, true);
|
||||
});
|
||||
test('revoking model metadata survives restart without losing the private record', t => {
|
||||
const f = fixture(t), c = f.open(), value = key(); t.after(() => c.close());
|
||||
assert.equal(typeof c.writeModel, 'function');
|
||||
c.writeModel('demo/coder', { ...record(value), state: 'revoking', revokingAt: '2026-10-24T01:00:00.000Z' });
|
||||
c.close(); const again = f.open(); t.after(() => again.close());
|
||||
assert.equal(again.status().find(s => s.service === 'model').state, 'revoking');
|
||||
assert.equal(again.modelRecord('demo/coder').key === value, true);
|
||||
assert.equal(JSON.stringify(again.status()).includes(value), false);
|
||||
});
|
||||
test('model record wrong file mode refuses startup rather than inventing a replacement', t => {
|
||||
const f = fixture(t), c = f.open(); t.after(() => c.close());
|
||||
assert.equal(typeof c.writeModel, 'function'); c.writeModel('demo/coder', record(key())); c.close();
|
||||
fs.chmodSync(f.file, 0o640);
|
||||
assert.throws(() => f.open(), e => e.code === 'credential-file');
|
||||
});
|
||||
test('non-proxy Credentials has no implicit model kind', t => {
|
||||
const f = fixture(t), c = new Credentials({ storeRoot: f.storeRoot, dataRoot: join(f.root, 'data') });
|
||||
t.after(() => c.close()); assert.deepEqual(c.status(), []);
|
||||
});
|
||||
test('credential-revoke-failed: generic remove cannot bypass remote model confirmation', t => {
|
||||
const f = fixture(t), c = f.open(); t.after(() => c.close());
|
||||
c.writeModel('demo/coder', record(key()));
|
||||
assert.throws(() => c.remove('demo/coder'), e => e.code === 'credential-revoke-failed');
|
||||
assert.equal(c.modelRecord('demo/coder').state, 'valid'); assert.equal(fs.existsSync(f.file), true);
|
||||
});
|
||||
test('retiring keys reload privately and remain covered by the credential leak guard', t => {
|
||||
const f = fixture(t), c = f.open(), value = key(), old = key(); t.after(() => c.close());
|
||||
c.writeModel('demo/coder', { ...record(value), retiring: [{ key: old, keyId: id(old), issuedAt: '2026-10-23T00:00:00.000Z', retireAt: '2026-10-24T02:00:00.000Z' }] });
|
||||
c.close(); const again = f.open(); t.after(() => again.close());
|
||||
assert.equal(again.modelRecord('demo/coder').retiring[0].key === old, true);
|
||||
const status = JSON.stringify(again.status()); assert.equal(status.includes(old), false); assert.equal(status.includes(value), false);
|
||||
assert.throws(() => again.assertClean({ value: old }), e => e.code === 'credential-leak');
|
||||
});
|
||||
@@ -0,0 +1,260 @@
|
||||
// P2: real private Credentials, fake loopback management/model server. No live config.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import http from 'node:http';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { Credentials } from '../src/credentials.mjs';
|
||||
import { ManagementClient } from '../../model-proxy/src/client.mjs';
|
||||
import { ModelKeys, REVOKE_TIMEOUT_MS, REVOKE_POLL_MS } from '../src/model-keys.mjs';
|
||||
import { startBroker } from '../src/runtime.mjs';
|
||||
import { busBusiness } from '../src/business.mjs';
|
||||
import { loadBusiness } from '../../business/src/business.mjs';
|
||||
import { resolveInstance } from '../../business/src/resolve.mjs';
|
||||
import { businessDoc, rolesCopy, systemFor, writeJson } from '../../business/tests/helpers.mjs';
|
||||
const hash = s => createHash('sha256').update(s).digest('hex').slice(0, 12);
|
||||
const failCode = code => e => e.code === code;
|
||||
async function fixture(t, { delayed = 0 } = {}) {
|
||||
const root = fs.mkdtempSync(join(tmpdir(), 'p2-keys-')), configDir = join(root, 'config');
|
||||
const storeRoot = join(configDir, 'secrets', 'agents'); fs.mkdirSync(storeRoot, { recursive: true, mode: 0o700 });
|
||||
const prefix = `msk-${hash(configDir)}-`, makeKey = () => prefix + randomBytes(32).toString('base64url');
|
||||
const sentinel = prefix + 'sentinel-' + randomBytes(32).toString('base64url');
|
||||
const state = { keys: [sentinel], calls: [], delay: delayed, removed: new Map(), failGet: false, failPut: false, hideConfirm: false, probes: 0, status: null };
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const management = req.url === '/v8/management/config/access/api-keys';
|
||||
state.calls.push(management ? req.method : 'PROBE');
|
||||
if (management) {
|
||||
if (req.headers.authorization !== 'Bearer fx331-manage') { res.writeHead(401); res.end('{}'); return; }
|
||||
if ((req.method === 'GET' && state.failGet) || (req.method === 'PUT' && state.failPut)) { res.writeHead(503); res.end('{}'); return; }
|
||||
if (req.method === 'GET') { res.end(JSON.stringify(state.hideConfirm ? [sentinel] : state.keys)); return; }
|
||||
const chunks = []; for await (const c of req) chunks.push(c);
|
||||
const next = JSON.parse(Buffer.concat(chunks));
|
||||
for (const k of state.keys.filter(k => !next.includes(k))) state.removed.set(k, performance.now());
|
||||
state.keys = next; res.end('{"status":"ok"}'); return;
|
||||
}
|
||||
state.probes++;
|
||||
const key = req.headers.authorization?.slice(7);
|
||||
const accepted = state.keys.includes(key) || (state.removed.has(key) && performance.now() - state.removed.get(key) < state.delay);
|
||||
state.onProbe?.(state.status ?? (accepted ? 200 : 401));
|
||||
res.writeHead(state.status ?? (accepted ? 200 : 401)); res.end('{}');
|
||||
});
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
const baseUrl = `http://127.0.0.1:${server.address().port}`, receipts = [], stopped = [], runs = [];
|
||||
let now = Date.parse('2026-10-24T00:00:00.000Z');
|
||||
const credentials = new Credentials({ storeRoot, dataRoot: join(root, 'data'), modelAgents: ['demo/coder', 'demo/other'] });
|
||||
const client = new ManagementClient({ baseUrl, key: 'fx331-manage', receipt: r => receipts.push(r) });
|
||||
const create = () => new ModelKeys({ credentials, client, configDir, baseUrl, receipt: r => receipts.push(r), now: () => now,
|
||||
runs: instance => runs.filter(r => r.instance === instance), stopRuns: async rs => { stopped.push(...rs.map(r => r.run)); for (const r of rs) runs.splice(runs.indexOf(r), 1); } });
|
||||
const manager = create();
|
||||
const closers = [];
|
||||
t.after(async () => { for (const close of closers) await close(); credentials.close(); server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); fs.rmSync(root, { recursive: true, force: true }); });
|
||||
return { root, configDir, credentials, client, server, closers, state, manager, create, makeKey, sentinel, receipts, baseUrl, stopped, runs, advance: ms => { now += ms; } };
|
||||
}
|
||||
test('issue confirms GET/PUT/GET before private file and returns P3 shape with only own key', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); f.state.calls.length = 0;
|
||||
const answer = await f.manager.ensure('demo/coder');
|
||||
assert.deepEqual(Object.keys(answer).sort(), ['baseUrl', 'key']); assert.equal(answer.baseUrl, f.baseUrl);
|
||||
assert.deepEqual(f.state.calls, ['GET', 'PUT', 'GET']);
|
||||
assert.equal(f.state.keys.includes(answer.key), true);
|
||||
assert.equal(f.credentials.status().find(s => s.instance === 'demo/coder').date, '2026-11-23');
|
||||
assert.equal(JSON.stringify(f.receipts).includes(answer.key), false);
|
||||
const other = await f.manager.ensure('demo/other'); assert.equal(other.key === answer.key, false);
|
||||
});
|
||||
for (const fault of ['failGet', 'failPut', 'hideConfirm']) test(`proxy-key-issue-failed: ${fault} writes no model record`, async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); f.state[fault] = true;
|
||||
await assert.rejects(f.manager.ensure('demo/coder'), failCode('proxy-key-issue-failed'));
|
||||
assert.equal(f.credentials.modelRecord('demo/coder') === null, true);
|
||||
assert.equal(fs.existsSync(join(f.configDir, 'secrets', 'agents', 'demo', 'coder', 'model')), false);
|
||||
});
|
||||
test('credential-revoke-failed: missing confirming GET keeps model file and revoking status', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder');
|
||||
f.state.failGet = true;
|
||||
await assert.rejects(f.manager.prepareRevoke('demo/coder'), failCode('credential-revoke-failed'));
|
||||
assert.equal(f.credentials.status().find(s => s.instance === 'demo/coder').state, 'revoking');
|
||||
});
|
||||
test('revoke confirms absence then polls delayed client refusal before local removal', async t => {
|
||||
const f = await fixture(t, { delayed: 105 }); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder');
|
||||
f.state.calls.length = 0; const before = performance.now();
|
||||
await f.manager.prepareRevoke('demo/coder');
|
||||
assert.ok(performance.now() - before >= 100); assert.deepEqual(f.state.calls.slice(0, 3), ['GET', 'PUT', 'GET']);
|
||||
assert.ok(f.state.probes >= 2); assert.equal(f.state.keys.includes(key), false);
|
||||
assert.equal(f.credentials.modelRecord('demo/coder')?.state, 'revoking');
|
||||
f.manager.completeRevoke('demo/coder'); assert.equal(f.credentials.modelRecord('demo/coder') === null, true);
|
||||
assert.equal(JSON.stringify(f.receipts).includes(key), false);
|
||||
});
|
||||
test('reconcile restores missing recorded keys, removes own unknown, and preserves foreign and sentinel', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder');
|
||||
const unknown = f.makeKey(), foreign = 'fx331-human'; f.state.keys = [f.sentinel, unknown, foreign]; f.state.calls.length = 0;
|
||||
await f.create().reconcile();
|
||||
assert.equal(f.state.keys.includes(key), true); assert.equal(f.state.keys.includes(unknown), false);
|
||||
assert.equal(f.state.keys.includes(foreign), true); assert.equal(f.state.keys.includes(f.sentinel), true);
|
||||
assert.deepEqual(f.state.calls.slice(0, 3), ['GET', 'PUT', 'GET']);
|
||||
});
|
||||
test('reconcile revoking never restores key and completes delayed revocation after restart', async t => {
|
||||
const f = await fixture(t, { delayed: 105 }); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder');
|
||||
const record = f.credentials.modelRecord('demo/coder');
|
||||
f.credentials.writeModel('demo/coder', { ...record, state: 'revoking', revokingAt: '2026-10-24T00:00:00.000Z' });
|
||||
await f.create().reconcile();
|
||||
assert.equal(f.state.keys.includes(key), false); assert.equal(f.credentials.modelRecord('demo/coder') === null, true);
|
||||
});
|
||||
test('proxy-open: no own sentinel blocks reconcile and every launch', async t => {
|
||||
const f = await fixture(t); f.state.keys = ['fx331-human'];
|
||||
await assert.rejects(f.manager.reconcile(), failCode('proxy-open'));
|
||||
await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-open'));
|
||||
assert.equal(f.state.probes, 0);
|
||||
});
|
||||
test('proxy-key-revoking: launch hold occurs before any preflight call', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder');
|
||||
const record = f.credentials.modelRecord('demo/coder'); f.credentials.writeModel('demo/coder', { ...record, state: 'revoking', revokingAt: '2026-10-24T00:00:00.000Z' });
|
||||
await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-key-revoking')); assert.equal(f.state.probes, 0);
|
||||
});
|
||||
test('proxy-revoke-revision-conflict keeps local revoking record and ids-only remote-revoked receipt', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder'); await f.manager.prepareRevoke('demo/coder');
|
||||
assert.throws(() => f.manager.revisionConflict('demo/coder'), failCode('proxy-revoke-revision-conflict'));
|
||||
assert.equal(f.credentials.modelRecord('demo/coder').state, 'revoking');
|
||||
assert.equal(f.state.keys.includes(key), false);
|
||||
assert.ok(f.receipts.some(r => r.remoteState === 'revoked' && r.localState === 'kept'));
|
||||
assert.equal(JSON.stringify(f.receipts).includes(key), false);
|
||||
await f.create().reconcile(); assert.equal(f.credentials.modelRecord('demo/coder') === null, true);
|
||||
});
|
||||
for (const status of [401, 503, 302]) test(`proxy-key-refused: preflight rejects HTTP ${status} with exactly one probe`, async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); f.state.status = status;
|
||||
await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-key-refused')); assert.equal(f.state.probes, 1);
|
||||
});
|
||||
test('rotate keeps old key while its run lives, new launches use new, exit removes old', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder');
|
||||
f.runs.push({ instance: 'demo/coder', run: 'old', keyId: hash(first.key) });
|
||||
const next = await f.manager.rotate('demo/coder'); assert.equal(next.key === first.key, false);
|
||||
assert.equal(f.state.keys.includes(first.key), true); assert.equal((await f.manager.ensure('demo/coder')).key === next.key, true);
|
||||
await f.manager.sweep(); assert.equal(f.state.keys.includes(first.key), true);
|
||||
f.runs.length = 0; await f.manager.sweep(); assert.equal(f.state.keys.includes(first.key), false);
|
||||
});
|
||||
test('rotation grace stops surviving old-key runs before remote revocation', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder');
|
||||
f.runs.push({ instance: 'demo/coder', run: 'old', keyId: hash(first.key) }); await f.manager.rotate('demo/coder');
|
||||
f.advance(120 * 60_000); await f.manager.sweep(); assert.deepEqual(f.stopped, ['old']); assert.equal(f.state.keys.includes(first.key), false);
|
||||
});
|
||||
test('revocation polling constants retain the approved five-second bound and 25ms spacing', () => {
|
||||
assert.equal(REVOKE_TIMEOUT_MS, 5000); assert.equal(REVOKE_POLL_MS, 25);
|
||||
});
|
||||
|
||||
test('credential-revoke-failed: polling cannot treat management absence as client revocation', async t => {
|
||||
const f = await fixture(t, { delayed: Infinity }); await f.manager.reconcile(); await f.manager.ensure('demo/coder');
|
||||
const start = performance.now();
|
||||
await assert.rejects(f.manager.prepareRevoke('demo/coder'), failCode('credential-revoke-failed'));
|
||||
const elapsed = performance.now() - start;
|
||||
assert.ok(elapsed >= 4900 && elapsed < 6500, 'five-second total revocation polling bound');
|
||||
assert.equal(f.credentials.modelRecord('demo/coder').state, 'revoking');
|
||||
});
|
||||
test('proxy-down: unreachable listener refuses preflight', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder');
|
||||
await new Promise(resolve => f.server.close(resolve));
|
||||
await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-down'));
|
||||
});
|
||||
test('proxy-stop-failed: old proxy key and retiring record survive a failed grace stop, next sweep retries', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder');
|
||||
f.runs.push({ instance: 'demo/coder', run: 'old', keyId: hash(first.key) });
|
||||
let stopped = false;
|
||||
const manager = new ModelKeys({ credentials: f.credentials, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl,
|
||||
now: () => Date.parse('2026-10-24T03:00:00.000Z'), runs: () => f.runs,
|
||||
stopRuns: async () => { if (!stopped) throw Error('fx331-stop'); f.runs.length = 0; } });
|
||||
await manager.reconcile(); await manager.rotate('demo/coder');
|
||||
// This manager created a fresh 120-minute window at 03:00; use a second view
|
||||
// with time beyond that deadline to exercise failure then retry of the same record.
|
||||
const overdue = new ModelKeys({ credentials: f.credentials, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl,
|
||||
now: () => Date.parse('2026-10-24T06:00:00.000Z'), runs: () => f.runs,
|
||||
stopRuns: async () => { if (!stopped) throw Error('fx331-stop'); f.runs.length = 0; } });
|
||||
await overdue.reconcile();
|
||||
await assert.rejects(overdue.sweep(), failCode('proxy-stop-failed'));
|
||||
assert.equal(f.state.keys.includes(first.key), true); assert.equal(f.credentials.modelRecord('demo/coder').retiring.length, 1);
|
||||
stopped = true; await overdue.sweep(); assert.equal(f.state.keys.includes(first.key), false);
|
||||
});
|
||||
|
||||
async function bootBusiness(f) {
|
||||
const rolesDir = rolesCopy(f.root), system = systemFor(f.root), doc = businessDoc(f.root, 'demo');
|
||||
doc.roles['sec-1'] = { definition: 'security-reviewer' }; doc.launch.instances.push('sec-1');
|
||||
const file = writeJson(join(f.configDir, 'businesses', 'demo.json'), doc);
|
||||
fs.writeFileSync(join(f.configDir, 'secrets', 'proxy-management.key'), 'fx331-manage', { mode: 0o600 });
|
||||
const business = loadBusiness('demo', { dir: f.configDir, rolesDir }), resolved = {};
|
||||
for (const instance of Object.keys(business.roles)) resolved[instance] = resolveInstance({ system, business, instance });
|
||||
fs.mkdirSync(join(f.root, 'data'), { mode: 0o700 });
|
||||
const bus = await startBroker({ dataRoot: join(f.root, 'data'), businesses: { demo: busBusiness(business, resolved) },
|
||||
organization: { dir: f.configDir, rolesDir, system }, proxy: { port: Number(new URL(f.baseUrl).port), binary: '/fixture-unused' } });
|
||||
f.closers.push(() => bus.close());
|
||||
const chief = bus.broker.bindLaunch({ business: 'demo', role: 'chief', run: 'chief-run', harness: 'pi' });
|
||||
bus.broker.request(chief, { verb: 'role.claim' });
|
||||
const fire = () => bus.broker.request(chief, { verb: 'instance.fire', args: { name: 'sec-1', reason: 'fixture fire' } });
|
||||
const listed = () => JSON.parse(fs.readFileSync(file, 'utf8')).launch.instances.includes('sec-1');
|
||||
return { bus, chief, fire, listed, file };
|
||||
}
|
||||
test('bus fire proves remote 401 before delisting the agent and deleting model file', async t => {
|
||||
const f = await fixture(t, { delayed: 105 }); await f.manager.reconcile(); const issued = await f.manager.ensure('demo/sec-1');
|
||||
const b = await bootBusiness(f); let sawRefusal = false;
|
||||
f.state.onProbe = status => {
|
||||
assert.equal(b.listed(), true); assert.equal(b.bus.credentials.modelRecord('demo/sec-1')?.state, 'revoking');
|
||||
if (status === 401) sawRefusal = true;
|
||||
};
|
||||
const result = await b.fire(); f.state.onProbe = null;
|
||||
assert.equal(sawRefusal, true); assert.equal(result.delisted, true); assert.equal(b.listed(), false);
|
||||
assert.equal(f.state.keys.includes(issued.key), false); assert.equal(b.bus.credentials.modelRecord('demo/sec-1') === null, true);
|
||||
});
|
||||
test('credential-revoke-failed: bus fire on failed remote PUT keeps membership and model record', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const issued = await f.manager.ensure('demo/sec-1'); const b = await bootBusiness(f);
|
||||
const before = fs.readFileSync(b.file, 'utf8'); f.state.failPut = true;
|
||||
await assert.rejects(b.fire(), failCode('credential-revoke-failed'));
|
||||
assert.equal(fs.readFileSync(b.file, 'utf8'), before); assert.equal(b.listed(), true);
|
||||
assert.equal(f.state.keys.includes(issued.key), true); assert.equal(b.bus.credentials.modelRecord('demo/sec-1')?.state, 'revoking');
|
||||
});
|
||||
test('proxy-revoke-revision-conflict: racing organization write keeps record, next boot finishes intent', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const issued = await f.manager.ensure('demo/sec-1'); const b = await bootBusiness(f);
|
||||
let raced = false;
|
||||
f.state.onProbe = status => { if (status === 401 && !raced) { raced = true; fs.appendFileSync(b.file, '\n'); } };
|
||||
await assert.rejects(b.fire(), failCode('proxy-revoke-revision-conflict')); f.state.onProbe = null;
|
||||
assert.equal(b.listed(), true); assert.equal(f.state.keys.includes(issued.key), false);
|
||||
assert.equal(b.bus.credentials.modelRecord('demo/sec-1')?.state, 'revoking');
|
||||
await b.bus.close(); f.closers.length = 0;
|
||||
const record = new Credentials({ storeRoot: join(f.configDir, 'secrets', 'agents'), dataRoot: join(f.root, 'data'), modelAgents: ['demo/sec-1'] });
|
||||
const manager = new ModelKeys({ credentials: record, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl });
|
||||
await manager.reconcile(); assert.equal(record.modelRecord('demo/sec-1') === null, true); record.close();
|
||||
});
|
||||
test('bus hire issues and confirms implicit model key before publishing new membership', async t => {
|
||||
const f = await fixture(t); const b = await bootBusiness(f); const calls = f.state.calls.length;
|
||||
const result = await b.bus.broker.request(b.chief, { verb: 'instance.hire', args: { name: 'sec-new', definition: 'security-reviewer', reason: 'fixture hire' } });
|
||||
assert.equal(typeof result.transaction, 'string');
|
||||
assert.equal(JSON.parse(fs.readFileSync(b.file, 'utf8')).launch.instances.includes('sec-new'), true);
|
||||
assert.equal(b.bus.credentials.modelRecord('demo/sec-new')?.state, 'valid');
|
||||
assert.deepEqual(f.state.calls.slice(calls, calls + 3), ['GET', 'PUT', 'GET']);
|
||||
});
|
||||
test('proxy-key-issue-failed: bus hire failure publishes neither membership nor model file', async t => {
|
||||
const f = await fixture(t); const b = await bootBusiness(f); f.state.failPut = true;
|
||||
const before = fs.readFileSync(b.file, 'utf8');
|
||||
await assert.rejects(b.bus.broker.request(b.chief, { verb: 'instance.hire', args: { name: 'sec-new', definition: 'security-reviewer', reason: 'fixture hire' } }), failCode('proxy-key-issue-failed'));
|
||||
assert.equal(fs.readFileSync(b.file, 'utf8'), before); assert.equal(b.bus.credentials.modelRecord('demo/sec-new') === null, true);
|
||||
});
|
||||
test('proxy-open: removing sentinel after boot refuses the next launch before models probe', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder');
|
||||
f.state.keys = f.state.keys.filter(k => k !== f.sentinel);
|
||||
await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-open')); assert.equal(f.state.probes, 0);
|
||||
});
|
||||
test('newly hired role can use runtime preflight without a broker restart', async t => {
|
||||
const f = await fixture(t); const b = await bootBusiness(f);
|
||||
await b.bus.broker.request(b.chief, { verb: 'instance.hire', args: { name: 'sec-new', definition: 'security-reviewer', reason: 'fixture hire' } });
|
||||
const proxy = await b.bus.modelPreflight({ business: 'demo', role: 'sec-new' });
|
||||
assert.equal(proxy.baseUrl, f.baseUrl); assert.equal(f.state.keys.includes(proxy.key), true);
|
||||
});
|
||||
test('first launch issues implicit key and performs exactly one successful models probe', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile();
|
||||
const answer = await f.manager.preflight('demo/coder');
|
||||
assert.equal(f.state.probes, 1); assert.equal(f.state.keys.includes(answer.key), true);
|
||||
});
|
||||
test('steady boot reconcile reads management list once and writes nothing', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); assert.deepEqual(f.state.calls, ['GET']);
|
||||
});
|
||||
test('thirty-day rotation due automatically replaces and retires an unused old key', async t => {
|
||||
const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder');
|
||||
f.advance(30 * 86400_000); await f.manager.sweep();
|
||||
const current = await f.manager.ensure('demo/coder'); assert.equal(current.key === first.key, false);
|
||||
assert.equal(f.state.keys.includes(first.key), false); assert.equal(f.state.keys.includes(current.key), true);
|
||||
});
|
||||
@@ -14,3 +14,25 @@ test('proxy.init not-configured',t=>{const f=fixture(t,null);assert.throws(()=>f
|
||||
test('proxy.init callback error redacts key and maps outcome to named refusal',async t=>{const f=fixture(t,()=>{throw Error('p1-management');});await assert.rejects(f.b.request(f.human,{verb:'proxy.init',args:{key:'p1-management'}}),e=>e.code==='proxy-init-failed'&&!e.message.includes('p1-management'));assert.equal(JSON.stringify(f.store.all('SELECT body FROM events')).includes('p1-management'),false);});
|
||||
test('socket awaits proxy.init and does not return a Promise object or retain human cap',async t=>{const {serve}=await import('../src/server.mjs');const {Client}=await import('../src/client.mjs');const f=fixture(t,async()=>{await new Promise(r=>setTimeout(r,10));return {port:18432,binary:'/fixture'};});const root=mkdtempSync(join(tmpdir(),'p1-sock-'));const socket=join(root,'s');const server=await serve({broker:f.b,path:socket,peerReader:{read:async()=>({pid:process.pid,uid:process.getuid(),gid:process.getgid()}),close(){}},authenticateHuman:()=>f.b.bindHuman({business:'demo',human:'owner',via:'cli',outsideAgent:true})});t.after(()=>server.close());const client=new Client({path:socket,human:{business:'demo',pid:process.pid,startTime:'1',nonce:'fixture'}});assert.deepEqual(await client.call('proxy.init',{key:'p1-management'}),{port:18432,binary:'/fixture'});});
|
||||
test('proxy-init-failed: extra callback fields cannot return a management key',async t=>{const f=fixture(t,async()=>({port:18432,binary:'/fixture',key:'p1-management'}));await assert.rejects(f.b.request(f.human,{verb:'proxy.init',args:{key:'p1-management'}}),e=>e.code==='proxy-init-failed');assert.equal(JSON.stringify(f.store.all('SELECT body FROM events')).includes('p1-management'),false);});
|
||||
|
||||
// P2 R2-N1: catches local broker predicates diverging from management-client rules.
|
||||
for (const [label, key] of [['Latin-1', 'fx331-caf\u00e9'], ['leading space', ' fx331-key'], ['trailing space', 'fx331-key '], ['internal space', 'fx331 key']]) {
|
||||
test(`proxy-key-invalid: broker rejects ${label} before initializer`, async t => {
|
||||
let called = false;
|
||||
const f = fixture(t, () => { called = true; return { port: 18432, binary: '/fixture' }; });
|
||||
let pending;
|
||||
try {
|
||||
assert.throws(() => { pending = f.b.request(f.human, { verb: 'proxy.init', args: { key } }); }, e => e.code === 'proxy-key-invalid');
|
||||
} finally { await Promise.resolve(pending).catch(() => {}); }
|
||||
assert.equal(called, false);
|
||||
assert.equal(JSON.stringify(f.store.all('SELECT body FROM events')).includes(key), false);
|
||||
});
|
||||
}
|
||||
|
||||
for (const [field, code, value] of [['rotateGraceMinutes', 'proxy-rotate-grace-invalid', 1441], ['firstOutputSeconds', 'proxy-first-output-invalid', 3601]]) {
|
||||
test(`proxy.init ${code}: human setting validated before initializer`, t => {
|
||||
let called = false; const f = fixture(t, () => { called = true; });
|
||||
assert.throws(() => f.b.request(f.human, { verb: 'proxy.init', args: { key: 'fx331-manage', [field]: value } }), e => e.code === code);
|
||||
assert.equal(called, false);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { stopModelRuns, MODEL_STOP_TIMEOUT_MS } from '../src/model-keys.mjs';
|
||||
const run = { business: 'demo', role: 'coder', run: 'r-test', keyId: '0123456789ab', pid: 123, startTime: '456' };
|
||||
const entry = { business: 'demo', instance: 'coder', run: 'r-test', modelKeyId: '0123456789ab', pid: 123, startTime: '456', live: true };
|
||||
test('bounded proxy stop is five seconds by default', () => assert.equal(MODEL_STOP_TIMEOUT_MS, 5000));
|
||||
for (const patch of [{ instance: 'other' }, { modelKeyId: 'fedcba987654' }, { pid: 999 }, { startTime: '999' }]) test(`proxy-stop-failed: mismatched ${Object.keys(patch)[0]} is never signalled`, async () => {
|
||||
let signals = 0; const receipts = [];
|
||||
await assert.rejects(stopModelRuns({ runs: [run], sessions: () => [{ ...entry, ...patch }], stop: async () => { signals++; }, receipt: r => receipts.push(r) }), e => e.code === 'proxy-stop-failed');
|
||||
assert.equal(signals, 0); assert.equal(receipts[0].code, 'proxy-stop-failed');
|
||||
});
|
||||
test('proxy-stop-failed: hung SIGTERM path is bounded and error text is not logged', async () => {
|
||||
const receipts = [], start = performance.now();
|
||||
await assert.rejects(stopModelRuns({ runs: [run], sessions: () => [entry], stop: () => new Promise(() => {}), receipt: r => receipts.push(r), timeoutMs: 20 }), e => e.code === 'proxy-stop-failed');
|
||||
assert.ok(performance.now() - start < 1000);
|
||||
assert.deepEqual(receipts, [{ action: 'proxy-rotation', code: 'proxy-stop-failed', keyIds: ['0123456789ab'], run: 'r-test' }]);
|
||||
});
|
||||
test('stop verifies the run exited and logs the proxy reason with ids only', async () => {
|
||||
let live = true; const receipts = [];
|
||||
await stopModelRuns({ runs: [run], sessions: () => [{ ...entry, live }], stop: async () => { live = false; return { stopped: true }; }, receipt: r => receipts.push(r), reason: 'proxy-stall' });
|
||||
assert.deepEqual(receipts, [{ action: 'proxy-stall', keyIds: ['0123456789ab'], business: 'demo', agent: 'coder', run: 'r-test', outcome: 'stopped' }]);
|
||||
});
|
||||
@@ -45,7 +45,7 @@ export const USAGE = `usage:
|
||||
${LIMITS_USAGE}
|
||||
${PLUGINS_USAGE}
|
||||
${CREDENTIALS_USAGE}
|
||||
mosaic proxy fetch | init [--port N] [--business ID]
|
||||
mosaic proxy fetch | init [--port N] [--business ID] [--rotate-grace-minutes N] [--first-output-seconds N]
|
||||
${HOSTS_USAGE}
|
||||
${GRANTS_USAGE}
|
||||
${INSTANCES_USAGE}
|
||||
|
||||
@@ -139,6 +139,8 @@
|
||||
// transaction; a later delete retries it.
|
||||
|
||||
import { createServer } from "node:net";
|
||||
import { createHash } from 'node:crypto';
|
||||
import { watchModelOutput } from './model-stall.mjs';
|
||||
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync } from "node:fs";
|
||||
import { dirname, join, relative } from "node:path";
|
||||
import { configDir, loadBusiness, resolveInstance, systemVars } from "../../business/src/index.mjs";
|
||||
@@ -507,6 +509,13 @@ export function createLauncher({
|
||||
({resolved,model,account,harness,route,routeReason}=await check(instance,{pm:instance===b.launch.by,thread,session,human}));
|
||||
admission(admissionGeneration);
|
||||
const { run, runDir } = newRun(dataRoot, b.id);
|
||||
let proxy = null, modelKeyId;
|
||||
if (system.proxy) {
|
||||
try { proxy = await host.op({ op: 'modelPreflight', business: b.id, role: instance }); }
|
||||
catch (error) { throw new Refusal(error.code ?? 'proxy-down', error.code ?? 'proxy-down'); }
|
||||
if (!proxy || typeof proxy.key !== 'string' || proxy.baseUrl !== `http://127.0.0.1:${system.proxy.port}`) throw new Refusal('proxy-down', 'proxy-down');
|
||||
modelKeyId = createHash('sha256').update(proxy.key).digest('hex').slice(0, 12);
|
||||
}
|
||||
const sessionDir = store ? store.session : join(runDir, "session");
|
||||
if (!store) mkdirSync(sessionDir, { mode: 0o700 });
|
||||
if (store) compactions(store, model.harness, harness, { instance, thread, session, run });
|
||||
@@ -522,6 +531,7 @@ export function createLauncher({
|
||||
const holding = holders?.filter((h) => h.instance === instance) ?? null;
|
||||
const land = thread ? await host.op({ op: "landHeld", record: { business: b.id, role: instance, thread } }) : holding.length > 0;
|
||||
const bundle = buildBundle({
|
||||
...(proxy ? { proxy } : {}),
|
||||
dir: join(runDir, "bundle"),
|
||||
resolved,
|
||||
models: b.launch.models,
|
||||
@@ -611,12 +621,13 @@ export function createLauncher({
|
||||
admission(admissionGeneration);
|
||||
if (!outerStart) throw new Error("the session process exited at once");
|
||||
lock?.update({ run, runPid: child.pid, runStartTime: outerStart });
|
||||
const launch = await host.bindLaunch({ business: b.id, role: instance, run, harness: model.harness, provider: model.provider, model: model.model, account, transcript, pid: child.pid, startTime: outerStart, ...(thread ? { thread, session } : {}) });
|
||||
const launch = await host.bindLaunch({ business: b.id, role: instance, run, harness: model.harness, provider: model.provider, model: model.model, account, transcript, pid: child.pid, startTime: outerStart, ...(thread ? { thread, session } : {}), ...(proxy ? { modelKeyId } : {}) });
|
||||
bound = true;
|
||||
admission(admissionGeneration);
|
||||
entry = {
|
||||
business: b.id, instance, ...(thread ? { thread, session, workspace } : {}), run, launchedBy, harness: model.harness, model: model.model, provider: model.provider, modelKey: model.modelKey,
|
||||
pid: child.pid, startTime: outerStart, runnerPid: runner.pid, runnerStartTime: runner.startTime, runDir, startedAt: new Date().toISOString(),
|
||||
...(proxy ? { modelKeyId } : {}),
|
||||
};
|
||||
registry.add(entry);
|
||||
// Row 156: one start per decision, whichever launch wrote it into landing.
|
||||
@@ -630,10 +641,14 @@ export function createLauncher({
|
||||
spawnedAt.delete(run);
|
||||
unregisterRuntime(dataRoot, managed.id);
|
||||
if (e instanceof Refusal) throw e;
|
||||
if (e.code?.startsWith('proxy-')) throw new Refusal(e.code, e.code);
|
||||
throw new Refusal("launch-failed", e.message);
|
||||
}
|
||||
children.set(run, exited);
|
||||
const outputWatch = proxy ? watchModelOutput({ runDir, keyId: modelKeyId, seconds: system.proxy.firstOutputSeconds ?? 120,
|
||||
stop: () => host.op({ op: 'modelStall', business: b.id, role: instance, run, keyId: modelKeyId }), receipt: record }) : null;
|
||||
exited.then(async ({ code, signal }) => {
|
||||
outputWatch?.cancel();
|
||||
let receipt;
|
||||
try { receipt = runtime(dataRoot, managed.id); }
|
||||
catch { log(`run ${run}: processing receipt unavailable at exit`); }
|
||||
@@ -1191,7 +1206,7 @@ export function createLauncher({
|
||||
log(`run ${s.run} (${s.instance}) from an earlier host still runs (pid ${s.pid}, runner ${s.runnerPid}): no session of ${s.instance} starts until it exits`);
|
||||
}
|
||||
try {
|
||||
await host.bindLaunch({ business: b.id, role: s.instance, run: s.run, harness: s.harness, pid: s.pid, startTime: s.startTime, ...(s.thread ? { thread: s.thread } : {}), ...(s.session ? { session: s.session } : {}) });
|
||||
await host.bindLaunch({ business: b.id, role: s.instance, run: s.run, harness: s.harness, pid: s.pid, startTime: s.startTime, ...(s.thread ? { thread: s.thread } : {}), ...(s.session ? { session: s.session } : {}), ...(s.modelKeyId ? { modelKeyId: s.modelKeyId } : {}) });
|
||||
} catch (e) {
|
||||
// A host that died after ending the run but before updating sessions.json.
|
||||
if (e.code === "run-ended") log(`run ${s.run} (${s.instance}) from an earlier host was already ended`);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CliError } from './errors.mjs';
|
||||
import { refuseInsideAgent } from './transport.mjs';
|
||||
import { fetchPinned } from '../../model-proxy/src/fetch.mjs';
|
||||
import { validManagementKey, proxyTiming } from '../../model-proxy/src/common.mjs';
|
||||
const failure = (code, exitCode = 2) => Object.assign(new CliError(code, exitCode), { code });
|
||||
// A raw terminal suppresses echo; input is never argv, env, readline history or stdout.
|
||||
export function promptProxyKey(io, label) {
|
||||
@@ -31,14 +32,14 @@ export function promptProxyKey(io, label) {
|
||||
export async function proxyCommand(args, io, { system, callFor, prompt = promptProxyKey, fetch = fetchPinned }) {
|
||||
const [verb, ...rest] = args;
|
||||
if (args.includes('--tester')) throw failure('human-required', 3);
|
||||
if (verb === 'init') {
|
||||
if (verb === 'init' || verb === 'fetch') {
|
||||
try { refuseInsideAgent(io.env); } catch { throw failure('human-required', 3); }
|
||||
}
|
||||
if (!['fetch', 'init'].includes(verb)) throw failure('proxy-usage', 4);
|
||||
const parsed = { flags: new Set(), values: {} }; let port;
|
||||
for (let i = 0; i < rest.length; i++) {
|
||||
const flag = rest[i];
|
||||
if (verb !== 'init' || !['--business', '--port'].includes(flag) || parsed.values[flag] !== undefined || !rest[i + 1]) throw failure('proxy-usage', 4);
|
||||
if (verb !== 'init' || !['--business', '--port', '--rotate-grace-minutes', '--first-output-seconds'].includes(flag) || parsed.values[flag] !== undefined || !rest[i + 1]) throw failure('proxy-usage', 4);
|
||||
parsed.values[flag] = rest[++i];
|
||||
}
|
||||
if (parsed.values['--port'] !== undefined) {
|
||||
@@ -46,6 +47,10 @@ export async function proxyCommand(args, io, { system, callFor, prompt = promptP
|
||||
port = Number(parsed.values['--port']);
|
||||
if (port < 1024 || port > 65535 || port === 8317) throw failure('proxy-config-invalid');
|
||||
}
|
||||
const timing = {};
|
||||
for (const [flag, field] of [['--rotate-grace-minutes', 'rotateGraceMinutes'], ['--first-output-seconds', 'firstOutputSeconds']])
|
||||
if (parsed.values[flag] !== undefined) timing[field] = /^[0-9]+$/.test(parsed.values[flag]) ? Number(parsed.values[flag]) : NaN;
|
||||
try { proxyTiming(timing); } catch (error) { throw failure(error.code); }
|
||||
try {
|
||||
const config = typeof system === 'function' ? system() : system;
|
||||
if (verb === 'fetch') { const result = await fetch({ dataRoot: config.dataRoot }); io.stdout.write(JSON.stringify({ binary: result.binary, panel: result.panel }) + '\n'); return; }
|
||||
@@ -54,8 +59,8 @@ export async function proxyCommand(args, io, { system, callFor, prompt = promptP
|
||||
const call = callFor(parsed);
|
||||
const first = await prompt(io, 'Management key: '), second = await prompt(io, 'Repeat management key: ');
|
||||
if (first !== second) throw failure('proxy-key-mismatch');
|
||||
if (!first.trim() || first.length > 4096 || /[\x00-\x1f\x7f]/.test(first)) throw failure('proxy-key-invalid');
|
||||
const result = await call('proxy.init', { key: first, ...(port === undefined ? {} : { port }) });
|
||||
if (!validManagementKey(first)) throw failure('proxy-key-invalid');
|
||||
const result = await call('proxy.init', { key: first, ...(port === undefined ? {} : { port }), ...timing });
|
||||
io.stdout.write(JSON.stringify(result) + '\n');
|
||||
} catch (e) {
|
||||
if (e instanceof CliError) throw e;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
// Observe existence only. Harness stdout and credentials never enter this watcher.
|
||||
export function watchModelOutput({ runDir, seconds = 120, keyId, stop, receipt }) {
|
||||
let timer, settled = false, finish;
|
||||
const done = new Promise(resolve => { finish = resolve; });
|
||||
const cancel = () => { if (settled) return; settled = true; clearTimeout(timer); finish(); };
|
||||
const deadline = performance.now() + seconds * 1000;
|
||||
const tick = async () => {
|
||||
if (settled) return;
|
||||
if (existsSync(join(runDir, 'proxy-first-output'))) { cancel(); return; }
|
||||
const left = deadline - performance.now();
|
||||
if (left > 0) { timer = setTimeout(tick, Math.min(1000, left)); return; }
|
||||
try { await stop(); receipt({ action: 'proxy-stall', keyIds: [keyId] }); }
|
||||
catch { receipt({ action: 'proxy-stall', code: 'proxy-stop-failed', keyIds: [keyId] }); }
|
||||
finally { cancel(); }
|
||||
};
|
||||
timer = setTimeout(tick, 0);
|
||||
return { cancel, done };
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// P2 RED tests: these catch fetch before eligibility and divergent key predicates.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { proxyCommand } from '../src/model-proxy.mjs';
|
||||
import { io } from './helpers.mjs';
|
||||
|
||||
for (const env of [{ CODEX_THREAD_ID: 'fixture' }, { MOSAIC_AGENT_RUN: 'fixture' }]) {
|
||||
test(`human-required: proxy fetch refuses ${Object.keys(env)[0]} before config or fetch`, async () => {
|
||||
let touched = false;
|
||||
const forbidden = () => { touched = true; throw new Error('eligibility must run first'); };
|
||||
await assert.rejects(proxyCommand(['fetch'], io(env), { system: forbidden, fetch: forbidden }), e => e.code === 'human-required');
|
||||
assert.equal(touched, false);
|
||||
});
|
||||
}
|
||||
test('human-required: proxy fetch tester refuses before config or fetch', async () => {
|
||||
let touched = false;
|
||||
const forbidden = () => { touched = true; throw new Error('eligibility must run first'); };
|
||||
await assert.rejects(proxyCommand(['fetch', '--tester'], io(), { system: forbidden, fetch: forbidden }), e => e.code === 'human-required');
|
||||
assert.equal(touched, false);
|
||||
});
|
||||
for (const key of ['fx331-caf\u00e9', ' fx331-key', 'fx331-key ', 'fx331 key']) {
|
||||
test(`proxy-key-invalid: CLI rejects non-management-key input (${JSON.stringify(key).length})`, async () => {
|
||||
const x = io(); x.stdin.isTTY = true; x.stdout.isTTY = true;
|
||||
let calls = 0;
|
||||
await assert.rejects(proxyCommand(['init'], x, {
|
||||
system: { dataRoot: '/unused-fixture' }, prompt: async () => key,
|
||||
callFor: () => async () => { calls++; return { port: 18432, binary: '/fixture' }; },
|
||||
}), e => e.code === 'proxy-key-invalid');
|
||||
assert.equal(calls, 0);
|
||||
assert.equal(x.out.text.includes(key), false);
|
||||
assert.equal(x.err.text.includes(key), false);
|
||||
});
|
||||
}
|
||||
|
||||
for (const [flag, code, value] of [['--rotate-grace-minutes', 'proxy-rotate-grace-invalid', '1441'], ['--first-output-seconds', 'proxy-first-output-invalid', '3601']]) {
|
||||
test(`${code}: init flag refuses before config or prompt`, async () => {
|
||||
let touched = false;
|
||||
const forbidden = () => { touched = true; throw new Error('must not run'); };
|
||||
await assert.rejects(proxyCommand(['init', flag, value], io(), { system: forbidden, prompt: forbidden }), e => e.code === code);
|
||||
assert.equal(touched, false);
|
||||
});
|
||||
}
|
||||
test('human proxy init passes optional timing flags only through authenticated call', async () => {
|
||||
const x = io(); x.stdin.isTTY = true; x.stdout.isTTY = true;
|
||||
const calls = [];
|
||||
await proxyCommand(['init', '--rotate-grace-minutes', '20', '--first-output-seconds', '10'], x, {
|
||||
system: { dataRoot: '/unused-fixture' }, prompt: async () => 'fx331-manage',
|
||||
callFor: () => async (verb, args) => { calls.push({ verb, args }); return { port: 18432, binary: '/fixture' }; },
|
||||
});
|
||||
assert.equal(calls.length, 1); assert.equal(calls[0].args.rotateGraceMinutes, 20); assert.equal(calls[0].args.firstOutputSeconds, 10);
|
||||
assert.equal(x.out.text.includes('fx331-manage'), false);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { watchModelOutput } from '../src/model-stall.mjs';
|
||||
function fixture(t) { const root = fs.mkdtempSync(join(tmpdir(), 'p2-stall-')); t.after(() => fs.rmSync(root, { recursive: true, force: true })); return root; }
|
||||
test('proxy-stall: silent run stops and logs only ids, never output or key values', async t => {
|
||||
const root = fixture(t), receipts = []; let stops = 0;
|
||||
const watch = watchModelOutput({ runDir: root, seconds: 0.02, keyId: '0123456789ab', stop: async () => { stops++; }, receipt: r => receipts.push(r) });
|
||||
t.after(watch.cancel); await watch.done;
|
||||
assert.equal(stops, 1); assert.deepEqual(receipts, [{ action: 'proxy-stall', keyIds: ['0123456789ab'] }]);
|
||||
});
|
||||
test('first stdout marker cancels stall without reading or logging marker text', async t => {
|
||||
const root = fixture(t), receipts = []; let stops = 0;
|
||||
fs.writeFileSync(join(root, 'proxy-first-output'), 'fx331-private-output');
|
||||
const watch = watchModelOutput({ runDir: root, seconds: 0.02, keyId: '0123456789ab', stop: async () => { stops++; }, receipt: r => receipts.push(r) });
|
||||
t.after(watch.cancel); await watch.done; assert.equal(stops, 0); assert.deepEqual(receipts, []);
|
||||
});
|
||||
test('proxy-stop-failed: failed stall stop is logged by code and never leaks error text', async t => {
|
||||
const root = fixture(t), receipts = [];
|
||||
const watch = watchModelOutput({ runDir: root, seconds: 0.02, keyId: '0123456789ab', stop: async () => { throw Error('fx331-private-output'); }, receipt: r => receipts.push(r) });
|
||||
t.after(watch.cancel); await watch.done;
|
||||
assert.deepEqual(receipts, [{ action: 'proxy-stall', code: 'proxy-stop-failed', keyIds: ['0123456789ab'] }]);
|
||||
});
|
||||
test('run exit cancellation settles watcher without stopping the run', async t => {
|
||||
const root = fixture(t); let stops = 0;
|
||||
const watch = watchModelOutput({ runDir: root, seconds: 0.02, keyId: '0123456789ab', stop: async () => { stops++; }, receipt: () => {} });
|
||||
watch.cancel(); await watch.done; assert.equal(stops, 0);
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
// The host needs an output-exists signal, never the output or a model credential.
|
||||
export function firstOutputMarker(runDir) {
|
||||
let seen = false;
|
||||
return chunk => {
|
||||
if (seen || !chunk.length) return;
|
||||
try { writeFileSync(join(runDir, 'proxy-first-output'), 'output\n', { flag: 'wx', mode: 0o600 }); }
|
||||
catch (error) { if (error.code !== 'EEXIST') throw error; }
|
||||
seen = true;
|
||||
};
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import { createServer, connect } from "node:net";
|
||||
import { join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { Client } from "../../bus/src/client.mjs";
|
||||
import { firstOutputMarker } from './first-output.mjs';
|
||||
import { GUARD_CODES, FIXED_TEXTS } from "../../library/src/reply-protocol.mjs";
|
||||
import { fetchReview, landWork, pushWorking } from "./git-tools.mjs";
|
||||
|
||||
@@ -344,8 +345,10 @@ function runTurn({ session, request, n, env, onChild }) {
|
||||
closeSync(errFd);
|
||||
onChild(child);
|
||||
let out = "";
|
||||
const markOutput = firstOutputMarker(session.runDir);
|
||||
child.stdout.setEncoding("utf8");
|
||||
child.stdout.on("data", (s) => {
|
||||
markOutput(s);
|
||||
if (out.length < 4 * BODY_MAX) out += s;
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -228,24 +228,25 @@ export function typedTools({ resolved, arbiter = false, tracker = false, project
|
||||
if (library) {
|
||||
const universe = str("Universe ID in the configured library", { minLength: 1 });
|
||||
const project = str("Library project ID; omit only when the business has one library");
|
||||
const scope = str("canon by default; playground is opt-in", { enum: ["canon", "playground"] });
|
||||
const scope = str("canon by default; playground and supporting are opt-in", { enum: ["canon", "playground", "supporting"] });
|
||||
const manuscriptScope = str("canon by default; playground is opt-in", { enum: ["canon", "playground"] });
|
||||
const book = str("Book folder from library_universe");
|
||||
const offset = { type: "integer", minimum: 0, description: "next from the previous page" };
|
||||
const line = { type: "integer", minimum: 1, description: "One-based line number" };
|
||||
const base = { universe, project };
|
||||
add("library_find", "library.find", null, "Find names in one universe, at the served commit. At most 50 results; continue with next as offset.", obj({
|
||||
add("library_find", "library.find", null, "Find names in one universe, at the served commit. At most 50 results; continue with next as offset. Playground and supporting are opt-in.", obj({
|
||||
...base, query: str("Case-insensitive name or filename substring; empty matches all"),
|
||||
type: str("Mapped type", { enum: ["character", "place", "research", "tracker", "summary"] }),
|
||||
book, scope, offset,
|
||||
}, ["universe", "query"]));
|
||||
add("library_get", "library.get", null, "Read a mapped note and visible links in one universe at the served commit. next pages lines; links_next pages links. An explicit own-universe playground path is readable.", obj({
|
||||
...base, path: str("Mapped note path relative to the library"), from: line, links_from: offset,
|
||||
add("library_get", "library.get", null, "Read a note and its blob at the served commit, canon by default. Playground/supporting paths need matching scope; mismatch returns null. next pages lines; links_next pages visible links in supporting/playground/canon order.", obj({
|
||||
...base, path: str("Mapped note path relative to the library"), scope, from: line, links_from: offset,
|
||||
}, ["universe", "path"]));
|
||||
add("library_search", "library.search", null, "Search text in one universe at the served commit. At most 50 hits; use next as offset. Playground is opt-in.", obj({
|
||||
add("library_search", "library.search", null, "Search text in one universe at the served commit. At most 50 hits; use next as offset. Playground and supporting are opt-in.", obj({
|
||||
...base, text: str("Nonempty case-insensitive substring", { minLength: 1 }), book, scope, offset,
|
||||
}, ["universe", "text"]));
|
||||
add("library_manuscript", "library.manuscript", null, "Read one chapter of a book at the served commit, at most 400 lines. Omit chapter for the first; continue with next or next_chapter.", obj({
|
||||
...base, book, scope, chapter: str("Chapter filename without extension"), from: line, to: line,
|
||||
...base, book, scope: manuscriptScope, chapter: str("Chapter filename without extension"), from: line, to: line,
|
||||
}, ["universe", "book"]));
|
||||
add("library_universe", "library.universe", null, "Read a universe's mapping at the served commit: books, types, playground prefixes, and hybrid sources/constraints/notes or draft metadata.", obj(base, ["universe"]));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { firstOutputMarker } from '../src/first-output.mjs';
|
||||
test('stdout marker contains a fixed token only, never output text; emitted once after nonempty output', t => {
|
||||
const root = fs.mkdtempSync(join(tmpdir(), 'p2-output-')); t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
||||
const observe = firstOutputMarker(root), file = join(root, 'proxy-first-output');
|
||||
observe(''); assert.equal(fs.existsSync(file), false);
|
||||
observe('fx331-private-output');
|
||||
assert.equal(fs.readFileSync(file, 'utf8'), 'output\n'); assert.equal(fs.statSync(file).mode & 0o777, 0o600);
|
||||
const before = fs.statSync(file).mtimeMs;
|
||||
observe('other output'); assert.equal(fs.statSync(file).mtimeMs, before);
|
||||
assert.equal(fs.readFileSync(file, 'utf8').includes('fx331-private-output'), false);
|
||||
});
|
||||
@@ -46,7 +46,7 @@ test('lookup schemas require a universe even for get, include paging, and cannot
|
||||
for (const key of ['repo', 'commit', 'business', 'accept']) assert.equal(Object.hasOwn(tool.parameters.properties, key), false);
|
||||
}
|
||||
const byName = Object.fromEntries(tools.map(t => [t.name, t]));
|
||||
assert.deepEqual(byName.library_find.parameters.properties.scope.enum, ['canon', 'playground']);
|
||||
assert.deepEqual(byName.library_find.parameters.properties.scope.enum, ['canon', 'playground', 'supporting']);
|
||||
assert.deepEqual(byName.library_find.parameters.properties.type.enum, [...TYPES]);
|
||||
assert.equal(byName.library_find.parameters.properties.offset.minimum, 0);
|
||||
assert.equal(byName.library_get.parameters.properties.from.minimum, 1);
|
||||
@@ -69,3 +69,14 @@ test('bundle flag reaches both harness tool manifests and creative prompt withou
|
||||
assert.doesNotMatch(prompt, /Bus tools: [^\n]*(read_spec|list_agents|inbox|trail|write_record|raise_decision|list_tasks)/);
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-05c scope schemas expand only find/search/get, keeping manuscript and tool names unchanged', t => {
|
||||
const tools = typedTools({ resolved: role(t, 'ideas'), library: true });
|
||||
assert.deepEqual(tools.map(t => t.name), ['send_message', ...LOOKUPS]);
|
||||
for (const name of ['library_find', 'library_search', 'library_get']) {
|
||||
const tool = tools.find(t => t.name === name);
|
||||
assert.deepEqual(tool.parameters.properties.scope.enum, ['canon', 'playground', 'supporting']);
|
||||
assert.ok(!tool.parameters.required.includes('scope'));
|
||||
}
|
||||
assert.deepEqual(tools.find(t => t.name === 'library_manuscript').parameters.properties.scope.enum, ['canon', 'playground']);
|
||||
});
|
||||
@@ -7,12 +7,15 @@
|
||||
import { raise } from './problems.mjs';
|
||||
import { TYPES } from './mapping.mjs';
|
||||
import { indexFor } from './index-build.mjs';
|
||||
import { listTree } from './reader.mjs';
|
||||
|
||||
// The same cap as packages/harness/src/tools.mjs REPLY_CAP. The query tests assert the two stay equal.
|
||||
export const QUERY_REPLY_CAP = 40_000;
|
||||
export const LOOKUP_LIMIT = 50;
|
||||
export const MANUSCRIPT_LINES = 400;
|
||||
const SCOPES = Object.freeze(['canon', 'playground']);
|
||||
const SCOPES = Object.freeze(['canon', 'playground', 'supporting']);
|
||||
const MANUSCRIPT_SCOPES = Object.freeze(['canon', 'playground']);
|
||||
const SCOPE_RANK = Object.freeze({ supporting: 0, playground: 1, canon: 2 });
|
||||
|
||||
const bytes = value => JSON.stringify(value).length;
|
||||
const invalid = message => raise('query-argument-invalid', message);
|
||||
@@ -22,9 +25,9 @@ function textArg(value, name) {
|
||||
if (typeof value !== 'string') invalid(`${name} must be text`);
|
||||
return value;
|
||||
}
|
||||
function scopeArg(value) {
|
||||
function scopeArg(value, allowed = SCOPES) {
|
||||
if (value === undefined) return 'canon';
|
||||
if (!SCOPES.includes(value)) invalid('scope must be "canon" or "playground"');
|
||||
if (!allowed.includes(value)) invalid(`scope must be one of ${allowed.join(", ")}`);
|
||||
return value;
|
||||
}
|
||||
function positionArg(value, name, fallback) {
|
||||
@@ -109,7 +112,8 @@ export async function find(source, { universe: name, query, type, book, scope, o
|
||||
});
|
||||
}
|
||||
|
||||
// get: one visible file, its front matter, its lines from `from`, and its visible links both ways.
|
||||
// get: one file in the requested scope (canon default), its served-tree blob, front matter,
|
||||
// lines and visible links both ways. Links keep all visible scopes, ordered supporting/playground/canon.
|
||||
// Returns null when the file is missing or not visible, so a caller cannot tell the two apart. Lines page
|
||||
// by `from` (reply `next`), links page together by `links_from` (reply `links_next`); `links_out` come
|
||||
// before `links_in`. Every page carries the front matter. Links take at most half the cap, so a hub note
|
||||
@@ -117,27 +121,33 @@ export async function find(source, { universe: name, query, type, book, scope, o
|
||||
// least one line, so links shrink when a line needs the room; read on with `from: next` and
|
||||
// `links_from: links_next`. A `from` past the last line returns links alone. Only front matter, or a
|
||||
// line that cannot fit with no links beside it, is refused.
|
||||
export async function get(source, { universe: name, path, from, links_from: linksFrom } = {}) {
|
||||
export async function get(source, { universe: name, path, scope, from, links_from: linksFrom } = {}) {
|
||||
const universe = universeArg(source.mapping, name);
|
||||
const requestedScope = scopeArg(scope);
|
||||
const startLine = positionArg(from, 'from', 1);
|
||||
const linkStart = offsetArg(linksFrom);
|
||||
if (typeof path !== 'string') invalid('path must be text');
|
||||
const index = await indexFor(source);
|
||||
const file = index.files.get(path);
|
||||
if (!file || !sees(index.mapping, universe, file)) return null;
|
||||
if (!file || file.scope !== requestedScope || !sees(index.mapping, universe, file)) return null;
|
||||
const reachable = links => links.filter(link => {
|
||||
const other = index.files.get(link.path);
|
||||
return other !== undefined && sees(index.mapping, universe, other);
|
||||
});
|
||||
}).sort((a, b) => SCOPE_RANK[index.files.get(a.path).scope] - SCOPE_RANK[index.files.get(b.path).scope]);
|
||||
const outs = reachable(index.linksOut.get(file.path) ?? []);
|
||||
const ins = reachable(index.linksIn.get(file.path) ?? []);
|
||||
const links = [...outs.map(link => ({ out: true, link })), ...ins.map(link => ({ out: false, link }))];
|
||||
// The reader/index does not keep tree oids. Resolve only in this immutable served
|
||||
// snapshot, never HEAD; listTree already checks/bounds the tree and its object IDs.
|
||||
const entry = (await listTree(source.repo, index.commit)).find(entry => entry.path === file.path);
|
||||
if (!entry || entry.mode !== '100644' || entry.type !== 'blob') raise('library-commit', 'indexed file is absent from the served tree');
|
||||
const blob = entry.oid;
|
||||
const lines = file.lines.slice(startLine - 1);
|
||||
const linkRest = Math.max(0, links.length - linkStart);
|
||||
const page = (nLines, nLinks) => {
|
||||
const taken = links.slice(linkStart, linkStart + nLinks);
|
||||
return {
|
||||
path: file.path, commit: index.commit, scope: file.scope, frontmatter: file.frontmatter,
|
||||
path: file.path, commit: index.commit, blob, scope: file.scope, frontmatter: file.frontmatter,
|
||||
lines: lines.slice(0, nLines),
|
||||
links_out: taken.filter(item => item.out).map(item => item.link),
|
||||
links_in: taken.filter(item => !item.out).map(item => item.link),
|
||||
@@ -201,7 +211,7 @@ export async function manuscript(source, { universe: name, book, scope, chapter,
|
||||
const start = positionArg(from, 'from', 1);
|
||||
const end = positionArg(to, 'to', Infinity);
|
||||
if (end < start) invalid('to must not be before from');
|
||||
const { index, files } = await visible(source, universe, { scope: scopeArg(scope), book: bookName, type: 'manuscript' });
|
||||
const { index, files } = await visible(source, universe, { scope: scopeArg(scope, MANUSCRIPT_SCOPES), book: bookName, type: 'manuscript' });
|
||||
let at = 0;
|
||||
if (chapterName !== undefined) {
|
||||
const matches = files.filter(file => file.stem === chapterName);
|
||||
|
||||
@@ -29,3 +29,22 @@ export const note = (type, name, body = '', extra = []) => ['---', `type: ${type
|
||||
// `exclude` (a path prefix today); swapping these two constants is the whole change to the tests.
|
||||
export const EXCLUSION = { exclude: 'Book One/Research' };
|
||||
export const EXCLUDED_PATHS = ['Book One/Research/Ferry Logs.md'];
|
||||
|
||||
// C2-05c: synthetic scope fixture, including same-prefix precedence and a draft.
|
||||
export const SUPPORT_FILES = {
|
||||
'Book One/Characters/Canon.md': note('character', 'Canon', 'Scope fixture canon.'),
|
||||
'Book One/Playground/Plan.md': note('summary', 'Plan', 'Scope fixture playground.'),
|
||||
'Book One/Support/Notes/Helper.md': note('summary', 'Helper', 'Scope fixture supporting.'),
|
||||
'Notes/HarborSupport/Loose.md': note('summary', 'Loose', 'Scope fixture supporting loose.'),
|
||||
'Drafts/Idea/Support/Seed.md': note('summary', 'Seed', 'Scope fixture supporting draft.'),
|
||||
};
|
||||
export const withSupporting = entry => ({
|
||||
...entry,
|
||||
universes: {
|
||||
...entry.universes,
|
||||
harbor: { ...entry.universes.harbor,
|
||||
playground: [...entry.universes.harbor.playground, 'Book One/Support/'],
|
||||
supporting: ['Book One/Support/', 'Notes/HarborSupport/'] },
|
||||
idea: { kind: 'draft', name: 'Idea', playground: ['Drafts/Idea/'], supporting: ['Drafts/Idea/Support/'] },
|
||||
},
|
||||
});
|
||||
@@ -1,11 +1,13 @@
|
||||
// Query tests for design 3 (C2-05a): universe isolation, scope labels, unmapped files, hybrids, paging
|
||||
// under the reply cap, refusals and manuscript paging. Fixtures are made up and built under the scratch area.
|
||||
import { test } from 'node:test';
|
||||
import { writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import assert from 'node:assert/strict';
|
||||
import { REPLY_CAP } from '../../harness/src/tools.mjs';
|
||||
import { find, get, search, manuscript, universeInfo, QUERY_REPLY_CAP, LOOKUP_LIMIT, MANUSCRIPT_LINES } from '../src/query.mjs';
|
||||
import { mapping, MARKER } from './helpers.mjs';
|
||||
import { scratchRepo, note, EXCLUSION, EXCLUDED_PATHS } from './query-fixture.mjs';
|
||||
import { mapping, MARKER, git } from './helpers.mjs';
|
||||
import { scratchRepo, note, EXCLUSION, EXCLUDED_PATHS, SUPPORT_FILES, withSupporting } from './query-fixture.mjs';
|
||||
|
||||
// A hybrid over harbor and second. Its one constraint is EXCLUSION (query-fixture.mjs) and it declares no
|
||||
// books or playground.
|
||||
@@ -345,9 +347,9 @@ test('PRD 1.14: a draft universe has no canon, so its lookups return files only
|
||||
assert.ok(found.results.every(r => r.scope === 'playground' && r.universe === 'idea' && r.book === null));
|
||||
const hits = (await search(s, { universe: 'idea', text: 'draft', scope: 'playground' })).results;
|
||||
assert.equal(hits.length, 2);
|
||||
const rin = await get(s, { universe: 'idea', path: 'Drafts/New World/Characters/Rin.md' });
|
||||
const rin = await get(s, { universe: 'idea', path: 'Drafts/New World/Characters/Rin.md', scope: 'playground' });
|
||||
assert.equal(rin.scope, 'playground');
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Drafts/New World/Characters/Rin.md' }), null);
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Drafts/New World/Characters/Rin.md', scope: 'playground' }), null);
|
||||
});
|
||||
|
||||
test('PRD 1.14: library_universe on a draft returns its kind, name and prefixes', async t => {
|
||||
@@ -363,7 +365,102 @@ test('PRD 1.14: library_universe on a draft returns its kind, name and prefixes'
|
||||
test('a hybrid gets a source playground file as null, while the source gets it with scope playground', async t => {
|
||||
const ref = scratchRepo(t, FILES);
|
||||
const path = 'Book One/Playground/What If.md';
|
||||
assert.equal(await get(src(ref, mapping(WITH_BLEND)), { universe: 'blend', path }), null);
|
||||
const own = await get(src(ref), { universe: 'harbor', path });
|
||||
assert.equal(await get(src(ref, mapping(WITH_BLEND)), { universe: 'blend', path, scope: 'playground' }), null);
|
||||
const own = await get(src(ref), { universe: 'harbor', path, scope: 'playground' });
|
||||
assert.equal(own.scope, 'playground');
|
||||
});
|
||||
|
||||
// mos-1330 item3: the new get contract defaults to canon even for an explicit playground path.
|
||||
test('C2-05c get without scope returns null for a playground path', async t => {
|
||||
const s = src(scratchRepo(t, FILES));
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Book One/Playground/What If.md' }), null);
|
||||
});
|
||||
|
||||
// Row284: scope is explicit for get as well as lists; blob and lines share a served snapshot.
|
||||
test('C2-05c supporting find/search/get are opt-in at book, universe and draft level', async t => {
|
||||
const s = src(scratchRepo(t, SUPPORT_FILES), mapping(withSupporting));
|
||||
const expected = ['Book One/Support/Notes/Helper.md', 'Notes/HarborSupport/Loose.md'];
|
||||
assert.deepEqual(paths(await find(s, { universe: 'harbor', query: '', scope: 'supporting' })), expected);
|
||||
assert.deepEqual((await search(s, { universe: 'harbor', text: 'Scope fixture', scope: 'supporting' })).results.map(x => x.path), expected);
|
||||
assert.deepEqual(paths(await find(s, { universe: 'harbor', query: '' })), ['Book One/Characters/Canon.md']);
|
||||
assert.deepEqual((await search(s, { universe: 'harbor', text: 'Scope fixture' })).results.map(x => x.path), ['Book One/Characters/Canon.md']);
|
||||
for (const path of expected) {
|
||||
assert.equal(await get(s, { universe: 'harbor', path }), null);
|
||||
assert.equal(await get(s, { universe: 'harbor', path, scope: 'playground' }), null);
|
||||
assert.equal((await get(s, { universe: 'harbor', path, scope: 'supporting' })).scope, 'supporting');
|
||||
assert.equal(await get(s, { universe: 'second', path, scope: 'supporting' }), null);
|
||||
}
|
||||
const draftPath = 'Drafts/Idea/Support/Seed.md';
|
||||
assert.deepEqual(paths(await find(s, { universe: 'idea', query: '', scope: 'supporting' })), [draftPath]);
|
||||
assert.equal((await get(s, { universe: 'idea', path: draftPath, scope: 'supporting' })).scope, 'supporting');
|
||||
assert.equal(await get(s, { universe: 'idea', path: draftPath }), null);
|
||||
assert.equal((await find(s, { universe: 'harbor', query: '', book: 'Book One', scope: 'supporting' })).results.length, 1);
|
||||
});
|
||||
|
||||
test('C2-05c an absent supporting prefix returns nothing without refusal', async t => {
|
||||
const s = src(scratchRepo(t, SUPPORT_FILES));
|
||||
assert.deepEqual((await find(s, { universe: 'harbor', query: '', scope: 'supporting' })).results, []);
|
||||
assert.deepEqual((await search(s, { universe: 'harbor', text: 'Scope fixture', scope: 'supporting' })).results, []);
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Book One/Characters/Canon.md', scope: 'supporting' }), null);
|
||||
assert.equal(await get(s, { universe: 'harbor', path: 'Book One/Support/Notes/Helper.md', scope: 'supporting' }), null);
|
||||
});
|
||||
|
||||
test('C2-05c each get blob equals its served tree object, even after source HEAD advances', async t => {
|
||||
const ref = scratchRepo(t, SUPPORT_FILES), s = src(ref, mapping(withSupporting));
|
||||
const canon = 'Book One/Characters/Canon.md';
|
||||
const priorBlob = git(ref.repo, 'rev-parse', `${ref.commit}:${canon}`);
|
||||
writeFileSync(join(ref.repo, canon), note('character', 'Canon changed', 'New HEAD must not be read.'));
|
||||
git(ref.repo, 'add', '-A'); git(ref.repo, 'commit', '-qm', 'advance head fixture');
|
||||
assert.notEqual(git(ref.repo, 'rev-parse', `HEAD:${canon}`), priorBlob);
|
||||
for (const [scope, path] of [['canon', canon], ['playground', 'Book One/Playground/Plan.md'], ['supporting', 'Book One/Support/Notes/Helper.md']]) {
|
||||
const result = await get(s, { universe: 'harbor', path, scope });
|
||||
assert.equal(result.commit, ref.commit);
|
||||
assert.equal(result.blob, git(ref.repo, 'rev-parse', `${ref.commit}:${path}`));
|
||||
assert.ok(!JSON.stringify(result.lines).includes('New HEAD must not be read'));
|
||||
}
|
||||
});
|
||||
|
||||
test('C2-05c full outgoing and incoming links stay in scope order across pages without losing cross-scope links', async t => {
|
||||
const files = {}, groups = { supporting: [], playground: [], canon: [] };
|
||||
for (const [scope, folder] of [['canon', 'Book One/Characters'], ['playground', 'Book One/Playground'], ['supporting', 'Book One/Support']]) {
|
||||
for (let n = 0; n < 100; n++) {
|
||||
const path = `${folder}/${scope}-${String(n).padStart(3, '0')}-${'x'.repeat(80)}.md`;
|
||||
groups[scope].push(path);
|
||||
files[path] = `[[Book One/Characters/Hub]]\n`;
|
||||
}
|
||||
}
|
||||
const all = [...groups.canon, ...groups.playground, ...groups.supporting];
|
||||
files['Book One/Characters/Hub.md'] = all.map(path => `[[${path.slice(0, -3)}]]`).join('\n');
|
||||
// Other-universe links are never returned, regardless of rank.
|
||||
files['Second World/Other.md'] = '[[Book One/Characters/Hub]]\n';
|
||||
const s = src(scratchRepo(t, files), mapping(withSupporting));
|
||||
const outs = [], ins = [];
|
||||
let offset = 0, pages = 0;
|
||||
for (; pages < 20; pages++) {
|
||||
const page = await get(s, { universe: 'harbor', path: 'Book One/Characters/Hub.md', from: 999_999, links_from: offset });
|
||||
assert.ok(JSON.stringify(page).length <= QUERY_REPLY_CAP);
|
||||
outs.push(...page.links_out.map(x => x.path)); ins.push(...page.links_in.map(x => x.path));
|
||||
if (page.links_next === null) break;
|
||||
offset = page.links_next;
|
||||
}
|
||||
const expected = [...groups.supporting, ...groups.playground, ...groups.canon];
|
||||
assert.ok(pages > 0 && pages < 20, 'links occupy multiple bounded pages');
|
||||
assert.deepEqual(outs, expected);
|
||||
assert.deepEqual(ins, expected);
|
||||
assert.equal(new Set(outs).size, 300);
|
||||
assert.equal(new Set(ins).size, 300);
|
||||
});
|
||||
|
||||
test('C2-05c manuscript still refuses supporting rather than silently widening its scope', async t => {
|
||||
const s = src(scratchRepo(t, SUPPORT_FILES), mapping(withSupporting));
|
||||
await assert.rejects(manuscript(s, { universe: 'harbor', book: 'Book One', scope: 'supporting' }), { code: 'query-argument-invalid' });
|
||||
});
|
||||
|
||||
test('C2-05c scope expansion keeps hybrid sources canon-only and refuses all/array scopes', async t => {
|
||||
const s = src(scratchRepo(t, SUPPORT_FILES), mapping(entry => withSupporting(WITH_BLEND(entry))));
|
||||
assert.deepEqual((await find(s, { universe: 'blend', query: '', scope: 'supporting' })).results, []);
|
||||
assert.equal(await get(s, { universe: 'blend', path: 'Book One/Support/Notes/Helper.md', scope: 'supporting' }), null);
|
||||
assert.equal((await get(s, { universe: 'blend', path: 'Book One/Characters/Canon.md' })).scope, 'canon');
|
||||
for (const scope of ['all', ['canon', 'supporting']])
|
||||
await assert.rejects(get(s, { universe: 'harbor', path: 'Book One/Characters/Canon.md', scope }), { code: 'query-argument-invalid' });
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
# Model proxy (P1, #1827)
|
||||
# Model proxy (P1 #1827, P2 #1829)
|
||||
|
||||
The bus initializes and manages one pinned CLIProxyAPI per instance. PIN records
|
||||
release URLs and independently reviewable SHA256s of the archive, executable and
|
||||
@@ -20,8 +20,8 @@ contains `proxy.port` and `proxy.binary` only when proxy.json exists.
|
||||
|
||||
The management client is imported by the bus; it only GETs/PUTs the v8 list on
|
||||
127.0.0.1, never follows redirects, and emits receipts with SHA256 key IDs only.
|
||||
Client-key values are held in memory, never returned by a bus verb. P2 adds
|
||||
per-agent lifecycle integration; this row creates only the sentinel.
|
||||
Client-key values are never returned by an agent-facing bus verb. The bus manages
|
||||
per-agent lifecycle below; initialization creates only the sentinel.
|
||||
|
||||
The unit starts through a hash-checking runner with the pinned panel directory
|
||||
and `-local-model`, so catalogs require no network. Render/install is explicit;
|
||||
@@ -47,3 +47,34 @@ Then start the bus normally and retry init from the human terminal. If the bus
|
||||
or initialization state is uncertain, leave the lock in place. A refused fetch
|
||||
may leave empty `opt/cliproxyapi/` parent directories; they are not an installed
|
||||
version and need not be removed before retrying.
|
||||
|
||||
P2 adds an implicit `model` credential for every agent in proxy mode. The bus
|
||||
confirms the v8 key-list write before storing a key at
|
||||
`<configDir>/secrets/agents/<business>/<agent>/model` (0600, directories 0700).
|
||||
The private file holds the key and its lifecycle metadata. Status and receipts
|
||||
contain key IDs, issue time, rotation date and state; they never contain values.
|
||||
No role or business `credentials.model` reference is needed or accepted.
|
||||
|
||||
Rotation defaults to 30 days. New launches use the replacement key. The old key
|
||||
stays until its runs end or its grace expires; a failed bounded session stop
|
||||
keeps both the old key and its retirement metadata for the next check. Revoke
|
||||
requires a confirming management GET and polling `/v1/models` to 401 before local
|
||||
cleanup. Polling has a five-second total deadline and 25ms intervals. A durable
|
||||
`revoking` state blocks launches before preflight, and boot reconciliation
|
||||
finishes it without restoring that key. Reconciliation preserves foreign keys
|
||||
and refuses launches when this instance's sentinel is absent.
|
||||
|
||||
For a new instance only, human `proxy init` also accepts
|
||||
`--rotate-grace-minutes N` (1–1440, default 120) and
|
||||
`--first-output-seconds N` (1–3600, default 120). These are optional fields in
|
||||
proxy.json; existing two-field sidecars remain valid. There is no per-agent
|
||||
rotation-period setter in P2. Fetch also refuses agent-marked and tester calls
|
||||
before reading config or touching the network/disk.
|
||||
|
||||
The trusted launcher requests `modelPreflight` with business/role and receives
|
||||
exactly `{ baseUrl, key }`. That answer never passes through an agent socket or
|
||||
status API. A 401 or other non-success HTTP status refuses `proxy-key-refused`;
|
||||
a failed connection refuses `proxy-down`. No stdout by the configured deadline
|
||||
requests a bounded stop and records `proxy-stall` with key IDs. The runner writes
|
||||
only a fixed token to `<run>/proxy-first-output`, never stdout contents. A stop
|
||||
failure records/refuses `proxy-stop-failed` and retains the key.
|
||||
@@ -62,6 +62,27 @@ export class ManagementClient {
|
||||
}
|
||||
getKeys() { return this.#call('GET'); }
|
||||
putKeys(keys) { if (!validKeys(keys)) refuse('proxy-client-invalid'); return this.#call('PUT', keys); }
|
||||
// Client-key authentication only. Never send the management key to the model route,
|
||||
// follow a Location, consume/log the response body or trust an ambient dispatcher.
|
||||
async probeKey(key, { timeout = this.#timeout } = {}) {
|
||||
if (!validManagementKey(key) || !Number.isFinite(timeout) || timeout <= 0) refuse('proxy-client-invalid');
|
||||
let timer, status = 0;
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const req = http.request(this.#base + '/v1/models', { method: 'GET', headers: { Authorization: `Bearer ${key}` } }, response => {
|
||||
status = response.statusCode;
|
||||
response.destroy();
|
||||
resolve(status);
|
||||
});
|
||||
req.on('error', () => reject(new ProxyError('proxy-down')));
|
||||
timer = setTimeout(() => { req.destroy(); reject(new ProxyError('proxy-down')); }, timeout);
|
||||
req.end();
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
this.#receipt({ call: 'GET /v1/models', status, keyIds: [sha256(key).slice(0, 12)] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trusted bus boot only: no CLI/Console/session exports this value or client.
|
||||
|
||||
@@ -34,3 +34,13 @@ export function validManagementKey(key) {
|
||||
try { validateHeaderValue('Authorization', `Bearer ${key}`); return true; }
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
export function proxyTiming(value) {
|
||||
const result = {};
|
||||
for (const [field, max, code] of [['rotateGraceMinutes', 1440, 'proxy-rotate-grace-invalid'], ['firstOutputSeconds', 3600, 'proxy-first-output-invalid']]) {
|
||||
if (!Object.hasOwn(value, field)) continue;
|
||||
if (!Number.isInteger(value[field]) || value[field] < 1 || value[field] > max) refuse(code);
|
||||
result[field] = value[field];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2,23 +2,26 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import net from 'node:net';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { PIN, refuse, safePath, regularBytes, sha256, validManagementKey } from './common.mjs';
|
||||
import { PIN, refuse, safePath, regularBytes, sha256, validManagementKey, proxyTiming } from './common.mjs';
|
||||
export function installPaths(dataRoot, pin = PIN) {
|
||||
const dir = path.join(dataRoot, 'opt', 'cliproxyapi', pin.version);
|
||||
return { dir, binary: path.join(dir, 'cli-proxy-api'), panel: path.join(dir, 'static', 'management.html') };
|
||||
}
|
||||
export function validateProxy(value, dataRoot, pin = PIN) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)
|
||||
|| Object.keys(value).sort().join(',') !== 'binary,port'
|
||||
|| Object.keys(value).some(k => !['binary', 'port', 'rotateGraceMinutes', 'firstOutputSeconds'].includes(k))
|
||||
|| !Number.isInteger(value.port) || value.port < 1024 || value.port > 65535 || value.port === 8317
|
||||
|| value.binary !== installPaths(dataRoot, pin).binary) refuse('proxy-config-invalid');
|
||||
return { port: value.port, binary: value.binary };
|
||||
return { port: value.port, binary: value.binary, ...proxyTiming(value) };
|
||||
}
|
||||
export function loadProxy(configDir, dataRoot) {
|
||||
const file = path.join(configDir, 'proxy.json');
|
||||
try { fs.lstatSync(file); } catch (e) { if (e.code === 'ENOENT') return undefined; refuse('proxy-config-invalid'); }
|
||||
try { return validateProxy(JSON.parse(regularBytes(file, 'proxy-config-invalid')), dataRoot); }
|
||||
catch { refuse('proxy-config-invalid'); }
|
||||
catch (error) {
|
||||
if (['proxy-rotate-grace-invalid', 'proxy-first-output-invalid'].includes(error.code)) throw error;
|
||||
refuse('proxy-config-invalid');
|
||||
}
|
||||
}
|
||||
export function verifyInstall(dataRoot, pin = PIN) {
|
||||
const paths = installPaths(dataRoot, pin);
|
||||
@@ -50,8 +53,9 @@ export function initialConfig({ dataRoot, port, key, sentinel }) {
|
||||
};
|
||||
}
|
||||
// Called only by the bus's path-bound human verb, never by an agent or the tester.
|
||||
export async function initializeProxy({ configDir, dataRoot, key, port, pin = PIN }) {
|
||||
export async function initializeProxy({ configDir, dataRoot, key, port, pin = PIN, ...options }) {
|
||||
if (!validManagementKey(key)) refuse('proxy-key-invalid');
|
||||
const timing = proxyTiming(options);
|
||||
safePath(configDir, 'proxy-init-failed'); safePath(dataRoot, 'proxy-init-failed');
|
||||
const paths = verifyInstall(dataRoot, pin);
|
||||
const settingsFile = path.join(configDir, 'proxy.json'), proxyDir = path.join(dataRoot, 'proxy');
|
||||
@@ -77,7 +81,7 @@ export async function initializeProxy({ configDir, dataRoot, key, port, pin = PI
|
||||
const tag = sha256(configDir).slice(0, 12);
|
||||
const sentinel = `msk-${tag}-sentinel-${randomBytes(32).toString('base64url')}`;
|
||||
fs.writeFileSync(path.join(proxyDir, 'config.yaml'), JSON.stringify(initialConfig({ dataRoot, port: reservation.port, key, sentinel }), null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
||||
const settings = { port: reservation.port, binary: paths.binary };
|
||||
const settings = { port: reservation.port, binary: paths.binary, ...timing };
|
||||
const settingsFd = fs.openSync(settingsFile, 'wx', 0o600); ownsSettings = true;
|
||||
try { fs.writeFileSync(settingsFd, JSON.stringify(settings, null, 2) + '\n'); } finally { fs.closeSync(settingsFd); }
|
||||
return settings;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// P2 RED: catch wrong-key preflight, 401 success, redirect following and unbounded probes.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import http from 'node:http';
|
||||
import { ManagementClient } from '../src/client.mjs';
|
||||
|
||||
async function fixture(t, handler) {
|
||||
const server = http.createServer(handler);
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
t.after(() => new Promise(resolve => { server.closeAllConnections(); server.close(resolve); }));
|
||||
const baseUrl = `http://127.0.0.1:${server.address().port}`;
|
||||
const receipts = [];
|
||||
return { server, receipts, client: new ManagementClient({ baseUrl, key: 'fx331-manage', receipt: r => receipts.push(r) }) };
|
||||
}
|
||||
for (const status of [200, 401, 503]) {
|
||||
test(`probeKey observes ${status} with one agent-authenticated GET /v1/models`, async t => {
|
||||
const calls = [];
|
||||
const f = await fixture(t, (req, res) => {
|
||||
calls.push({ path: req.url, method: req.method, ownKey: req.headers.authorization === 'Bearer fx331-agent' });
|
||||
res.writeHead(status); res.end('fixture-body');
|
||||
});
|
||||
assert.equal(typeof f.client.probeKey, 'function', 'client must support bounded agent-key probe');
|
||||
assert.equal(await f.client.probeKey('fx331-agent', { timeout: 100 }), status);
|
||||
assert.deepEqual(calls, [{ path: '/v1/models', method: 'GET', ownKey: true }]);
|
||||
const output = JSON.stringify(f.receipts);
|
||||
assert.equal(output.includes('fx331-agent'), false);
|
||||
assert.equal(output.includes('fx331-manage'), false);
|
||||
});
|
||||
}
|
||||
test('proxy-down: agent-key probe connection failure is sanitized', async t => {
|
||||
const f = await fixture(t, (_req, res) => res.end());
|
||||
await new Promise(resolve => f.server.close(resolve));
|
||||
assert.equal(typeof f.client.probeKey, 'function');
|
||||
await assert.rejects(f.client.probeKey('fx331-agent', { timeout: 100 }), e => e.code === 'proxy-down' && !e.message.includes('fx331-agent'));
|
||||
});
|
||||
test('proxy-down: silent listener cannot extend probe request deadline', async t => {
|
||||
const f = await fixture(t, () => {});
|
||||
assert.equal(typeof f.client.probeKey, 'function');
|
||||
const started = performance.now();
|
||||
await assert.rejects(f.client.probeKey('fx331-agent', { timeout: 50 }), e => e.code === 'proxy-down');
|
||||
assert.ok(performance.now() - started < 1000, 'bounded HTTP probe must settle');
|
||||
});
|
||||
test('agent-key probe returns redirect status without following Location or exposing key', async t => {
|
||||
let calls = 0;
|
||||
const f = await fixture(t, (_req, res) => { calls++; res.writeHead(302, { Location: '/wrong' }); res.end(); });
|
||||
assert.equal(typeof f.client.probeKey, 'function');
|
||||
assert.equal(await f.client.probeKey('fx331-agent', { timeout: 100 }), 302);
|
||||
assert.equal(calls, 1);
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
// P2 named settings refuse independently; legacy sidecars retain their shape.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { validateProxy, installPaths } from '../src/settings.mjs';
|
||||
const dataRoot = '/fixture-data';
|
||||
const base = { port: 18432, binary: installPaths(dataRoot).binary };
|
||||
test('proxy settings without optional timing fields remain valid', () => assert.deepEqual(validateProxy(base, dataRoot), base));
|
||||
test('proxy settings accept each positive upper bound', () => {
|
||||
assert.deepEqual(validateProxy({ ...base, rotateGraceMinutes: 1440, firstOutputSeconds: 3600 }, dataRoot), { ...base, rotateGraceMinutes: 1440, firstOutputSeconds: 3600 });
|
||||
});
|
||||
for (const [field, maximum, code] of [['rotateGraceMinutes', 1440, 'proxy-rotate-grace-invalid'], ['firstOutputSeconds', 3600, 'proxy-first-output-invalid']]) {
|
||||
for (const value of [0, -1, maximum + 1, 1.5, '120', null]) test(`${code}: ${JSON.stringify(value)}`, () => {
|
||||
assert.throws(() => validateProxy({ ...base, [field]: value }, dataRoot), e => e.code === code);
|
||||
});
|
||||
}
|
||||
@@ -353,7 +353,7 @@ async function validate(document, file) {
|
||||
const { loadProxy } = await import("../packages/model-proxy/src/settings.mjs");
|
||||
proxy = loadProxy(path.dirname(file), dataRoot);
|
||||
}
|
||||
} catch { fail(2, "proxy-config-invalid"); }
|
||||
} catch (error) { fail(2, ['proxy-rotate-grace-invalid', 'proxy-first-output-invalid'].includes(error.code) ? error.code : "proxy-config-invalid"); }
|
||||
|
||||
return {
|
||||
configVersion: document.configVersion,
|
||||
|
||||
Reference in new issue
Block a user