launchpad: row 294 run 4 (#1800): whole gate green on 8616b9a4f (webui 365, cli 909, bus 873, 10 scripts), M1-M11 killed; M1 record names G4's first failure and G5 green (Duckworth C1); receipts in run4/

This commit is contained in:
jason.woltje committed 2026-10-23 01:37:03 -05:00
1 parent 6f07930679
commit d3059c2c3d
41 files changed
+2646

No files matched your search

+52
View File
@@ -416,3 +416,55 @@ failure was compactions-launch, so the :229 rerun rule did not apply. The slot w
…/packages/cli/src/config.mjs". My launchpad-0076 put the two subtests under row 233; they are
row196's.
- access-boot.test.mjs B1, B2 and B3: `warnings` also held the fixture's tracker line.
### Run 4, 2026-10-23 06:20Z (sage-3244 CLEAR, sage-3248 go), head 8616b9a4f, manifest a1ff5d67…9b99
Duckworth's round 4 approve was recorded first (duckworth-0257b, rev 3469, c31053). The run started
at 06:20:53Z, on duckworth-0257b's relay of sage-3247; sage-3248 arrived seconds later with the same
go. TMPDIR was /mnt/storage/scratch/tmp/gd3JQ8r. Receipts are in run4/: summary.txt, the drivers,
suite-totals.txt (the three suites' node --test totals; the whole logs stay in scratch), every
scripts/test-*.sh log, and each mutant's diff and TAP output with mutants.json.
| Step | Result | Time | Limit |
|---|---|---|---|
| npm ci --ignore-scripts, root | ok | 1.9 s | 900 s |
| npm ci --ignore-scripts, packages/webui | ok | 0.5 s | 900 s |
| webui suite | 365 tests: 365 pass, 0 fail, 0 skipped | 57.9 s | 600 s |
| cli suite | 909 tests: 909 pass, 0 fail, 0 skipped | 600.1 s | 2100 s |
| bus suite | 873 tests: 873 pass, 0 fail, 0 skipped | 24.4 s | 900 s |
| scripts/test-auth.sh | ok, 15 passed | 0.8 s | 126 s |
| scripts/test-conductor.sh | ok | 6.8 s | 145 s |
| scripts/test-config.sh | ok | 2.5 s | 130 s |
| scripts/test-discord.sh | ok | 7.3 s | 147 s |
| scripts/test-extension-package.sh | ok | 1.2 s | 130 s |
| scripts/test-foundation.sh | ok | 39.1 s | 269 s |
| scripts/test-land-work.sh | ok | 11.8 s | 171 s |
| scripts/test-queue.sh | ok | 20.4 s | 226 s |
| scripts/test-release.sh | ok | 2.7 s | 157 s |
| scripts/test-task.sh | ok, 91 passed; the two live provider cases skip by design (row 244), no Docker group skipped | 13.9 s | 231 s |
No step was rerun, so the :229 rule was not used. The worktree was clean after the scripts and after
every mutant restore.
Mutants, each its killing file alone under timeout -k 10 300, all killed (MUTANTS rc=0, 06:34:22Z):
| # | Seconds | Red |
|---|---|---|
| M1 | 1.9 | G4 only; G5 ok in the same run |
| M2 | 2.1 | G3 |
| M3 | 1.8 | G6, and G7 (the page text changed under it) |
| M4 | 0.5 | the F8 peer test, B1 |
| M5 | 2.1 | G2, G3 |
| M6 | 2.0 | G8 |
| M7 | 0.3 | B1, B3 |
| M8 | 2.1 | G1, G9 |
| M9 | 0.4 | B3 |
| M10 | 2.0 | G9 |
| M11 | 2.0 | G9 |
M1 record (Duckworth C1): G4's first failure is its first route, `GET /api/bus/inbox: 502
{"error":"access-refused","message":"access-refused"}`, expected 403 (run4/M1.tap). The failing
assertion is granted-user.test.mjs:241, the loop's status check. The Mutants table above says :239;
round 2's `tables` helper moved it down two lines. With the Console gate gone, the broker refuses
the owner verb itself, and the Console answers 502 with the broker's code. G5, the bus refusing
every owner verb on its own, is `ok 5` in the same TAP.
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
index cd51a0b6e..f93fb49e0 100644
--- a/packages/webui/src/serve.mjs
+++ b/packages/webui/src/serve.mjs
@@ -683,7 +683,7 @@ export async function startServer({ host = '127.0.0.1', port = 7330, board = DEF
// FED R6 (#1800): a granted user. The Console's gate first; behind it no human-transport
// object is left, so a route past the gate reaches the broker only as this user.
if (session?.user !== undefined) {
- if (!userRoute(path, req.method)) return json(res, 403, { error: 'owner-only', message: 'this part of the Console is the business owner\'s' });
+ if (false) return json(res, 403, { error: 'owner-only', message: 'this part of the Console is the business owner\'s' });
if (userOptions === null) return json(res, 503, { error: 'not-configured', message: 'the Console has no user transport' });
({ bus, send, settingsWrite, projectFiles, projectFile, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing } = userOptions(session));
reads = null; upstream = null; files = USER_FILES; granted = true;
+145
View File
@@ -0,0 +1,145 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 97.950367
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 97.105064
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 60.372976
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 65.745937
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 53.325523
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 68.487478
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 86.961472
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 432.952739
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
ok 1 - grant removed
---
duration_ms: 59.298593
type: 'test'
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 67.687849
type: 'test'
...
# Subtest: grant expired
ok 3 - grant expired
---
duration_ms: 59.604679
type: 'test'
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 71.375547
type: 'test'
...
1..4
ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 258.570476
type: 'test'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
not ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 67.489703
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:235:1'
failureType: 'testCodeFailure'
error: |-
GET /api/bus/inbox: 502 {"error":"access-refused","message":"access-refused"}
502 !== 403
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 403
actual: 502
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:241:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 73.143926
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 65.906106
type: 'test'
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 55.03254
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 682.517872
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 39.36235
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 18
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1903.614611
# stderr
@@ -0,0 +1,11 @@
diff --git a/packages/webui/src/console-bus.mjs b/packages/webui/src/console-bus.mjs
index a446370c2..41a77074c 100644
--- a/packages/webui/src/console-bus.mjs
+++ b/packages/webui/src/console-bus.mjs
@@ -45,5 +45,5 @@ export function consoleBus(call, { send: sendOn = false, owner = null } = {}) {
// request reaches `owner` (mos-1394, sage-3157): createChat sends it only `plugins` with {}.
export function userOptionsFor({ owner, transport, send = false }) {
if (typeof owner !== 'function' || typeof transport !== 'function') throw new TypeError('userOptionsFor needs owner and transport functions');
- return session => consoleBus(transport(session.user), { send, owner });
+ return session => consoleBus(transport(session.user), { send, owner: (v, a) => owner(v, { ...a, sub: session.sub }) });
}
@@ -0,0 +1,157 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 109.420278
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 92.156219
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 64.042376
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 46.110537
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 55.140611
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 98.591293
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 121.289506
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 478.250572
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
ok 1 - grant removed
---
duration_ms: 47.439106
type: 'test'
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 65.33328
type: 'test'
...
# Subtest: grant expired
ok 3 - grant expired
---
duration_ms: 70.57336
type: 'test'
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 52.579125
type: 'test'
...
1..4
ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 236.514118
type: 'test'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 68.104497
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 73.362804
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
not ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 59.549676
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:279:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
[
'plugins',
+ {
+ sub: 'alice-sub'
+ }
- {}
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0: 'plugins'
1:
actual:
0: 'plugins'
1:
sub: 'alice-sub'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:297:46)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 64.639126
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 754.648348
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 44.114514
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 18
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2018.907276
# stderr
@@ -0,0 +1,17 @@
diff --git a/packages/webui/src/chat.mjs b/packages/webui/src/chat.mjs
index 1b0a41dce..311048755 100644
--- a/packages/webui/src/chat.mjs
+++ b/packages/webui/src/chat.mjs
@@ -13,9 +13,10 @@ import { BusReadError } from './bus.mjs';
export function createChat({ call, owner = null }) {
if (typeof call !== 'function') throw new TypeError('chat needs a bus transport function');
if (owner !== null && typeof owner !== 'function') throw new TypeError('chat owner must be a bus transport function');
- const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => verb === 'plugins' ? owner('plugins', {}) : call(verb, args) });
+ let last = {};
+ const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => verb === 'plugins' ? owner('plugins', last) : call(verb, args) });
const channel = async (verb, args) => {
- try { return await dispatch(verb, args); }
+ try { last = args; return await dispatch(verb, args); }
catch (err) {
if (err instanceof PluginError) throw new BusReadError(err.code, err.message);
throw err;
@@ -0,0 +1,159 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 102.153087
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 41.816765
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 53.103013
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 66.865735
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 59.528254
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 77.000699
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 59.00091
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 358.182177
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
ok 1 - grant removed
---
duration_ms: 43.655065
type: 'test'
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 82.752965
type: 'test'
...
# Subtest: grant expired
ok 3 - grant expired
---
duration_ms: 75.927345
type: 'test'
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 52.720211
type: 'test'
...
1..4
ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 255.656191
type: 'test'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 61.988092
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 72.391514
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
not ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 100.121943
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:279:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
[
'plugins',
+ {
+ name: 'alice work',
+ project: '6ca7e806-cb2e-432f-8456-32d161a596c5'
+ }
- {}
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0: 'plugins'
1:
actual:
0: 'plugins'
1:
project: '6ca7e806-cb2e-432f-8456-32d161a596c5'
name: 'alice work'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:297:46)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 81.778691
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 736.353706
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 36.965803
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 18
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1931.825146
# stderr
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/sso.mjs b/packages/webui/src/sso.mjs
index dd5ad8f61..e9506af29 100644
--- a/packages/webui/src/sso.mjs
+++ b/packages/webui/src/sso.mjs
@@ -306,7 +306,7 @@ export async function createSso(config, { secrets, fetch: customFetch = null, lo
if (now - s.created >= SESSION_ABSOLUTE || now - s.seen >= SESSION_IDLE) { sessions.delete(key); return null; }
// FED R6 (design 3.1 D5): a user's grant is read again on every request, so a user revoked,
// removed or expired since sign-in has no session from the next request on.
- if (s.user !== undefined && !users?.check(s)) {
+ if (s.user !== undefined && false) {
sessions.delete(key);
refuse('user-revoked', s.provider, ` user=${JSON.stringify(s.user)}`);
return null;
+214
View File
@@ -0,0 +1,214 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 99.922321
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 92.761557
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 92.20228
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 63.082847
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 71.423611
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 53.437758
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 88.763112
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 462.628046
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
not ok 1 - grant removed
---
duration_ms: 41.439982
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:13'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ 'access-revoked'
- 'sign-in required'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'sign-in required'
actual: 'access-revoked'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:207:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: subject removed
not ok 2 - subject removed
---
duration_ms: 77.978266
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:13'
failureType: 'testCodeFailure'
error: |-
subject removed: {"rows":[{"id":"76c13564-e781-4b83-ad16-cd1f57b1a97f","at":"2026-10-23T06:34:07.869Z","name":"shared","repo":null},{"id":"3019668b-2d1d-47d9-a523-5e76b50de295","at":"2026-10-23T06:34:07.870Z","name":"private","repo":null}],"at":"2026-10-23T06:34:07.876Z"}
200 !== 401
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 401
actual: 200
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:206:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: grant expired
not ok 3 - grant expired
---
duration_ms: 100.74638
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:13'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ 'access-revoked'
- 'sign-in required'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'sign-in required'
actual: 'access-revoked'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:207:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: file now refused
not ok 4 - file now refused
---
duration_ms: 64.871699
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:13'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ 'access-revoked'
- 'sign-in required'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'sign-in required'
actual: 'access-revoked'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:207:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
1..4
not ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 285.771094
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:192:1'
failureType: 'subtestsFailed'
error: '4 subtests failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 120.543137
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 101.715208
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 44.655459
type: 'test'
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 78.519066
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 747.453516
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 37.353078
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 14
# fail 5
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2105.529578
# stderr
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/user.html b/packages/webui/src/public/user.html
index 0397597af..39e937f10 100644
--- a/packages/webui/src/public/user.html
+++ b/packages/webui/src/public/user.html
@@ -10,7 +10,7 @@
<div class="cmd-right"><form id="sign-out" method="post" action="/auth/logout"><button class="btn" type="submit">Sign out</button></form></div></header>
<main id="main" class="content" tabindex="-1">
<div id="signed-out" class="banner ref" role="alert" tabindex="-1" hidden>You are signed out. <a href="/">Sign in again</a>.</div>
-<p id="user-notice" class="banner ref">The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your messages.</p>
+<p id="user-notice" class="banner ref">The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your replies.</p>
<div id="chat"></div>
</main>
<footer class="foot"><span id="footer">Mosaic Stack</span></footer>
+170
View File
@@ -0,0 +1,170 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 99.908034
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 38.222854
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 35.871094
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 62.921189
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 48.162704
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 49.598628
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 41.671478
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 277.403073
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
ok 1 - grant removed
---
duration_ms: 63.714717
type: 'test'
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 46.793543
type: 'test'
...
# Subtest: grant expired
ok 3 - grant expired
---
duration_ms: 103.148596
type: 'test'
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 62.737012
type: 'test'
...
1..4
ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 276.979306
type: 'test'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 50.94161
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 139.368171
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 69.328685
type: 'test'
...
# Subtest: R6 G6 the user page holds the notice word for word
not ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 1.0549
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:306:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your replies."
- "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your messages."
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your messages."
actual: "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your replies."
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:310:10)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.processPendingSubtests (node:internal/test_runner/test:974:18)
Test.postRun (node:internal/test_runner/test:1542:19)
Test.run (node:internal/test_runner/test:1467:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
not ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 659.67841
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:333:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your replies."
- "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your messages."
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your messages."
actual: "The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files, but not your replies."
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:352:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 39.006102
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 17
# fail 2
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1740.402277
# stderr
@@ -0,0 +1,13 @@
diff --git a/packages/cli/src/access.mjs b/packages/cli/src/access.mjs
index 9f1134347..cd3c503ce 100644
--- a/packages/cli/src/access.mjs
+++ b/packages/cli/src/access.mjs
@@ -59,7 +59,7 @@ export function accessContextFor(system, env) {
businesses[id] = { human: doc.human, instances: Object.keys(doc.roles) };
}
}
- const peers = (system.federation?.peers ?? []).map((p) => p.id);
+ const peers = [];
return { providers: providers.map((p) => p.id), peers, businesses, ssoSubjects };
}
@@ -0,0 +1,81 @@
TAP version 13
# Subtest: R6 a configured peer id validates as a user's home and an unconfigured one refuses
not ok 1 - R6 a configured peer id validates as a user's home and an unconfigured one refuses
---
duration_ms: 8.951306
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:31:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'factory'
- ]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0: 'factory'
actual:
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:35:10)
Test.runInAsyncScope (node:async_hooks:226:14)
Test.run (node:internal/test_runner/test:1402:25)
Test.start (node:internal/test_runner/test:1262:17)
startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17)
...
# Subtest: R6 B1 a boot with access.json binds a granted user, whose home is a configured peer
not ok 2 - R6 B1 a boot with access.json binds a granted user, whose home is a configured peer
---
duration_ms: 80.96728
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:68:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'factory'
- ]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0: 'factory'
actual:
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:71:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 B2 a boot without access.json has no access and binds no user
ok 3 - R6 B2 a boot without access.json has no access and binds no user
---
duration_ms: 62.765959
type: 'test'
...
# Subtest: R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once
ok 4 - R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once
---
duration_ms: 180.02261
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 2
# fail 2
# cancelled 0
# skipped 0
# todo 0
# duration_ms 449.373472
# stderr
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/access.mjs b/packages/webui/src/access.mjs
index 7446814da..721a09207 100644
--- a/packages/webui/src/access.mjs
+++ b/packages/webui/src/access.mjs
@@ -14,7 +14,7 @@ export function consoleAccess({ load, business }) {
try {
const doc = load(), b = business();
const user = doc.users.find(u => u.subjects.some(s => s.provider === provider && s.sub === sub));
- return user && live(doc, user.id, b) ? user.id : null;
+ return user ? user.id : null;
} catch { return null; }
};
return Object.freeze({
+254
View File
@@ -0,0 +1,254 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 137.858619
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
not ok 1 - a listed user with no grant
---
duration_ms: 69.429562
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:13'
failureType: 'testCodeFailure'
error: |-
a listed user with no grant
303 !== 403
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 403
actual: 303
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:176:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: a grant in another business
not ok 2 - a grant in another business
---
duration_ms: 49.204153
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:13'
failureType: 'testCodeFailure'
error: |-
a grant in another business
303 !== 403
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 403
actual: 303
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:176:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: an expired grant
not ok 3 - an expired grant
---
duration_ms: 120.517717
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:13'
failureType: 'testCodeFailure'
error: |-
an expired grant
303 !== 403
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 403
actual: 303
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:176:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: another user's grant
not ok 4 - another user's grant
---
duration_ms: 77.385678
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:13'
failureType: 'testCodeFailure'
error: |-
another user's grant
303 !== 403
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 403
actual: 303
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:176:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:173:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 40.652981
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 74.92896
type: 'test'
...
1..6
not ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 433.064399
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:164:1'
failureType: 'subtestsFailed'
error: '4 subtests failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
not ok 1 - grant removed
---
duration_ms: 64.630321
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:13'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ 'access-revoked'
- 'sign-in required'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'sign-in required'
actual: 'access-revoked'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:207:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 50.619797
type: 'test'
...
# Subtest: grant expired
not ok 3 - grant expired
---
duration_ms: 123.640684
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:13'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
+ actual - expected
+ 'access-revoked'
- 'sign-in required'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'sign-in required'
actual: 'access-revoked'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:207:14)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:199:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 78.007438
type: 'test'
...
1..4
not ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 317.562485
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:192:1'
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 72.835794
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 105.145618
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 59.180853
type: 'test'
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 37.327463
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 775.474923
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 39.045355
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 11
# fail 8
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2107.663356
# stderr
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/user-bus.mjs b/packages/webui/src/user-bus.mjs
index 29c6302bf..9a4b344ee 100644
--- a/packages/webui/src/user-bus.mjs
+++ b/packages/webui/src/user-bus.mjs
@@ -18,7 +18,7 @@ export function userCall({ socket, business, user, env = process.env, timeoutMs
return (verb, args = {}) => new Promise((resolve, reject) => {
let child;
try {
- child = spawn(process.execPath, [USER_CLI, socket], { env: { ...rest, MOSAIC_BUS_USER_NONCE: randomBytes(32).toString('hex') }, stdio: ['pipe', 'pipe', 'pipe'] });
+ child = spawn(process.execPath, [USER_CLI, socket], { env: { ...env, MOSAIC_BUS_USER_NONCE: randomBytes(32).toString('hex') }, stdio: ['pipe', 'pipe', 'pipe'] });
} catch { return reject(new BusReadError('outcome-unknown', 'the bus transport did not start')); }
const out = [], err = [];
let size = 0, settled = false;
+145
View File
@@ -0,0 +1,145 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 122.653189
type: 'test'
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 56.174331
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 44.586843
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 95.990798
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 39.137341
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 93.933879
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 46.821824
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 377.544309
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
ok 1 - grant removed
---
duration_ms: 54.902614
type: 'test'
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 42.309062
type: 'test'
...
# Subtest: grant expired
ok 3 - grant expired
---
duration_ms: 95.824061
type: 'test'
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 55.098887
type: 'test'
...
1..4
ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 248.867763
type: 'test'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 52.680841
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 82.486074
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 80.801522
type: 'test'
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 107.231563
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 762.991755
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
not ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 42.199074
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:359:1'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
true !== false
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: false
actual: true
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:388:10)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
1..9
# tests 19
# suites 0
# pass 18
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2018.947231
# stderr
@@ -0,0 +1,12 @@
diff --git a/packages/cli/src/config.mjs b/packages/cli/src/config.mjs
index 6de5fdc6c..7f7c45bcd 100644
--- a/packages/cli/src/config.mjs
+++ b/packages/cli/src/config.mjs
@@ -133,7 +133,6 @@ export function bootConfig({ system, businessId, env = process.env, warn = () =>
const tracker = trackerFor(vars, b, warn);
if (tracker) config.trackers = { [b.id]: tracker };
const access = accessFor(system, env, warn);
- if (access) config.access = access;
return config;
});
}
@@ -0,0 +1,56 @@
TAP version 13
# Subtest: R6 a configured peer id validates as a user's home and an unconfigured one refuses
ok 1 - R6 a configured peer id validates as a user's home and an unconfigured one refuses
---
duration_ms: 8.577936
type: 'test'
...
# Subtest: R6 B1 a boot with access.json binds a granted user, whose home is a configured peer
not ok 2 - R6 B1 a boot with access.json binds a granted user, whose home is a configured peer
---
duration_ms: 74.086833
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:68:1'
failureType: 'testCodeFailure'
error: "Cannot read properties of undefined (reading 'file')"
code: 'ERR_TEST_FAILURE'
name: 'TypeError'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:70:32)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 B2 a boot without access.json has no access and binds no user
ok 3 - R6 B2 a boot without access.json has no access and binds no user
---
duration_ms: 60.651858
type: 'test'
...
# Subtest: R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once
not ok 4 - R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once
---
duration_ms: 61.005239
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:85:1'
failureType: 'testCodeFailure'
error: "Cannot read properties of undefined (reading 'file')"
code: 'ERR_TEST_FAILURE'
name: 'TypeError'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:92:34)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
1..4
# tests 4
# suites 0
# pass 2
# fail 2
# cancelled 0
# skipped 0
# todo 0
# duration_ms 316.211304
# stderr
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/chat.mjs b/packages/webui/src/chat.mjs
index 1b0a41dce..2a1c203f6 100644
--- a/packages/webui/src/chat.mjs
+++ b/packages/webui/src/chat.mjs
@@ -13,7 +13,7 @@ import { BusReadError } from './bus.mjs';
export function createChat({ call, owner = null }) {
if (typeof call !== 'function') throw new TypeError('chat needs a bus transport function');
if (owner !== null && typeof owner !== 'function') throw new TypeError('chat owner must be a bus transport function');
- const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => verb === 'plugins' ? owner('plugins', {}) : call(verb, args) });
+ const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => verb === 'plugins' ? owner('plugins', {}) : verb === 'thread.list' ? owner(verb, args) : call(verb, args) });
const channel = async (verb, args) => {
try { return await dispatch(verb, args); }
catch (err) {
+156
View File
@@ -0,0 +1,156 @@
TAP version 13
# Subtest: R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
not ok 1 - R6 G1 a granted user signs in, gets the user page and reaches the broker as itself
---
duration_ms: 129.678949
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:130:1'
failureType: 'testCodeFailure'
error: "Cannot read properties of undefined (reading 'map')"
code: 'ERR_TEST_FAILURE'
name: 'TypeError'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:152:38)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
...
# Subtest: R6 G2 a subject with no live grant in this business is refused subject-unknown
# Subtest: a listed user with no grant
ok 1 - a listed user with no grant
---
duration_ms: 102.308353
type: 'test'
...
# Subtest: a grant in another business
ok 2 - a grant in another business
---
duration_ms: 53.531302
type: 'test'
...
# Subtest: an expired grant
ok 3 - an expired grant
---
duration_ms: 44.641678
type: 'test'
...
# Subtest: another user's grant
ok 4 - another user's grant
---
duration_ms: 68.283139
type: 'test'
...
# Subtest: a sub in no file
ok 5 - a sub in no file
---
duration_ms: 90.290255
type: 'test'
...
# Subtest: a file the loader refuses
ok 6 - a file the loader refuses
---
duration_ms: 41.803535
type: 'test'
...
1..6
ok 2 - R6 G2 a subject with no live grant in this business is refused subject-unknown
---
duration_ms: 401.809515
type: 'test'
...
# Subtest: R6 G3 a revoke counts on the next request: 401 and the session is gone
# Subtest: grant removed
ok 1 - grant removed
---
duration_ms: 77.095691
type: 'test'
...
# Subtest: subject removed
ok 2 - subject removed
---
duration_ms: 90.099697
type: 'test'
...
# Subtest: grant expired
ok 3 - grant expired
---
duration_ms: 74.152955
type: 'test'
...
# Subtest: file now refused
ok 4 - file now refused
---
duration_ms: 55.749108
type: 'test'
...
1..4
ok 3 - R6 G3 a revoke counts on the next request: 401 and the session is gone
---
duration_ms: 297.743475
type: 'test'
...
# Subtest: R6 G4 every owner route answers 403 owner-only before any bus call
ok 4 - R6 G4 every owner route answers 403 owner-only before any bus call
---
duration_ms: 70.614829
type: 'test'
...
# Subtest: R6 G5 the bus refuses every owner verb over the user transport on its own
ok 5 - R6 G5 the bus refuses every owner verb over the user transport on its own
---
duration_ms: 79.166288
type: 'test'
...
# Subtest: R6 G9 a user chat sends only the plugins read over the owner transport
not ok 6 - R6 G9 a user chat sends only the plugins read over the owner transport
---
duration_ms: 75.311089
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:279:1'
failureType: 'testCodeFailure'
error: |-
GET /api/chat/threads?project=ec96b734-6752-4afc-bc54-58a188b804a1: {"error":"invalid-response","message":"Chat operation refused (invalid-response)."}
502 !== 200
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 200
actual: 502
operator: 'strictEqual'
stack: |-
go (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:285:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/webui/tests/granted-user.test.mjs:291:3)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
# Subtest: R6 G6 the user page holds the notice word for word
ok 7 - R6 G6 the user page holds the notice word for word
---
duration_ms: 84.352581
type: 'test'
...
# Subtest: R6 G7 the hub label and plain peer links render from config on both pages
ok 8 - R6 G7 the hub label and plain peer links render from config on both pages
---
duration_ms: 775.624362
type: 'test'
...
# Subtest: R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
ok 9 - R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request
---
duration_ms: 40.63858
type: 'test'
...
1..9
# tests 19
# suites 0
# pass 17
# fail 2
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2083.017773
# stderr
@@ -0,0 +1,12 @@
diff --git a/packages/cli/src/config.mjs b/packages/cli/src/config.mjs
index 6de5fdc6c..ff3733104 100644
--- a/packages/cli/src/config.mjs
+++ b/packages/cli/src/config.mjs
@@ -109,7 +109,6 @@ function accessFor(system, env, warn) {
loadAccess(file, context);
} catch (e) {
const code = e instanceof BusinessError && e.exitCode !== 4 ? "access-invalid" : "access-unreadable";
- warn(`access file refused at boot (${code}), no granted user binds until it is fixed: ${file}`);
}
return { file, context };
}
@@ -0,0 +1,60 @@
TAP version 13
# Subtest: R6 a configured peer id validates as a user's home and an unconfigured one refuses
ok 1 - R6 a configured peer id validates as a user's home and an unconfigured one refuses
---
duration_ms: 9.41985
type: 'test'
...
# Subtest: R6 B1 a boot with access.json binds a granted user, whose home is a configured peer
ok 2 - R6 B1 a boot with access.json binds a granted user, whose home is a configured peer
---
duration_ms: 74.90319
type: 'test'
...
# Subtest: R6 B2 a boot without access.json has no access and binds no user
ok 3 - R6 B2 a boot without access.json has no access and binds no user
---
duration_ms: 62.418469
type: 'test'
...
# Subtest: R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once
not ok 4 - R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once
---
duration_ms: 65.709307
type: 'test'
location: '/mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:85:1'
failureType: 'testCodeFailure'
error: |-
not JSON
+ actual - expected
[
'business acme sets tracker.baseUrl, but no project file sets tracker.project; the broker gets no task verbs',
- 'access file refused at boot (access-invalid), no granted user binds until it is fixed: /mnt/storage/scratch/tmp/gd3JQ8r/mosaic-access-boot-UPOnK2/config/access.json'
]
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected:
0: 'business acme sets tracker.baseUrl, but no project file sets tracker.project; the broker gets no task verbs'
1: 'access file refused at boot (access-invalid), no granted user binds until it is fixed: /mnt/storage/scratch/tmp/gd3JQ8r/mosaic-access-boot-UPOnK2/config/access.json'
actual:
0: 'business acme sets tracker.baseUrl, but no project file sets tracker.project; the broker gets no task verbs'
operator: 'deepStrictEqual'
stack: |-
TestContext.<anonymous> (file:///mnt/storage/scratch/launchpad-r294/wt/packages/cli/tests/access-boot.test.mjs:94:12)
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
async Test.run (node:internal/test_runner/test:1409:7)
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
...
1..4
# tests 4
# suites 0
# pass 3
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 328.382551
# stderr
@@ -0,0 +1,28 @@
npm-root rc=0 seconds=1.926 limit=900 06:20:55Z
npm-webui rc=0 seconds=0.452 limit=900 06:20:55Z
webui rc=0 seconds=57.861 limit=600 06:21:53Z
cli rc=0 seconds=600.132 limit=2100 06:31:53Z
bus rc=0 seconds=24.429 limit=900 06:32:18Z
test-auth rc=0 seconds=0.781 limit=126 06:32:19Z
test-conductor rc=0 seconds=6.825 limit=145 06:32:25Z
test-config rc=0 seconds=2.482 limit=130 06:32:28Z
test-discord rc=0 seconds=7.314 limit=147 06:32:35Z
test-extension-package rc=0 seconds=1.229 limit=130 06:32:37Z
test-foundation rc=0 seconds=39.067 limit=269 06:33:16Z
test-land-work rc=0 seconds=11.848 limit=171 06:33:27Z
test-queue rc=0 seconds=20.432 limit=226 06:33:48Z
test-release rc=0 seconds=2.663 limit=157 06:33:51Z
test-task rc=0 seconds=13.923 limit=231 06:34:04Z
SUITES GREEN 06:34:04Z
M1 rc=1 seconds=1.9 killed=True red=['R6 G4 every owner route answers 403 owner-only before any bus call']
M2 rc=1 seconds=2.1 killed=True red=['R6 G3 a revoke counts on the next request: 401 and the session is gone']
M3 rc=1 seconds=1.8 killed=True red=['R6 G6 the user page holds the notice word for word', 'R6 G7 the hub label and plain peer links render from config on both pages']
M4 rc=1 seconds=0.5 killed=True red=["R6 a configured peer id validates as a user's home and an unconfigured one refuses", 'R6 B1 a boot with access.json binds a granted user, whose home is a configured peer']
M5 rc=1 seconds=2.1 killed=True red=['R6 G2 a subject with no live grant in this business is refused subject-unknown', 'R6 G3 a revoke counts on the next request: 401 and the session is gone']
M6 rc=1 seconds=2.0 killed=True red=['R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request']
M7 rc=1 seconds=0.3 killed=True red=['R6 B1 a boot with access.json binds a granted user, whose home is a configured peer', "R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once"]
M8 rc=1 seconds=2.1 killed=True red=['R6 G1 a granted user signs in, gets the user page and reaches the broker as itself', 'R6 G9 a user chat sends only the plugins read over the owner transport']
M9 rc=1 seconds=0.4 killed=True red=["R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once"]
M10 rc=1 seconds=2.0 killed=True red=['R6 G9 a user chat sends only the plugins read over the owner transport']
M11 rc=1 seconds=2.0 killed=True red=['R6 G9 a user chat sends only the plugins read over the owner transport']
MUTANTS rc=0 06:34:22Z
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Row 294 gate run 3, sage-3199: npm ci (root and packages/webui), suites whole, every
# scripts/test-*.sh, then the mutants. Stops at the first red. FROM=<step> resumes at a step
# (the mos-1411 single cli rerun uses FROM=cli TAG=-rerun).
set -u -o pipefail
WT=/mnt/storage/scratch/launchpad-r294/wt
OUT=/mnt/storage/scratch/launchpad-r294/run4
FROM=${FROM:-npm-root}; TAG=${TAG:-}
export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/gXXXXXX)
echo "start $(date -u +%H:%M:%SZ) FROM=$FROM TAG=$TAG TMPDIR $TMPDIR head $(git -C $WT rev-parse HEAD)" >> $OUT/summary.txt
[ -z "$(git -C $WT status --porcelain)" ] || { echo "STOP worktree not clean" | tee -a $OUT/summary.txt; exit 9; }
on=0
step() { # name limit dir cmd...
local name=$1 limit=$2 dir=$3; shift 3
[ $on = 1 ] || [ $name = $FROM ] || return 0; on=1
local t0=$(date +%s%N)
( cd $dir && timeout -k 10 $limit "$@" ) > $OUT/$name$TAG.log 2>&1
local rc=$?
local ms=$(( ($(date +%s%N) - t0) / 1000000 )); local dt=$((ms/1000)).$(printf %03d $((ms%1000)))
echo "$name$TAG rc=$rc seconds=$dt limit=$limit $(date -u +%H:%M:%SZ)" | tee -a $OUT/summary.txt
[ $rc -eq 0 ] || { echo "STOP at $name$TAG" | tee -a $OUT/summary.txt; exit $rc; }
}
step npm-root 900 $WT npm ci --ignore-scripts
step npm-webui 900 $WT/packages/webui npm ci --ignore-scripts
step webui 600 $WT/packages/webui node --test tests/
step cli 2100 $WT/packages/cli node --test tests/*.test.mjs
step bus 900 $WT/packages/bus node --test tests/*.test.mjs
declare -A L=([auth]=126 [conductor]=145 [config]=130 [discord]=147 [extension-package]=130 [foundation]=269 [land-work]=171 [queue]=226 [release]=157 [task]=231)
for s in auth conductor config discord extension-package foundation land-work queue release task; do
step test-$s ${L[$s]} $WT bash scripts/test-$s.sh
done
[ -z "$(git -C $WT status --porcelain)" ] || { echo "STOP worktree dirty after the scripts" | tee -a $OUT/summary.txt; git -C $WT status --porcelain >> $OUT/summary.txt; exit 9; }
echo "SUITES GREEN $(date -u +%H:%M:%SZ)" | tee -a $OUT/summary.txt
python3 -B $OUT/mutants.py
rc=$?; echo "MUTANTS rc=$rc $(date -u +%H:%M:%SZ)" | tee -a $OUT/summary.txt; exit $rc
@@ -0,0 +1,153 @@
[
{
"mutant": "M1",
"rc": 1,
"seconds": 1.9,
"killed": true,
"red": [
"R6 G4 every owner route answers 403 owner-only before any bus call"
],
"killers": [
"R6 G4"
],
"greens": [
"R6 G5"
]
},
{
"mutant": "M2",
"rc": 1,
"seconds": 2.1,
"killed": true,
"red": [
"R6 G3 a revoke counts on the next request: 401 and the session is gone"
],
"killers": [
"R6 G3"
],
"greens": []
},
{
"mutant": "M3",
"rc": 1,
"seconds": 1.8,
"killed": true,
"red": [
"R6 G6 the user page holds the notice word for word",
"R6 G7 the hub label and plain peer links render from config on both pages"
],
"killers": [
"R6 G6"
],
"greens": []
},
{
"mutant": "M4",
"rc": 1,
"seconds": 0.5,
"killed": true,
"red": [
"R6 a configured peer id validates as a user's home and an unconfigured one refuses",
"R6 B1 a boot with access.json binds a granted user, whose home is a configured peer"
],
"killers": [
"R6 a configured peer id",
"R6 B1"
],
"greens": []
},
{
"mutant": "M5",
"rc": 1,
"seconds": 2.1,
"killed": true,
"red": [
"R6 G2 a subject with no live grant in this business is refused subject-unknown",
"R6 G3 a revoke counts on the next request: 401 and the session is gone"
],
"killers": [
"R6 G2"
],
"greens": []
},
{
"mutant": "M6",
"rc": 1,
"seconds": 2.0,
"killed": true,
"red": [
"R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request"
],
"killers": [
"R6 G8"
],
"greens": []
},
{
"mutant": "M7",
"rc": 1,
"seconds": 0.3,
"killed": true,
"red": [
"R6 B1 a boot with access.json binds a granted user, whose home is a configured peer",
"R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once"
],
"killers": [
"R6 B1"
],
"greens": []
},
{
"mutant": "M8",
"rc": 1,
"seconds": 2.1,
"killed": true,
"red": [
"R6 G1 a granted user signs in, gets the user page and reaches the broker as itself",
"R6 G9 a user chat sends only the plugins read over the owner transport"
],
"killers": [
"R6 G9"
],
"greens": []
},
{
"mutant": "M9",
"rc": 1,
"seconds": 0.4,
"killed": true,
"red": [
"R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once"
],
"killers": [
"R6 B3"
],
"greens": []
},
{
"mutant": "M10",
"rc": 1,
"seconds": 2.0,
"killed": true,
"red": [
"R6 G9 a user chat sends only the plugins read over the owner transport"
],
"killers": [
"R6 G9"
],
"greens": []
},
{
"mutant": "M11",
"rc": 1,
"seconds": 2.0,
"killed": true,
"red": [
"R6 G9 a user chat sends only the plugins read over the owner transport"
],
"killers": [
"R6 G9"
],
"greens": []
}
]
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
# Row 294 mutants M1-M11 (BUILD.md "Mutants"). Each mutant edits the worktree (every old text must
# match exactly once), runs its killing file alone under timeout -k 10 300 with the TAP reporter,
# then restores with git checkout and checks the tree is clean. Killed = the named killing test(s)
# report "not ok". M1 also needs G5 "ok" in the same run (Duckworth C1).
import json, os, re, subprocess, sys, time
WT = '/mnt/storage/scratch/launchpad-r294/wt'
OUT = '/mnt/storage/scratch/launchpad-r294/run4'
GU = ('packages/webui', 'tests/granted-user.test.mjs')
AB = ('packages/cli', 'tests/access-boot.test.mjs')
CHAT_CALL = "verb === 'plugins' ? owner('plugins', {}) : call(verb, args)"
MUTANTS = [
('M1', [('packages/webui/src/serve.mjs', "if (!userRoute(path, req.method)) return json(res, 403, { error: 'owner-only'",
"if (false) return json(res, 403, { error: 'owner-only'")], GU, ['R6 G4'], ['R6 G5']),
('M2', [('packages/webui/src/sso.mjs', "if (s.user !== undefined && !users?.check(s)) {", "if (s.user !== undefined && false) {")], GU, ['R6 G3'], []),
('M3', [('packages/webui/src/public/user.html', "but not your messages.", "but not your replies.")], GU, ['R6 G6'], []),
('M4', [('packages/cli/src/access.mjs', "const peers = (system.federation?.peers ?? []).map((p) => p.id);", "const peers = [];")], AB,
['R6 a configured peer id', 'R6 B1'], []),
('M5', [('packages/webui/src/access.mjs', "return user && live(doc, user.id, b) ? user.id : null;", "return user ? user.id : null;")], GU, ['R6 G2'], []),
('M6', [('packages/webui/src/user-bus.mjs', "env: { ...rest, MOSAIC_BUS_USER_NONCE", "env: { ...env, MOSAIC_BUS_USER_NONCE")], GU, ['R6 G8'], []),
('M7', [('packages/cli/src/config.mjs', " if (access) config.access = access;\n", "")], AB, ['R6 B1'], []),
('M8', [('packages/webui/src/chat.mjs', CHAT_CALL,
"verb === 'plugins' ? owner('plugins', {}) : verb === 'thread.list' ? owner(verb, args) : call(verb, args)")], GU, ['R6 G9'], []),
('M9', [('packages/cli/src/config.mjs', " warn(`access file refused at boot (${code}), no granted user binds until it is fixed: ${file}`);\n", "")],
AB, ['R6 B3'], []),
('M10', [('packages/webui/src/console-bus.mjs', "return session => consoleBus(transport(session.user), { send, owner });",
"return session => consoleBus(transport(session.user), { send, owner: (v, a) => owner(v, { ...a, sub: session.sub }) });")], GU, ['R6 G9'], []),
('M11', [('packages/webui/src/chat.mjs', "const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => " + CHAT_CALL + " });",
"let last = {};\n const dispatch = createChannelCaller({ name: 'webui', call: owner === null ? call : (verb, args) => verb === 'plugins' ? owner('plugins', last) : call(verb, args) });"),
('packages/webui/src/chat.mjs', " try { return await dispatch(verb, args); }", " try { last = args; return await dispatch(verb, args); }")], GU, ['R6 G9'], []),
]
def git(*a):
return subprocess.run(['git', '-C', WT, *a], capture_output=True, text=True, check=True).stdout
def restore(files):
if files: git('checkout', '--', *sorted(files))
if git('status', '--porcelain'): raise SystemExit('worktree not clean after restore')
def results(tap):
# Top-level TAP test lines: "ok N - name" / "not ok N - name" at no indentation.
return {m.group(2): m.group(1) == 'ok' for m in re.finditer(r'^(ok|not ok) \d+ - (.*)$', tap, re.M)}
def main():
only = sys.argv[1:]
if git('status', '--porcelain'): raise SystemExit('worktree not clean at start')
env = dict(os.environ)
summary, bad = [], 0
for mid, edits, (pkg, test), killers, greens in MUTANTS:
if only and mid not in only: continue
files = set()
try:
for path, old, new in edits:
p = os.path.join(WT, path); s = open(p).read()
if s.count(old) != 1: raise SystemExit(f'{mid}: anchor count {s.count(old)} in {path}')
open(p, 'w').write(s.replace(old, new)); files.add(path)
diff = git('diff')
t0 = time.time()
r = subprocess.run(['timeout', '-k', '10', '300', 'node', '--test', '--test-reporter=tap', test],
cwd=os.path.join(WT, pkg), capture_output=True, text=True, env=env)
dt = round(time.time() - t0, 1)
finally:
restore(files)
open(f'{OUT}/{mid}.diff', 'w').write(diff)
open(f'{OUT}/{mid}.tap', 'w').write(r.stdout + '\n# stderr\n' + r.stderr)
res = results(r.stdout)
hit = lambda pre: [n for n, ok in res.items() if n.startswith(pre)]
red = [n for n, ok in res.items() if not ok]
killed = r.returncode not in (0, 124, 137) and all(any(not res[n] for n in hit(k)) for k in killers) \
and all(hit(g) and all(res[n] for n in hit(g)) for g in greens)
bad += not killed
line = f'{mid} rc={r.returncode} seconds={dt} killed={killed} red={red}'
print(line, flush=True)
summary.append({'mutant': mid, 'rc': r.returncode, 'seconds': dt, 'killed': killed, 'red': red,
'killers': killers, 'greens': greens})
json.dump(summary, open(f'{OUT}/mutants.json', 'w'), indent=1)
sys.exit(1 if bad else 0)
main()
@@ -0,0 +1,16 @@
npm warn deprecated [email protected]: Use your platform's native DOMException instead
added 128 packages, and audited 129 packages in 2s
10 packages are looking for funding
run `npm fund` for details
3 vulnerabilities (1 moderate, 2 high)
To address issues that do not require attention, run:
npm audit fix
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
@@ -0,0 +1,4 @@
added 2 packages, and audited 3 packages in 401ms
found 0 vulnerabilities
@@ -0,0 +1,27 @@
== webui
ℹ tests 365
ℹ suites 0
ℹ pass 365
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 57832.913871
== cli
ℹ tests 909
ℹ suites 0
ℹ pass 909
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 600102.466154
== bus
ℹ tests 873
ℹ suites 0
ℹ pass 873
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 24399.905747
@@ -0,0 +1,18 @@
start 06:20:53Z FROM=npm-root TAG= TMPDIR /mnt/storage/scratch/tmp/gd3JQ8r head 8616b9a4f61d2546ac640f8f63e0a2ae558a49d5
npm-root rc=0 seconds=1.926 limit=900 06:20:55Z
npm-webui rc=0 seconds=0.452 limit=900 06:20:55Z
webui rc=0 seconds=57.861 limit=600 06:21:53Z
cli rc=0 seconds=600.132 limit=2100 06:31:53Z
bus rc=0 seconds=24.429 limit=900 06:32:18Z
test-auth rc=0 seconds=0.781 limit=126 06:32:19Z
test-conductor rc=0 seconds=6.825 limit=145 06:32:25Z
test-config rc=0 seconds=2.482 limit=130 06:32:28Z
test-discord rc=0 seconds=7.314 limit=147 06:32:35Z
test-extension-package rc=0 seconds=1.229 limit=130 06:32:37Z
test-foundation rc=0 seconds=39.067 limit=269 06:33:16Z
test-land-work rc=0 seconds=11.848 limit=171 06:33:27Z
test-queue rc=0 seconds=20.432 limit=226 06:33:48Z
test-release rc=0 seconds=2.663 limit=157 06:33:51Z
test-task rc=0 seconds=13.923 limit=231 06:34:04Z
SUITES GREEN 06:34:04Z
MUTANTS rc=0 06:34:22Z
@@ -0,0 +1,17 @@
OK status with missing harness credential exits 3 and still lists accounts
OK status reports harness credential (read-only) + mosaic accounts
OK api key material never reaches output
OK oauth token material never reaches output
OK unparseable credential file exits 2
OK symlinked credential file exits 4
OK env-side credential names reported
OK env var values never reach output
OK accounts without an accounts dir reports none and creates nothing
OK accounts lists files and marks the active one
OK loose account perms flagged in listing
OK agent --auth with missing account file refuses (exit 4)
OK agent --auth with non-0600 account file refuses
OK agent --auth with invalid account name refuses
OK auth.sh without valid config refuses
selftest: 15 passed, 0 failed
@@ -0,0 +1,23 @@
On branch launchpad/row294
Your branch is up to date with 'origin/launchpad/row294'.
nothing to commit, working tree clean
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
OK dry-run committed nothing
OK apply: allowed change exits 0 (exit 0)
OK apply: attribution in commit subject
OK apply: target tree clean after commit
OK disallowed path refused (exit 1)
OK disallowed path: target untouched
OK syntax gate refused broken .mjs (exit 1)
OK syntax gate: target untouched
OK suite failure refused (exit 1)
OK suite failure: target reverted to clean
OK disabled policy refused (exit 2)
OK disabled policy: target untouched
OK failed run refused (exit 1)
OK failed run: target untouched
OK missing run exits 4 (exit 4)
OK invalid policy exits 2 (exit 2)
selftest: 17 passed, 0 failed
@@ -0,0 +1,57 @@
OK absent adapter defaults to pi
OK adapter mock validates (exit 0)
OK unsupported adapter exits 2 (exit 2)
OK env exports adapter
OK bootstrap creates default when absent (exit 0)
OK bootstrap wrote config file
OK bootstrap is idempotent on existing config (exit 0)
OK bootstrap did not rewrite existing config
OK validate missing config exits 3 (exit 3)
OK malformed JSON exits 2 (exit 2)
OK unsupported configVersion exits 2 (exit 2)
OK unknown top-level key exits 2 (exit 2)
OK unknown execution key exits 2 (exit 2)
OK unsupported backend exits 2 (exit 2)
OK unsupported environment exits 2 (exit 2)
OK relative dataRoot exits 2 (exit 2)
OK non-canonical dataRoot exits 2 (exit 2)
OK filesystem root dataRoot exits 2 (exit 2)
OK home directory dataRoot exits 2 (exit 2)
OK dataRoot containing config dir exits 2 (exit 2)
OK control character in provider exits 2 (exit 2)
OK symlinked config file exits 2 (exit 2)
OK env exports resolve correctly
OK valid console.sso loads and is in the output
OK console.sso HS256 alg exits 2 naming the path (exit 2)
OK console.sso alg none exits 2 naming the path (exit 2)
OK console.sso missing slot field exits 2 naming the path (exit 2)
OK console.sso slot name outside the pattern exits 2 naming the path (exit 2)
OK unknown key under console exits 2 naming the path (exit 2)
OK valid federation key loads and is in the output
OK federation unknown sub-key exits 2 naming the key (exit 2)
OK federation peer unknown sub-key exits 2 naming the key (exit 2)
OK federation peer origin over http exits 2 naming the key (exit 2)
OK federation peer origin with a path exits 2 naming the key (exit 2)
OK federation peer origin with userinfo exits 2 naming the key (exit 2)
OK federation peer id equal to self exits 2 naming the key (exit 2)
OK federation peer label empty exits 2 naming the key (exit 2)
OK federation not an object exits 2 naming the key (exit 2)
OK federation self pattern exits 2 naming the key (exit 2)
OK federation peers not an array exits 2 naming the key (exit 2)
OK federation peer not an object exits 2 naming the key (exit 2)
OK federation peer id pattern exits 2 naming the key (exit 2)
OK federation peer label over 80 characters exits 2 naming the key (exit 2)
OK federation duplicate peer id exits 2 naming the key (exit 2)
OK federation peer origin with a trailing slash exits 2 naming the key (exit 2)
OK federation self missing exits 2 naming the key (exit 2)
OK federation peer id missing exits 2 naming the key (exit 2)
OK federation peer label missing exits 2 naming the key (exit 2)
OK config without federation loads and has no federation output
OK config without console loads with unchanged output
OK compactionReadBytes 8388608 loads and is in the output as written
OK compactionReadBytes "x" loads and is in the output as written
OK compactionStreams 2048 loads and is in the output as written
OK compactionStreams "x" loads and is in the output as written
OK failed validation modified nothing
selftest: 55 passed, 0 failed
@@ -0,0 +1,85 @@
toolchain: node v26.8.1
OK syntax: packages/discord/src/approvals.mjs
OK syntax: packages/discord/src/authorize.mjs
OK syntax: packages/discord/src/binding.mjs
OK syntax: packages/discord/src/cli.mjs
OK syntax: packages/discord/src/connector.mjs
OK syntax: packages/discord/src/context.mjs
OK syntax: packages/discord/src/engine-pi.mjs
OK syntax: packages/discord/src/errors.mjs
OK syntax: packages/discord/src/gateway.mjs
OK syntax: packages/discord/src/git.mjs
OK syntax: packages/discord/src/journal.mjs
OK syntax: packages/discord/src/notify.mjs
OK syntax: packages/discord/src/processing.mjs
OK syntax: packages/discord/src/rest.mjs
OK syntax: packages/discord/src/setspark.mjs
OK syntax: packages/discord/src/tools.mjs
OK syntax: packages/discord/src/web.mjs
OK syntax: packages/discord/bin/git-credential.mjs
OK syntax: packages/discord/extension/tools.mjs
OK syntax: packages/discord/tests/approvals.test.mjs
OK syntax: packages/discord/tests/authorize.test.mjs
OK syntax: packages/discord/tests/binding.test.mjs
OK syntax: packages/discord/tests/connector.test.mjs
OK syntax: packages/discord/tests/context.test.mjs
OK syntax: packages/discord/tests/copy-service.test.mjs
OK syntax: packages/discord/tests/engine.test.mjs
OK syntax: packages/discord/tests/fake-pi.mjs
OK syntax: packages/discord/tests/gateway.test.mjs
OK syntax: packages/discord/tests/git.test.mjs
OK syntax: packages/discord/tests/helpers.mjs
OK syntax: packages/discord/tests/journal.test.mjs
OK syntax: packages/discord/tests/notify.test.mjs
OK syntax: packages/discord/tests/offline-check.test.mjs
OK syntax: packages/discord/tests/processing-stop.test.mjs
OK syntax: packages/discord/tests/processing.test.mjs
OK syntax: packages/discord/tests/recover.test.mjs
OK syntax: packages/discord/tests/rest.test.mjs
OK syntax: packages/discord/tests/setspark.test.mjs
OK syntax: packages/discord/tests/tools.test.mjs
OK syntax: packages/discord/tests/web.test.mjs
OK syntax: packages/discord/fixtures/claim-worker.mjs
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
OK syntax: scripts/discord.sh
OK syntax: scripts/discord-service.sh
OK packages/discord declares no dependencies
OK no bot-token-shaped string in packages/discord
OK fixture binding uses placeholder ids only
OK fixture binding validates
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
OK real pi with protocol vault adds reserve_id to the git verbs
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
OK real pi refuses a git key on a read-only root (fail closed)
OK real pi with the pilot flags (--no-tools) exposes no tool at all
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test packages/discord/tests/ (ℹ pass 192)
OK scripts/discord.sh --help exits 0
OK scripts/discord.sh check without a binding exits 4
OK scripts/discord.sh recover without a binding exits 4
OK scripts/discord.sh reload without a binding exits 4
OK scripts/discord-service.sh without a command exits 4
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
OK service install writes the rendered unit (0644) and leaves no temp file
OK service install a second time reports unchanged
OK systemd-analyze verify accepts the rendered unit
OK service uninstall removes the unit file
OK service install with an unknown flag exits 4
OK service install with USER unset finishes and names the account for lingering
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
OK pinned install writes the rendered unit in a temporary unit directory
OK pinned install refuses short prerequisite and writes no unit
OK pinned install refuses missing prerequisite and writes no unit
OK pinned install refuses cli prerequisite and writes no unit
OK pinned install refuses pi prerequisite and writes no unit
OK status names the copy from fake systemctl effective cwd
OK checkout install explicitly recommends --copy
OK wrapper check --offline passes local fixture and makes no network call
OK wrapper run --offline refuses usage
discord suite: 81 passed, 0 failed
@@ -0,0 +1,21 @@
OK initial ordinary-file install
OK installed tree matches canonical source
OK installed tree has no symlinks
OK check detects installation drift
OK sync refuses to overwrite installation drift
OK check detects an extra destination file
OK check detects an extra destination directory
OK check rejects a destination symlink
OK sync accepts a canonical source update
OK updated installation matches canonical source
scripts/test-extension-package.sh: line 14: 3315913 Killed "$@" > /dev/null 2>&1
OK forced interruption kills the replacing process
OK next invocation recovers old consistent installation
OK interrupted replacement rolled back
OK sync succeeds after interruption recovery
OK unlocked stale lock file does not block
OK active lock refuses a concurrent sync
OK source symlink fails closed
OK nested second entrypoint fails closed
extension package selftest: 18 passed, 0 failed
@@ -0,0 +1,53 @@
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
OK syntax: scripts/foundation-inspect.mjs
OK syntax: scripts/foundation/strict-json.mjs
OK syntax: scripts/foundation/canonical.mjs
OK syntax: scripts/foundation/resolve.mjs
OK syntax: scripts/foundation/validate-record.mjs
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
OK syntax: scripts/foundation/canonical.test.mjs
OK syntax: scripts/foundation/cli.test.mjs
OK syntax: scripts/foundation/fixtures.test.mjs
OK syntax: scripts/foundation/resolve.test.mjs
OK syntax: scripts/foundation/strict-json.test.mjs
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
OK fixture generator runs
OK checked-in fixtures/bundles equal a fresh generation
OK checked-in fixtures/raw equal a fresh generation
OK checked-in fixtures/index.json equal a fresh generation
OK checked-in demo bundles equal a fresh generation
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test scripts/foundation/ (ℹ pass 80)
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
OK oracle: zero schema-column disagreements with the pinned checker
OK oracle: strict-only profile refusals are counted and asserted
OK demo: permitted read preview exits 0 (exit 0)
OK demo: permitted file.change preview exits 0 (exit 0)
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
OK demo: revoked registration is refused (exit 3) (exit 3)
OK demo: message is not authority (exit 3) (exit 3)
OK usage: no arguments exits 2 (exit 2)
OK io: missing file exits 4 (exit 4)
OK io: directory exits 4 (exit 4)
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
OK bound: oversize fixture exits 2 (exit 2)
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
OK text output starts with the disclaimer
OK json output is valid JSON with result allowed and exactly the charter §7 fields
OK json golden matches byte-for-byte
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
OK canary never printed (bundle run and credential-file run)
OK a non-bundle JSON file is refused at the shape gate, not read into output
OK no field of the non-bundle file is echoed
selftest: 44 passed, 0 failed
@@ -0,0 +1,88 @@
OK args: exit 2, target-branch
OK args: exit 2, target-branch
OK args: exit 4
OK args: exit 4
OK args: exit 4
OK --base next refuses before the config is read (got 2)
OK a missing system config exits 3 (got 3)
OK nobus: exit 3
OK schema-table: exit 2, bus-schema
OK schema-meta: exit 2, bus-schema
OK openBus opens the database read-only
OK other-business: exit 2, decision-not-found
OK no-raise: exit 2, decision-not-found
OK not-a-merge: exit 2, not-a-merge
OK open: exit 2, not-resolved
OK withdrawn: exit 2, not-resolved
OK by-agent: exit 2, not-human
OK via-webui: exit 2, not-human
OK no-answer: exit 2, not-human
OK answer-role: exit 2, not-human
OK answer-run: exit 2, not-human
OK answer-choice: exit 2, not-human
OK two-answers: exit 2, not-human
OK arbiter-cross: exit 2, not-human
OK arbiter-cited: exit 2, not-human
OK chose-no: exit 2, not-merge-option
OK two-refs: exit 2, work-ref
OK bad-ref: exit 2, work-ref
OK task-ref-disagrees: exit 2, work-ref
OK raise-task-disagrees: exit 2, work-ref
OK target-main: exit 2, target-branch
OK superseded-question: exit 2, superseded
OK superseded-task: exit 2, superseded
OK superseded-by: exit 2, superseded
OK no-verdict: exit 2, no-verdict
OK changes-later: exit 2, not-approved
OK short-commit: exit 2, not-approved
OK wrong-task-body: exit 2, not-approved
OK args: exit 2, decision-not-found
OK earlier-merge: exit 0
OK ref-moved: exit 2, ref-moved
OK root-top: exit 2, root-file
OK root-newdir: exit 2, root-file
OK root-src: exit 2, root-file
OK unrelated: exit 2, base-conflict
OK empty: exit 2, base-conflict
OK an empty reviewed diff says so
OK dirty-changed: exit 2, checkout-dirty
OK dirty-staged: exit 2, checkout-dirty
OK dirty-untracked: exit 2, checkout-dirty
OK conflict: exit 2, base-conflict
OK a refusal after the worktree is added leaves no worktree or scratch
OK cleanup looks for the worktree's Docker network
OK dirty-during: exit 2, checkout-dirty
OK a reviewed path dirtied during the gate moves no ref
OK drift: exit 2, patch-drift
OK patch-drift leaves no worktree or scratch
OK gate-pkg: exit 2, gate-failed
OK gate-failed names the failing step's log, and it exists
OK gate-failed leaves no worktree or scratch
OK gate-script: exit 2, gate-failed
OK a failing scripts/test-*.sh fails the gate
OK a failed gate moves no ref
OK nomods: exit 4
OK a target without node_modules leaves no worktree or scratch
OK moved: exit 2, base-moved
OK base-moved keeps the base where it moved to
OK dry: exit 0
OK --dry-run writes no ref
OK --dry-run prints the commit with --subject and --attribution
OK --dry-run runs the gate
OK --dry-run leaves no worktree or scratch
OK land: exit 0
OK the landing is one commit on the old base
OK its diff is the reviewed diff by patch-id
OK its tree is the dry run's
OK the message has the option's subject, the citations and the attribution last
OK it prints the line for the PM
OK the checked-out base has the reviewed paths and keeps the unrelated edit
OK only refs/heads/refactor exists in the target
OK the work ref is unchanged
OK the bus database is unchanged
OK the gate ran each package suite but queue, and each scripts/test-*.sh
OK the landing leaves no worktree or scratch
OK the target's node_modules is still there, with its contents
OK the worktree's Docker network is removed
---
land-work selftests: 86 passed, 0 failed
@@ -0,0 +1,36 @@
toolchain: node v26.8.1, git version 2.55.0
OK syntax: packages/queue/src/cli.mjs
OK syntax: packages/queue/src/errors.mjs
OK syntax: packages/queue/src/io.mjs
OK syntax: packages/queue/src/lock.mjs
OK syntax: packages/queue/src/queue.mjs
OK syntax: packages/queue/src/review.mjs
OK syntax: packages/queue/src/store.mjs
OK syntax: packages/queue/tests/commit.test.mjs
OK syntax: packages/queue/tests/data.test.mjs
OK syntax: packages/queue/tests/dispatch.test.mjs
OK syntax: packages/queue/tests/gate-owner.test.mjs
OK syntax: packages/queue/tests/helpers.mjs
OK syntax: packages/queue/tests/lock.test.mjs
OK syntax: packages/queue/tests/migration.test.mjs
OK syntax: packages/queue/tests/review.test.mjs
OK syntax: packages/queue/tests/store.test.mjs
OK syntax: packages/queue/tests/write.test.mjs
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
OK syntax: scripts/queue-commit.sh
OK syntax: scripts/git-hooks/pre-commit
OK syntax: scripts/mosaic
OK queue-commit.sh, the guard and scripts/mosaic are executable
OK packages/queue declares no dependencies
ℹ tests 156
ℹ pass 156
ℹ fail 0
OK node --test packages/queue/tests/
OK scripts/mosaic queue help
skip queue verify and render --check: this checkout (/mnt/storage/scratch/launchpad-r294/wt) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
queue suite: 28 passed, 0 failed
@@ -0,0 +1,16 @@
OK valid RELEASE resolves (exit 0)
OK invalid RELEASE exits 1 (exit 1)
OK missing RELEASE exits 1 (exit 1)
OK valid RELEASE leaves image tag consistent with version
OK status safe on empty state (exit 0)
OK status created no pointer
OK fault-injected activation refuses (exit 1)
OK refused activation wrote no pointer
OK refusal logged exactly once with valid fields
OK healthy activation succeeds (exit 0)
OK pointer written with valid fields
OK repeat activation succeeds (log grows) (exit 0)
OK log is append-only across activations
OK rollback without previous refuses (exit 1)
selftest: 14 passed, 0 failed
@@ -0,0 +1,96 @@
OK valid task validates (exit 0)
OK unknown task key exits 2 (exit 2)
OK unsupported taskVersion exits 2 (exit 2)
OK invalid task id exits 2 (exit 2)
OK empty prompt exits 2 (exit 2)
OK NUL in expectExact exits 2 (exit 2)
OK out-of-range timeout exits 2 (exit 2)
OK missing mission file exits 4 (exit 4)
OK task with valid mission validates (exit 0)
OK invalid mission exits 2 (exit 2)
OK validate missing task exits 4 (exit 4)
OK validation does not modify the task file
OK prune dry-run exits 0 (exit 0)
OK dry-run deleted nothing
OK prune --keep=2 --yes removes oldest (exit 0)
OK kept exactly 2 newest runs
OK newest run kept, oldest pruned
OK append-only receipt written (3 entries)
OK sessions/workspaces untouched by prune
OK prune with invalid keep exits 4 (exit 4)
OK mock adapter: gate passes on matching mock response (exit 0)
OK mock adapter: expect-mismatch recorded (exit 1)
OK mismatch reason recorded
OK unknown adapter fails closed (exit 1)
OK mission task runs via mock adapter (exit 0)
OK mission section injected into generated prompt
OK retry of mission run succeeds (exit 0)
OK retriedFrom lineage recorded
OK mission section present after retry (relative path resolved)
OK retry of missing run exits 4 (exit 4)
OK skill install bundled ms-tools (exit 0)
OK installed to skills-available
OK double install refuses (exit 1)
OK activate enables skill (exit 0)
OK enabled dir populated
OK uninstall while enabled refuses (exit 1)
OK deactivate moves back to available (exit 0)
OK uninstall removes from available (exit 0)
OK seat with enabled skill launches
OK skill path delivered to adapter
OK shipped researcher contract resolves (ceiling + network)
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
OK resolve-role refuses name/filename mismatch (exit 2)
OK resolve-role refuses unknown network declaration (exit 2)
OK resolve-role refuses duplicate role tool (exit 2)
OK resolve-role refuses unsupported tool (exit 2)
OK resolve-role refuses missing contract file (exit 4)
OK version 1 role prints no contract line
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
OK resolve-role accepts a minimal version 2 role (exit 0)
OK resolve-role refuses an action outside the vocabulary (exit 2)
OK resolve-role refuses a gated-only action in a role file (exit 2)
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
OK resolve-role refuses a contract that is a symbolic link (exit 2)
OK seat launches under role ceiling
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
OK narrowing recorded loudly
OK missing role contract refuses launch (exit 2)
OK missing-contract refusal names the role
OK empty ceiling intersection -> tool-free seat
OK tool-free outcome recorded loudly
OK overridden agents dir without seat definition refuses (exit 4)
OK seat-resolution refusal names the seat
OK policy: mission only -> mission tools
OK policy: task only -> task tools
OK policy: both -> intersection (task narrowed)
OK policy: empty intersection -> tool-free
OK invalid mission capabilities rejected (exit 2)
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
OK task run with user layer present (exit 0)
OK user context dispatched into generated prompt
OK workspace+tools task runs via mock (exit 0)
OK workspace path + tools delivered to adapter
OK persistent workspace created on host
OK plain task still runs (no workspace/tools) (exit 0)
OK workspace var present but empty when absent
OK tools empty when absent
OK unknown tool exits 2 (exit 2)
OK workspace traversal exits 2 (exit 2)
OK hello task succeeds with exact marker
OK result.json written in run dir
OK result.json contents are correct
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
OK each run gets a distinct run dir (no clobber)
OK list shows both runs
skip live provider cases (row 244: no provider call in the gate)
OK onboard without name exits 4 (non-interactive) (exit 4)
OK onboard --name renders profile (exit 0)
OK profile written
OK canon structure: required filled, optional placeholdered
OK canon sections present
skip live provider cases (row 244: no provider call in the gate)
OK no agent identity on headless run
selftest: 91 passed, 0 failed