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:
1 parent
6f07930679
commit
d3059c2c3d
41 files changed
+2646
No files matched your search
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
@@ -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({
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
Executable
+35
@@ -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
|
||||
Reference in new issue
Block a user