agents: Dewey row 139 round 1 packet (#1633)
Candidate on 5661bd233: GATED and GATED_ONLY become PRD 1.5's eight hard
waits (data.delete, network.expose and account.create are new), derived
as ACTIONS minus an explicit ROUTED list (REQ-WAIT-3). A gated raise
citing the current spec goes to the domain arbiter unless it is a hard
wait. land-work refuses an arbiter-resolved merge with not-human.
Mutants 5/5 killed; gate green (bus, business and every scripts/test-*.sh).
Row 137 has not landed, so its REQ-LAND-1 refusal and the host-op
arbiter acceptance are left out, as BUILD.md says.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
# Row 139 (#1633): decisions that cite the spec go to an agent; only hard waits reach Jason
|
||||
|
||||
Builder: Dewey. Reviewer: Filbert. Gate owner: Sage.
|
||||
Brief: docs/plans/2026-10-15_q167-rows-r3.md at b2a2d741c (blob 0fd6647bf), lines 414-478.
|
||||
Requirements: PRD 1.5 REQ-DEC-2 and REQ-WAIT-1 to REQ-WAIT-3 (docs/prd/mosaic-stack.md:288-352).
|
||||
Calls from sage-0502, 0504, 0512, 0514 and 0516.
|
||||
|
||||
Base: 5661bd233bbe3e1f03a64a76ad7edcd9f6e41e89 (base.txt, origin/refactor after row 135). The
|
||||
candidate is build.patch on that base. candidate-manifest.sha256 lists every changed path with its
|
||||
sha256. repro.sh applies the patch to a fresh detached worktree and checks the digests.
|
||||
|
||||
## What ships
|
||||
|
||||
1. **The hard waits.** The broker's GATED and the business's GATED_ONLY are now exactly these:
|
||||
spend, data.delete, network.expose, account.create, credential.mint, message.external,
|
||||
git.push.protected and deploy. data.delete, network.expose and account.create are new actions
|
||||
in both ACTIONS lists, placed after deploy.
|
||||
2. **The actions that left.** git.merge.protected, policy.change, prd.approve and role.revoke are
|
||||
no longer on either list. A role file may grant them, and a raise routes them under REQ-DEC-2.
|
||||
3. **REQ-WAIT-3 by construction.** Neither list is written out by hand. Each side holds an
|
||||
explicit ROUTED list of 21 actions: `ROUTED` exported from vocabulary.mjs, and a Set in
|
||||
broker.mjs. The hard waits are ACTIONS minus ROUTED, so an action added to ACTIONS is a hard
|
||||
wait until a reviewed commit adds it to ROUTED. The bus imports no business code, so the two
|
||||
lists stay separate. packages/bus/tests/hard-waits.test.mjs pins them equal: HARD_WAITS
|
||||
equals GATED_ONLY, and the broker's ACTIONS equal the vocabulary's.
|
||||
4. **Routing (broker.mjs:1206-1211).**
|
||||
`arbitrated = cls === 'cross-role' || (cls === 'gated' && spec !== null && !GATED.has(action))`.
|
||||
An arbitrated raise goes to `arbiters[domain]`. Otherwise a gated raise goes to the human and a
|
||||
within-role raise to the raiser. If the arbiter would be the raiser, the decision goes to the
|
||||
human, as cross-role routing already did. `spec` is non-null only when the raise cited a spec
|
||||
and readSpec accepted it. readSpec refuses a stale citation (`stale-spec`) and a spec whose
|
||||
last commit isn't the human's (`spec-unconfirmed`) before any decision is stored.
|
||||
5. **REQ-WAIT-2.** The validator's refusal (role.mjs:41, "which is always gated") and the broker's
|
||||
`invalid-authority` already applied to every GATED_ONLY/GATED entry at both classes. They now
|
||||
cover the new list. No code change was needed there; the tests cover each hard wait at each
|
||||
class.
|
||||
6. **roles/\*.json.** No change. No shipped role grants any of the four actions that left, and
|
||||
PRD 1.5 requires no new grant from this row. merge-gate's cross-role git.merge.protected grant
|
||||
is row 149's.
|
||||
|
||||
## Tests
|
||||
|
||||
- packages/bus/tests/hard-waits.test.mjs (new, 7 tests):
|
||||
- the lists match each other and the vocabulary;
|
||||
- the Broker refuses each hard wait at either class;
|
||||
- each hard wait goes to the human uncited, cited, and cited with a domain, and the arbiter's
|
||||
resolve gets `human-required`;
|
||||
- each action that left goes uncited to the human, cited to the technical or delivery arbiter;
|
||||
- an arbiter-approved cited role.revoke authorizes the raiser, and the human's resolve gets
|
||||
`agent-required`;
|
||||
- merge-gate's cited merge goes to the technical arbiter (cross-role, resolved via 'broker');
|
||||
a coder's cited merge goes to the arbiter as `gated`, and uncited to the human;
|
||||
- a stale or unconfirmed citation is refused with no decision stored; an arbiter raising a
|
||||
cited policy.change of its own goes to the human.
|
||||
- packages/business/tests/hard-waits.test.mjs (new, 3 tests): GATED_ONLY is the list; every
|
||||
action is routed or a hard wait, never both; the validator refuses each hard wait at either
|
||||
class and accepts the actions that left.
|
||||
- packages/bus/tests/spec-fixtures.mjs: `fixture()` takes `arbiters` and extra `roles`, and
|
||||
returns `agent`. Existing callers are unchanged.
|
||||
- scripts/test-land-work.sh: two cases, `not-human:arbiter-cross` (merge-gate raised cross-role,
|
||||
cto resolved via broker) and `not-human:arbiter-cited` (coder raised gated with a citation, cto
|
||||
resolved). Both exit 2 `not-human`. scripts/land-work/read.mjs is unchanged (sage-0516).
|
||||
|
||||
## Left out, as the brief and sage-0512/0516 say
|
||||
|
||||
1. **REQ-LAND-1's refusal of a `gated` merge decision, and its coder test** (coder raises a cited
|
||||
merge, the arbiter approves, the land refuses). This ships with whichever of 137 and 139 lands
|
||||
second. On 2026-10-16 at 18:31Z, `git log 5661bd233..origin/refactor` is empty: row 137 hasn't
|
||||
landed, so 139 doesn't carry it. If 137 lands first, 139 needs a round 2 that adds it.
|
||||
2. **The host land op accepting an arbiter-resolved merge decision**, in 137's shared module.
|
||||
This also ships with whichever of 137 and 139 lands second. The module isn't on this base.
|
||||
3. **The chief-of-staff grant to set instance names** (row 141, queued). Whichever of 139 and 141
|
||||
lands second adds it. Until then, names are set only by the human through the CLI.
|
||||
|
||||
## For the reviewer
|
||||
|
||||
- **role.revoke can now be granted within-role.** A within-role raise resolves itself, so a role
|
||||
file granting it would let that role approve its own revoke. REQ-DEC-2 and REQ-WAIT-1 allow
|
||||
this, and REQ-ROLE-2's "resolved decision for a revoke" still holds. No shipped role grants it.
|
||||
A file that adds it is a reviewed commit.
|
||||
- **role.launch where launch.by isn't the caller.** It still classifies `gated`, but it isn't a
|
||||
hard wait, so a cited raise now goes to the arbiter. REQ-DEC-2 reads that way. I didn't add a
|
||||
carve-out.
|
||||
- **"A change to the hard-waits list, or a grant of a hard-wait action, is itself a hard wait"
|
||||
(REQ-WAIT-2).** The list lives in code and changes only by a reviewed commit to vocabulary.mjs
|
||||
and broker.mjs. The validator and the broker refuse any role file that grants a hard wait. No
|
||||
decision can change either, so the broker has no decision action to classify for them.
|
||||
policy.change is routed. A policy.change decision can't edit the list, because the list isn't
|
||||
policy data.
|
||||
- **git.push.protected and force pushes.** REQ-WAIT-1 says git.push.protected covers any force
|
||||
push or history rewrite on any branch. I grepped packages/cli/src/git-socket.mjs and
|
||||
broker.mjs's push path for a non-fast-forward check and found none. I didn't trace the whole
|
||||
push path. It isn't in row 139's brief or files, so it's a follow-up question for Sage, not a
|
||||
change here.
|
||||
|
||||
## Follow-ups (not row 139's files)
|
||||
|
||||
- **role.revoke with no `target`.** The holder lookup throws a non-BusError, which surfaces as
|
||||
`storage-refused` instead of a validation refusal. This predates 139; the bus test passes a
|
||||
target.
|
||||
- **land-work's fixture** (scripts/land-work/fixture.mjs) can't set spec_path/spec_commit, so the
|
||||
`arbiter-cited` case has no citation columns. land-work doesn't read them, so the case still
|
||||
tests what it should.
|
||||
- **Contract text.** roles/pm.md:42-43 and roles/chief-of-staff.md:41-46 still say anything
|
||||
ungranted "is gated" and goes to the human, and the chief text lists policy and PRD changes
|
||||
among them. Under REQ-DEC-2 a cited raise goes to the arbiter. That text is row 149's
|
||||
(proactive contracts).
|
||||
- **The harness raise_decision description** (packages/harness/src/tools.mjs:195) doesn't mention
|
||||
that a spec citation changes the route. Harness text isn't in row 139's files.
|
||||
|
||||
## Gate
|
||||
|
||||
Run under Sage's slot (sage-0518), from the worktree at the candidate, with
|
||||
COMPOSE_PROJECT_NAME=dewey-r139. gate-summary.txt has each run's exit code and times.
|
||||
|
||||
- 2026-10-16T18:28:41Z to 18:31:11Z: all 12 runs rc=0, with no flakes.
|
||||
- scripts/test-land-work.sh: the two new cases print `OK arbiter-cross: exit 2, not-human`
|
||||
and `OK arbiter-cited: exit 2, not-human`.
|
||||
- bus 301/301 and business 112/112, 0 skipped.
|
||||
- test-task selftest 98 passed, 0 failed.
|
||||
- auth, conductor, config, discord, extension-package, foundation, queue and release: rc=0.
|
||||
- The 19 affected test files also passed run singly, 0 skipped (18:25:46Z to 18:26:03Z):
|
||||
- bus 10;
|
||||
- business 7;
|
||||
- harness bundle and tools, since both read ACTIONS.
|
||||
- dewey-r139_default was removed with 0 containers on it.
|
||||
|
||||
## Mutants
|
||||
|
||||
mutants-summary.txt. Each mutant changed one file and reran its owning suite in its own process
|
||||
group. The file was restored and its hash checked before the next mutant. All five were killed:
|
||||
|
||||
| Mutant | Change | Suite | New tests that failed |
|
||||
| --- | --- | --- | --- |
|
||||
| m1 | drop `spec !== null`: an uncited gated raise goes to the arbiter | bus (14 fail) | both REQ-DEC-2 citation tests, and 12 older tests that expect an uncited gated raise at the human |
|
||||
| m2 | drop `!GATED.has(...)`: a cited hard wait goes to the arbiter | bus (2 fail) | REQ-DEC-2 hard waits; spec.test's current-citation test |
|
||||
| m3 | add `account.create` to the broker's ROUTED | bus (3 fail) | REQ-WAIT-1, REQ-WAIT-2, REQ-DEC-2 hard waits |
|
||||
| m4 | add `spend` to the vocabulary's ROUTED | business (2 fail) | REQ-WAIT-1, REQ-WAIT-2 |
|
||||
| m5 | disable role.mjs:41's refusal of a hard-wait grant | business (3 fail) | REQ-WAIT-2, plus two older role tests |
|
||||
@@ -0,0 +1 @@
|
||||
5661bd233bbe3e1f03a64a76ad7edcd9f6e41e89
|
||||
@@ -0,0 +1,470 @@
|
||||
diff --git a/packages/bus/README.md b/packages/bus/README.md
|
||||
index 1db7e067e..19e568d24 100644
|
||||
--- a/packages/bus/README.md
|
||||
+++ b/packages/bus/README.md
|
||||
@@ -138,9 +138,9 @@ an operator inspect content and delivery evidence after an uncertain result.
|
||||
|---|---|
|
||||
| `role.claim` | No args; claims capability's role/run. One current holder per business/instance; a revoked run cannot reclaim that role, including after restart. |
|
||||
| `role.release` | No args; only its current holder releases. |
|
||||
-| `role.revoke` | `{role,decision}`; needs human-approved gated decision for this exact target and holder incarnation. A role whose file lists `role.end` (the chief of staff, row 123, REQ-COS-1) sends `{role,reason}` instead: no decision, a reason of 1 to 500 characters, logged in the `action.allowed` body. Sending both is `invalid-request`; the caller's own run and the assistant's are `role-end-refused`; any other role sending `reason` gets `decision-required`. |
|
||||
+| `role.revoke` | `{role,decision}`; needs an approved decision for this exact target and holder incarnation, routed as REQ-DEC-2 says: the human's for an uncited gated raise, the arbiter's for one citing the current spec. A role whose file lists `role.end` (the chief of staff, row 123, REQ-COS-1) sends `{role,reason}` instead: no decision, a reason of 1 to 500 characters, logged in the `action.allowed` body. Sending both is `invalid-request`; the caller's own run and the assistant's are `role-end-refused`; any other role sending `reason` gets `decision-required`. |
|
||||
| `organization.set` | `{revision,limits?,models?,instances?}`; the keys `limits.set` takes, through the same writer and receipt. Needs `organization.change` in the caller's role file (the chief of staff); the receipt's actor is `<definition>.<instance>`, e.g. `chief-of-staff.chief`. Every other key is `invalid-request`. `limits` and `limits.set` stay human-only. |
|
||||
-| `decision.raise` | `{action,question,options:[{key,text}],recommendation,blocking,domain?,target?,project?,task_ref?,requirement_ref?,supersedes?,choice?,approvalChoice?}`. Returns decision with decoded options and authorization context. A blocking decision holds a task: `blocking` true with no `task_ref` refuses `task-required`. A non-blocking one needs no task, and a gated action such as `prd.approve` or `role.revoke` still routes to the human's inbox. |
|
||||
+| `decision.raise` | `{action,question,options:[{key,text}],recommendation,blocking,domain?,target?,project?,task_ref?,requirement_ref?,supersedes?,choice?,approvalChoice?}`. Returns decision with decoded options and authorization context. A blocking decision holds a task: `blocking` true with no `task_ref` refuses `task-required`. A non-blocking one needs no task, and an uncited gated action such as `prd.approve` or `role.revoke` still routes to the human's inbox. |
|
||||
| `decision.resolve` | `{id,choice,note?}`; option must exist, resolver must be routed role or outside-agent human CLI. Sends the raising role a DECISION message (Decision close messages, below). |
|
||||
| `decision.seen` | `{id,note?}`; acknowledgement by resolver, not a resolution. |
|
||||
| `decision.withdraw` | `{id,note?}`; raiser or human. Never rewrites the decision. A human withdraw sends the raising role a DECISION message; the raising run's own withdraw sends none. |
|
||||
@@ -274,9 +274,15 @@ RESULT, INFO, DECISION and REACTION. `staff.request` also writes STAFF-REQUEST (
|
||||
|
||||
Routing is immutable at raise: within-role closes atomically with `choice`,
|
||||
cross-role routes to the configured delivery arbiter (or technical arbiter with
|
||||
-`domain:'technical'`). If that arbiter is the raiser, resolution routes to human
|
||||
-while the decision keeps its cross-role class. Gated routes to human. Unlisted actions are gated and the
|
||||
-nine gated-only actions cannot appear in authority maps. A blocking decision
|
||||
+`domain:'technical'`). A gated raise that cites the project's current spec commit routes the same way,
|
||||
+to the arbiter for its domain (PRD 1.5 REQ-DEC-2, row 139, #1633). If that arbiter is the raiser,
|
||||
+resolution routes to human while the decision keeps its class. An uncited gated raise routes to human.
|
||||
+Unlisted actions are gated. The hard waits (`HARD_WAITS`, REQ-WAIT-1: `spend`, `data.delete`,
|
||||
+`network.expose`, `account.create`, `credential.mint`, `message.external`, `git.push.protected`,
|
||||
+`deploy`) always route to human, cited or not, and cannot appear in authority maps. An action is a hard
|
||||
+wait unless the broker's `ROUTED` list names it (REQ-WAIT-3), and both lists match the business
|
||||
+vocabulary. `git.merge.protected`, `policy.change`, `prd.approve` and `role.revoke` left the gated-only
|
||||
+list in 1.5, so a role file may grant them. A blocking decision
|
||||
requires a strict task ref. `role.launch` is gated unless the business launch
|
||||
block's `by` names the acting role and refused when launch state is revoked. S6 still owns launch allowlists,
|
||||
capacity and actual spawning.
|
||||
@@ -675,8 +681,9 @@ human version. A later docs commit does not change the spec's file commit.
|
||||
spec_commit together. The broker reads the current committed snapshot and
|
||||
refuses a different commit (stale-spec), unconfirmed file (spec-unconfirmed),
|
||||
or incomplete citation (invalid-spec-citation). Missing files and unsafe
|
||||
-repositories retain the project reader's refusal codes. Uncited raises keep the
|
||||
-existing synchronous validity and routing rules. Decisions store spec_path and
|
||||
+repositories retain the project reader's refusal codes. A cited gated raise that
|
||||
+isn't a hard wait routes to the arbiter for its domain (REQ-DEC-2). Uncited raises
|
||||
+keep the existing synchronous validity and routing rules. Decisions store spec_path and
|
||||
spec_commit; later Git commits never reopen or rewrite settled decisions.
|
||||
|
||||
`record.write {project,path,record,decision?}` uses the shared package writer.
|
||||
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
|
||||
index 031634bb8..16de3f23a 100644
|
||||
--- a/packages/bus/src/broker.mjs
|
||||
+++ b/packages/bus/src/broker.mjs
|
||||
@@ -26,21 +26,41 @@ export const ACTIONS = Object.freeze([
|
||||
'credential.mint',
|
||||
'spend',
|
||||
'deploy',
|
||||
+ 'data.delete',
|
||||
+ 'network.expose',
|
||||
+ 'account.create',
|
||||
'policy.change',
|
||||
'prd.approve',
|
||||
'decision.resolve.technical',
|
||||
]);
|
||||
-const GATED = new Set([
|
||||
- 'credential.mint',
|
||||
+// PRD 1.5 REQ-WAIT-3 (row 139, #1633): every action is a hard wait unless listed here, so a new
|
||||
+// action stays one until a reviewed commit names it routed. Matches the business vocabulary's ROUTED.
|
||||
+const ROUTED = new Set([
|
||||
+ 'task.create',
|
||||
+ 'task.assign',
|
||||
+ 'task.schedule',
|
||||
+ 'task.update.assigned',
|
||||
+ 'task.close',
|
||||
+ 'task.reassign',
|
||||
+ 'task.scope.change',
|
||||
+ 'task.priority.change',
|
||||
+ 'git.push.working',
|
||||
'git.merge.protected',
|
||||
- 'git.push.protected',
|
||||
- 'deploy',
|
||||
- 'spend',
|
||||
- 'message.external',
|
||||
+ 'review.request',
|
||||
+ 'review.verdict',
|
||||
+ 'record.write',
|
||||
+ 'message.send',
|
||||
+ 'role.launch',
|
||||
+ 'role.revoke',
|
||||
+ 'role.end',
|
||||
+ 'organization.change',
|
||||
'policy.change',
|
||||
'prd.approve',
|
||||
- 'role.revoke',
|
||||
+ 'decision.resolve.technical',
|
||||
]);
|
||||
+// REQ-WAIT-1's hard waits: always gated, and a raise for one goes to the human, cited or not.
|
||||
+export const HARD_WAITS = Object.freeze(ACTIONS.filter((action) => !ROUTED.has(action)));
|
||||
+const GATED = new Set(HARD_WAITS);
|
||||
const CLOSED = "('resolved','withdrawn','expired')";
|
||||
// How many closed decisions `decisions` with list 'recent' returns, newest close first.
|
||||
export const RECENT = 50;
|
||||
@@ -1183,8 +1203,12 @@ export class Broker {
|
||||
if (!choices.includes(approvalChoice)) fail('invalid-approval-choice');
|
||||
const domain = a.domain ?? 'delivery';
|
||||
if (!['technical', 'delivery'].includes(domain)) fail('invalid-domain');
|
||||
- const proposedRoute = cls === 'gated' ? 'human' : cls === 'cross-role' ? b.arbiters[domain] : s.role;
|
||||
- const route = cls === 'cross-role' && proposedRoute === s.role ? 'human' : proposedRoute;
|
||||
+ // PRD 1.5 REQ-DEC-2 (row 139, #1633): a gated raise citing the project's current spec commit
|
||||
+ // goes to the arbiter for its domain, like a cross-role one. An uncited gated raise and any
|
||||
+ // hard wait go to the human. readSpec already refused a stale or unconfirmed citation.
|
||||
+ const arbitrated = cls === 'cross-role' || (cls === 'gated' && spec !== null && !GATED.has(a.action));
|
||||
+ const proposedRoute = arbitrated ? b.arbiters[domain] : cls === 'gated' ? 'human' : s.role;
|
||||
+ const route = arbitrated && proposedRoute === s.role ? 'human' : proposedRoute;
|
||||
// Row 133 (#1627): whoever closes the decision messages the raiser, so the route must be
|
||||
// allowed to message the raising role before anything is stored. A within-role decision is
|
||||
// routed to the raiser's own role and closes in this call.
|
||||
diff --git a/packages/bus/tests/hard-waits.test.mjs b/packages/bus/tests/hard-waits.test.mjs
|
||||
new file mode 100644
|
||||
index 000000000..fed94f7b3
|
||||
--- /dev/null
|
||||
+++ b/packages/bus/tests/hard-waits.test.mjs
|
||||
@@ -0,0 +1,103 @@
|
||||
+import test from 'node:test';
|
||||
+import assert from 'node:assert/strict';
|
||||
+import { mkdtempSync, rmSync } from 'node:fs';
|
||||
+import { tmpdir } from 'node:os';
|
||||
+import { join } from 'node:path';
|
||||
+import { Store } from '../src/store.mjs';
|
||||
+import { Broker, ACTIONS, HARD_WAITS } from '../src/broker.mjs';
|
||||
+import { ACTIONS as VOCABULARY, GATED_ONLY } from '../../business/src/vocabulary.mjs';
|
||||
+import { fixture } from './spec-fixtures.mjs';
|
||||
+
|
||||
+// PRD 1.5 REQ-WAIT-1, REQ-WAIT-2 and REQ-DEC-2 (row 139, #1633).
|
||||
+const WAITS = ['spend', 'data.delete', 'network.expose', 'account.create', 'credential.mint', 'message.external',
|
||||
+ 'git.push.protected', 'deploy'];
|
||||
+const LEFT = ['git.merge.protected', 'policy.change', 'prd.approve', 'role.revoke'];
|
||||
+const cite = f => ({ project: 'stack', spec_path: 'spec/north-star.md', spec_commit: f.commit });
|
||||
+const routed = (f, d) => ({ ...f.store.get('SELECT class, route_to FROM decisions WHERE id=?', d.id) });
|
||||
+
|
||||
+test('REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary', () => {
|
||||
+ assert.deepEqual([...HARD_WAITS].sort(), [...WAITS].sort());
|
||||
+ assert.deepEqual([...HARD_WAITS], [...GATED_ONLY]);
|
||||
+ assert.deepEqual([...ACTIONS], [...VOCABULARY]);
|
||||
+ for (const action of LEFT) assert.ok(ACTIONS.includes(action) && !HARD_WAITS.includes(action), action);
|
||||
+});
|
||||
+
|
||||
+test('REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable', t => {
|
||||
+ const root = mkdtempSync(join(tmpdir(), 'hard-waits-'));
|
||||
+ const store = new Store(root);
|
||||
+ t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
||||
+ const business = authority => ({ demo: { id: 'demo', human: 'jason', arbiters: { technical: 'coder', delivery: 'coder' },
|
||||
+ roles: { coder: { definition: 'coder', authority } } } });
|
||||
+ for (const action of WAITS) {
|
||||
+ assert.throws(() => new Broker({ store, businesses: business({ withinRole: [action], crossRole: [] }) }), /invalid-authority/, action);
|
||||
+ assert.throws(() => new Broker({ store, businesses: business({ withinRole: [], crossRole: [action] }) }), /invalid-authority/, action);
|
||||
+ }
|
||||
+ assert.ok(new Broker({ store, businesses: business({ withinRole: [], crossRole: LEFT }) }));
|
||||
+});
|
||||
+
|
||||
+test('REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it', async t => {
|
||||
+ const f = await fixture(t);
|
||||
+ for (const action of WAITS) {
|
||||
+ for (const args of [{}, cite(f), { ...cite(f), domain: 'technical' }]) {
|
||||
+ const d = await f.raise({ action, ...args });
|
||||
+ assert.deepEqual(routed(f, d), { class: 'gated', route_to: 'human' }, `${action} ${JSON.stringify(args)}`);
|
||||
+ }
|
||||
+ }
|
||||
+ const d = await f.raise({ action: 'data.delete', ...cite(f) });
|
||||
+ assert.throws(() => f.call(f.reviewer, 'decision.resolve', { id: d.id, choice: 'yes' }), /human-required/);
|
||||
+ f.call(f.human, 'decision.resolve', { id: d.id, choice: 'yes' });
|
||||
+});
|
||||
+
|
||||
+test('REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human', async t => {
|
||||
+ const f = await fixture(t, { arbiters: { technical: 'cto', delivery: 'reviewer' },
|
||||
+ roles: { cto: { definition: 'cto', authority: { withinRole: ['message.send'], crossRole: [] } } } });
|
||||
+ f.agent('cto');
|
||||
+ for (const action of LEFT) {
|
||||
+ const raise = { action, ...(action === 'role.revoke' ? { target: 'reviewer' } : {}) };
|
||||
+ assert.deepEqual(routed(f, f.raise(raise)), { class: 'gated', route_to: 'human' }, action);
|
||||
+ assert.deepEqual(routed(f, await f.raise({ ...raise, ...cite(f), domain: 'technical' })), { class: 'gated', route_to: 'cto' }, action);
|
||||
+ assert.deepEqual(routed(f, await f.raise({ ...raise, ...cite(f) })), { class: 'gated', route_to: 'reviewer' }, action);
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+test('REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it', async t => {
|
||||
+ const f = await fixture(t, { roles: { pm: { definition: 'pm', authority: { withinRole: ['message.send'], crossRole: [] } } } });
|
||||
+ f.agent('pm');
|
||||
+ const d = await f.raise({ action: 'role.revoke', target: 'pm', ...cite(f) });
|
||||
+ assert.deepEqual(routed(f, d), { class: 'gated', route_to: 'reviewer' });
|
||||
+ assert.throws(() => f.call(f.human, 'decision.resolve', { id: d.id, choice: 'yes' }), /agent-required/);
|
||||
+ f.call(f.reviewer, 'decision.resolve', { id: d.id, choice: 'yes' });
|
||||
+ assert.deepEqual(f.call(f.coder, 'role.revoke', { role: 'pm', decision: d.id }), { revoked: true });
|
||||
+});
|
||||
+
|
||||
+test('REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation', async t => {
|
||||
+ const f = await fixture(t, { arbiters: { technical: 'cto', delivery: 'reviewer' }, roles: {
|
||||
+ cto: { definition: 'cto', authority: { withinRole: ['message.send'], crossRole: [] } },
|
||||
+ 'merge-gate': { definition: 'merge-gate', authority: { withinRole: [], crossRole: ['git.merge.protected'] } },
|
||||
+ } });
|
||||
+ const cto = f.agent('cto'), gate = f.agent('merge-gate');
|
||||
+ const merge = { action: 'git.merge.protected', question: 'Merge?', options: [{ key: 'yes', text: 'Merge' }, { key: 'no', text: 'Hold' }],
|
||||
+ recommendation: 'yes', blocking: false, domain: 'technical' };
|
||||
+ const d = await f.call(gate, 'decision.raise', { ...merge, ...cite(f) });
|
||||
+ assert.deepEqual(routed(f, d), { class: 'cross-role', route_to: 'cto' });
|
||||
+ f.call(cto, 'decision.resolve', { id: d.id, choice: 'yes' });
|
||||
+ assert.equal(f.store.get('SELECT via FROM decision_events WHERE decision=?', d.id).via, 'broker');
|
||||
+ assert.deepEqual(routed(f, f.call(gate, 'decision.raise', merge)), { class: 'cross-role', route_to: 'cto' });
|
||||
+ assert.deepEqual(routed(f, await f.raise({ ...merge, ...cite(f) })), { class: 'gated', route_to: 'cto' });
|
||||
+ assert.deepEqual(routed(f, f.raise(merge)), { class: 'gated', route_to: 'human' });
|
||||
+});
|
||||
+
|
||||
+test('REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human', async t => {
|
||||
+ const f = await fixture(t);
|
||||
+ const old = cite(f);
|
||||
+ f.save('# Newer\n');
|
||||
+ await assert.rejects(f.raise({ action: 'policy.change', ...old }), { code: 'stale-spec' });
|
||||
+ const edit = f.save('# Agent edit\n', 'coder.coder');
|
||||
+ await assert.rejects(f.raise({ action: 'policy.change', ...old, spec_commit: edit }), { code: 'spec-unconfirmed' });
|
||||
+ assert.equal(f.store.get('SELECT count(*) n FROM decisions').n, 0);
|
||||
+ const current = f.save('# Current\n');
|
||||
+ const own = await f.call(f.reviewer, 'decision.raise', { action: 'policy.change', question: 'Change?',
|
||||
+ options: [{ key: 'yes', text: 'Yes' }, { key: 'no', text: 'No' }], recommendation: 'yes', blocking: false,
|
||||
+ project: 'stack', spec_path: 'spec/north-star.md', spec_commit: current });
|
||||
+ assert.deepEqual(routed(f, own), { class: 'gated', route_to: 'human' });
|
||||
+});
|
||||
diff --git a/packages/bus/tests/spec-fixtures.mjs b/packages/bus/tests/spec-fixtures.mjs
|
||||
index eafd30fb9..e24b27f94 100644
|
||||
--- a/packages/bus/tests/spec-fixtures.mjs
|
||||
+++ b/packages/bus/tests/spec-fixtures.mjs
|
||||
@@ -7,15 +7,16 @@ import { Broker } from '../src/broker.mjs';
|
||||
import { initProject, readSpec, writeRecord, ProjectError } from '../../project/src/index.mjs';
|
||||
export const git = (repo, ...args) => execFileSync('git', ['-c', 'core.hooksPath=/dev/null', '-C', repo, ...args], { encoding: 'utf8' }).trim();
|
||||
const options = [{ key: 'yes', text: 'Yes' }, { key: 'no', text: 'No' }];
|
||||
-export async function fixture(t, { adapter = true } = {}) {
|
||||
+export async function fixture(t, { adapter = true, roles = {}, arbiters = { technical: 'reviewer', delivery: 'reviewer' } } = {}) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'bus-spec-'));
|
||||
const store = new Store(root);
|
||||
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
||||
const authority = grants => ({ withinRole: grants, crossRole: [] });
|
||||
- const business = { id: 'demo', human: 'jason', arbiters: { technical: 'reviewer', delivery: 'reviewer' },
|
||||
+ const business = { id: 'demo', human: 'jason', arbiters,
|
||||
projects: { stack: { root: join(root, 'code'), repo: join(root, 'records') } }, roles: {
|
||||
coder: { definition: 'coder', authority: authority(['message.send', 'record.write']) },
|
||||
reviewer: { definition: 'reviewer', authority: authority(['message.send']) },
|
||||
+ ...roles,
|
||||
} };
|
||||
const { repo, commit } = await initProject({ business, project: 'stack', dataRoot: root });
|
||||
const broker = new Broker({ store, businesses: { demo: business }, project: adapter ? {
|
||||
@@ -38,5 +39,5 @@ export async function fixture(t, { adapter = true } = {}) {
|
||||
git(repo, '-c', `user.name=${author}`, '-c', '[email protected]', 'commit', '-m', 'Spec');
|
||||
return git(repo, 'rev-parse', 'HEAD');
|
||||
};
|
||||
- return { root, store, business, broker, repo, commit, coder, reviewer, human, call, raise, save };
|
||||
+ return { root, store, business, broker, repo, commit, agent, coder, reviewer, human, call, raise, save };
|
||||
}
|
||||
diff --git a/packages/business/README.md b/packages/business/README.md
|
||||
index e5ca85610..504fbd14d 100644
|
||||
--- a/packages/business/README.md
|
||||
+++ b/packages/business/README.md
|
||||
@@ -48,10 +48,17 @@ in this repository, or `$MOSAIC_ROLES_DIR` if set.
|
||||
empty.
|
||||
- `authority` names actions from the closed list in `src/vocabulary.mjs`.
|
||||
`withinRole` actions run without asking. `crossRole` actions need the
|
||||
- arbiter. Any action not listed is gated: it goes to Jason. The nine
|
||||
- always-gated actions (`deploy`, `spend`, `git.push.protected`,
|
||||
- `git.merge.protected`, `credential.mint`, `message.external`,
|
||||
- `policy.change`, `prd.approve`, `role.revoke`) can't appear in either list.
|
||||
+ arbiter. Any action not listed is gated: it goes to Jason, or to the
|
||||
+ arbiter for its domain when the raise cites the project's current spec
|
||||
+ commit (PRD 1.5 REQ-DEC-2). The eight hard waits in `GATED_ONLY`
|
||||
+ (REQ-WAIT-1: `spend`, `data.delete`, `network.expose`, `account.create`,
|
||||
+ `credential.mint`, `message.external`, `git.push.protected`, `deploy`)
|
||||
+ always go to Jason and can't appear in either list (REQ-WAIT-2).
|
||||
+ `GATED_ONLY` is every action that `ROUTED` doesn't name, so an action
|
||||
+ added to the vocabulary is a hard wait until a PRD says otherwise and the
|
||||
+ commit adds it to `ROUTED` (REQ-WAIT-3). `git.merge.protected`,
|
||||
+ `policy.change`, `prd.approve` and `role.revoke` left the list in 1.5
|
||||
+ (row 139, #1633).
|
||||
- Gitea scopes are `read:<category>` or `write:<category>`, one level per
|
||||
category. `all` and `admin` are refused. Vikunja scopes map a route group
|
||||
to verbs, and only the pairs in `VIKUNJA_GRANTABLE` pass.
|
||||
@@ -134,7 +141,7 @@ terminates role instances, owns the structure, takes work from the
|
||||
assistant and never talks to the user. It holds `message.send` and
|
||||
`role.launch` within role (decision 139), plus `role.end` and
|
||||
`organization.change` (row 123): it ends a run with a reason and no decision
|
||||
-(`role.revoke` stays gated for every role), and it changes the organization
|
||||
+(no shipped role is granted `role.revoke`, so it stays gated), and it changes the organization
|
||||
file through the broker's `organization.set`, limited to the keys the human's
|
||||
`limits.set` takes; the harness's `end_run` and `change_organization` tools
|
||||
call them (row 132). It has no credentials and the same read-only tools. It is the only shipped role that holds `role.launch`, so
|
||||
diff --git a/packages/business/src/vocabulary.mjs b/packages/business/src/vocabulary.mjs
|
||||
index a63316935..05d09d626 100644
|
||||
--- a/packages/business/src/vocabulary.mjs
|
||||
+++ b/packages/business/src/vocabulary.mjs
|
||||
@@ -29,32 +29,59 @@ export const ACTIONS = Object.freeze([
|
||||
"role.revoke",
|
||||
// Row 123 (#1617, REQ-COS-1): the chief of staff ends a run with a reason and no decision,
|
||||
// and changes the organization file through the shared write path. Neither is gated-only,
|
||||
- // so the grant lives in a role file; role.revoke itself stays gated for every role.
|
||||
+ // so the grant lives in a role file. No shipped role is granted role.revoke, so it stays gated.
|
||||
"role.end",
|
||||
"organization.change",
|
||||
"credential.mint",
|
||||
"spend",
|
||||
"deploy",
|
||||
+ // PRD 1.5 REQ-WAIT-1 (row 139, #1633): new hard waits. data.delete deletes data; network.expose
|
||||
+ // opens a listener, port, tunnel, DNS record or proxy route reachable from outside the host;
|
||||
+ // account.create opens an external account.
|
||||
+ "data.delete",
|
||||
+ "network.expose",
|
||||
+ "account.create",
|
||||
"policy.change",
|
||||
"prd.approve",
|
||||
"decision.resolve.technical",
|
||||
]);
|
||||
|
||||
-// Always gated: a role file may not list these under withinRole or
|
||||
-// crossRole. role.revoke is here because lead decision 46 (6.7) keeps every
|
||||
-// revoke gated in slice 1.
|
||||
-export const GATED_ONLY = Object.freeze([
|
||||
- "credential.mint",
|
||||
+// The actions that are not hard waits. PRD 1.5 REQ-WAIT-3: an action added
|
||||
+// to ACTIONS is a hard wait until a PRD version says it isn't, so a new
|
||||
+// action joins this list only by the reviewed commit that names it routed.
|
||||
+// git.merge.protected, policy.change, prd.approve and role.revoke left the
|
||||
+// hard waits in 1.5 and route by REQ-DEC-2.
|
||||
+export const ROUTED = Object.freeze([
|
||||
+ "task.create",
|
||||
+ "task.assign",
|
||||
+ "task.schedule",
|
||||
+ "task.update.assigned",
|
||||
+ "task.close",
|
||||
+ "task.reassign",
|
||||
+ "task.scope.change",
|
||||
+ "task.priority.change",
|
||||
+ "git.push.working",
|
||||
"git.merge.protected",
|
||||
- "git.push.protected",
|
||||
- "deploy",
|
||||
- "spend",
|
||||
- "message.external",
|
||||
+ "review.request",
|
||||
+ "review.verdict",
|
||||
+ "record.write",
|
||||
+ "message.send",
|
||||
+ "role.launch",
|
||||
+ "role.revoke",
|
||||
+ "role.end",
|
||||
+ "organization.change",
|
||||
"policy.change",
|
||||
"prd.approve",
|
||||
- "role.revoke",
|
||||
+ "decision.resolve.technical",
|
||||
]);
|
||||
|
||||
+// The hard waits (PRD 1.5 REQ-WAIT-1): always gated, always the human's,
|
||||
+// cited spec or not. A role file may not list these under withinRole or
|
||||
+// crossRole (REQ-WAIT-2). Today this is spend, data.delete, network.expose,
|
||||
+// account.create, credential.mint, message.external, git.push.protected and
|
||||
+// deploy; a test pins the list.
|
||||
+export const GATED_ONLY = Object.freeze(ACTIONS.filter((action) => !ROUTED.includes(action)));
|
||||
+
|
||||
// pi's documented built-in tools; the same list scripts/mosaic-task.mjs
|
||||
// accepts for tasks.
|
||||
export const BUILTIN_TOOLS = Object.freeze(["read", "write", "edit", "bash", "grep", "find", "ls"]);
|
||||
diff --git a/packages/business/tests/hard-waits.test.mjs b/packages/business/tests/hard-waits.test.mjs
|
||||
new file mode 100644
|
||||
index 000000000..57132425d
|
||||
--- /dev/null
|
||||
+++ b/packages/business/tests/hard-waits.test.mjs
|
||||
@@ -0,0 +1,41 @@
|
||||
+// PRD 1.5 REQ-WAIT-1 to REQ-WAIT-3 (row 139, #1633): the hard waits in the
|
||||
+// closed vocabulary, and the role validator's refusal of a grant of one.
|
||||
+import test from "node:test";
|
||||
+import assert from "node:assert/strict";
|
||||
+import { writeFileSync } from "node:fs";
|
||||
+import { join } from "node:path";
|
||||
+import { ACTIONS, GATED_ONLY, ROUTED } from "../src/vocabulary.mjs";
|
||||
+import { validateRoleDocument } from "../src/index.mjs";
|
||||
+import { tmp } from "./helpers.mjs";
|
||||
+
|
||||
+const WAITS = ["spend", "data.delete", "network.expose", "account.create", "credential.mint", "message.external",
|
||||
+ "git.push.protected", "deploy"];
|
||||
+const LEFT = ["git.merge.protected", "policy.change", "prd.approve", "role.revoke"];
|
||||
+const role = (authority) => {
|
||||
+ const dir = tmp();
|
||||
+ writeFileSync(join(dir, "r.md"), "# contract\n");
|
||||
+ const doc = { roleVersion: 2, name: "r", title: "R", contract: "r.md", tools: ["read"], network: "none", authority,
|
||||
+ credentials: [{ service: "gitea", scopes: ["read:issue"] }] };
|
||||
+ return validateRoleDocument(doc, join(dir, "r.json"));
|
||||
+};
|
||||
+
|
||||
+test("REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary", () => {
|
||||
+ assert.deepEqual([...GATED_ONLY].sort(), [...WAITS].sort());
|
||||
+ for (const action of ["data.delete", "network.expose", "account.create"]) assert.ok(ACTIONS.includes(action), action);
|
||||
+ for (const action of LEFT) assert.ok(ACTIONS.includes(action) && !GATED_ONLY.includes(action), action);
|
||||
+});
|
||||
+
|
||||
+test("REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions", () => {
|
||||
+ for (const action of ROUTED) assert.ok(ACTIONS.includes(action), action);
|
||||
+ for (const action of ACTIONS) assert.notEqual(ROUTED.includes(action), GATED_ONLY.includes(action), action);
|
||||
+});
|
||||
+
|
||||
+test("REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted", () => {
|
||||
+ for (const action of WAITS) {
|
||||
+ assert.throws(() => role({ withinRole: [action], crossRole: [] }), /always gated/, action);
|
||||
+ assert.throws(() => role({ withinRole: [], crossRole: [action] }), /always gated/, action);
|
||||
+ }
|
||||
+ assert.deepEqual(role({ withinRole: [], crossRole: ["git.merge.protected"] }).authority.crossRole,
|
||||
+ ["git.merge.protected"]);
|
||||
+ assert.deepEqual(role({ withinRole: LEFT.slice(1), crossRole: [] }).authority.withinRole, LEFT.slice(1));
|
||||
+});
|
||||
diff --git a/scripts/test-land-work.sh b/scripts/test-land-work.sh
|
||||
index 1bfe402db..d689e0e14 100644
|
||||
--- a/scripts/test-land-work.sh
|
||||
+++ b/scripts/test-land-work.sh
|
||||
@@ -122,6 +122,10 @@ const biz = variant === 'other-business' ? 'other' : 'fx';
|
||||
const ref = `work/vikunja-${p}-${t}`;
|
||||
const task = `vikunja:${p}/${t}`;
|
||||
const action = variant === 'not-a-merge' ? 'git.push.protected' : MERGE;
|
||||
+// Row 139 (#1633, PRD 1.5 REQ-DEC-2): a merge decision the arbiter resolved, raised cross-role by
|
||||
+// merge-gate or gated with a spec citation by a coder. land-work still lands only a human's.
|
||||
+const arbiter = { 'arbiter-cross': ['merge-gate', 'cross-role'], 'arbiter-cited': ['coder', 'gated'] }[variant];
|
||||
+const raiser = arbiter?.[0] ?? 'pm';
|
||||
const question =
|
||||
{
|
||||
'two-refs': `Squash-merge ${ref} and work/vikunja-7-99 into refactor?`,
|
||||
@@ -135,6 +139,7 @@ const rows = [
|
||||
{
|
||||
table: 'decisions', id: D, business: biz, project: null, action, question, options,
|
||||
...(variant === 'task-ref-disagrees' ? { task_ref: 'vikunja:7/99' } : {}),
|
||||
+ ...(arbiter ? { raised_by_role: raiser, class: arbiter[1], route_to: 'cto' } : {}),
|
||||
},
|
||||
];
|
||||
// An earlier merge decision on the same ref, withdrawn before D was raised:
|
||||
@@ -146,7 +151,7 @@ if (variant === 'earlier-merge') {
|
||||
}
|
||||
if (variant !== 'no-raise')
|
||||
rows.push({
|
||||
- table: 'events', business: biz, kind: 'action.allowed', actor_role: 'pm', actor_run: 'rfixture', subject: D,
|
||||
+ table: 'events', business: biz, kind: 'action.allowed', actor_role: raiser, actor_run: 'rfixture', subject: D,
|
||||
body: { decision: D, action, operation: 'decision.raise',
|
||||
task_ref: variant === 'raise-task-disagrees' ? 'vikunja:7/99' : null,
|
||||
target: variant === 'target-main' ? 'main' : 'refactor', approvalChoice: 'yes', holderRun: null },
|
||||
@@ -154,14 +159,14 @@ if (variant !== 'no-raise')
|
||||
const choice = variant === 'chose-no' ? 'no' : 'yes';
|
||||
if (variant === 'withdrawn') rows.push({ table: 'decision_events', decision: D, op: 'withdrawn', by: 'pm', choice: null, via: null });
|
||||
else if (variant !== 'open') {
|
||||
- rows.push({ table: 'decision_events', decision: D, op: 'resolved', by: variant === 'by-agent' ? 'pm' : 'jason', choice,
|
||||
- via: variant === 'via-webui' ? 'webui' : 'cli' });
|
||||
+ rows.push({ table: 'decision_events', decision: D, op: 'resolved', by: variant === 'by-agent' ? 'pm' : arbiter ? 'cto' : 'jason', choice,
|
||||
+ via: variant === 'via-webui' ? 'webui' : arbiter ? 'broker' : 'cli' });
|
||||
const answer = {
|
||||
table: 'events', business: biz, kind: 'human.input', subject: D,
|
||||
actor_role: variant === 'answer-role' ? 'pm' : null, actor_run: variant === 'answer-run' ? 'rfixture' : null,
|
||||
body: { kind: 'answer', decision: D, op: 'resolved', choice: variant === 'answer-choice' ? 'no' : choice },
|
||||
};
|
||||
- if (variant !== 'no-answer') rows.push(answer);
|
||||
+ if (variant !== 'no-answer' && !arbiter) rows.push(answer);
|
||||
if (variant === 'two-answers') rows.push({ ...answer });
|
||||
}
|
||||
const later = {
|
||||
@@ -262,7 +267,7 @@ check "openBus opens the database read-only" "$rc"
|
||||
for v in "decision-not-found:other-business" "decision-not-found:no-raise" "not-a-merge:not-a-merge" \
|
||||
"not-resolved:open" "not-resolved:withdrawn" "not-human:by-agent" "not-human:via-webui" \
|
||||
"not-human:no-answer" "not-human:answer-role" "not-human:answer-run" "not-human:answer-choice" \
|
||||
- "not-human:two-answers" "not-merge-option:chose-no" "work-ref:two-refs" "work-ref:bad-ref" \
|
||||
+ "not-human:two-answers" "not-human:arbiter-cross" "not-human:arbiter-cited" "not-merge-option:chose-no" "work-ref:two-refs" "work-ref:bad-ref" \
|
||||
"work-ref:task-ref-disagrees" "work-ref:raise-task-disagrees" "target-branch:target-main" "superseded:superseded-question" \
|
||||
"superseded:superseded-task" "superseded:superseded-by" "no-verdict:no-verdict" \
|
||||
"not-approved:changes-later" "not-approved:short-commit" "not-approved:wrong-task-body"; do
|
||||
@@ -0,0 +1,8 @@
|
||||
cb4effc15f5567a1cc90c6901a67cf29915281d2031143eaab5d0d54166a7825 packages/business/README.md
|
||||
b94dc6a5b8ec522003a66874bdeb67c5397ab1e6d8835eb9e538f665c85a5832 packages/business/src/vocabulary.mjs
|
||||
8ad7db43ede9724d5537e4c7a8278013e790a4fb8173e48dcbe2f20ae1f02b51 packages/business/tests/hard-waits.test.mjs
|
||||
b85db4464b32c5d0e0b7aba799b5749c15522eb52b412b8bce6d8c107400c58e packages/bus/README.md
|
||||
e2b0099471665f4fa828bec5b02b6393b6ab20de62ca86f328e4aca2aeb6bc74 packages/bus/src/broker.mjs
|
||||
68ae53935bdd128639c49d667984564609aa3b923874fa205bba079528f99fff packages/bus/tests/hard-waits.test.mjs
|
||||
bb021a3a49b2f9cd0e6e220e8e01952135543fc67fe0d77e74ac9c01c14bae60 packages/bus/tests/spec-fixtures.mjs
|
||||
c9d1d5c9b006d5c12e5f796a9828361059f8e0410846fc55c070c0148b15cef4 scripts/test-land-work.sh
|
||||
@@ -0,0 +1,13 @@
|
||||
land-work rc=0 start=2026-10-16T18:28:41Z end=2026-10-16T18:28:53Z
|
||||
bus rc=0 start=2026-10-16T18:28:53Z end=2026-10-16T18:29:05Z
|
||||
business rc=0 start=2026-10-16T18:29:05Z end=2026-10-16T18:29:07Z
|
||||
test-auth rc=0 start=2026-10-16T18:29:07Z end=2026-10-16T18:29:08Z
|
||||
test-conductor rc=0 start=2026-10-16T18:29:08Z end=2026-10-16T18:29:14Z
|
||||
test-config rc=0 start=2026-10-16T18:29:14Z end=2026-10-16T18:29:16Z
|
||||
test-discord rc=0 start=2026-10-16T18:29:16Z end=2026-10-16T18:29:23Z
|
||||
test-extension-package rc=0 start=2026-10-16T18:29:23Z end=2026-10-16T18:29:24Z
|
||||
test-foundation rc=0 start=2026-10-16T18:29:24Z end=2026-10-16T18:30:01Z
|
||||
test-queue rc=0 start=2026-10-16T18:30:01Z end=2026-10-16T18:30:31Z
|
||||
test-release rc=0 start=2026-10-16T18:30:31Z end=2026-10-16T18:30:43Z
|
||||
test-task rc=0 start=2026-10-16T18:30:43Z end=2026-10-16T18:31:11Z
|
||||
gate done 2026-10-16T18:31:11Z
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 139 gate (Dewey, slot sage-0518): land-work, bus, business, every scripts/test-*.sh.
|
||||
set -uo pipefail
|
||||
W=/home/jwoltje/dewey-scratch/r139/wt
|
||||
O=/home/jwoltje/dewey-scratch/r139/out/gate
|
||||
export TMPDIR=/home/jwoltje/dewey-scratch/tmp COMPOSE_PROJECT_NAME=dewey-r139
|
||||
mkdir -p "$O" "$TMPDIR"
|
||||
cleanup() {
|
||||
n=$(docker ps -aq --filter network=dewey-r139_default 2>/dev/null | wc -l)
|
||||
if docker network inspect dewey-r139_default >/dev/null 2>&1; then
|
||||
if [ "$n" -eq 0 ]; then docker network rm dewey-r139_default >/dev/null && echo "network removed"; else echo "network kept: $n containers"; fi
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
cd "$W" || exit 2
|
||||
run() { # name, command...
|
||||
local name=$1; shift
|
||||
local start; start=$(date -u +%FT%TZ)
|
||||
"$@" > "$O/$name.txt" 2>&1; local rc=$?
|
||||
echo "$name rc=$rc start=$start end=$(date -u +%FT%TZ)" | tee -a "$O/summary.txt"
|
||||
}
|
||||
: > "$O/summary.txt"
|
||||
run land-work bash scripts/test-land-work.sh
|
||||
run bus bash -c 'cd packages/bus && npm test'
|
||||
run business bash -c 'cd packages/business && npm test'
|
||||
for s in scripts/test-*.sh; do
|
||||
b=$(basename "$s" .sh)
|
||||
[ "$b" = test-land-work ] && continue
|
||||
run "$b" bash "$s"
|
||||
done
|
||||
echo "gate done $(date -u +%FT%TZ)" | tee -a "$O/summary.txt"
|
||||
@@ -0,0 +1,311 @@
|
||||
npm notice run test
|
||||
npm notice run node --test tests/*.test.mjs
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (132.444358ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (251.778235ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (327.184703ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (162.952854ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (210.149324ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (189.652892ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (228.172558ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (222.25126ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (915.358403ms)
|
||||
✔ agents: stranded is per role and per business (372.511361ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (169.10111ms)
|
||||
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (312.926179ms)
|
||||
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (189.756813ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (255.632819ms)
|
||||
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (235.325013ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (250.785512ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (373.174376ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (156.486711ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (304.173323ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (268.055771ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (168.885558ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (185.101067ms)
|
||||
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (277.859397ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (194.357257ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (193.980139ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (116.697591ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (205.669054ms)
|
||||
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (304.384379ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (169.065543ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (254.790291ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (164.206991ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (271.823159ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (192.41945ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.44808ms)
|
||||
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (302.555277ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (308.956132ms)
|
||||
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (226.485741ms)
|
||||
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (225.400716ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (275.28396ms)
|
||||
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (301.026253ms)
|
||||
✔ REQ-COS-1: the chief of staff cannot end its own run or the assistant's through role.end (277.927679ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (240.120267ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (340.36891ms)
|
||||
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (319.599442ms)
|
||||
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (440.190821ms)
|
||||
✔ REQ-ROLE-3, REQ-LIMIT-1: a chief-of-staff change is validated, written and logged like a human's; an invalid one is refused (590.701281ms)
|
||||
✔ REQ-ROLE-2, REQ-COS-1: the 3c→3d migration lets the chief of staff end a held role with a reason; neither is still refused (1051.365633ms)
|
||||
✔ comment cursors isolate businesses, move only forward and remain append-only (117.220096ms)
|
||||
✔ only trusted sync writes validated cursors; events and cursor share rollback (164.941143ms)
|
||||
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (881.526511ms)
|
||||
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1565.599494ms)
|
||||
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (887.128711ms)
|
||||
✔ migration refuses mismatched source metadata without changing schema or evidence (892.220869ms)
|
||||
✔ offline command migrates only the supplied fixture root and reports usage errors (1019.022503ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (91.387807ms)
|
||||
✔ credentials refuses a role session and any argument (220.096214ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (228.962277ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (222.718862ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.215846ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.08442ms)
|
||||
✔ expiry refuses use and env references never become client data (0.945777ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.916464ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.498051ms)
|
||||
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (2.426824ms)
|
||||
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (524.228565ms)
|
||||
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (442.032919ms)
|
||||
✔ chief-executive-officer: another C-suite role isn't on the lists either (410.055909ms)
|
||||
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (485.032579ms)
|
||||
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (467.015189ms)
|
||||
✔ chief-financial-officer: another C-suite role isn't on the lists either (424.518686ms)
|
||||
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (534.503974ms)
|
||||
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (502.037723ms)
|
||||
✔ chief-information-officer: another C-suite role isn't on the lists either (467.515948ms)
|
||||
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (555.42298ms)
|
||||
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (563.306408ms)
|
||||
✔ chief-information-security-officer: another C-suite role isn't on the lists either (433.539743ms)
|
||||
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (524.336693ms)
|
||||
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (471.774305ms)
|
||||
✔ chief-marketing-officer: another C-suite role isn't on the lists either (391.15258ms)
|
||||
✔ a human resolve sends the raising role one message from the human, and its request is the answer (270.540744ms)
|
||||
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (248.262723ms)
|
||||
✔ a human withdraw sends one message with the note (230.799514ms)
|
||||
✔ a human seen before the close leaves the request on the close's answer, not the seen (278.600515ms)
|
||||
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (290.399157ms)
|
||||
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (309.236804ms)
|
||||
✔ a raising role nobody holds at the close gets the message on the next claim (263.483761ms)
|
||||
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (254.576158ms)
|
||||
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (343.523396ms)
|
||||
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (287.621043ms)
|
||||
✔ a route the raiser hears from is allowed, checked from the route to the raiser (328.914015ms)
|
||||
✔ a refused supersede withdraws nothing (293.07112ms)
|
||||
✔ the assistant's within-role and gated raises still work (313.072953ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (273.963247ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (274.329694ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (296.81738ms)
|
||||
✔ recent stops at RECENT rows (818.221113ms)
|
||||
✔ every row is what trail already gives the same session for that id (278.853445ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (410.919997ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (421.606308ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (155.816868ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (175.93514ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (137.262817ms)
|
||||
✔ endLaunch leaves a claim another run took alone (189.799453ms)
|
||||
✔ refuse records action.refused against the caller with the code only (128.679347ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (198.024687ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (317.717768ms)
|
||||
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.193414ms)
|
||||
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (118.650717ms)
|
||||
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (757.531091ms)
|
||||
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (455.744135ms)
|
||||
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (256.057082ms)
|
||||
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (293.300112ms)
|
||||
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (277.763823ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.664322ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.502588ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.666884ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.247019ms)
|
||||
✔ a message to a stopped role sends launch.by one notice, and the result says so (254.789971ms)
|
||||
✔ a message to a role outside launch.instances says so in its result and in the notice (156.797965ms)
|
||||
✔ a message to a running role sends no notice and its result is unchanged (138.60398ms)
|
||||
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (244.913375ms)
|
||||
✔ a run launched but not yet claimed counts as no holder (129.69449ms)
|
||||
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (278.82952ms)
|
||||
✔ a business with no launch block tells nobody and says there is no launcher (128.29816ms)
|
||||
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (355.378964ms)
|
||||
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (266.766916ms)
|
||||
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (283.149418ms)
|
||||
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (416.878587ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (209.291617ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (190.399369ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (234.42727ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (2691.816662ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (308.295576ms)
|
||||
✔ views messages passes only its paging arguments through (0.292313ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (195.992079ms)
|
||||
✔ the assistant sends to the user and to the chief of staff only (323.599854ms)
|
||||
✔ an assistant send to any other role, or to the tester, is refused and logged (382.58668ms)
|
||||
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (374.827058ms)
|
||||
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (323.839981ms)
|
||||
✔ a sender-side list alone refuses a send to a role that has no list (350.34954ms)
|
||||
✔ a target-side list alone refuses a send from a role that has no list (381.831703ms)
|
||||
✔ the sender list is checked before the target list (343.100644ms)
|
||||
✔ roles without the field behave exactly as before (474.188568ms)
|
||||
✔ a business with no role definitions at all is unlimited (393.459059ms)
|
||||
✔ a malformed messaging field refuses at boot (123.201186ms)
|
||||
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (430.548337ms)
|
||||
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (473.885132ms)
|
||||
✔ review requests and verdicts between roles without the field are unchanged (364.659948ms)
|
||||
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (208.239109ms)
|
||||
✔ runtime: the switch changes the organization file, the receipt carries the actor, and a bad shape, an unknown module and a stale revision are refused with nothing written (494.73471ms)
|
||||
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (427.187684ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (214.609311ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (66.404545ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (215.774472ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (218.997876ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (218.399394ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (68.741675ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (213.603133ms)
|
||||
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (147.452853ms)
|
||||
✔ D2: spec.read refuses without content if its role is released during the read (156.027146ms)
|
||||
✔ D3: spec.read secret check refuses flagged content without content (155.168204ms)
|
||||
✔ D3: spec.read secret check refuses flagged subject without content (138.425225ms)
|
||||
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.491622ms)
|
||||
✔ without a trusted project adapter all three async paths refuse project-not-configured (200.140815ms)
|
||||
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (204.502562ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (232.131673ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (209.801547ms)
|
||||
✔ a message with no reply gives an empty list (175.23397ms)
|
||||
✔ replies come oldest first and stop at REPLIES (829.428465ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (339.752405ms)
|
||||
✔ strict arguments; a role session gets human-required (409.457652ms)
|
||||
✔ views passes replies through as one call with the id (0.285344ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (227.094908ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (167.457966ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (178.30535ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (261.768944ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (257.408309ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (318.623174ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (427.875111ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (289.415565ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (345.917978ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (442.542328ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (315.38029ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1641.629598ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (281.726628ms)
|
||||
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (228.244157ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (163.975861ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (367.73548ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (256.504957ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (288.004369ms)
|
||||
✔ class drift gated to within-role refuses before consumption (268.604802ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (294.752141ms)
|
||||
✔ class drift within-role to gated refuses before consumption (277.115728ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (256.07251ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (297.640322ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (318.595982ms)
|
||||
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1525.429796ms)
|
||||
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (283.051325ms)
|
||||
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (205.184437ms)
|
||||
✔ two real socket writes to one repository yield one commit and one project-busy refusal (253.2246ms)
|
||||
✔ client disconnect during await tolerates late reply (194.703225ms)
|
||||
✔ client disconnect during await tolerates late refusal (190.276122ms)
|
||||
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (387.638362ms)
|
||||
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (335.4821ms)
|
||||
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (330.128717ms)
|
||||
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (252.224831ms)
|
||||
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (282.274955ms)
|
||||
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (290.647482ms)
|
||||
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (351.050202ms)
|
||||
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (190.154252ms)
|
||||
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (515.479454ms)
|
||||
✔ REQ-COS-1: the sender is judged by definition: an instance `lead` of the pm definition can ask, an instance `pm` of the coder definition can't (470.578971ms)
|
||||
✔ REQ-COS-1: the launcher is judged by definition: an instance `boss` of the chief-of-staff definition receives, a `chief` of the coder definition gives no-chief-of-staff (488.107664ms)
|
||||
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (261.013992ms)
|
||||
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (684.833932ms)
|
||||
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (496.567951ms)
|
||||
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (246.491572ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (152.444398ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (168.130066ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (160.675772ms)
|
||||
✔ a tombstone is not open (150.745524ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (162.222779ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (179.749397ms)
|
||||
✔ a human session reads the same state, with the same cap (165.513219ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (178.875741ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (136.379748ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (190.808614ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (212.878514ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (225.953861ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (118.43227ms)
|
||||
✔ async transactions refuse before invoking their function (107.188748ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (335.307783ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (268.869391ms)
|
||||
✔ each filter, alone and together, and total counts every match (384.678102ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (341.549556ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (258.578075ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (991.90034ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (376.506201ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (397.969766ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (156.381188ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (220.501957ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (127.196849ms)
|
||||
✔ a bad entry refuses the whole record (130.624138ms)
|
||||
✔ taskView reads the projection for one business (157.798748ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (287.680531ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (483.522474ms)
|
||||
✔ without an adapter the server refuses every task verb (259.926421ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (233.814771ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (358.514355ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (975.488031ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (278.408944ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (646.937169ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (270.382996ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (286.981784ms)
|
||||
✔ the tester reads the human views a reader reads (187.667135ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.333787ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.411423ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.219817ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (121.332394ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (866.964268ms)
|
||||
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1190.744718ms)
|
||||
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (924.415257ms)
|
||||
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (891.492288ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (384.880549ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1355.92584ms)
|
||||
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (390.439688ms)
|
||||
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3693.918526ms)
|
||||
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (104.673423ms)
|
||||
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (157.703219ms)
|
||||
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (165.981225ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (188.174849ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (166.686489ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (145.713532ms)
|
||||
✔ REQ-USER-2: the human verbs refuse another owner's thread (164.016544ms)
|
||||
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (126.02312ms)
|
||||
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (170.18676ms)
|
||||
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (473.226674ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (188.601212ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (213.30273ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (172.435068ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.581325ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (152.744703ms)
|
||||
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (174.951498ms)
|
||||
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (191.269738ms)
|
||||
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (258.710377ms)
|
||||
✔ a broker with no web service refuses a granted role (161.470809ms)
|
||||
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (242.344415ms)
|
||||
✔ a session that is not the role holder is refused (141.901347ms)
|
||||
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (446.722495ms)
|
||||
✔ web.search with no search source configured is a refused result, not an error (174.153696ms)
|
||||
✔ a search source that fails with its own refusal, or that throws, is handled (187.23312ms)
|
||||
✔ a slow search source ends in a refusal at the deadline (203.854878ms)
|
||||
✔ a secret in the arguments is refused before anything is trailed or fetched (132.437514ms)
|
||||
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (248.316867ms)
|
||||
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.273515ms)
|
||||
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (218.105939ms)
|
||||
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (161.533027ms)
|
||||
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (468.133055ms)
|
||||
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (180.113384ms)
|
||||
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (199.225226ms)
|
||||
✔ one query limit: over SEARCH_MAX_QUERY_CHARS or empty is refused with the same reason before any backend, and the tool schema carries the same number (161.696343ms)
|
||||
ℹ tests 301
|
||||
ℹ suites 0
|
||||
ℹ pass 301
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 12001.64549
|
||||
@@ -0,0 +1,122 @@
|
||||
npm notice run @mosaic/[email protected] test
|
||||
npm notice run node --test tests/
|
||||
✔ the role file loads and resolves through loadBusiness (8.504862ms)
|
||||
✔ the shipped file is exactly the approved list (1.044517ms)
|
||||
✔ the assistant's authority is message.send and record.write within role (3.09732ms)
|
||||
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (1.047311ms)
|
||||
✔ a business grant through limits does not widen the role (2.482339ms)
|
||||
✔ a launch block whose by is the assistant is refused (2.717366ms)
|
||||
✔ the instance takes no credential references and no tracker (4.721492ms)
|
||||
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (8.202015ms)
|
||||
✔ an arbiter without messaging lists is accepted beside the limited assistant (3.299292ms)
|
||||
✔ the contract passes work to the chief of staff and directs no one (0.895336ms)
|
||||
✔ REQ-HARN-2: the assistant holds the web grant, and no other role file does (2.283346ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.527298ms)
|
||||
✔ the fixture business validates and comes back frozen (6.538725ms)
|
||||
✔ two instances may share a definition (3.201127ms)
|
||||
✔ top-level refusals (8.238936ms)
|
||||
✔ arbiters and projects (13.578514ms)
|
||||
✔ role instances (4.965848ms)
|
||||
✔ Vikunja bots (11.669074ms)
|
||||
✔ a role without Vikunja takes no tracker block (2.381614ms)
|
||||
✔ credential references match the definition's services (2.776914ms)
|
||||
✔ launch (8.270622ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (4.189168ms)
|
||||
✔ loadBusiness: file checks (1.906556ms)
|
||||
✔ loadBusiness: not a regular file (35.708642ms)
|
||||
✔ loading writes nothing (0.976715ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.551327ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.729975ms)
|
||||
✔ the role file loads and resolves through loadBusiness (9.022092ms)
|
||||
✔ the shipped file is exactly the approved list (0.31375ms)
|
||||
✔ the resolved record matches the approved list, so a widened file fails (2.617182ms)
|
||||
✔ the chief of staff holds role.launch, message.send, role.end, organization.change and record.write; every other action is gated (2.107068ms)
|
||||
✔ the pm does not hold role.launch (2.500409ms)
|
||||
✔ a business grant of a gated action through limits does not widen the role (3.572351ms)
|
||||
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.784787ms)
|
||||
✔ a launch block whose by is the pm instance is refused (3.326824ms)
|
||||
✔ the contract names no duty to talk to the human and the pm contract no launch (0.515998ms)
|
||||
✔ the instance takes no credential references and no tracker (3.01532ms)
|
||||
✔ usage errors exit 4 (283.162014ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (67.502752ms)
|
||||
✔ validate: project files (328.803931ms)
|
||||
✔ validate: missing files and a broken system config (232.646881ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (66.313227ms)
|
||||
✔ validate: a token file inside the repository is refused (62.704751ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (188.4663ms)
|
||||
✔ resolve: prints one instance's record (246.098916ms)
|
||||
✔ resolve: refusals (361.921447ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.348009ms)
|
||||
✔ check: a good file has no problems (1.067994ms)
|
||||
✔ check never opens the file: a write-only token passes (0.73366ms)
|
||||
✔ check: file problems (1.135169ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.197637ms)
|
||||
✔ check: dates and environment references (0.544157ms)
|
||||
✔ each definition loads, with a contract file, and is exactly the approved list (4.867046ms)
|
||||
✔ an organization file that declares them passes, and each resolves to least privilege (9.412498ms)
|
||||
✔ the chief of staff launches each one; no other role does (5.389288ms)
|
||||
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (3.669591ms)
|
||||
✔ limits can narrow a C-suite role but never widen it (2.353266ms)
|
||||
✔ any unknown key, tool, action or version in a role file is refused (1.343607ms)
|
||||
✔ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (1.709403ms)
|
||||
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.372031ms)
|
||||
✔ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (5.18566ms)
|
||||
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (21.452415ms)
|
||||
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.109391ms)
|
||||
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (241.64372ms)
|
||||
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (283.552996ms)
|
||||
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (3.008458ms)
|
||||
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (198.430731ms)
|
||||
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.815973ms)
|
||||
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (108.718061ms)
|
||||
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (17.568802ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (282.205194ms)
|
||||
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (409.358874ms)
|
||||
✔ vikunja:19/309 an unknown kind (set or removed), a bad module, an empty patch and null on the wrong shape are refused, nothing written (1.511728ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (40.208536ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.532234ms)
|
||||
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (80.71092ms)
|
||||
✔ optional project repo is preserved and must be a normalized absolute path (14.381881ms)
|
||||
✔ loaded business project repo cannot be nested in any declared code checkout (4.608396ms)
|
||||
✔ path and load (3.002123ms)
|
||||
✔ refusals (1.944389ms)
|
||||
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (4.650648ms)
|
||||
✔ systemVars flattens the validated config (2.73646ms)
|
||||
✔ precedence: system, business, project, project role, agent (7.656247ms)
|
||||
✔ limits narrow the definition and never widen it (2.629276ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (4.766722ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (1.738186ms)
|
||||
✔ limits.authority narrows cross-role actions too (0.983755ms)
|
||||
✔ classify (1.407903ms)
|
||||
✔ the record carries what the broker and launcher need (0.779581ms)
|
||||
✔ digest: key order doesn't matter, any value change does (9.313198ms)
|
||||
✔ refusals (1.753989ms)
|
||||
✔ the four shipped version 2 roles load (4.107757ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.374197ms)
|
||||
✔ shipped authority follows the note's table (1.025077ms)
|
||||
✔ every role file in roles/ loads through the validator, and a v2 contract exists (1.403295ms)
|
||||
✔ version 1 files keep loading with no authority (1.123689ms)
|
||||
✔ the conductor policy isn't a role (0.241901ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.504403ms)
|
||||
✔ version 2 refusals (1.827322ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (2.715718ms)
|
||||
✔ messaging: optional, role definitions and human, never the tester (1.667058ms)
|
||||
✔ credentials: Gitea scopes (0.653212ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.957115ms)
|
||||
✔ credentials: services (0.435809ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (1.056003ms)
|
||||
✔ every key names known layers and a merge rule (1.182158ms)
|
||||
✔ unknown keys and wrong layers refuse (1.013077ms)
|
||||
✔ types (2.732872ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.358004ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.447769ms)
|
||||
✔ merge doesn't change its inputs (0.179837ms)
|
||||
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.262735ms)
|
||||
ℹ tests 112
|
||||
ℹ suites 0
|
||||
ℹ pass 112
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1918.521824
|
||||
@@ -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,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,55 @@
|
||||
Note: switching to '5661bd233bbe3e1f03a64a76ad7edcd9f6e41e89'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to '5661bd233bbe3e1f03a64a76ad7edcd9f6e41e89'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,82 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/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/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 183)
|
||||
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: 78 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: 18802 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,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 (/home/jwoltje/dewey-scratch/r139/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,101 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
OK mock adapter: gate passes on matching mock response (exit 0)
|
||||
OK mock adapter: expect-mismatch recorded (exit 1)
|
||||
OK mismatch reason recorded
|
||||
OK unknown adapter fails closed (exit 1)
|
||||
OK mission task runs via mock adapter (exit 0)
|
||||
OK mission section injected into generated prompt
|
||||
OK retry of mission run succeeds (exit 0)
|
||||
OK retriedFrom lineage recorded
|
||||
OK mission section present after retry (relative path resolved)
|
||||
OK retry of missing run exits 4 (exit 4)
|
||||
OK skill install bundled ms-tools (exit 0)
|
||||
OK installed to skills-available
|
||||
OK double install refuses (exit 1)
|
||||
OK activate enables skill (exit 0)
|
||||
OK enabled dir populated
|
||||
OK uninstall while enabled refuses (exit 1)
|
||||
OK deactivate moves back to available (exit 0)
|
||||
OK uninstall removes from available (exit 0)
|
||||
OK seat with enabled skill launches
|
||||
OK skill path delivered to adapter
|
||||
OK shipped researcher contract resolves (ceiling + network)
|
||||
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
|
||||
OK resolve-role refuses name/filename mismatch (exit 2)
|
||||
OK resolve-role refuses unknown network declaration (exit 2)
|
||||
OK resolve-role refuses duplicate role tool (exit 2)
|
||||
OK resolve-role refuses unsupported tool (exit 2)
|
||||
OK resolve-role refuses missing contract file (exit 4)
|
||||
OK version 1 role prints no contract line
|
||||
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
|
||||
OK resolve-role accepts a minimal version 2 role (exit 0)
|
||||
OK resolve-role refuses an action outside the vocabulary (exit 2)
|
||||
OK resolve-role refuses a gated-only action in a role file (exit 2)
|
||||
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
|
||||
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
|
||||
OK resolve-role refuses a contract that is a symbolic link (exit 2)
|
||||
OK seat launches under role ceiling
|
||||
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
|
||||
OK narrowing recorded loudly
|
||||
OK missing role contract refuses launch (exit 2)
|
||||
OK missing-contract refusal names the role
|
||||
OK empty ceiling intersection -> tool-free seat
|
||||
OK tool-free outcome recorded loudly
|
||||
OK overridden agents dir without seat definition refuses (exit 4)
|
||||
OK seat-resolution refusal names the seat
|
||||
OK policy: mission only -> mission tools
|
||||
OK policy: task only -> task tools
|
||||
OK policy: both -> intersection (task narrowed)
|
||||
OK policy: empty intersection -> tool-free
|
||||
OK invalid mission capabilities rejected (exit 2)
|
||||
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
|
||||
OK task run with user layer present (exit 0)
|
||||
OK user context dispatched into generated prompt
|
||||
OK workspace+tools task runs via mock (exit 0)
|
||||
OK workspace path + tools delivered to adapter
|
||||
OK persistent workspace created on host
|
||||
OK plain task still runs (no workspace/tools) (exit 0)
|
||||
OK workspace var present but empty when absent
|
||||
OK tools empty when absent
|
||||
OK unknown tool exits 2 (exit 2)
|
||||
OK workspace traversal exits 2 (exit 2)
|
||||
OK live hello task succeeds with exact marker
|
||||
OK result.json written in run dir
|
||||
OK result.json contents are correct
|
||||
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
|
||||
OK each run gets a distinct run dir (no clobber)
|
||||
OK list shows both runs
|
||||
OK fork base: teach succeeds (exit 0)
|
||||
OK fork child recalls ancestor context (exit 0)
|
||||
OK forked child recalled ancestor code word
|
||||
OK ancestor session untouched by fork
|
||||
OK child session dir has its own branch file
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
OK user recall run succeeds
|
||||
OK recalled user name (response: Jason)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 98 passed, 0 failed
|
||||
@@ -0,0 +1,5 @@
|
||||
m1-no-spec-check packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T18:31:23Z end=2026-10-16T18:31:38Z
|
||||
m2-no-hard-wait-check packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T18:31:38Z end=2026-10-16T18:31:51Z
|
||||
m3-new-action-not-hard-wait packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T18:31:51Z end=2026-10-16T18:32:04Z
|
||||
m4-hard-wait-routed packages/business/src/vocabulary.mjs suite=business rc=1 KILLED restored=True start=2026-10-16T18:32:04Z end=2026-10-16T18:32:06Z
|
||||
m5-validator-refusal packages/business/src/role.mjs suite=business rc=1 KILLED restored=True start=2026-10-16T18:32:06Z end=2026-10-16T18:32:08Z
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
# Row 139 mutants (slot sage-0518). Each mutates one file, runs the owning suite in its own
|
||||
# process group (killpg on timeout), restores the file and checks its hash before the next.
|
||||
import hashlib, os, signal, subprocess, sys, time
|
||||
|
||||
W = "/home/jwoltje/dewey-scratch/r139/wt"
|
||||
O = "/home/jwoltje/dewey-scratch/r139/out/mutants"
|
||||
os.makedirs(O, exist_ok=True)
|
||||
ENV = dict(os.environ, TMPDIR="/home/jwoltje/dewey-scratch/tmp", COMPOSE_PROJECT_NAME="dewey-r139")
|
||||
SUITES = {"bus": ["bash", "-c", "cd packages/bus && npm test"],
|
||||
"business": ["bash", "-c", "cd packages/business && npm test"]}
|
||||
MUTANTS = [
|
||||
("m1-no-spec-check", "packages/bus/src/broker.mjs",
|
||||
"(cls === 'gated' && spec !== null && !GATED.has(a.action))", "(cls === 'gated' && !GATED.has(a.action))", "bus"),
|
||||
("m2-no-hard-wait-check", "packages/bus/src/broker.mjs",
|
||||
"(cls === 'gated' && spec !== null && !GATED.has(a.action))", "(cls === 'gated' && spec !== null)", "bus"),
|
||||
("m3-new-action-not-hard-wait", "packages/bus/src/broker.mjs",
|
||||
"const ROUTED = new Set([\n", "const ROUTED = new Set([\n 'account.create',\n", "bus"),
|
||||
("m4-hard-wait-routed", "packages/business/src/vocabulary.mjs",
|
||||
"export const ROUTED = Object.freeze([\n", "export const ROUTED = Object.freeze([\n \"spend\",\n", "business"),
|
||||
("m5-validator-refusal", "packages/business/src/role.mjs",
|
||||
"if (GATED_ONLY.includes(action)) refuse(", "if (false && GATED_ONLY.includes(action)) refuse(", "business"),
|
||||
]
|
||||
sha = lambda p: hashlib.sha256(open(p, "rb").read()).hexdigest()
|
||||
summary = open(os.path.join(O, "summary.txt"), "w")
|
||||
for name, rel, old, new, suite in MUTANTS:
|
||||
path = os.path.join(W, rel)
|
||||
orig = open(path, "rb").read()
|
||||
h0 = sha(path)
|
||||
text = orig.decode()
|
||||
if text.count(old) != 1:
|
||||
line = f"{name} SKIP anchor count {text.count(old)}"
|
||||
print(line); summary.write(line + "\n"); summary.flush(); sys.exit(3)
|
||||
open(path, "w").write(text.replace(old, new))
|
||||
assert sha(path) != h0
|
||||
start = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
with open(os.path.join(O, f"{name}.txt"), "w") as out:
|
||||
p = subprocess.Popen(SUITES[suite], cwd=W, env=ENV, stdout=out, stderr=subprocess.STDOUT, start_new_session=True)
|
||||
try:
|
||||
rc = p.wait(timeout=600)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(p.pid, signal.SIGKILL); p.wait(); rc = "timeout"
|
||||
open(path, "wb").write(orig)
|
||||
restored = sha(path) == h0
|
||||
end = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
verdict = "KILLED" if rc not in (0, "timeout") else ("TIMEOUT" if rc == "timeout" else "SURVIVED")
|
||||
line = f"{name} {rel} suite={suite} rc={rc} {verdict} restored={restored} start={start} end={end}"
|
||||
print(line); summary.write(line + "\n"); summary.flush()
|
||||
if not restored:
|
||||
sys.exit(4)
|
||||
@@ -0,0 +1,561 @@
|
||||
npm notice run test
|
||||
npm notice run node --test tests/*.test.mjs
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (187.979573ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (250.003513ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (379.306477ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (197.134471ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (233.288492ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (218.968439ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (242.747582ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (242.656328ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (1087.728853ms)
|
||||
✔ agents: stranded is per role and per business (389.888348ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (149.982698ms)
|
||||
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (359.128532ms)
|
||||
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (213.17523ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (305.385864ms)
|
||||
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (285.470316ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (282.032239ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (410.881641ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (226.242414ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (285.577332ms)
|
||||
✖ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (276.311729ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (222.646139ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (217.032618ms)
|
||||
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (325.297846ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (203.250439ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (226.816188ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (130.141629ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (239.451653ms)
|
||||
✖ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (300.745369ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (208.663163ms)
|
||||
✖ revocation permanently bars the old run from reclaiming first, including after broker restart (235.877466ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (177.49322ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (261.487303ms)
|
||||
✖ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (221.123239ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.629091ms)
|
||||
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (286.51485ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (376.855894ms)
|
||||
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (315.645328ms)
|
||||
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (282.389298ms)
|
||||
✖ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (294.778571ms)
|
||||
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (359.17504ms)
|
||||
✔ REQ-COS-1: the chief of staff cannot end its own run or the assistant's through role.end (315.915045ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (248.758725ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (396.76465ms)
|
||||
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (372.368335ms)
|
||||
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (443.270185ms)
|
||||
✔ REQ-ROLE-3, REQ-LIMIT-1: a chief-of-staff change is validated, written and logged like a human's; an invalid one is refused (634.986751ms)
|
||||
✔ REQ-ROLE-2, REQ-COS-1: the 3c→3d migration lets the chief of staff end a held role with a reason; neither is still refused (2731.791486ms)
|
||||
✔ comment cursors isolate businesses, move only forward and remain append-only (157.074188ms)
|
||||
✔ only trusted sync writes validated cursors; events and cursor share rollback (129.364417ms)
|
||||
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (921.994142ms)
|
||||
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1656.441477ms)
|
||||
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (927.175538ms)
|
||||
✔ migration refuses mismatched source metadata without changing schema or evidence (956.583424ms)
|
||||
✔ offline command migrates only the supplied fixture root and reports usage errors (2670.905327ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (117.05724ms)
|
||||
✔ credentials refuses a role session and any argument (210.285793ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (239.234307ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (243.549598ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.547202ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (16.875163ms)
|
||||
✔ expiry refuses use and env references never become client data (0.987034ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.834952ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.408702ms)
|
||||
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.712827ms)
|
||||
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (573.081941ms)
|
||||
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (546.976966ms)
|
||||
✔ chief-executive-officer: another C-suite role isn't on the lists either (481.643232ms)
|
||||
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (523.396143ms)
|
||||
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (511.342094ms)
|
||||
✔ chief-financial-officer: another C-suite role isn't on the lists either (518.7901ms)
|
||||
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (532.140244ms)
|
||||
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (538.318988ms)
|
||||
✔ chief-information-officer: another C-suite role isn't on the lists either (1023.446239ms)
|
||||
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (1672.162917ms)
|
||||
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (566.190642ms)
|
||||
✔ chief-information-security-officer: another C-suite role isn't on the lists either (447.91515ms)
|
||||
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (566.441962ms)
|
||||
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (486.013911ms)
|
||||
✔ chief-marketing-officer: another C-suite role isn't on the lists either (401.813492ms)
|
||||
✔ a human resolve sends the raising role one message from the human, and its request is the answer (269.791553ms)
|
||||
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (279.668194ms)
|
||||
✔ a human withdraw sends one message with the note (279.751241ms)
|
||||
✔ a human seen before the close leaves the request on the close's answer, not the seen (318.065418ms)
|
||||
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (324.683167ms)
|
||||
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (351.390022ms)
|
||||
✔ a raising role nobody holds at the close gets the message on the next claim (332.645138ms)
|
||||
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (275.980035ms)
|
||||
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (365.155698ms)
|
||||
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (317.986305ms)
|
||||
✔ a route the raiser hears from is allowed, checked from the route to the raiser (416.913303ms)
|
||||
✔ a refused supersede withdraws nothing (349.568301ms)
|
||||
✖ the assistant's within-role and gated raises still work (343.772815ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (264.83617ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (341.355751ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (365.813778ms)
|
||||
✔ recent stops at RECENT rows (979.228023ms)
|
||||
✔ every row is what trail already gives the same session for that id (301.144837ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (506.439107ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (456.208585ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (174.168961ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (159.685964ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (184.562049ms)
|
||||
✔ endLaunch leaves a claim another run took alone (192.792038ms)
|
||||
✔ refuse records action.refused against the caller with the code only (180.228604ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (223.314069ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (383.022954ms)
|
||||
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.161747ms)
|
||||
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (140.265647ms)
|
||||
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (820.390344ms)
|
||||
✖ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (230.400088ms)
|
||||
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (305.342949ms)
|
||||
✖ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (367.670214ms)
|
||||
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (302.641604ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.362564ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.408004ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.532803ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.240907ms)
|
||||
✔ a message to a stopped role sends launch.by one notice, and the result says so (258.493483ms)
|
||||
✔ a message to a role outside launch.instances says so in its result and in the notice (173.098783ms)
|
||||
✔ a message to a running role sends no notice and its result is unchanged (159.898419ms)
|
||||
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (286.970608ms)
|
||||
✔ a run launched but not yet claimed counts as no holder (153.62933ms)
|
||||
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (313.273224ms)
|
||||
✔ a business with no launch block tells nobody and says there is no launcher (138.606381ms)
|
||||
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (407.808997ms)
|
||||
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (240.785342ms)
|
||||
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (298.560363ms)
|
||||
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (457.18615ms)
|
||||
✖ within-role sends cite an open gated launch decision without spending it or naming it in grants (204.910763ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (214.333281ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (314.37551ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (3004.727286ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (293.034715ms)
|
||||
✔ views messages passes only its paging arguments through (0.292194ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (204.518473ms)
|
||||
✔ the assistant sends to the user and to the chief of staff only (373.702951ms)
|
||||
✔ an assistant send to any other role, or to the tester, is refused and logged (444.283744ms)
|
||||
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (379.777055ms)
|
||||
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (410.094974ms)
|
||||
✔ a sender-side list alone refuses a send to a role that has no list (422.077088ms)
|
||||
✔ a target-side list alone refuses a send from a role that has no list (378.15478ms)
|
||||
✔ the sender list is checked before the target list (389.851609ms)
|
||||
✔ roles without the field behave exactly as before (522.092854ms)
|
||||
✔ a business with no role definitions at all is unlimited (1123.363262ms)
|
||||
✔ a malformed messaging field refuses at boot (599.227879ms)
|
||||
✖ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (831.315414ms)
|
||||
✖ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (441.257481ms)
|
||||
✔ review requests and verdicts between roles without the field are unchanged (357.274451ms)
|
||||
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (232.513864ms)
|
||||
✔ runtime: the switch changes the organization file, the receipt carries the actor, and a bad shape, an unknown module and a stale revision are refused with nothing written (550.598557ms)
|
||||
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (427.551415ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (246.234445ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (66.597794ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (226.100074ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (232.132126ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (242.682086ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (66.457801ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (243.860667ms)
|
||||
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (186.823442ms)
|
||||
✔ D2: spec.read refuses without content if its role is released during the read (177.97009ms)
|
||||
✔ D3: spec.read secret check refuses flagged content without content (163.203327ms)
|
||||
✔ D3: spec.read secret check refuses flagged subject without content (161.487905ms)
|
||||
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.304767ms)
|
||||
✔ without a trusted project adapter all three async paths refuse project-not-configured (232.009629ms)
|
||||
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (239.876655ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (269.374347ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (236.544707ms)
|
||||
✔ a message with no reply gives an empty list (204.998597ms)
|
||||
✔ replies come oldest first and stop at REPLIES (895.427908ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (383.767138ms)
|
||||
✔ strict arguments; a role session gets human-required (452.435983ms)
|
||||
✔ views passes replies through as one call with the id (0.483178ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (254.604094ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (197.706036ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (217.674955ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (270.161035ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (316.946075ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (348.70753ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (454.883738ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (267.705036ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (348.708778ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (1229.249132ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (1125.844239ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1720.18795ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (292.839604ms)
|
||||
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (279.290723ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (185.653169ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (358.109852ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (321.650147ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (273.25285ms)
|
||||
✔ class drift gated to within-role refuses before consumption (318.484176ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (979.054528ms)
|
||||
✔ class drift within-role to gated refuses before consumption (1177.380931ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (278.984877ms)
|
||||
✖ message.send consumes approval and prevents a later send or authorize (236.690492ms)
|
||||
✖ role.revoke consumes approval and prevents a later revoke or authorize (250.764187ms)
|
||||
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1609.582169ms)
|
||||
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (329.769413ms)
|
||||
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (179.197301ms)
|
||||
✔ two real socket writes to one repository yield one commit and one project-busy refusal (269.127449ms)
|
||||
✔ client disconnect during await tolerates late reply (217.683626ms)
|
||||
✔ client disconnect during await tolerates late refusal (184.591481ms)
|
||||
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (417.806631ms)
|
||||
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (337.278147ms)
|
||||
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (323.492821ms)
|
||||
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (308.833779ms)
|
||||
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (256.848569ms)
|
||||
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (272.717214ms)
|
||||
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (410.558239ms)
|
||||
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (184.606945ms)
|
||||
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (544.290978ms)
|
||||
✔ REQ-COS-1: the sender is judged by definition: an instance `lead` of the pm definition can ask, an instance `pm` of the coder definition can't (2020.429682ms)
|
||||
✔ REQ-COS-1: the launcher is judged by definition: an instance `boss` of the chief-of-staff definition receives, a `chief` of the coder definition gives no-chief-of-staff (535.239498ms)
|
||||
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (272.749896ms)
|
||||
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (773.817625ms)
|
||||
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (489.552281ms)
|
||||
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (269.913411ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (198.179306ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (156.225685ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (176.09302ms)
|
||||
✔ a tombstone is not open (152.463685ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (228.380239ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (523.78999ms)
|
||||
✔ a human session reads the same state, with the same cap (605.586855ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (971.371138ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (129.701536ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (191.316249ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (201.574306ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (301.365789ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (688.85823ms)
|
||||
✔ async transactions refuse before invoking their function (385.707745ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (574.356049ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (1046.990941ms)
|
||||
✔ each filter, alone and together, and total counts every match (1062.39483ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (364.728058ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (279.912541ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (1052.395499ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (445.883938ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (1932.304887ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (166.710313ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (252.845317ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (123.574883ms)
|
||||
✔ a bad entry refuses the whole record (755.210208ms)
|
||||
✔ taskView reads the projection for one business (593.919053ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (859.753903ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (459.803584ms)
|
||||
✔ without an adapter the server refuses every task verb (251.271116ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (239.005323ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (407.20966ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (2578.139466ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (320.406607ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (659.221624ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (295.353561ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (284.226912ms)
|
||||
✔ the tester reads the human views a reader reads (201.455935ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.260133ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.370595ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.254634ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (122.328879ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1055.922702ms)
|
||||
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (2828.27177ms)
|
||||
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (989.540477ms)
|
||||
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (921.441195ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (1627.132146ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1759.858535ms)
|
||||
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (435.49787ms)
|
||||
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (4276.825466ms)
|
||||
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (115.513289ms)
|
||||
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (163.796028ms)
|
||||
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (168.699679ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (164.949356ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (155.352812ms)
|
||||
✖ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (143.436125ms)
|
||||
✔ REQ-USER-2: the human verbs refuse another owner's thread (165.797872ms)
|
||||
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (125.381076ms)
|
||||
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (181.587597ms)
|
||||
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (530.503046ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (916.77671ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (240.561181ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (155.322315ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.524891ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (194.86412ms)
|
||||
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (224.275158ms)
|
||||
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (190.303442ms)
|
||||
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (257.263786ms)
|
||||
✔ a broker with no web service refuses a granted role (158.431598ms)
|
||||
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (215.31721ms)
|
||||
✔ a session that is not the role holder is refused (164.011553ms)
|
||||
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (512.346079ms)
|
||||
✔ web.search with no search source configured is a refused result, not an error (172.325922ms)
|
||||
✔ a search source that fails with its own refusal, or that throws, is handled (205.692043ms)
|
||||
✔ a slow search source ends in a refusal at the deadline (202.232761ms)
|
||||
✔ a secret in the arguments is refused before anything is trailed or fetched (145.815819ms)
|
||||
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (231.454879ms)
|
||||
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.257028ms)
|
||||
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (430.324555ms)
|
||||
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (181.78835ms)
|
||||
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (484.501624ms)
|
||||
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (183.270186ms)
|
||||
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (304.832084ms)
|
||||
✔ one query limit: over SEARCH_MAX_QUERY_CHARS or empty is refused with the same reason before any backend, and the tool schema carries the same number (179.988413ms)
|
||||
ℹ tests 301
|
||||
ℹ suites 0
|
||||
ℹ pass 287
|
||||
ℹ fail 14
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 14719.50106
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/broker.test.mjs:96:1
|
||||
✖ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (276.311729ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:45:45)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:111:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/broker.test.mjs:274:1
|
||||
✖ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (300.745369ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:45:45)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:290:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/broker.test.mjs:322:1
|
||||
✖ revocation permanently bars the old run from reclaiming first, including after broker restart (235.877466ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:45:45)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:329:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/broker.test.mjs:393:1
|
||||
✖ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (221.123239ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'pm' !== 'human'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/broker.test.mjs:407:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'pm',
|
||||
expected: 'human',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/chief-of-staff.test.mjs:139:1
|
||||
✖ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (294.778571ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/chief-of-staff.test.mjs:73:44)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/chief-of-staff.test.mjs:152:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/decision-route.test.mjs:139:1
|
||||
✖ the assistant's within-role and gated raises still work (343.772815ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'chief' !== 'human'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/decision-route.test.mjs:150:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'chief',
|
||||
expected: 'human',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/hard-waits.test.mjs:51:1
|
||||
✖ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (230.400088ms)
|
||||
AssertionError [ERR_ASSERTION]: git.merge.protected
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
class: 'gated',
|
||||
+ route_to: 'reviewer'
|
||||
- route_to: 'human'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:57:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { class: 'gated', route_to: 'reviewer' },
|
||||
expected: { class: 'gated', route_to: 'human' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/hard-waits.test.mjs:73:1
|
||||
✖ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (367.670214ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
class: 'gated',
|
||||
+ route_to: 'cto'
|
||||
- route_to: 'human'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:87:10)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { class: 'gated', route_to: 'cto' },
|
||||
expected: { class: 'gated', route_to: 'human' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/message-citation.test.mjs:53:1
|
||||
✖ within-role sends cite an open gated launch decision without spending it or naming it in grants (204.910763ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at Object.call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/message-citation.test.mjs:27:44)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/message-citation.test.mjs:69:5)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/messaging.test.mjs:263:1
|
||||
✖ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (831.315414ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/messaging.test.mjs:76:44)
|
||||
at approved (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/messaging.test.mjs:259:3)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/messaging.test.mjs:266:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/messaging.test.mjs:280:1
|
||||
✖ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (441.257481ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/messaging.test.mjs:76:44)
|
||||
at approved (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/messaging.test.mjs:259:3)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/messaging.test.mjs:284:20)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
|
||||
test at tests/single-use.test.mjs:208:1
|
||||
✖ message.send consumes approval and prevents a later send or authorize (236.690492ms)
|
||||
Error: not-resolver
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1298:38)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/single-use.test.mjs:25:42)
|
||||
at Object.approve (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/single-use.test.mjs:56:7)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/single-use.test.mjs:210:11)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'not-resolver'
|
||||
}
|
||||
|
||||
test at tests/single-use.test.mjs:231:1
|
||||
✖ role.revoke consumes approval and prevents a later revoke or authorize (250.764187ms)
|
||||
Error: not-resolver
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1298:38)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/single-use.test.mjs:25:42)
|
||||
at Object.approve (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/single-use.test.mjs:56:7)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/single-use.test.mjs:235:15)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'not-resolver'
|
||||
}
|
||||
|
||||
test at tests/threads.test.mjs:396:1
|
||||
✖ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (143.436125ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/threads.test.mjs:50:44)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/threads.test.mjs:409:3)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
npm notice run test
|
||||
npm notice run node --test tests/*.test.mjs
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (149.297683ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (241.967961ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (360.729023ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (209.426712ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (231.000325ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (214.53029ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (234.182767ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (265.841704ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (1524.741113ms)
|
||||
✔ agents: stranded is per role and per business (465.409609ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (161.713361ms)
|
||||
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (316.87925ms)
|
||||
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (216.226761ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (311.715502ms)
|
||||
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (282.397311ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (289.396034ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (400.995162ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (191.001262ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (299.487792ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (318.458196ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (212.788511ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (214.926583ms)
|
||||
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (311.91606ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (212.821863ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (371.731264ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (475.478015ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (233.59709ms)
|
||||
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (355.020365ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (203.03035ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (332.842701ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (186.887411ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (313.063332ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (249.020047ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.637482ms)
|
||||
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (288.767629ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (367.488661ms)
|
||||
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (284.771223ms)
|
||||
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (268.305456ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (290.69439ms)
|
||||
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (349.405262ms)
|
||||
✔ REQ-COS-1: the chief of staff cannot end its own run or the assistant's through role.end (785.803186ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (278.999687ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (391.11676ms)
|
||||
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (438.629599ms)
|
||||
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (478.913095ms)
|
||||
✔ REQ-ROLE-3, REQ-LIMIT-1: a chief-of-staff change is validated, written and logged like a human's; an invalid one is refused (898.627249ms)
|
||||
✔ REQ-ROLE-2, REQ-COS-1: the 3c→3d migration lets the chief of staff end a held role with a reason; neither is still refused (1123.402545ms)
|
||||
✔ comment cursors isolate businesses, move only forward and remain append-only (140.243798ms)
|
||||
✔ only trusted sync writes validated cursors; events and cursor share rollback (146.345756ms)
|
||||
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (966.465967ms)
|
||||
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (2265.823674ms)
|
||||
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (1122.081855ms)
|
||||
✔ migration refuses mismatched source metadata without changing schema or evidence (1146.36836ms)
|
||||
✔ offline command migrates only the supplied fixture root and reports usage errors (1129.17588ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (100.4841ms)
|
||||
✔ credentials refuses a role session and any argument (195.109866ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (260.480191ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (238.351962ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (5.31123ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (3.143797ms)
|
||||
✔ expiry refuses use and env references never become client data (1.221262ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (2.053421ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.447134ms)
|
||||
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (2.884139ms)
|
||||
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (538.624735ms)
|
||||
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (535.408231ms)
|
||||
✔ chief-executive-officer: another C-suite role isn't on the lists either (451.249656ms)
|
||||
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (910.392079ms)
|
||||
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (658.82195ms)
|
||||
✔ chief-financial-officer: another C-suite role isn't on the lists either (553.943753ms)
|
||||
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (586.265857ms)
|
||||
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (763.631716ms)
|
||||
✔ chief-information-officer: another C-suite role isn't on the lists either (524.421366ms)
|
||||
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (559.006254ms)
|
||||
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (618.391398ms)
|
||||
✔ chief-information-security-officer: another C-suite role isn't on the lists either (440.021522ms)
|
||||
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (789.356334ms)
|
||||
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (554.390363ms)
|
||||
✔ chief-marketing-officer: another C-suite role isn't on the lists either (384.194231ms)
|
||||
✔ a human resolve sends the raising role one message from the human, and its request is the answer (277.056558ms)
|
||||
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (277.442534ms)
|
||||
✔ a human withdraw sends one message with the note (277.790383ms)
|
||||
✔ a human seen before the close leaves the request on the close's answer, not the seen (330.08642ms)
|
||||
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (310.710218ms)
|
||||
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (340.964676ms)
|
||||
✔ a raising role nobody holds at the close gets the message on the next claim (809.344869ms)
|
||||
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (299.484169ms)
|
||||
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (332.265233ms)
|
||||
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (328.072816ms)
|
||||
✔ a route the raiser hears from is allowed, checked from the route to the raiser (398.655356ms)
|
||||
✔ a refused supersede withdraws nothing (332.776776ms)
|
||||
✔ the assistant's within-role and gated raises still work (344.185846ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (252.718484ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (313.113422ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (346.730457ms)
|
||||
✔ recent stops at RECENT rows (940.151028ms)
|
||||
✔ every row is what trail already gives the same session for that id (797.283222ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (472.68597ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (559.33004ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (148.489814ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (162.997555ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (160.116279ms)
|
||||
✔ endLaunch leaves a claim another run took alone (203.167895ms)
|
||||
✔ refuse records action.refused against the caller with the code only (157.747544ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (228.666899ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (350.951692ms)
|
||||
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.31189ms)
|
||||
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (128.871618ms)
|
||||
✖ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (245.374032ms)
|
||||
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (489.818545ms)
|
||||
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (288.406608ms)
|
||||
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (326.136954ms)
|
||||
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (311.850877ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.361123ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.613942ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.728889ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.376243ms)
|
||||
✔ a message to a stopped role sends launch.by one notice, and the result says so (242.906122ms)
|
||||
✔ a message to a role outside launch.instances says so in its result and in the notice (168.686485ms)
|
||||
✔ a message to a running role sends no notice and its result is unchanged (153.458461ms)
|
||||
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (276.484758ms)
|
||||
✔ a run launched but not yet claimed counts as no holder (155.927668ms)
|
||||
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (299.384416ms)
|
||||
✔ a business with no launch block tells nobody and says there is no launcher (121.181607ms)
|
||||
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (475.30502ms)
|
||||
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (237.013904ms)
|
||||
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (320.951298ms)
|
||||
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (479.574377ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (232.640354ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (213.071973ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (296.86153ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (3649.234674ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (523.505249ms)
|
||||
✔ views messages passes only its paging arguments through (0.36625ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (228.589475ms)
|
||||
✔ the assistant sends to the user and to the chief of staff only (360.069802ms)
|
||||
✔ an assistant send to any other role, or to the tester, is refused and logged (480.870349ms)
|
||||
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (851.991752ms)
|
||||
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (385.823285ms)
|
||||
✔ a sender-side list alone refuses a send to a role that has no list (451.77821ms)
|
||||
✔ a target-side list alone refuses a send from a role that has no list (425.339538ms)
|
||||
✔ the sender list is checked before the target list (407.401926ms)
|
||||
✔ roles without the field behave exactly as before (732.337112ms)
|
||||
✔ a business with no role definitions at all is unlimited (406.464816ms)
|
||||
✔ a malformed messaging field refuses at boot (127.013606ms)
|
||||
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (443.504998ms)
|
||||
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (508.972509ms)
|
||||
✔ review requests and verdicts between roles without the field are unchanged (361.074577ms)
|
||||
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (223.469663ms)
|
||||
✔ runtime: the switch changes the organization file, the receipt carries the actor, and a bad shape, an unknown module and a stale revision are refused with nothing written (680.990482ms)
|
||||
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (815.108676ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (229.911203ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (76.233229ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (430.103654ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (466.038972ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (243.126081ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (91.337103ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (258.401573ms)
|
||||
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (337.111688ms)
|
||||
✔ D2: spec.read refuses without content if its role is released during the read (527.865692ms)
|
||||
✔ D3: spec.read secret check refuses flagged content without content (153.97774ms)
|
||||
✔ D3: spec.read secret check refuses flagged subject without content (170.674907ms)
|
||||
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.409372ms)
|
||||
✔ without a trusted project adapter all three async paths refuse project-not-configured (630.843397ms)
|
||||
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (297.42951ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (722.918291ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (234.44344ms)
|
||||
✔ a message with no reply gives an empty list (192.504264ms)
|
||||
✔ replies come oldest first and stop at REPLIES (992.746588ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (398.965148ms)
|
||||
✔ strict arguments; a role session gets human-required (654.418963ms)
|
||||
✔ views passes replies through as one call with the id (0.629531ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (660.381655ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (180.556626ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (207.85988ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (290.569023ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (360.342791ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (372.826991ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (482.266257ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (341.786105ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (527.257866ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (452.668736ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (310.30674ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (2096.26554ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (331.049211ms)
|
||||
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (329.09763ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (206.672647ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (430.470152ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (242.166284ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (505.265317ms)
|
||||
✔ class drift gated to within-role refuses before consumption (293.003743ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (311.537772ms)
|
||||
✔ class drift within-role to gated refuses before consumption (264.765102ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (307.992856ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (319.032559ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (316.699857ms)
|
||||
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1982.438247ms)
|
||||
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (349.395822ms)
|
||||
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (273.546695ms)
|
||||
✔ two real socket writes to one repository yield one commit and one project-busy refusal (301.314901ms)
|
||||
✔ client disconnect during await tolerates late reply (263.228038ms)
|
||||
✔ client disconnect during await tolerates late refusal (221.956386ms)
|
||||
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (613.893653ms)
|
||||
✖ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (328.594377ms)
|
||||
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (383.734378ms)
|
||||
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (307.089846ms)
|
||||
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (360.673252ms)
|
||||
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (303.039966ms)
|
||||
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (419.277072ms)
|
||||
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (312.837119ms)
|
||||
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (616.496839ms)
|
||||
✔ REQ-COS-1: the sender is judged by definition: an instance `lead` of the pm definition can ask, an instance `pm` of the coder definition can't (472.921904ms)
|
||||
✔ REQ-COS-1: the launcher is judged by definition: an instance `boss` of the chief-of-staff definition receives, a `chief` of the coder definition gives no-chief-of-staff (530.404355ms)
|
||||
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (297.487393ms)
|
||||
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (728.63433ms)
|
||||
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (791.611112ms)
|
||||
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (256.394396ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (168.481337ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (207.039477ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (229.208653ms)
|
||||
✔ a tombstone is not open (334.228681ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (191.135236ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (177.154796ms)
|
||||
✔ a human session reads the same state, with the same cap (159.539332ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (199.651294ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (368.991082ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (231.045667ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (217.615082ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (212.682237ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (128.707429ms)
|
||||
✔ async transactions refuse before invoking their function (134.527459ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (509.271579ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (294.231188ms)
|
||||
✔ each filter, alone and together, and total counts every match (373.424925ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (398.661589ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (331.242303ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (1319.879238ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (392.255715ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (409.518142ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (138.609638ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (219.705621ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (133.120975ms)
|
||||
✔ a bad entry refuses the whole record (140.412225ms)
|
||||
✔ taskView reads the projection for one business (171.304176ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (298.6573ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (490.726474ms)
|
||||
✔ without an adapter the server refuses every task verb (266.179577ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (229.644292ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (402.363364ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (1009.604164ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (331.219791ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (637.270049ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (316.797624ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (585.164465ms)
|
||||
✔ the tester reads the human views a reader reads (213.678535ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.369404ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.416741ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.264215ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (125.801878ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (944.772774ms)
|
||||
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1248.150894ms)
|
||||
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (981.5624ms)
|
||||
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (1205.30459ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (386.334164ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1417.829574ms)
|
||||
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (430.245351ms)
|
||||
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (4069.066315ms)
|
||||
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (101.721662ms)
|
||||
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (165.026754ms)
|
||||
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (169.419425ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (179.679387ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (167.272754ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (139.044412ms)
|
||||
✔ REQ-USER-2: the human verbs refuse another owner's thread (151.801478ms)
|
||||
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (105.350136ms)
|
||||
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (154.176884ms)
|
||||
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (443.066388ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (174.19527ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (252.777474ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (164.533452ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.663997ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (172.022008ms)
|
||||
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (184.954951ms)
|
||||
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (257.198251ms)
|
||||
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (247.370957ms)
|
||||
✔ a broker with no web service refuses a granted role (165.124097ms)
|
||||
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (235.60862ms)
|
||||
✔ a session that is not the role holder is refused (139.400978ms)
|
||||
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (701.623619ms)
|
||||
✔ web.search with no search source configured is a refused result, not an error (227.782176ms)
|
||||
✔ a search source that fails with its own refusal, or that throws, is handled (200.227667ms)
|
||||
✔ a slow search source ends in a refusal at the deadline (220.21246ms)
|
||||
✔ a secret in the arguments is refused before anything is trailed or fetched (144.965353ms)
|
||||
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (228.52774ms)
|
||||
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.290542ms)
|
||||
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (217.246078ms)
|
||||
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (180.787434ms)
|
||||
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (467.778953ms)
|
||||
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (182.020933ms)
|
||||
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (200.31363ms)
|
||||
✔ one query limit: over SEARCH_MAX_QUERY_CHARS or empty is refused with the same reason before any backend, and the tool schema carries the same number (172.708056ms)
|
||||
ℹ tests 301
|
||||
ℹ suites 0
|
||||
ℹ pass 299
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 13524.703091
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/hard-waits.test.mjs:38:1
|
||||
✖ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (245.374032ms)
|
||||
AssertionError [ERR_ASSERTION]: spend {"project":"stack","spec_path":"spec/north-star.md","spec_commit":"2534d9fc1cab817a1ec8b917822e5a7ccf395e2f"}
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
class: 'gated',
|
||||
+ route_to: 'reviewer'
|
||||
- route_to: 'human'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:43:14)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { class: 'gated', route_to: 'reviewer' },
|
||||
expected: { class: 'gated', route_to: 'human' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/spec.test.mjs:9:1
|
||||
✖ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (328.594377ms)
|
||||
Error: agent-required
|
||||
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
|
||||
at #agent (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:295:18)
|
||||
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1297:22)
|
||||
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:950:38
|
||||
at Store.transaction (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/store.mjs:242:22)
|
||||
at Broker.request (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:944:26)
|
||||
at Object.call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/spec-fixtures.mjs:33:49)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/spec.test.mjs:14:5)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7) {
|
||||
code: 'agent-required'
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
npm notice run test
|
||||
npm notice run node --test tests/*.test.mjs
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (158.852387ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (226.686792ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (377.390339ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (184.915529ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (235.956323ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (216.647682ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (254.452651ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (242.955828ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (1454.169315ms)
|
||||
✔ agents: stranded is per role and per business (392.240164ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (143.604081ms)
|
||||
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (305.852219ms)
|
||||
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (203.392529ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (311.771906ms)
|
||||
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (268.690421ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (276.742374ms)
|
||||
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (425.657707ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (172.755095ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (296.484752ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (306.932388ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (207.575236ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (194.822197ms)
|
||||
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (321.890465ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (198.131901ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (235.784926ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (374.745678ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (270.179649ms)
|
||||
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (522.823998ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (179.29752ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (254.370239ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (169.205766ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (283.130176ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (221.139267ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.305942ms)
|
||||
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (285.083102ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (379.285031ms)
|
||||
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (277.177173ms)
|
||||
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (268.940041ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (312.129222ms)
|
||||
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (327.941359ms)
|
||||
✔ REQ-COS-1: the chief of staff cannot end its own run or the assistant's through role.end (573.146813ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (475.409903ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (355.008617ms)
|
||||
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (330.165916ms)
|
||||
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (451.17413ms)
|
||||
✔ REQ-ROLE-3, REQ-LIMIT-1: a chief-of-staff change is validated, written and logged like a human's; an invalid one is refused (586.894912ms)
|
||||
✔ REQ-ROLE-2, REQ-COS-1: the 3c→3d migration lets the chief of staff end a held role with a reason; neither is still refused (1091.018826ms)
|
||||
✔ comment cursors isolate businesses, move only forward and remain append-only (155.736672ms)
|
||||
✔ only trusted sync writes validated cursors; events and cursor share rollback (135.283852ms)
|
||||
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (957.022942ms)
|
||||
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (2222.8056ms)
|
||||
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (947.534489ms)
|
||||
✔ migration refuses mismatched source metadata without changing schema or evidence (885.40912ms)
|
||||
✔ offline command migrates only the supplied fixture root and reports usage errors (1025.081194ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (120.90232ms)
|
||||
✔ credentials refuses a role session and any argument (191.173835ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (245.613564ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (233.853724ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (3.4114ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.159921ms)
|
||||
✔ expiry refuses use and env references never become client data (0.790453ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.429596ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.318902ms)
|
||||
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.81265ms)
|
||||
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (532.61353ms)
|
||||
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (510.815703ms)
|
||||
✔ chief-executive-officer: another C-suite role isn't on the lists either (477.27573ms)
|
||||
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (798.364598ms)
|
||||
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (737.477022ms)
|
||||
✔ chief-financial-officer: another C-suite role isn't on the lists either (439.614816ms)
|
||||
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (554.771035ms)
|
||||
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (507.063169ms)
|
||||
✔ chief-information-officer: another C-suite role isn't on the lists either (483.356378ms)
|
||||
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (574.040574ms)
|
||||
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (526.489936ms)
|
||||
✔ chief-information-security-officer: another C-suite role isn't on the lists either (421.762912ms)
|
||||
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (467.85934ms)
|
||||
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (561.054281ms)
|
||||
✔ chief-marketing-officer: another C-suite role isn't on the lists either (412.689952ms)
|
||||
✔ a human resolve sends the raising role one message from the human, and its request is the answer (265.535251ms)
|
||||
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (270.427862ms)
|
||||
✔ a human withdraw sends one message with the note (280.096578ms)
|
||||
✔ a human seen before the close leaves the request on the close's answer, not the seen (316.870391ms)
|
||||
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (344.320685ms)
|
||||
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (340.160759ms)
|
||||
✔ a raising role nobody holds at the close gets the message on the next claim (575.170681ms)
|
||||
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (502.545442ms)
|
||||
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (324.925177ms)
|
||||
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (341.95446ms)
|
||||
✔ a route the raiser hears from is allowed, checked from the route to the raiser (395.031373ms)
|
||||
✔ a refused supersede withdraws nothing (340.520266ms)
|
||||
✔ the assistant's within-role and gated raises still work (349.935154ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (246.561376ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (330.874597ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (340.988613ms)
|
||||
✔ recent stops at RECENT rows (939.162422ms)
|
||||
✔ every row is what trail already gives the same session for that id (567.259315ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (668.82381ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (442.063569ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (162.083668ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (158.0268ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (165.316851ms)
|
||||
✔ endLaunch leaves a claim another run took alone (204.011496ms)
|
||||
✔ refuse records action.refused against the caller with the code only (164.228191ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (223.03234ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (359.433797ms)
|
||||
✖ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (2.016996ms)
|
||||
✖ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (126.247913ms)
|
||||
✖ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (441.501859ms)
|
||||
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (486.116786ms)
|
||||
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (257.740737ms)
|
||||
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (351.828921ms)
|
||||
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (315.704732ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.486996ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.426453ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.558863ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.234265ms)
|
||||
✔ a message to a stopped role sends launch.by one notice, and the result says so (234.016948ms)
|
||||
✔ a message to a role outside launch.instances says so in its result and in the notice (190.149383ms)
|
||||
✔ a message to a running role sends no notice and its result is unchanged (186.801665ms)
|
||||
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (273.968431ms)
|
||||
✔ a run launched but not yet claimed counts as no holder (159.164011ms)
|
||||
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (307.616054ms)
|
||||
✔ a business with no launch block tells nobody and says there is no launcher (148.857752ms)
|
||||
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (406.742208ms)
|
||||
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (240.819236ms)
|
||||
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (336.483809ms)
|
||||
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (470.110164ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (233.240186ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (219.292645ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (284.356801ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (3391.192795ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (294.293278ms)
|
||||
✔ views messages passes only its paging arguments through (0.240008ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (213.528107ms)
|
||||
✔ the assistant sends to the user and to the chief of staff only (352.237833ms)
|
||||
✔ an assistant send to any other role, or to the tester, is refused and logged (422.152287ms)
|
||||
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (718.863515ms)
|
||||
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (512.09799ms)
|
||||
✔ a sender-side list alone refuses a send to a role that has no list (364.899728ms)
|
||||
✔ a target-side list alone refuses a send from a role that has no list (379.973535ms)
|
||||
✔ the sender list is checked before the target list (353.790159ms)
|
||||
✔ roles without the field behave exactly as before (483.788527ms)
|
||||
✔ a business with no role definitions at all is unlimited (401.426218ms)
|
||||
✔ a malformed messaging field refuses at boot (140.86924ms)
|
||||
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (414.186441ms)
|
||||
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (436.825252ms)
|
||||
✔ review requests and verdicts between roles without the field are unchanged (343.639019ms)
|
||||
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (229.555558ms)
|
||||
✔ runtime: the switch changes the organization file, the receipt carries the actor, and a bad shape, an unknown module and a stale revision are refused with nothing written (594.750543ms)
|
||||
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (930.744482ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (224.039706ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (64.864734ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (473.949451ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (253.843076ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (402.471597ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (64.099186ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (228.391828ms)
|
||||
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (193.969407ms)
|
||||
✔ D2: spec.read refuses without content if its role is released during the read (419.979233ms)
|
||||
✔ D3: spec.read secret check refuses flagged content without content (220.631625ms)
|
||||
✔ D3: spec.read secret check refuses flagged subject without content (318.79446ms)
|
||||
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.395315ms)
|
||||
✔ without a trusted project adapter all three async paths refuse project-not-configured (472.845275ms)
|
||||
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (280.889445ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (502.159732ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (441.135235ms)
|
||||
✔ a message with no reply gives an empty list (184.319278ms)
|
||||
✔ replies come oldest first and stop at REPLIES (845.573571ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (355.230918ms)
|
||||
✔ strict arguments; a role session gets human-required (436.439168ms)
|
||||
✔ views passes replies through as one call with the id (0.225867ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (460.919772ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (379.4194ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (199.136884ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (413.18469ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (277.144349ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (323.63546ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (441.687722ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (271.022032ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (350.365205ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (459.602689ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (294.252208ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1653.30888ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (290.144653ms)
|
||||
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (238.819126ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (188.896183ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (387.504275ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (257.497592ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (276.659873ms)
|
||||
✔ class drift gated to within-role refuses before consumption (276.445513ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (317.929383ms)
|
||||
✔ class drift within-role to gated refuses before consumption (257.454828ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (277.50328ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (258.66475ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (299.629265ms)
|
||||
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1547.81461ms)
|
||||
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (281.104222ms)
|
||||
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (208.929412ms)
|
||||
✔ two real socket writes to one repository yield one commit and one project-busy refusal (241.327702ms)
|
||||
✔ client disconnect during await tolerates late reply (220.235433ms)
|
||||
✔ client disconnect during await tolerates late refusal (189.946431ms)
|
||||
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (376.051206ms)
|
||||
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (318.543863ms)
|
||||
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (355.586574ms)
|
||||
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (261.488714ms)
|
||||
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (274.678193ms)
|
||||
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (284.96336ms)
|
||||
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (358.877119ms)
|
||||
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (184.525118ms)
|
||||
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (493.790282ms)
|
||||
✔ REQ-COS-1: the sender is judged by definition: an instance `lead` of the pm definition can ask, an instance `pm` of the coder definition can't (511.687054ms)
|
||||
✔ REQ-COS-1: the launcher is judged by definition: an instance `boss` of the chief-of-staff definition receives, a `chief` of the coder definition gives no-chief-of-staff (469.58599ms)
|
||||
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (278.766118ms)
|
||||
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (649.484798ms)
|
||||
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (485.743499ms)
|
||||
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (333.765179ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (170.807676ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (177.368539ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (163.020666ms)
|
||||
✔ a tombstone is not open (175.010008ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (174.664887ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (187.848831ms)
|
||||
✔ a human session reads the same state, with the same cap (200.829496ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (175.034143ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (139.317322ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (192.025632ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (221.976971ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (224.734432ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (154.507332ms)
|
||||
✔ async transactions refuse before invoking their function (102.453599ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (333.629681ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (320.851663ms)
|
||||
✔ each filter, alone and together, and total counts every match (354.707729ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (356.669335ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (271.486455ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (974.159804ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (381.574532ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (416.534763ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (136.082805ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (214.164074ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (141.81423ms)
|
||||
✔ a bad entry refuses the whole record (154.474418ms)
|
||||
✔ taskView reads the projection for one business (145.849149ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (299.295443ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (442.097155ms)
|
||||
✔ without an adapter the server refuses every task verb (255.235676ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (225.653937ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (327.665504ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (957.57404ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (294.551995ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (619.49851ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (222.792205ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (303.217405ms)
|
||||
✔ the tester reads the human views a reader reads (181.942016ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.302711ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.388049ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.216945ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (130.841614ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1052.763317ms)
|
||||
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1196.245306ms)
|
||||
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (851.565101ms)
|
||||
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (1007.160865ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (411.611684ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1301.013027ms)
|
||||
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (343.897366ms)
|
||||
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3898.281215ms)
|
||||
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (96.425108ms)
|
||||
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (161.868489ms)
|
||||
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (166.491822ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (219.202009ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (162.820051ms)
|
||||
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (143.121593ms)
|
||||
✔ REQ-USER-2: the human verbs refuse another owner's thread (155.175452ms)
|
||||
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (114.27003ms)
|
||||
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (142.81511ms)
|
||||
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (492.301988ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (185.66172ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (229.316502ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (107.816407ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (14.805629ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (155.009016ms)
|
||||
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (177.356502ms)
|
||||
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (193.875947ms)
|
||||
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (217.2835ms)
|
||||
✔ a broker with no web service refuses a granted role (179.593398ms)
|
||||
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (218.838833ms)
|
||||
✔ a session that is not the role holder is refused (144.684523ms)
|
||||
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (398.711363ms)
|
||||
✔ web.search with no search source configured is a refused result, not an error (185.176281ms)
|
||||
✔ a search source that fails with its own refusal, or that throws, is handled (182.212276ms)
|
||||
✔ a slow search source ends in a refusal at the deadline (234.187688ms)
|
||||
✔ a secret in the arguments is refused before anything is trailed or fetched (170.369853ms)
|
||||
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (267.805071ms)
|
||||
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.246066ms)
|
||||
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (220.912075ms)
|
||||
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (160.726113ms)
|
||||
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (529.714395ms)
|
||||
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (174.848023ms)
|
||||
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (196.423347ms)
|
||||
✔ one query limit: over SEARCH_MAX_QUERY_CHARS or empty is refused with the same reason before any backend, and the tool schema carries the same number (160.360532ms)
|
||||
ℹ tests 301
|
||||
ℹ suites 0
|
||||
ℹ pass 298
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 12754.018715
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/hard-waits.test.mjs:18:1
|
||||
✖ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (2.016996ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
- 'account.create',
|
||||
'credential.mint',
|
||||
'data.delete',
|
||||
'deploy',
|
||||
'git.push.protected',
|
||||
'message.external',
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:19:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ 'credential.mint', 'data.delete', 'deploy', 'git.push.protected', 'message.external', 'network.expose', 'spend' ],
|
||||
expected: [ 'account.create', 'credential.mint', 'data.delete', 'deploy', 'git.push.protected', 'message.external', 'network.expose', 'spend' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/hard-waits.test.mjs:25:1
|
||||
✖ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (126.247913ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: account.create
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:32:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /invalid-authority/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/hard-waits.test.mjs:38:1
|
||||
✖ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (441.501859ms)
|
||||
AssertionError [ERR_ASSERTION]: account.create {"project":"stack","spec_path":"spec/north-star.md","spec_commit":"b195639afce66c41ba968195053ed3b93128217f"}
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
class: 'gated',
|
||||
+ route_to: 'reviewer'
|
||||
- route_to: 'human'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:43:14)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { class: 'gated', route_to: 'reviewer' },
|
||||
expected: { class: 'gated', route_to: 'human' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
npm notice run @mosaic/[email protected] test
|
||||
npm notice run node --test tests/
|
||||
✔ the role file loads and resolves through loadBusiness (9.062744ms)
|
||||
✔ the shipped file is exactly the approved list (1.220115ms)
|
||||
✔ the assistant's authority is message.send and record.write within role (3.528783ms)
|
||||
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (1.018233ms)
|
||||
✔ a business grant through limits does not widen the role (2.958656ms)
|
||||
✔ a launch block whose by is the assistant is refused (2.969563ms)
|
||||
✔ the instance takes no credential references and no tracker (6.174677ms)
|
||||
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (12.002031ms)
|
||||
✔ an arbiter without messaging lists is accepted beside the limited assistant (2.579381ms)
|
||||
✔ the contract passes work to the chief of staff and directs no one (0.841014ms)
|
||||
✔ REQ-HARN-2: the assistant holds the web grant, and no other role file does (1.895017ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.896338ms)
|
||||
✔ the fixture business validates and comes back frozen (5.897514ms)
|
||||
✔ two instances may share a definition (2.53481ms)
|
||||
✔ top-level refusals (6.626479ms)
|
||||
✔ arbiters and projects (9.016263ms)
|
||||
✔ role instances (8.853577ms)
|
||||
✔ Vikunja bots (9.576833ms)
|
||||
✔ a role without Vikunja takes no tracker block (2.785722ms)
|
||||
✔ credential references match the definition's services (2.490167ms)
|
||||
✔ launch (9.028897ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (4.168391ms)
|
||||
✔ loadBusiness: file checks (1.79882ms)
|
||||
✔ loadBusiness: not a regular file (37.248606ms)
|
||||
✔ loading writes nothing (0.961502ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.653336ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.742068ms)
|
||||
✔ the role file loads and resolves through loadBusiness (10.452038ms)
|
||||
✔ the shipped file is exactly the approved list (0.422976ms)
|
||||
✔ the resolved record matches the approved list, so a widened file fails (2.371605ms)
|
||||
✔ the chief of staff holds role.launch, message.send, role.end, organization.change and record.write; every other action is gated (2.096599ms)
|
||||
✔ the pm does not hold role.launch (2.200569ms)
|
||||
✔ a business grant of a gated action through limits does not widen the role (4.340535ms)
|
||||
✔ a limit that drops role.launch leaves the chief of staff with no launch block (3.742764ms)
|
||||
✔ a launch block whose by is the pm instance is refused (5.146751ms)
|
||||
✔ the contract names no duty to talk to the human and the pm contract no launch (0.516464ms)
|
||||
✔ the instance takes no credential references and no tracker (7.684913ms)
|
||||
✔ usage errors exit 4 (294.987124ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (64.824159ms)
|
||||
✔ validate: project files (312.011694ms)
|
||||
✔ validate: missing files and a broken system config (230.890158ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (64.898216ms)
|
||||
✔ validate: a token file inside the repository is refused (70.074223ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (190.198252ms)
|
||||
✔ resolve: prints one instance's record (248.805936ms)
|
||||
✔ resolve: refusals (364.514549ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.416888ms)
|
||||
✔ check: a good file has no problems (1.939594ms)
|
||||
✔ check never opens the file: a write-only token passes (0.591717ms)
|
||||
✔ check: file problems (1.710453ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.387239ms)
|
||||
✔ check: dates and environment references (0.632429ms)
|
||||
✔ each definition loads, with a contract file, and is exactly the approved list (5.39202ms)
|
||||
✔ an organization file that declares them passes, and each resolves to least privilege (9.275874ms)
|
||||
✔ the chief of staff launches each one; no other role does (4.244221ms)
|
||||
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (3.970014ms)
|
||||
✔ limits can narrow a C-suite role but never widen it (2.352134ms)
|
||||
✔ any unknown key, tool, action or version in a role file is refused (1.44205ms)
|
||||
✖ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (2.796907ms)
|
||||
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.532953ms)
|
||||
✖ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (1.635226ms)
|
||||
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (23.481945ms)
|
||||
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.87826ms)
|
||||
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (256.19063ms)
|
||||
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (227.427664ms)
|
||||
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (3.106165ms)
|
||||
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (177.790293ms)
|
||||
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.824865ms)
|
||||
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (98.964263ms)
|
||||
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (18.940918ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (300.144169ms)
|
||||
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (337.371504ms)
|
||||
✔ vikunja:19/309 an unknown kind (set or removed), a bad module, an empty patch and null on the wrong shape are refused, nothing written (1.458307ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (35.667459ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.414928ms)
|
||||
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (81.056355ms)
|
||||
✔ optional project repo is preserved and must be a normalized absolute path (12.593611ms)
|
||||
✔ loaded business project repo cannot be nested in any declared code checkout (4.045789ms)
|
||||
✔ path and load (5.186383ms)
|
||||
✔ refusals (2.667348ms)
|
||||
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (3.840837ms)
|
||||
✔ systemVars flattens the validated config (2.894789ms)
|
||||
✔ precedence: system, business, project, project role, agent (7.208158ms)
|
||||
✔ limits narrow the definition and never widen it (2.088124ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (5.057357ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (2.019956ms)
|
||||
✔ limits.authority narrows cross-role actions too (0.999348ms)
|
||||
✔ classify (1.079597ms)
|
||||
✔ the record carries what the broker and launcher need (4.246036ms)
|
||||
✔ digest: key order doesn't matter, any value change does (7.632276ms)
|
||||
✔ refusals (1.846114ms)
|
||||
✔ the four shipped version 2 roles load (3.347824ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.865691ms)
|
||||
✔ shipped authority follows the note's table (0.561561ms)
|
||||
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.938867ms)
|
||||
✔ version 1 files keep loading with no authority (0.841133ms)
|
||||
✔ the conductor policy isn't a role (0.186393ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.346816ms)
|
||||
✔ version 2 refusals (1.261006ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (1.61519ms)
|
||||
✔ messaging: optional, role definitions and human, never the tester (2.002197ms)
|
||||
✔ credentials: Gitea scopes (0.813145ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.331941ms)
|
||||
✔ credentials: services (0.665441ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.873809ms)
|
||||
✔ every key names known layers and a merge rule (0.80677ms)
|
||||
✔ unknown keys and wrong layers refuse (0.645251ms)
|
||||
✔ types (1.919054ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.250611ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.30658ms)
|
||||
✔ merge doesn't change its inputs (0.131023ms)
|
||||
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.179521ms)
|
||||
ℹ tests 112
|
||||
ℹ suites 0
|
||||
ℹ pass 110
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1918.822029
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/hard-waits.test.mjs:22:1
|
||||
✖ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (2.796907ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
... Skipped lines
|
||||
|
||||
[
|
||||
'account.create',
|
||||
'credential.mint',
|
||||
'data.delete',
|
||||
'deploy',
|
||||
...
|
||||
'network.expose',
|
||||
- 'spend'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/business/tests/hard-waits.test.mjs:23:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ 'account.create', 'credential.mint', 'data.delete', 'deploy', 'git.push.protected', 'message.external', 'network.expose' ],
|
||||
expected: [ 'account.create', 'credential.mint', 'data.delete', 'deploy', 'git.push.protected', 'message.external', 'network.expose', 'spend' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/hard-waits.test.mjs:33:1
|
||||
✖ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (1.635226ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: spend
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/business/tests/hard-waits.test.mjs:35:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /always gated/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
npm notice run @mosaic/[email protected] test
|
||||
npm notice run node --test tests/
|
||||
✔ the role file loads and resolves through loadBusiness (8.925882ms)
|
||||
✔ the shipped file is exactly the approved list (1.254028ms)
|
||||
✔ the assistant's authority is message.send and record.write within role (3.450438ms)
|
||||
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (1.112897ms)
|
||||
✔ a business grant through limits does not widen the role (2.318812ms)
|
||||
✔ a launch block whose by is the assistant is refused (2.672539ms)
|
||||
✔ the instance takes no credential references and no tracker (4.509903ms)
|
||||
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (5.246705ms)
|
||||
✔ an arbiter without messaging lists is accepted beside the limited assistant (2.777764ms)
|
||||
✔ the contract passes work to the chief of staff and directs no one (0.855761ms)
|
||||
✔ REQ-HARN-2: the assistant holds the web grant, and no other role file does (1.82242ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (2.462515ms)
|
||||
✔ the fixture business validates and comes back frozen (7.838083ms)
|
||||
✔ two instances may share a definition (2.259436ms)
|
||||
✔ top-level refusals (8.497155ms)
|
||||
✔ arbiters and projects (9.957921ms)
|
||||
✔ role instances (4.447792ms)
|
||||
✔ Vikunja bots (11.938268ms)
|
||||
✔ a role without Vikunja takes no tracker block (2.387215ms)
|
||||
✔ credential references match the definition's services (2.581014ms)
|
||||
✔ launch (8.571043ms)
|
||||
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (4.079076ms)
|
||||
✔ loadBusiness: file checks (2.022094ms)
|
||||
✔ loadBusiness: not a regular file (38.236488ms)
|
||||
✔ loading writes nothing (0.972232ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.646369ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.731812ms)
|
||||
✔ the role file loads and resolves through loadBusiness (9.320344ms)
|
||||
✔ the shipped file is exactly the approved list (0.342777ms)
|
||||
✔ the resolved record matches the approved list, so a widened file fails (3.031044ms)
|
||||
✔ the chief of staff holds role.launch, message.send, role.end, organization.change and record.write; every other action is gated (2.040227ms)
|
||||
✔ the pm does not hold role.launch (2.290278ms)
|
||||
✔ a business grant of a gated action through limits does not widen the role (3.454538ms)
|
||||
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.70263ms)
|
||||
✔ a launch block whose by is the pm instance is refused (2.064263ms)
|
||||
✔ the contract names no duty to talk to the human and the pm contract no launch (0.540211ms)
|
||||
✔ the instance takes no credential references and no tracker (3.085732ms)
|
||||
✔ usage errors exit 4 (290.405929ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (62.767637ms)
|
||||
✔ validate: project files (308.317604ms)
|
||||
✔ validate: missing files and a broken system config (220.783132ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (62.732638ms)
|
||||
✔ validate: a token file inside the repository is refused (60.523541ms)
|
||||
✖ validate: role definitions come from MOSAIC_ROLES_DIR (189.520632ms)
|
||||
✔ resolve: prints one instance's record (242.278689ms)
|
||||
✔ resolve: refusals (364.220227ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.909654ms)
|
||||
✔ check: a good file has no problems (1.178004ms)
|
||||
✔ check never opens the file: a write-only token passes (0.484023ms)
|
||||
✔ check: file problems (2.311802ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (2.06525ms)
|
||||
✔ check: dates and environment references (0.665685ms)
|
||||
✔ each definition loads, with a contract file, and is exactly the approved list (5.09591ms)
|
||||
✔ an organization file that declares them passes, and each resolves to least privilege (9.255545ms)
|
||||
✔ the chief of staff launches each one; no other role does (5.413263ms)
|
||||
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (3.954703ms)
|
||||
✔ limits can narrow a C-suite role but never widen it (2.431789ms)
|
||||
✔ any unknown key, tool, action or version in a role file is refused (1.602781ms)
|
||||
✔ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (1.709051ms)
|
||||
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.403745ms)
|
||||
✖ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (2.140379ms)
|
||||
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (22.738431ms)
|
||||
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.157134ms)
|
||||
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (199.773637ms)
|
||||
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (325.744851ms)
|
||||
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (2.939604ms)
|
||||
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (365.885068ms)
|
||||
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.771528ms)
|
||||
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (114.657955ms)
|
||||
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (18.819982ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (247.036043ms)
|
||||
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (617.521932ms)
|
||||
✔ vikunja:19/309 an unknown kind (set or removed), a bad module, an empty patch and null on the wrong shape are refused, nothing written (1.452871ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (42.711867ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.47741ms)
|
||||
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (83.505595ms)
|
||||
✔ optional project repo is preserved and must be a normalized absolute path (13.692697ms)
|
||||
✔ loaded business project repo cannot be nested in any declared code checkout (5.478447ms)
|
||||
✔ path and load (3.072143ms)
|
||||
✔ refusals (1.702369ms)
|
||||
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (4.077419ms)
|
||||
✔ systemVars flattens the validated config (2.73038ms)
|
||||
✔ precedence: system, business, project, project role, agent (6.615582ms)
|
||||
✔ limits narrow the definition and never widen it (2.046215ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (4.904845ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (1.911268ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.035966ms)
|
||||
✔ classify (1.100887ms)
|
||||
✔ the record carries what the broker and launcher need (1.073082ms)
|
||||
✔ digest: key order doesn't matter, any value change does (9.574909ms)
|
||||
✔ refusals (1.937847ms)
|
||||
✔ the four shipped version 2 roles load (3.460093ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.977342ms)
|
||||
✔ shipped authority follows the note's table (0.564749ms)
|
||||
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.965121ms)
|
||||
✔ version 1 files keep loading with no authority (0.865515ms)
|
||||
✔ the conductor policy isn't a role (0.211661ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.592301ms)
|
||||
✔ version 2 refusals (1.262936ms)
|
||||
✖ authority: closed vocabulary, no gated-only action, no overlap (0.911426ms)
|
||||
✔ messaging: optional, role definitions and human, never the tester (1.736617ms)
|
||||
✔ credentials: Gitea scopes (0.747483ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.349474ms)
|
||||
✔ credentials: services (0.478004ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (1.193669ms)
|
||||
✔ every key names known layers and a merge rule (1.229253ms)
|
||||
✔ unknown keys and wrong layers refuse (0.918914ms)
|
||||
✔ types (2.444938ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.344682ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.32771ms)
|
||||
✔ merge doesn't change its inputs (0.128833ms)
|
||||
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.174259ms)
|
||||
ℹ tests 112
|
||||
ℹ suites 0
|
||||
ℹ pass 109
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1888.892846
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/cli.test.mjs:109:1
|
||||
✖ validate: role definitions come from MOSAIC_ROLES_DIR (189.520632ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
0 !== 2
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/business/tests/cli.test.mjs:122:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 2,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/hard-waits.test.mjs:33:1
|
||||
✖ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (2.140379ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: spend
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/business/tests/hard-waits.test.mjs:35:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
expected: /always gated/,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/role.test.mjs:126:1
|
||||
✖ authority: closed vocabulary, no gated-only action, no overlap (0.911426ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at refuses (file:///home/jwoltje/dewey-scratch/r139/wt/packages/business/tests/role.test.mjs:9:10)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/business/tests/role.test.mjs:129:5)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 139: apply build.patch to a detached worktree at base.txt and check the candidate manifest.
|
||||
# Usage: repro.sh <canonical checkout> <new worktree path>
|
||||
set -euo pipefail
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
REPO=$1 WT=$2
|
||||
git -C "$REPO" worktree add -q --detach "$WT" "$(cat "$HERE/base.txt")"
|
||||
git -C "$WT" apply "$HERE/build.patch"
|
||||
(cd "$WT" && sha256sum -c --quiet "$HERE/candidate-manifest.sha256") && echo DIGESTS_OK
|
||||
Reference in New Issue
Block a user