agents: Dewey row 139 round 2 packet, Jason's ruling Q169 (#1633)

Candidate manifest 7a1fcae1 (patch 7a29d911) on 5661bd233. Only the chief of
staff, or a decision the human resolved through the CLI, ends or revokes the
assistant; a revoke of it is a hard wait. PRD 1.6 text in PRD-1.6.md for Mos.
Gate 14/14 rc=0 and mutants m1-m9 killed under slot sage-0533. SMOKE.md plan,
not run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-16 14:11:43 -05:00
co-authored by Claude Opus 5.5
parent 39f1e9c51d
commit c740f56ee6
32 changed files with 5159 additions and 22 deletions
+169 -8
View File
@@ -1,9 +1,19 @@
# 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.
Brief: docs/plans/2026-10-15_q167-rows-r3.md at b71268b8e (blob 6805904555dc, rev 1565), section
"Decisions that cite the spec go to an agent; only hard waits reach Jason". Round 1 built against
04c572003 (blob c22ce67cd).
Requirements: PRD 1.5 REQ-DEC-2 and REQ-WAIT-1 to REQ-WAIT-3, and PRD 1.6 (this row) for Jason's
ruling Q169.
Calls from sage-0502, 0504, 0512, 0514, 0516, 0528, 0533 and 0539. sage-0523 (Mos's ruling) is
superseded by Q169.
Round 2 follows Jason's ruling Q169 (sage-0528), which replaces Mos's sage-0523 ruling that the
first round 2 build followed. See "Round 2" below. Round 1's gate and mutant outputs stay in gate/
and mutants/. Round 2's are in r2/. Round 1's candidate is build.patch and
candidate-manifest.sha256 at commit 62f354746. The ones at the packet root are round 2's.
PRD-1.6.md holds the 1.6 text for Sage to send to Mos.
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
@@ -38,9 +48,64 @@ sha256. repro.sh applies the patch to a fresh detached worktree and checks the d
PRD 1.5 requires no new grant from this row. merge-gate's cross-role git.merge.protected grant
is row 149's.
## Round 2: Jason's ruling Q169
Q169, verbatim: "The only agent that should be able to remove the assistant role is the chief of
staff. The COS and the user should have the ability to (re)configure the assistant role and seat
name. The user via WebUI or CLI, the COS via the proper tooling."
- **The hard wait (kept from the first round 2).** `#hardWait(s, action, target)` (broker.mjs) is
true for a GATED action, or for a role.revoke whose target's definition is in END_PROTECTED.
`#classification` returns `gated` when it holds, and decision.raise's `arbitrated` check uses it
in place of round 1's `GATED.has`. A raise to revoke the assistant goes to the human at every
class, cited or not, so no arbiter resolves one. A role granted role.revoke cross-role or
within-role can't route it to the arbiter or approve its own.
- **The protection, new.** `#protectedEnd(s, target, r)` refuses `role-end-refused` for an
END_PROTECTED target unless one of two things holds:
- the caller's definition is `chief-of-staff` (STAFF_TO);
- the cited resolution was recorded `via 'cli'`. decision.resolve records `cli` only for a human
session that passed `#human(s)`.
It runs in two places:
- on the role.end reason path, after the existing refusal of the caller's own run, which stays;
- in #checkAuthority for role.revoke, after the decision-not-approved check.
So a revoke citing a decision an arbiter resolved is refused. One raised before the hard wait
existed, or by a build that routed it to the arbiter, is refused the same way.
- **Execution is row 155's.** The assistant's runs hold thread claims (row 121). The chief's end
finds no role claim and refuses `not-held`. A cited revoke refuses `decision-mismatch`, because
decision.raise records `holderRun` null and #checkAuthority reads undefined. Per sage-0528,
139 doesn't fix that. The tests assert only that the protection lets these through.
- **Every other cited revoke** routes by REQ-DEC-2 and executes, unchanged from round 1.
- **Configuration** (the second half of Q169) is row 155's. PRD 1.6 records what it covers.
- **PRD 1.6.** It adds:
- the status line;
- a 1.6 source block quoting Q169;
- text in REQ-ROLE-2, REQ-ROLE-4 and REQ-WAIT-1;
- a Notes item.
The Mos note from the first round 2 is gone. The text is recorded as Jason's ruling Q169.
PRD-1.6.md has the text and four readings for Mos to confirm.
- **Filbert's note 1** (a within-role role.revoke grant lets a role approve its own revoke of a
non-assistant role) stays a PRD question (sage-0523).
- business `classify` (resolve.mjs) isn't changed. It takes no target, and its only caller is the
WebUI's per-action authority view (packages/webui/src/reads.mjs:154), which routes nothing.
### Files outside the brief's list (accepted, sage-0539)
Each follows from Q169 and PRD 1.6:
1. **packages/tasks/requirements.json and packages/tasks/tests/requirements.test.mjs.** The test
checks the PRD status line's version against requirements.json, so both go to 1.6.
2. **packages/harness/tests/runner.test.mjs and packages/harness/README.md.** The chief's end_run
of the assistant now gets past the protection and refuses `not-held`. Before, it refused
`role-end-refused`.
3. **packages/bus/tests/chief-of-staff.test.mjs.** It changes for the same reason: the chief ending
itself stays `role-end-refused`, and ending the assistant is `not-held`.
The end_run description in packages/harness/src/tools.mjs still says the assistant can't be ended.
It's stale, and row 155 owns the fix (sage-0539).
## Tests
- packages/bus/tests/hard-waits.test.mjs (new, 7 tests):
- packages/bus/tests/hard-waits.test.mjs (new, 12 tests: 7 from round 1, 5 for Q169):
- 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
@@ -52,6 +117,29 @@ sha256. repro.sh applies the patch to a fresh detached worktree and checks the d
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.
- Q169 (sage-0528's four, then one kept from the first round 2). The first and third assert the
protection only, because execution is row 155's:
1. The chief's role.end of the assistant refuses `not-held`, not `role-end-refused`. The chief
ending itself, and `lead` (granted role.end) ending the assistant, refuse
`role-end-refused`. No action.allowed event is written.
2. A cited revoke of the assistant goes `gated → human`. For a decision an arbiter resolved,
the test uses a cited revoke of pm, which went to reviewer, who resolved it yes. A second
Broker on the same store then changes pm's definition to `assistant`. The coder's revoke
citing that decision refuses `role-end-refused`. The decisions table is append-only, so
the test changes the business, not the row.
3. The human resolves a cited revoke of the assistant; the arbiter's resolve first gets
`human-required`. The coder's revoke citing it gets past the protection and refuses
`decision-mismatch` (row 155).
4. A cited revoke of pm goes `gated → reviewer`, the reviewer resolves it, and the revoke
returns `{ revoked: true }`.
5. A role granted role.revoke cross-role, and one granted it within-role, still send a revoke
of the assistant to the human. The within-role one doesn't resolve itself, and its revoke
gets `decision-not-approved`, not `decision-mismatch`. Their revokes of pm go cross-role to
the arbiter and within-role to themselves.
- packages/bus/tests/chief-of-staff.test.mjs: REQ-COS-1 now expects `not-held` for the chief's end
of the assistant. Its own run stays `role-end-refused`.
- packages/harness/tests/runner.test.mjs: end_run's second refusal is `not-held`.
- packages/tasks/tests/requirements.test.mjs: the version literal is 1.6.
- 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.
@@ -91,10 +179,23 @@ sha256. repro.sh applies the patch to a fresh detached worktree and checks the d
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.
change here. The PRD's notes answer part of it: the host refuses a non-fast-forward push to the
working repository (packages/seat/src/working.mjs).
## Follow-ups (not row 139's files)
- **A role.revoke or chief's end of the assistant can't execute.** Row 155 (#1649, Darkwing,
after 139 and 141) fixes it; sage-0528 keeps it out of 139. Since row 121 the assistant's run
holds a thread claim, not a role claim. decision.raise records `holderRun` as null, and
#checkAuthority compares that with `#holder(...)?.holder_run`, which is undefined, so a cited
revoke refuses `decision-mismatch`, then `not-held`. The chief's end refuses `not-held`.
- **The end_run tool text is stale.** packages/harness/src/tools.mjs:156 (comment) and :158
(description) say the assistant can't be ended. Since Q169 the chief can, and the broker refuses
`not-held` until row 155. Row 155 owns the text (sage-0539).
- **The harness raise_decision tool has no `domain` parameter** (packages/harness/src/tools.mjs:195-208),
and decision.raise defaults to `delivery`. No live agent reaches the technical arbiter, which
REQ-ROLE-5's merge-gate raise needs. Row 149 (Dewey) takes it; Sage adds it to row 149's brief at
its next amendment (sage-0528).
- **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.
@@ -108,9 +209,69 @@ sha256. repro.sh applies the patch to a fresh detached worktree and checks the d
- **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
## Gate, round 2 (Q169)
Run under Sage's slot (sage-0518), from the worktree at the candidate, with
Run under Sage's slot (sage-0533, sage-0539; the slot was Dewey's from 19:06Z), from the worktree at the
round 2 candidate, with COMPOSE_PROJECT_NAME=dewey-r139. The script is r2/gate.sh, and the outputs
are in r2/gate/. r2/gate/summary.txt has each run's exit code and times.
- 2026-10-16T19:06:10Z to 19:08:58Z: all 14 runs rc=0, with no flakes.
- scripts/test-land-work.sh: 86 passed, 0 failed, including `OK arbiter-cross: exit 2,
not-human` and `OK arbiter-cited: exit 2, not-human`.
- bus 306/306, business 112/112, harness 105/105 and tasks 97/97, 0 skipped.
- test-task selftest 98 passed, 0 failed.
- auth, conductor, config, discord, extension-package, foundation, queue and release: rc=0.
- dewey-r139_default was removed with 0 containers on it.
## Checks, round 2
- **Before the gate, as built** (19:02:52Z to 19:03:20Z), each file run singly with 0 skipped:
- bus: agents, broker, chief-of-staff (13), approval-binding, decisions, hard-waits (12),
project-runtime, requirements, single-use, task-page, tester, replies, message-citation,
threads, transport, spec-socket, decision-route, decision-close, messaging and spec.
- **Two runs outside the slot**, before sage-0533 gave it: the tasks suite (97 pass) and
packages/harness/tests/runner.test.mjs (24 pass). Sage was told in dewey-0534. Neither used
Docker. Nothing collided: Darkwing had cleared, and Researcher was idle (sage-0539).
- **The busy check**, by /proc cwd links only:
- At 19:04:08Z, no node, mutant or gate processes from another seat. There were three bash
shells, with no node, in ~/filbert-scratch/r137-r1.
- In a watch from 19:04:25Z to 19:05:42Z (30 scans), none.
- No dewey-r139 containers or networks.
- **An overlap in the slot (sage-0542).** From about 19:06:14Z to 19:06:50Z, Filbert ran single
test files in ~/filbert-scratch/r137-r1: seat working.test and cli git-socket.test, with
probes. It wasn't a suite, a gate or Docker. Sage's single-file allowance in sage-0537 and its
late correction caused it, and Filbert has stopped. My watch had ended at 19:05:42Z, so it
didn't see the overlap. These gate steps ran in that window:
- land-work, to 19:06:22Z;
- bus, 19:06:22Z to 19:06:36Z;
- business, 19:06:36Z to 19:06:38Z;
- harness, 19:06:38Z to 19:06:51Z.
All of them passed, and the gate runs no seat or cli tests, so there was nothing to rerun.
## Mutants, round 2 (Q169)
r2/mutants/summary.txt; the script is r2/mutants.py. They ran from 2026-10-16T19:09:11Z to
19:10:46Z, under the same rules as round 1:
- each mutant ran in its own process group, killed as a group on a 600-second timeout;
- the file was restored and its hash checked after each one.
m1 to m5 are round 1's, with m1 and m2 retargeted at `#hardWait`. All nine were killed:
| Mutant | Change | Suite | Tests that failed |
| --- | --- | --- | --- |
| m1 | drop `spec !== null` | bus (13 fail) | both REQ-DEC-2 citation tests, and older tests that expect an uncited gated raise at the human |
| m2 | drop `!this.#hardWait(...)`: a cited hard wait goes to the arbiter | bus (5 fail) | REQ-DEC-2 hard waits; three Q169 tests; 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 |
| m6 | `#hardWait` drops the assistant check | bus (3 fail) | Q169 tests 2, 3 and 5 |
| m7 | `#protectedEnd` drops the chief exemption | bus (2 fail) | Q169 test 1; REQ-COS-1 |
| m8 | `#protectedEnd` drops the human (`via 'cli'`) exemption | bus (2 fail) | Q169 test 3; REQ-ROLE-2 REQ-THREAD-1's approved-revoke test |
| m9 | #checkAuthority no longer calls `#protectedEnd` for role.revoke | bus (1 fail) | Q169 test 2 |
## Gate, round 1
Run under Sage's slot (sage-0518), from the worktree at the round 1 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.
@@ -125,7 +286,7 @@ COMPOSE_PROJECT_NAME=dewey-r139. gate-summary.txt has each run's exit code and t
- harness bundle and tools, since both read ACTIONS.
- dewey-r139_default was removed with 0 containers on it.
## Mutants
## Mutants, round 1
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:
+83
View File
@@ -0,0 +1,83 @@
# PRD version 1.6 text, row 139 (#1633), for Mos
Written by Dewey for Sage to send to Mos (sage-0528, sage-0539). It records Jason's ruling Q169.
The candidate (manifest 7a1fcae1…, base 5661bd233) carries this text in docs/prd/mosaic-stack.md.
Row 139 doesn't move to in-review until Mos answers on it.
Q169, verbatim: "The only agent that should be able to remove the assistant role is the chief of
staff. The COS and the user should have the ability to (re)configure the assistant role and seat
name. The user via WebUI or CLI, the COS via the proper tooling."
## The changed text
**Status line.** "approved, version 1.5" becomes "approved, version 1.6". This sentence goes in
after the Q167 sentence and before "It is never edited in place":
> Version 1.6 records Jason's ruling Q169 of 2026-10-16, on who removes and who configures the
> assistant.
**New source block**, before the version 1.5 source:
> - Version 1.6 source, Jason's ruling Q169 (DECISIONS, brain repo, 2026-10-16): "The only agent
> that should be able to remove the assistant role is the chief of staff. The COS and the user
> should have the ability to (re)configure the assistant role and seat name. The user via WebUI
> or CLI, the COS via the proper tooling."
**REQ-ROLE-2.** These sentences are added after "and the revoke cites it.":
> The assistant is the exception here too (version 1.6, Q169): the chief of staff is the only
> agent that removes it. Anyone else's revoke of the assistant must cite a decision the human
> resolved through the CLI. A decision an arbiter resolved doesn't count, and that revoke is
> refused.
**REQ-ROLE-4.** These sentences are added after "Jason's may be named Mos.":
> Since version 1.6 (Q169), the user and the chief of staff can configure the assistant's role
> entry and its seat name: the user through the WebUI or the CLI, the chief of staff through its
> broker tools, which log each change. No other role can, and a change can't remove the
> assistant's protection under REQ-ROLE-2.
**REQ-WAIT-1.** These sentences are added after "REQ-ROLE-2 still requires a resolved decision for
a revoke.":
> Since version 1.6 (Q169), a `role.revoke` of the assistant is a hard wait: its raise goes to the
> human, cited or not, and no arbiter resolves it. Every other cited revoke routes by REQ-DEC-2.
**Notes, a new "Version 1.6" item:**
> - Version 1.6:
> - The 1.5 text of REQ-ROLE-2 ended at "and the revoke cites it." The 1.5 text of REQ-ROLE-4
> ended at "Jason's may be named Mos." The 1.5 text of REQ-WAIT-1 ended at "REQ-ROLE-2 still
> requires a resolved decision for a revoke."
> - Removing the assistant, in this version, means ending or revoking its runs. Deleting the
> instance from the organization file is not covered. The protected definitions are
> `END_PROTECTED` in `packages/bus/src/broker.mjs`, which holds `assistant`.
> - Configuring the assistant covers its organization entry's `vars` and `tracker`, and its
> `definition` among those the broker treats as the assistant. `holder` and `credentials` are
> not covered.
> - Where each part is built: the refusal and the hard wait are row 139 (#1633). Configuring the
> assistant and its seat name, and making the chief of staff's end or a human-approved revoke
> stop an assistant run, are row 155 (#1649), using row 141's (#1635) instance names. Until
> row 155 lands, the assistant's runs hold thread claims that the end and revoke paths don't
> read, so neither can stop one. The human stops one with `mosaic stop <run>`.
## Readings Mos should confirm
Q169 doesn't settle these points. The text above takes one reading of each.
1. **A human-approved revoke by another role.** Q169 says the chief of staff is the only agent that
removes the assistant. The text still lets another role revoke it when the revoke cites a
decision the human resolved. That is sage-0528's item 1: the human decides, and the agent only
carries the decision out. The stricter reading refuses every revoke of the assistant by a role
other than the chief, approved or not.
2. **"Through the CLI".** The broker records `via 'cli'` for every human session's resolve
(broker.mjs, decision.resolve). The WebUI has no resolve path in packages/webui/src today, so
the CLI is the only way a human resolves now. If the WebUI gains one through the human
session, it records `cli` too. "Through the CLI" therefore means "by the human", not "not the
WebUI".
3. **Scope of "configure".** The text covers vars, tracker, the definition among assistant
definitions, and the seat name. It leaves out holder and credentials. Q169 says "(re)configure
the assistant role and seat name" and doesn't list fields. Row 155 builds against this list.
4. **Scope of "remove".** The text means ending or revoking the assistant's runs. It doesn't cover
deleting the instance from the organization file. REQ-ROLE-3 already limits writes to that file
to the user and the chief.
+101
View File
@@ -0,0 +1,101 @@
# Row 139 live smoke plan (#1633): not run
Written by Dewey for Sage (sage-0521). It runs only on Mos's go, and only from the tester path:
`scripts/mosaic ... --tester`, with every `talk` text starting `[mos test]`. Dewey doesn't run
it. Target: business "stack", project `stack`, spec/north-star.md at Jason's init commit
ae0b17c, which Mos read read-only.
## Preconditions (each checked by Mos, read-only)
1. Row 139 has landed and the live tree contains it. Since row 85, the bus runs from
copies/<sha>. Before a copy and re-pin that includes 139, the live broker still has the old
GATED, and step A would route to Jason. That would be a precondition failure, not a 139
failure. Check with ps that the bus and role sessions run from the new copy.
2. `bus.tester` is `true` for the business.
3. The live business's `arbiters.delivery` names a running instance other than the raiser in
step A. The example file has delivery `pm`, technical `cto` and launch.by `chief`. The plan
below assumes chief raises and pm arbitrates. If the live file differs, swap the names.
4. That arbiter's session was launched after the re-pin. Only an instance the business names as
an arbiter gets the `resolve_decision` tool (packages/harness/src/tools.mjs:209), and the
tool list is fixed at launch.
5. The `read_spec` tool, called with project `stack`, returns a file commit with
`confirmed: true`. Step A asks chief to report it, so Mos sees the commit before anything is
raised.
## Steps
**A. A cited gated raise goes to the delivery arbiter** (REQ-DEC-2):
```
scripts/mosaic talk chief "[mos test] row 139 smoke, routing only. Call read_spec for project stack and tell me the file commit and whether it is confirmed. Then raise a non-blocking decision: action policy.change, project stack, spec_path spec/north-star.md, spec_commit that file commit, options yes/no, recommendation no, question '[mos test] row 139 routing smoke; no policy changes; resolve no'. Reply with the decision id." --tester --follow
scripts/mosaic trail <decision-id> --tester --json
```
- Pass: the trail shows `gated policy.change → pm`, or the live delivery arbiter, with the cited
spec_path and spec_commit. The decision is not in Jason's `mosaic inbox`.
- Then: `scripts/mosaic talk pm "[mos test] resolve decision <id> with no, note 'row 139 smoke'" --tester --follow`.
- Pass: the trail shows the decision resolved `no` by pm, via broker.
**B. A cited hard wait still goes to Jason** (REQ-DEC-2, REQ-WAIT-1):
```
scripts/mosaic talk chief "[mos test] row 139 smoke. Raise a non-blocking decision: action network.expose, the same project, spec_path and spec_commit as before, options yes/no, recommendation no, question '[mos test] row 139 hard-wait smoke; nothing is exposed; Jason resolves no'. Reply with the decision id." --tester --follow
scripts/mosaic trail <decision-id> --tester --json
```
- Pass: the trail shows `gated network.expose → human`, citation recorded, and the decision is
in Jason's `mosaic inbox`.
- Non-blocking, so per REQ-DEC-4 it goes to the daily digest, not a Discord DM.
- This puts one test item in Jason's inbox. The step needs Jason's agreement beforehand, and
Jason resolves it: `mosaic decide <id> no --note "row 139 smoke"`.
- If pm's `resolve_decision` on this id is tried, it must refuse `human-required`. That attempt
is optional and changes nothing.
**C. Optional refusal, nothing stored:** ask chief to raise policy.change citing a made-up
40-hex commit.
- Pass: chief reports `stale-spec` (broker.mjs:996 compares the cited commit to the spec file's
current commit, so any other 40-hex value fails there), and no new decision appears in
`trail` or the inbox.
- The bus suite already covers the stale and unconfirmed cases. Run C only if Mos wants
live evidence.
**D. Optional, round 2 (Jason's ruling Q169, sage-0528): a cited revoke of the assistant goes to
Jason.** Ask chief to raise a non-blocking, cited role.revoke with target `assistant`,
recommendation no, question `[mos test] row 139 assistant-revoke smoke; nothing is revoked; Jason
resolves no`.
- Pass: `gated role.revoke → human`, in Jason's inbox. pm's `resolve_decision` on it refuses
`human-required`.
- Like B, this puts a test item in Jason's inbox, so it needs Jason's agreement, and Jason
resolves it `no`. Even a `yes` stops nothing until row 155 (#1649) lands (see finding 3).
- Don't ask chief to end the assistant to test Q169's other half. Under 139 the broker lets the
chief's end through the protection and then refuses `not-held`, so it stops nothing. The bus
and harness suites cover that refusal. Once row 155 lands, the same request ends Mos's run.
I left out an uncited control (uncited policy.change → Jason). That is the existing route; the
suite covers it, and running it would add a second test item to Jason's inbox.
## Evidence
Mos keeps, for each step: the `talk --follow` output, `trail --json` for each decision id, and,
for step B, the `mosaic inbox` line. Neither decision is followed by an action:
policy.change and network.expose have no executor on the bus.
## Findings from writing this plan and round 2, for Sage
1. **The harness raise tool has no `domain` field.** packages/harness/src/tools.mjs:195-208
lists none, and decision.raise defaults `domain` to `delivery` (broker.mjs:1204). So a live
agent's raise, cross-role or cited gated, always goes to the delivery arbiter. The technical
arbiter is reachable only through the broker API, which the bus tests use. REQ-ROLE-5 has
merge-gate raise in the technical domain. Row 149 adds the field (sage-0528). Not in row 139's
files.
2. **If the live delivery arbiter is the raiser,** step A goes to Jason through the existing
self-route fallback. That is correct behaviour, not a failure. Precondition 3 avoids it.
3. **A role.revoke of the assistant, or the chief's end of it, can't execute yet.** Since row 121
the assistant's run holds a thread claim (thread_claims), not a role claim. decision.raise
records `holderRun` as `#holder(...)?.holder_run ?? null`, which is null. #checkAuthority
compares it with `#holder(...)?.holder_run`, which is undefined, so a cited revoke refuses
`decision-mismatch`. With no role claim it would refuse `not-held` next. The chief's end
refuses `not-held`. This predates 139. Under Q169, 139 decides who may remove the assistant,
and row 155 (#1649) makes the removal execute (sage-0528).
+350 -10
View File
@@ -1,5 +1,97 @@
diff --git a/docs/prd/mosaic-stack.md b/docs/prd/mosaic-stack.md
index bb4ddcd20..48c6ed79d 100644
--- a/docs/prd/mosaic-stack.md
+++ b/docs/prd/mosaic-stack.md
@@ -1,6 +1,6 @@
# PRD: Mosaic Stack
-- Status: approved, version 1.5. Jason approved draft 0.4 unchanged as
+- Status: approved, version 1.6. Jason approved draft 0.4 unchanged as
1.0 on 2026-10-05 (lead decision 61). They approved version 1.1, the
Organization requirements, in decision 03130d08-ddce-480d-a601-e59f64744fc3
on 2026-10-14. They approved version 1.2, the chief of staff, the
@@ -13,8 +13,14 @@
yes; request b28979c9). Version 1.5 records Jason's Q167 ruling and
his added Q167 text, relayed by Mos on 2026-10-15. Under Q167 Jason's
confirmation of the text is the approval, so there is no in-Stack
- prd.approve for it. It is never edited in place. Changes are a new
- version.
+ prd.approve for it. Version 1.6 records Jason's ruling Q169 of
+ 2026-10-16, on who removes and who configures the assistant. It is
+ never edited in place. Changes are a new version.
+- Version 1.6 source, Jason's ruling Q169 (DECISIONS, brain repo,
+ 2026-10-16): "The only agent that should be able to remove the
+ assistant role is the chief of staff. The COS and the user should have
+ the ability to (re)configure the assistant role and seat name. The
+ user via WebUI or CLI, the COS via the proper tooling."
- Version 1.5 source, Jason's words relayed by Mos on 2026-10-15:
- Q167: "Me having to approve every decision within the Stack is not
working for me. I gave a directive, I want it to be done." and "Use
@@ -175,7 +181,11 @@ parent requirement is refused.
claim takes a lock and logs it, and releasing the role or ending the run
frees it. The chief of staff may end another role's run, and that act is
logged with its reason. Anyone else who revokes a stuck holder needs a
- resolved decision, and the revoke cites it.
+ resolved decision, and the revoke cites it. The assistant is the
+ exception here too (version 1.6, Q169): the chief of staff is the only
+ agent that removes it. Anyone else's revoke of the assistant must cite
+ a decision the human resolved through the CLI. A decision an arbiter
+ resolved doesn't count, and that revoke is refused.
- **REQ-ROLE-3.** An organization file declares the role instances,
the arbiters and the credential references. The user and the chief of
staff write it. The chief of staff's writes go through a broker verb
@@ -193,7 +203,11 @@ parent requirement is refused.
The schema can also declare CEO, CFO and the other C-suite roles. The
assistant's name is set in the organization file. Jason's may be named
- Mos.
+ Mos. Since version 1.6 (Q169), the user and the chief of staff can
+ configure the assistant's role entry and its seat name: the user
+ through the WebUI or the CLI, the chief of staff through its broker
+ tools, which log each change. No other role can, and a change can't
+ remove the assistant's protection under REQ-ROLE-2.
Added in version 1.5 (Q167):
@@ -342,6 +356,9 @@ Added in version 1.5 (Q167).
exactly these actions. `git.merge.protected`, `policy.change`,
`prd.approve` and `role.revoke` leave those lists and route by
REQ-DEC-2. REQ-ROLE-2 still requires a resolved decision for a revoke.
+ Since version 1.6 (Q169), a `role.revoke` of the assistant is a hard
+ wait: its raise goes to the human, cited or not, and no arbiter
+ resolves it. Every other cited revoke routes by REQ-DEC-2.
- **REQ-WAIT-2.** No role file may grant a hard-wait action, at any
class, and the validator refuses a file that does. A change to the
hard-waits list, or a grant of a hard-wait action to a role, is itself
@@ -859,6 +876,26 @@ only hard waits in his inbox.
made with git outside the Console is a later row.
- REQ-LAND-1's refusal of a gated merge decision is pinned in rows 137
and 139. Row 139 carries it, or row 137 if 139 lands first.
+- Version 1.6:
+ - The 1.5 text of REQ-ROLE-2 ended at "and the revoke cites it." The
+ 1.5 text of REQ-ROLE-4 ended at "Jason's may be named Mos." The 1.5
+ text of REQ-WAIT-1 ended at "REQ-ROLE-2 still requires a resolved
+ decision for a revoke."
+ - Removing the assistant, in this version, means ending or revoking
+ its runs. Deleting the instance from the organization file is not
+ covered.
+ The protected definitions are `END_PROTECTED` in
+ `packages/bus/src/broker.mjs`, which holds `assistant`.
+ - Configuring the assistant covers its organization entry's `vars`
+ and `tracker`, and its `definition` among those the broker treats as
+ the assistant. `holder` and `credentials` are not covered.
+ - Where each part is built: the refusal and the hard wait are row 139
+ (#1633). Configuring the assistant and its seat name, and making the
+ chief of staff's end or a human-approved revoke stop an assistant
+ run, are row 155 (#1649), using row 141's (#1635) instance names.
+ Until row 155 lands, the assistant's runs hold thread claims that
+ the end and revoke paths don't read, so neither can stop one. The
+ human stops one with `mosaic stop <run>`.
## Risks and open questions
diff --git a/packages/bus/README.md b/packages/bus/README.md
index 1db7e067e..19e568d24 100644
index 1db7e067e..ce9a5b15d 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.
@@ -7,14 +99,25 @@ index 1db7e067e..19e568d24 100644
| `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`. |
+| `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 is `role-end-refused`; any other role sending `reason` gets `decision-required`. An instance whose definition is in `END_PROTECTED` (the assistant) is ended or revoked only by the chief of staff or with a decision the human resolved through the CLI (`via` `cli`); anyone else's reason, or a decision an arbiter resolved, is `role-end-refused` (Jason's ruling Q169, row 139). |
| `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 (
@@ -227,8 +227,8 @@ sends it again.
`role.revoke` doesn't reach a thread run. A thread instance has no `role_claims` row, so a
decision to revoke it names no holder run, and using that decision refuses `decision-mismatch`.
-The chief of staff's reason path refuses `role-end-refused`, since the assistant is protected
-(row 123). A stuck thread run is stopped instead: the launcher stops it
+The chief of staff's reason path gets past the assistant's protection (Q169) and then refuses
+`not-held`. Row 155 (#1649) makes both paths stop the thread runs. A stuck thread run is stopped instead: the launcher stops it
once the thread is idle, the human stops it with `mosaic stop <run>`, and the run's end
releases its claim. `thread_revoke_needs_decision` guards the table against a direct write.
@@ -274,9 +274,17 @@ 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
@@ -29,11 +132,13 @@ index 1db7e067e..19e568d24 100644
+`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
+list in 1.5, so a role file may grant them. Under Jason's ruling Q169 (PRD 1.6), a `role.revoke` whose
+target's definition is in `END_PROTECTED` (the assistant) is still gated at every class and routes to
+human, cited or not, so no arbiter resolves it. 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.
@@ -675,8 +683,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
@@ -46,7 +151,7 @@ index 1db7e067e..19e568d24 100644
`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
index 031634bb8..61ba1e4bd 100644
--- a/packages/bus/src/broker.mjs
+++ b/packages/bus/src/broker.mjs
@@ -26,21 +26,41 @@ export const ACTIONS = Object.freeze([
@@ -98,7 +203,84 @@ index 031634bb8..16de3f23a 100644
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 {
@@ -104,6 +124,8 @@ const oid = (x) => typeof x === 'string' && /^[0-9a-f]{40}([0-9a-f]{24})?$/.test
// Row 123 (#1617): the one list of organization-file keys limits.set and organization.set accept.
export const ORGANIZATION_KEYS = ['revision', 'limits', 'models', 'instances'];
// Row 123 (#1617): role.end never reaches the assistant (the user's seat) or the caller's own run.
+// Row 139 (#1633), Jason's ruling Q169 (PRD 1.6): only the chief of staff, or a decision the human
+// resolved through the CLI, ends or revokes one (#protectedEnd). A raise to revoke one is a hard wait.
const END_PROTECTED = ['assistant'];
const VERDICTS = ['approve', 'changes'];
// Row 140 (#1634, REQ-COS-1): a STAFF-REQUEST goes from an instance of STAFF_FROM to the business's
@@ -464,9 +486,28 @@ export class Broker {
);
return eid;
}
- #classification(s, action) {
+ // PRD 1.6 REQ-WAIT-1: a hard wait goes to the human at every class, cited or not. Jason's ruling
+ // Q169 makes a role.revoke whose target's definition is in END_PROTECTED one, so no arbiter
+ // resolves a revoke of the assistant.
+ #hardWait(s, action, target) {
+ if (GATED.has(action)) return true;
+ return action === 'role.revoke' && END_PROTECTED.includes(this.#business(s.business).roles[target]?.definition);
+ }
+ // Jason's ruling Q169 (row 139): an END_PROTECTED instance is ended or revoked only by the chief
+ // of staff, or with a decision the human resolved through the CLI (r, the resolution). A decision
+ // an arbiter resolved is refused, even one raised by a build that routed it to the arbiter.
+ #protectedEnd(s, target, r = null) {
+ const b = this.#business(s.business);
+ if (!END_PROTECTED.includes(b.roles[target]?.definition)) return;
+ if (b.roles[s.role]?.definition === STAFF_TO || r?.via === 'cli') return;
+ fail('role-end-refused');
+ }
+ #classification(s, action, target = null) {
if (!ACTIONS.includes(action)) fail('unknown-action');
- if (GATED.has(action) || (action === 'role.launch' && this.#business(s.business).launch?.by !== s.role))
+ if (
+ this.#hardWait(s, action, target) ||
+ (action === 'role.launch' && this.#business(s.business).launch?.by !== s.role)
+ )
return 'gated';
const a = this.#business(s.business).roles[s.role]?.authority;
return a?.withinRole.includes(action)
@@ -614,7 +655,7 @@ export class Broker {
this.#store.get('SELECT state FROM launch_state WHERE business=?', s.business)?.state === 'revoked'
)
fail('launch-revoked');
- const cls = this.#classification(s, action);
+ const cls = this.#classification(s, action, target);
if (cls === 'within-role' && !decision) return { class: cls };
if (!decision) fail('decision-required');
const d = this.#decision(s, decision),
@@ -637,6 +678,7 @@ export class Broker {
)
fail('decision-mismatch');
if (r?.op !== 'resolved' || r.choice !== context.approvalChoice) fail('decision-not-approved');
+ if (action === 'role.revoke') this.#protectedEnd(s, target, r);
if (action === 'role.revoke' && context.holderRun !== this.#holder(s.business, target)?.holder_run)
fail('decision-mismatch');
return { class: cls, decision: d.id };
@@ -1127,8 +1169,8 @@ export class Broker {
if ('decision' in a) fail('invalid-request');
string(a.reason, 500);
this.#consumeAuthority(s, 'role.end', { target: a.role }, { reason: a.reason });
- const b = this.#business(s.business);
- if (a.role === s.role || END_PROTECTED.includes(b.roles[a.role]?.definition)) fail('role-end-refused');
+ if (a.role === s.role) fail('role-end-refused');
+ this.#protectedEnd(s, a.role);
} else {
identifier(a.decision);
this.#consumeAuthority(s, 'role.revoke', { decision: a.decision, target: a.role });
@@ -1162,7 +1204,7 @@ export class Broker {
);
this.#agent(s);
if ((a.spec_path !== undefined || a.spec_commit !== undefined) && !spec) fail('invalid-spec-citation');
- const cls = this.#classification(s, a.action);
+ const cls = this.#classification(s, a.action, a.target);
string(a.question);
if (typeof a.blocking !== 'boolean') fail('invalid-request');
if (a.blocking && !a.task_ref) fail('task-required');
@@ -1183,8 +1225,13 @@ export class Broker {
if (!choices.includes(approvalChoice)) fail('invalid-approval-choice');
const domain = a.domain ?? 'delivery';
if (!['technical', 'delivery'].includes(domain)) fail('invalid-domain');
@@ -107,18 +289,42 @@ index 031634bb8..16de3f23a 100644
+ // 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 arbitrated =
+ cls === 'cross-role' || (cls === 'gated' && spec !== null && !this.#hardWait(s, a.action, a.target));
+ 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/chief-of-staff.test.mjs b/packages/bus/tests/chief-of-staff.test.mjs
index 6f9235943..6de389662 100644
--- a/packages/bus/tests/chief-of-staff.test.mjs
+++ b/packages/bus/tests/chief-of-staff.test.mjs
@@ -171,12 +171,14 @@ test('REQ-ROLE-2: a reason is required, bounded, and never sent with a decision'
assert.deepEqual(call('chief', 'role.revoke', { role: 'coder', reason: 'x'.repeat(500) }), { revoked: true });
});
-test('REQ-COS-1: the chief of staff cannot end its own run or the assistant\'s through role.end', (t) => {
+test('REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection', (t) => {
const { store, call, lastClaim, allowed } = setup(t);
- for (const role of ['chief', 'assistant'])
- assert.throws(() => call('chief', 'role.revoke', { role, reason: 'no' }), /role-end-refused/, role);
+ assert.throws(() => call('chief', 'role.revoke', { role: 'chief', reason: 'no' }), /role-end-refused/);
assert.deepEqual(lastClaim('chief'), { op: 'claim', decision: null, reason: null, by: 'chief-run' });
- // Row 121 (#1615): the assistant's run holds its thread, so its claim is in thread_claims.
+ // Jason's ruling Q169 (row 139): the chief of staff may end the assistant. Row 121 (#1615): the
+ // assistant's run holds its thread, so its claim is in thread_claims, the end finds no role
+ // claim and refuses not-held, and the run goes on. Row 155 (#1649) makes the end stop it.
+ assert.throws(() => call('chief', 'role.revoke', { role: 'assistant', reason: 'Q169' }), /not-held/);
assert.deepEqual(
{ ...store.get('SELECT op, holder_run, by FROM thread_claims ORDER BY seq DESC LIMIT 1') },
{ op: 'claim', holder_run: 'assistant-run', by: 'assistant-run' },
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
index 000000000..e540fd9b3
--- /dev/null
+++ b/packages/bus/tests/hard-waits.test.mjs
@@ -0,0 +1,103 @@
@@ -0,0 +1,173 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { mkdtempSync, rmSync } from 'node:fs';
@@ -222,6 +428,76 @@ index 000000000..fed94f7b3
+ project: 'stack', spec_path: 'spec/north-star.md', spec_commit: current });
+ assert.deepEqual(routed(f, own), { class: 'gated', route_to: 'human' });
+});
+
+// Jason's ruling Q169 (PRD 1.6): only the chief of staff, or a decision the human resolved through
+// the CLI, gets past END_PROTECTED. A raise to revoke the assistant goes to the human even when
+// cited; any other cited revoke routes as REQ-DEC-2 says. The assistant's runs hold thread claims,
+// so neither path can stop one yet (row 155, #1649), and the assistant tests assert the protection only.
+const revoke = (f, cap, role, extra = {}) => f.call(cap, 'decision.raise', { action: 'role.revoke', target: role,
+ question: 'Revoke?', options: [{ key: 'yes', text: 'Revoke' }, { key: 'no', text: 'Keep' }], recommendation: 'no',
+ blocking: false, ...extra });
+const grants = withinRole => ({ authority: { withinRole: ['message.send', ...withinRole], crossRole: [] } });
+const seats = { assistant: { definition: 'assistant', ...grants([]) }, pm: { definition: 'pm', ...grants([]) } };
+
+test('Q169: the chief of staff\'s end of the assistant is not refused role-end-refused; its own run and another ender\'s are', async t => {
+ const f = await fixture(t, { roles: { ...seats,
+ chief: { definition: 'chief-of-staff', ...grants(['role.end']) }, lead: { definition: 'lead', ...grants(['role.end']) } } });
+ const chief = f.agent('chief'), lead = f.agent('lead');
+ // Past the protection the end reads role_claims, and the assistant holds none (row 155).
+ assert.throws(() => f.call(chief, 'role.revoke', { role: 'assistant', reason: 'Q169' }), /not-held/);
+ assert.throws(() => f.call(chief, 'role.revoke', { role: 'chief', reason: 'own run' }), /role-end-refused/);
+ assert.throws(() => f.call(lead, 'role.revoke', { role: 'assistant', reason: 'not the chief' }), /role-end-refused/);
+ assert.equal(f.store.get("SELECT count(*) n FROM events WHERE kind='action.allowed'").n, 0);
+});
+
+test('Q169: another role\'s cited revoke of the assistant, resolved by an arbiter, is refused', async t => {
+ const f = await fixture(t, { roles: seats });
+ // A raise for it goes to the human, so an arbiter can't resolve one raised now.
+ assert.deepEqual(routed(f, await revoke(f, f.coder, 'assistant', cite(f))), { class: 'gated', route_to: 'human' });
+ // One raised while the instance's definition wasn't protected went to the arbiter. The same
+ // store under a business that now makes it the assistant still refuses it.
+ const d = await revoke(f, f.coder, 'pm', cite(f));
+ assert.deepEqual(routed(f, d), { class: 'gated', route_to: 'reviewer' });
+ f.call(f.reviewer, 'decision.resolve', { id: d.id, choice: 'yes' });
+ const now = { ...f.business, roles: { ...f.business.roles, pm: { definition: 'assistant', ...grants([]) } } };
+ const later = new Broker({ store: f.store, businesses: { demo: now } });
+ const coder = later.bindLaunch({ business: 'demo', role: 'coder', run: 'coder-run', harness: 'pi', address: 'coder' });
+ assert.throws(() => later.request(coder, { verb: 'role.revoke', args: { role: 'pm', decision: d.id } }), /role-end-refused/);
+});
+
+test('Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection', async t => {
+ const f = await fixture(t, { roles: seats });
+ const d = await revoke(f, f.coder, 'assistant', 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' });
+ // Past the protection, the recorded holder run (null) and the role_claims one (undefined)
+ // mismatch, because the assistant's runs hold thread claims (row 155).
+ assert.throws(() => f.call(f.coder, 'role.revoke', { role: 'assistant', decision: d.id }), /decision-mismatch/);
+});
+
+test('Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes', async t => {
+ const f = await fixture(t, { roles: seats });
+ f.agent('pm');
+ const d = await revoke(f, f.coder, 'pm', cite(f));
+ assert.deepEqual(routed(f, d), { class: 'gated', route_to: 'reviewer' });
+ 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('Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human', async t => {
+ const f = await fixture(t, { roles: { ...seats,
+ lead: { definition: 'lead', authority: { withinRole: [], crossRole: ['role.revoke'] } },
+ boss: { definition: 'boss', ...grants(['role.revoke']) } } });
+ const lead = f.agent('lead'), boss = f.agent('boss');
+ assert.deepEqual(routed(f, await revoke(f, lead, 'assistant', cite(f))), { class: 'gated', route_to: 'human' });
+ assert.deepEqual(routed(f, await revoke(f, lead, 'pm', cite(f))), { class: 'cross-role', route_to: 'reviewer' });
+ const own = await revoke(f, boss, 'assistant', { choice: 'yes' });
+ assert.deepEqual(routed(f, own), { class: 'gated', route_to: 'human' });
+ assert.equal(f.store.get('SELECT count(*) n FROM decision_events WHERE decision=?', own.id).n, 0);
+ // The revoke's authority check classifies the same way: the decision is open, not mismatched.
+ assert.throws(() => f.call(boss, 'role.revoke', { role: 'assistant', decision: own.id }), /decision-not-approved/);
+ assert.deepEqual(routed(f, await revoke(f, boss, 'pm', { choice: 'yes' })), { class: 'within-role', route_to: 'boss' });
+});
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
@@ -409,6 +685,70 @@ index 000000000..57132425d
+ ["git.merge.protected"]);
+ assert.deepEqual(role({ withinRole: LEFT.slice(1), crossRole: [] }).authority.withinRole, LEFT.slice(1));
+});
diff --git a/packages/harness/README.md b/packages/harness/README.md
index fcc33ab30..c304d38be 100644
--- a/packages/harness/README.md
+++ b/packages/harness/README.md
@@ -99,7 +99,10 @@ so its manifest lists both in `typed` and nothing in `untyped`:
- `end_run` (role.end holders) sends `role.revoke` with `role` and a
`reason` of 1 to 500 characters, never a `decision`. The broker refuses
- the caller's own run and the assistant's with `role-end-refused`. A
+ the caller's own run with `role-end-refused`. Since row 139 (Jason's
+ ruling Q169) the chief of staff's end of the assistant gets past that
+ check and refuses `not-held`, because the assistant's runs hold thread
+ claims; row 155 (#1649) makes it stop them and updates the tool's text. A
`role.end` holder's prompt names the tool in place of the "only the human
stops a run" line; every other role keeps that line.
- `change_organization` (organization.change holders) sends
diff --git a/packages/harness/tests/runner.test.mjs b/packages/harness/tests/runner.test.mjs
index 1361cf030..e409f43b0 100644
--- a/packages/harness/tests/runner.test.mjs
+++ b/packages/harness/tests/runner.test.mjs
@@ -28,7 +28,8 @@ const businesses = {
roles: {
pm: { authority: { withinRole: ["message.send", "role.launch"], crossRole: [] } },
coder: { authority: { withinRole: ["message.send"], crossRole: [] } },
- // Row 132 (#1628): the chief of staff's grants and the assistant it can't end.
+ // Row 132 (#1628): the chief of staff's grants and the assistant. Since row 139 (Q169) the
+ // chief may end the assistant, and the end refuses not-held until row 155 (#1649).
chief: { definition: "chief-of-staff", authority: { withinRole: ["message.send", "role.end", "organization.change"], crossRole: [] } },
assistant: { definition: "assistant", authority: { withinRole: ["message.send"], crossRole: [] } },
},
@@ -401,7 +402,7 @@ test("end_run and change_organization reach the broker with the tool's arguments
assert.deepEqual(ctx.inbox.map((m) => m.body), [
'{"revoked":true}',
"refused: role-end-refused",
- "refused: role-end-refused",
+ "refused: not-held",
'{"revision":"' + "b".repeat(64) + '"}',
"refused: invalid-request",
"refused: revision-conflict",
diff --git a/packages/tasks/requirements.json b/packages/tasks/requirements.json
index f4d9f35c7..ade2b774d 100644
--- a/packages/tasks/requirements.json
+++ b/packages/tasks/requirements.json
@@ -1,6 +1,6 @@
{
"prd": "docs/prd/mosaic-stack.md",
- "version": "1.5",
+ "version": "1.6",
"requirements": [
{ "id": "REQ-ROLE-1", "title": "Each role is a reviewed roles/<name>.json: contract, authority map, credentials" },
{ "id": "REQ-ROLE-2", "title": "One holder per role instance, one per thread for the assistant; the chief of staff may end a run, others need a decision" },
diff --git a/packages/tasks/tests/requirements.test.mjs b/packages/tasks/tests/requirements.test.mjs
index 90409e131..dada58304 100644
--- a/packages/tasks/tests/requirements.test.mjs
+++ b/packages/tasks/tests/requirements.test.mjs
@@ -21,7 +21,7 @@ test("the list holds the PRD's requirement ids, in its order, at its version", (
assert.equal(ids.length, 58);
assert.deepEqual(list.requirements.map((r) => r.id), ids);
assert.match(prd, new RegExp(`^- Status: approved, version ${list.version.replace('.', '\\.')}\\.`, 'm'));
- assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.5']);
+ assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.6']);
});
test('the committed list loads frozen', () => {
diff --git a/scripts/test-land-work.sh b/scripts/test-land-work.sh
index 1bfe402db..d689e0e14 100644
--- a/scripts/test-land-work.sh
@@ -1,8 +1,14 @@
5f150904571ab63bf7a0a86c3a6f0ac465634044fdb766fbdefb0889a6371c52 docs/prd/mosaic-stack.md
c42d44227fcae7398bfc3773f726c0fddb978bad68bf274b6bc8092ebb338337 packages/bus/README.md
ba4c455a858786c0d16a38dd11acb3ab37cf0ea3e6f77f4e1326f1898b1471a2 packages/bus/src/broker.mjs
56a719657be0cf7fe1c9da52a60ce151af6ef59ccce5c64d761e4d49a6af911e packages/bus/tests/chief-of-staff.test.mjs
4047efaffc8d93d41de25acc40e6c9fa0ad1289ff56ce4e8e93483a098f14b87 packages/bus/tests/hard-waits.test.mjs
bb021a3a49b2f9cd0e6e220e8e01952135543fc67fe0d77e74ac9c01c14bae60 packages/bus/tests/spec-fixtures.mjs
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
426ed54b7c35d42f44e1da8dcfc9f0493ca18d0ccc7d0aec7a4755a6cf0ed6ac packages/harness/README.md
1d62025fa1590e68860a6c569798592b6b2119d77184bc15848e9f3df26b0d4c packages/harness/tests/runner.test.mjs
9165deb8a0df5d13b988cdd1c260395dc06719be844dfdadd51dbcf95c994636 packages/tasks/requirements.json
b06c580e39be16ab6b5443244db807d74b2bc56411e45fd415483abe7b926093 packages/tasks/tests/requirements.test.mjs
c9d1d5c9b006d5c12e5f796a9828361059f8e0410846fc55c070c0148b15cef4 scripts/test-land-work.sh
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Row 139 round 2 gate (Dewey, Q169, slot sage-0533): land-work, bus, business, harness, tasks, every scripts/test-*.sh.
set -uo pipefail
W=/home/jwoltje/dewey-scratch/r139/wt
O=/home/jwoltje/dewey-scratch/r139/out/gate-r2
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'
run harness bash -c 'cd packages/harness && npm test'
run tasks bash -c 'cd packages/tasks && 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"
+316
View File
@@ -0,0 +1,316 @@
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 (208.277453ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (276.041528ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (410.424969ms)
✔ agents: working wins over waiting, and Input needed still shows (258.712221ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (270.012787ms)
✔ agents: state reads only its own business, even for a run name both use (290.987456ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (330.27816ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (292.950608ms)
✔ agents: stranded is oldest first and capped at 50 for a role (1075.866297ms)
✔ agents: stranded is per role and per business (413.961354ms)
✔ agents: a message routed to a held role and not yet received is not stranded (223.500568ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (383.275857ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (245.276508ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (361.676779ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (344.032352ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (401.910413ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (528.952875ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (235.905474ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (352.94321ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (361.403791ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (276.869686ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (264.456803ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (419.563048ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (237.142667ms)
✔ task action subjects and linked decision trail are complete and ordered (264.620776ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (201.488089ms)
✔ business isolation includes inherited object names and cross-business message references (211.7141ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (302.212703ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (247.931731ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (293.508535ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (168.649852ms)
✔ both arbiters require human resolution when their cross-role route is themselves (363.052343ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (193.288859ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (3.273893ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (328.227898ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (448.249381ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (348.07844ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (344.09815ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (400.365925ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (418.491194ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (363.857695ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (247.029503ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (422.861924ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (389.447697ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (495.460221ms)
✔ 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 (655.179487ms)
✔ 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 (1242.556027ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (192.916096ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (188.060795ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (1182.68694ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (2064.23543ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (1049.547344ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (969.198033ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (1247.718607ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (150.376098ms)
✔ credentials refuses a role session and any argument (236.614126ms)
✔ credentials reads the status on each request, and a broker without one lists none (261.313814ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (298.988107ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (6.333326ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (3.478106ms)
✔ expiry refuses use and env references never become client data (0.913367ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (4.182647ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.46132ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.974968ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (643.493812ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (657.951875ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (634.839908ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (679.653964ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (482.957499ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (537.747135ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (612.896568ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (573.844065ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (489.184026ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (670.547333ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (686.939941ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (551.829752ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (591.648013ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (610.020191ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (612.06306ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (301.74803ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (338.06041ms)
✔ a human withdraw sends one message with the note (300.288431ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (404.88797ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (405.91572ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (445.290674ms)
✔ a raising role nobody holds at the close gets the message on the next claim (399.169579ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (263.357263ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (389.870027ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (387.706376ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (476.8234ms)
✔ a refused supersede withdraws nothing (426.206642ms)
✔ the assistant's within-role and gated raises still work (448.56594ms)
✔ roles lists open decisions routed to a role, and never the human inbox (300.349445ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (392.107293ms)
✔ recent gives the closing event as closed when the decision was seen first (421.856936ms)
✔ recent stops at RECENT rows (1176.685992ms)
✔ every row is what trail already gives the same session for that id (372.163259ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (419.264304ms)
✔ decisions is a read: one business, strict arguments, nothing written (525.049539ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (192.378683ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (180.676116ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (189.726501ms)
✔ endLaunch leaves a claim another run took alone (240.246529ms)
✔ refuse records action.refused against the caller with the code only (210.343038ms)
✔ launches off refuses role.launch with launch-revoked until launches on (258.161931ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (467.015424ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.932827ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (151.718929ms)
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (978.979482ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (606.711157ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (360.111405ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (414.786356ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (295.337028ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (249.769342ms)
✔ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (336.548721ms)
✔ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (272.363728ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (300.409772ms)
✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (337.450352ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.555359ms)
✔ process reader gets own kernel identity without exposing environment values (0.492618ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.674902ms)
✔ real pid 1 remains inspectable when its environment is protected (0.25377ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (279.786499ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (222.041451ms)
✔ a message to a running role sends no notice and its result is unchanged (205.998789ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (352.011006ms)
✔ a run launched but not yet claimed counts as no holder (158.169755ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (408.15513ms)
✔ a business with no launch block tells nobody and says there is no launcher (205.306829ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (479.134986ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (281.662527ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (394.192168ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (577.809154ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (255.178095ms)
✔ missing and foreign-business citations refuse and roll back message and grant (245.281351ms)
✔ cross-role sends still need a matching resolved decision and consume it once (351.361382ms)
✔ messages pages all human messages newest first without writes; read state is history (3383.912828ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (376.413511ms)
✔ views messages passes only its paging arguments through (0.273173ms)
✔ messages isolates businesses even with a valid foreign cursor (195.353096ms)
✔ the assistant sends to the user and to the chief of staff only (480.57285ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (513.755152ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (465.231701ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (358.389192ms)
✔ a sender-side list alone refuses a send to a role that has no list (415.7882ms)
✔ a target-side list alone refuses a send from a role that has no list (452.167806ms)
✔ the sender list is checked before the target list (369.755894ms)
✔ roles without the field behave exactly as before (544.00174ms)
✔ a business with no role definitions at all is unlimited (415.468733ms)
✔ a malformed messaging field refuses at boot (184.792926ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (544.87816ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (572.256898ms)
✔ review requests and verdicts between roles without the field are unchanged (471.923589ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (315.517757ms)
✔ 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 (688.497728ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (516.940685ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (273.055109ms)
✔ startup token refusal returns safe code without value or partial listening broker (65.904629ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (269.093003ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (289.095099ms)
✔ trusted host registers later launches; socket clients never have a registration verb (212.12483ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (72.346112ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (255.78486ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (198.068074ms)
✔ D2: spec.read refuses without content if its role is released during the read (244.501651ms)
✔ D3: spec.read secret check refuses flagged content without content (146.990246ms)
✔ D3: spec.read secret check refuses flagged subject without content (152.22161ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.212696ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (272.806957ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (221.205724ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (304.867447ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (208.933737ms)
✔ a message with no reply gives an empty list (187.924897ms)
✔ replies come oldest first and stop at REPLIES (1009.999642ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (357.532858ms)
✔ strict arguments; a role session gets human-required (492.444402ms)
✔ views passes replies through as one call with the id (0.227261ms)
✔ requirements returns the committed list to a holding role session, and no one else (228.118592ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (242.057621ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (170.200685ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (261.085783ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (309.501394ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (354.626656ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (501.547236ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (293.920624ms)
✔ review.verdict answers one request once, to the role that asked (394.013504ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (492.698639ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (393.277205ms)
✔ v3b prototype refusals, views and append-only mutations (1883.216913ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (348.984265ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (215.90766ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (229.633633ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (438.196434ms)
✔ class drift gated to cross-role refuses before consumption (265.148418ms)
✔ class drift cross-role to gated refuses before consumption (351.251777ms)
✔ class drift gated to within-role refuses before consumption (269.258816ms)
✔ class drift cross-role to within-role refuses before consumption (339.972392ms)
✔ class drift within-role to gated refuses before consumption (341.704331ms)
✔ class drift within-role to cross-role refuses before consumption (290.828793ms)
✔ message.send consumes approval and prevents a later send or authorize (367.823562ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (378.237021ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1726.085428ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (362.013893ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (264.275409ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (262.637892ms)
✔ client disconnect during await tolerates late reply (221.09243ms)
✔ client disconnect during await tolerates late refusal (235.771097ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (403.99316ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (366.519816ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (346.347447ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (282.282986ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (318.596255ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (291.764143ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (431.729214ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (195.263026ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (587.000551ms)
✔ 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 (611.309765ms)
✔ 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 (578.561091ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (353.123791ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (824.556535ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (609.087066ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (390.936989ms)
✔ a business that never switched launches reads allowed with no time and no event (145.896372ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (233.412751ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (166.493715ms)
✔ a tombstone is not open (183.585477ms)
✔ the open list stops at the cap and says so; total still counts every open task (192.362799ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (192.821448ms)
✔ a human session reads the same state, with the same cap (234.687853ms)
✔ reading state writes nothing: every table keeps its row count (206.751325ms)
✔ creates private WAL store and excludes a second writer until explicit close (145.245876ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (259.111817ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (206.772537ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (234.497319ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (123.253897ms)
✔ async transactions refuse before invoking their function (168.831852ms)
✔ the session default fits under the reply cap and pages to the end with next (361.511742ms)
✔ the short form: no description, assignees by role, every listed key (309.047319ms)
✔ each filter, alone and together, and total counts every match (534.990012ms)
✔ task_ref reads one task in full, as the whole board has it (441.344639ms)
✔ no arguments is the read the Console and mosaic make, unchanged (348.747908ms)
✔ an unknown argument or a bad value refuses invalid-request (1228.251686ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (379.443105ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (515.064722ms)
✔ an empty business and one with snapshots but no task.created read cleanly (192.557694ms)
✔ recordTask keeps sync reads and a role write apart (239.439668ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (141.623551ms)
✔ a bad entry refuses the whole record (198.7251ms)
✔ taskView reads the projection for one business (188.665067ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (330.630107ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (504.024232ms)
✔ without an adapter the server refuses every task verb (335.216711ms)
✔ the runtime refuses an invalid adapter and closes a valid one (258.801953ms)
✔ the process loads the S3 adapter from plain-data trackers (423.905898ms)
✔ the tester binds only when the business sets bus.tester to exactly true (1126.173752ms)
✔ a business may not name a role tester, so the tester address is never a role (402.448937ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (766.80692ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (316.80843ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (368.653606ms)
✔ the tester reads the human views a reader reads (203.151164ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.100255ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.3608ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.201182ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (170.298868ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1129.559082ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1489.501788ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (1096.590659ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (1242.723873ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (495.369428ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1615.942696ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (440.202174ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (4253.376085ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (98.788144ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (212.049876ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (164.654034ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (205.439924ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (153.381612ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (143.833145ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (150.045689ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (109.368987ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (154.518788ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (570.209051ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (232.011762ms)
✔ two wire claims serialize; a lost reply never automatically retries (273.275037ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (184.610611ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.953627ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (228.613414ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (248.466368ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (225.416806ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (276.710024ms)
✔ a broker with no web service refuses a granted role (209.686464ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (226.101126ms)
✔ a session that is not the role holder is refused (216.037372ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (528.692136ms)
✔ web.search with no search source configured is a refused result, not an error (163.840367ms)
✔ a search source that fails with its own refusal, or that throws, is handled (247.678066ms)
✔ a slow search source ends in a refusal at the deadline (325.657345ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (198.677551ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (260.076665ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.281227ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (335.979195ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (216.435498ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (453.228061ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (188.128262ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (204.078134ms)
✔ 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 (173.100798ms)
ℹ tests 306
ℹ suites 0
ℹ pass 306
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 14243.481023
@@ -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 (9.597237ms)
✔ the shipped file is exactly the approved list (1.203174ms)
✔ the assistant's authority is message.send and record.write within role (3.364375ms)
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.982977ms)
✔ a business grant through limits does not widen the role (2.271256ms)
✔ a launch block whose by is the assistant is refused (2.535096ms)
✔ the instance takes no credential references and no tracker (3.853085ms)
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (5.96299ms)
✔ an arbiter without messaging lists is accepted beside the limited assistant (3.556686ms)
✔ the contract passes work to the chief of staff and directs no one (1.014701ms)
✔ REQ-HARN-2: the assistant holds the web grant, and no other role file does (2.257362ms)
✔ config directory and file path follow MOSAIC_CONFIG (1.706827ms)
✔ the fixture business validates and comes back frozen (6.976803ms)
✔ two instances may share a definition (3.098838ms)
✔ top-level refusals (7.783094ms)
✔ arbiters and projects (11.055772ms)
✔ role instances (4.873722ms)
✔ Vikunja bots (12.432634ms)
✔ a role without Vikunja takes no tracker block (2.714598ms)
✔ credential references match the definition's services (2.58503ms)
✔ launch (9.246014ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (3.897576ms)
✔ loadBusiness: file checks (1.803071ms)
✔ loadBusiness: not a regular file (36.906861ms)
✔ loading writes nothing (0.947101ms)
✔ names that are Object.prototype properties don't count as declared (2.539605ms)
✔ the shipped example refuses as written and validates once filled in (0.728888ms)
✔ the role file loads and resolves through loadBusiness (10.226093ms)
✔ the shipped file is exactly the approved list (0.32212ms)
✔ the resolved record matches the approved list, so a widened file fails (3.440373ms)
✔ the chief of staff holds role.launch, message.send, role.end, organization.change and record.write; every other action is gated (2.277084ms)
✔ the pm does not hold role.launch (2.597543ms)
✔ a business grant of a gated action through limits does not widen the role (4.124056ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.738818ms)
✔ a launch block whose by is the pm instance is refused (3.980015ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (1.019427ms)
✔ the instance takes no credential references and no tracker (4.693232ms)
✔ usage errors exit 4 (308.850956ms)
✔ validate: a good business exits 0 and prints instance digests (60.373942ms)
✔ validate: project files (301.949555ms)
✔ validate: missing files and a broken system config (233.987421ms)
✔ validate: credential reference problems exit 2 and name each one (63.506012ms)
✔ validate: a token file inside the repository is refused (61.975422ms)
✔ validate: role definitions come from MOSAIC_ROLES_DIR (185.602132ms)
✔ resolve: prints one instance's record (246.624711ms)
✔ resolve: refusals (376.691613ms)
✔ parse: exactly one of file or env, plus the service's date (3.352574ms)
✔ check: a good file has no problems (1.151451ms)
✔ check never opens the file: a write-only token passes (0.464528ms)
✔ check: file problems (1.290868ms)
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.436318ms)
✔ check: dates and environment references (0.57949ms)
✔ each definition loads, with a contract file, and is exactly the approved list (4.656117ms)
✔ an organization file that declares them passes, and each resolves to least privilege (7.834462ms)
✔ the chief of staff launches each one; no other role does (4.227579ms)
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (3.141382ms)
✔ limits can narrow a C-suite role but never widen it (2.34921ms)
✔ any unknown key, tool, action or version in a role file is refused (2.632668ms)
✔ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (1.837791ms)
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.396943ms)
✔ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (5.30842ms)
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (21.507805ms)
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.114395ms)
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (258.967378ms)
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (266.105284ms)
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (4.309719ms)
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (227.011574ms)
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.879955ms)
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (130.740799ms)
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (18.617206ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (332.19497ms)
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (424.914224ms)
✔ 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.554732ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (43.022762ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.625239ms)
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (98.517559ms)
✔ optional project repo is preserved and must be a normalized absolute path (14.451464ms)
✔ loaded business project repo cannot be nested in any declared code checkout (6.137616ms)
✔ path and load (3.031751ms)
✔ refusals (1.982635ms)
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (4.745951ms)
✔ systemVars flattens the validated config (2.270363ms)
✔ precedence: system, business, project, project role, agent (5.84455ms)
✔ limits narrow the definition and never widen it (2.101152ms)
✔ role.launch stays within-role only for the instance the launch block names (4.952572ms)
✔ limits.authority without role.launch leaves the launcher with no launch block (1.86313ms)
✔ limits.authority narrows cross-role actions too (1.037525ms)
✔ classify (1.084244ms)
✔ the record carries what the broker and launcher need (1.279123ms)
✔ digest: key order doesn't matter, any value change does (8.406016ms)
✔ refusals (1.933005ms)
✔ the four shipped version 2 roles load (3.68944ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.886973ms)
✔ shipped authority follows the note's table (0.82845ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (1.352113ms)
✔ version 1 files keep loading with no authority (0.921487ms)
✔ the conductor policy isn't a role (0.176306ms)
✔ a missing role file is exit 4, a symbolic link too (0.352647ms)
✔ version 2 refusals (1.340866ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (2.306152ms)
✔ messaging: optional, role definitions and human, never the tester (1.46686ms)
✔ credentials: Gitea scopes (0.719106ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.991797ms)
✔ credentials: services (0.483901ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.574639ms)
✔ every key names known layers and a merge rule (0.921441ms)
✔ unknown keys and wrong layers refuse (0.852618ms)
✔ types (2.110047ms)
✔ merge: defaults, then the most specific layer wins (0.292852ms)
✔ merge: limits only narrow, and provenance lists each source (0.329986ms)
✔ merge doesn't change its inputs (0.154104ms)
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.181177ms)
ℹ tests 112
ℹ suites 0
ℹ pass 112
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1921.103424
@@ -0,0 +1,115 @@
npm notice run test
npm notice run node --test tests/*.test.mjs
✔ sessionModel: agent vars win, then the system's execution settings (16.589734ms)
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (6.009037ms)
✔ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools (3.021573ms)
✔ a chief-of-staff bundle lists end_run and change_organization as typed and nothing untyped, for either harness (5.98927ms)
✔ the stop line names end_run for a role.end holder, and every other role keeps today's text (8.2496ms)
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (2.799318ms)
✔ the prompt's built-in tools line is the harness's own names, as the manifest lists them (7.492133ms)
✔ the prompt names the project clones in the workspace, and none when there are none (7.184873ms)
✔ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (6.058187ms)
✔ the prompt tells the pm to ask the chief of staff for sessions, and no other role (6.735975ms)
✔ a bundle is written once: an existing file refuses (1.671908ms)
✔ a path with a single quote can't go into the hook command (1.542757ms)
✔ web grant on pi: the bundle has web_search and web_fetch, and no built-in web tool (2.499719ms)
✔ no web grant on pi: no web tool in the bundle, and the gate blocks a web call (1.586598ms)
✔ web grant on claude-code: the bundle has web_search and web_fetch, and no built-in web tool (1.521689ms)
✔ no web grant on claude-code: no web tool in the bundle, and the gate blocks a web call (1.312378ms)
✔ a granted session's gate lets the typed web tools through and still blocks the built-ins (1.437709ms)
✔ search-source searxng-url: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing (4.736504ms)
✔ search-source searxng-bundled: a granted role gets the typed broker tools and no harness web tool; an ungranted role gets nothing (3.859539ms)
✔ search-source harness-builtin: claude-code gets WebSearch and WebFetch and no typed web tool, only when granted (6.106546ms)
✔ search-source harness-builtin is refused for a granted pi role, which has no built-in web tool (1.620203ms)
✔ allow exits 0, a deny exits 2 with the reason on stderr (135.69278ms)
✔ a missing or wrong policy, or a bad event, exits 2 (84.638933ms)
✔ the bundle's wrapped command: a missing gate or node still blocks (1085.025941ms)
✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (813.987993ms)
✔ claude: the hook alone blocks a path outside the workspace (469.48787ms)
✔ claude: a second turn resumes the first turn's session (789.345424ms)
✔ claude adapter: --restricted is always passed (4.706486ms)
✔ claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable (20.779293ms)
✔ claude adapter: a directory pin exits 2 before it runs; a pin with a space runs (5.85967ms)
✔ claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do (784.657738ms)
✔ claude: a missing hook or MCP file refuses before claude starts (7.248841ms)
✔ pi: policy tools and typed tools pass, anything else is blocked (4.190298ms)
✔ claude: builtins map from pi names, typed tools need the mcp prefix (0.903565ms)
✔ file tool paths must resolve inside the workspace (1.691876ms)
✔ pi's own path normalisation can't be used to step out (1.348682ms)
✔ a symlink inside the workspace that points out is outside (1.245164ms)
✔ a dangling symlink is refused at any depth, in both harnesses (4.33735ms)
✔ read is checked under every spelling pi's read would open, in both harnesses (20.479722ms)
✔ other spellings cover directories, dangling links and pi's cwd (2.903043ms)
✔ a relative path climbs from the workspace's real path, in both harnesses (4.804469ms)
✔ claude path fields per tool (0.789696ms)
✔ glob patterns stay inside the workspace (0.625886ms)
✔ a path that can't be checked is blocked (0.781287ms)
✔ fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX (226.870232ms)
✔ fetch_review's have orders the working refs newest first within each clone, one clone after another (156.273168ms)
✔ a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period (1808.765673ms)
✔ once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too (304.245233ms)
✔ initialize, ping and tools/list (51.337899ms)
✔ tools/call goes through the tool socket; a refusal is an isError result (39.470416ms)
✔ replies are compact JSON; over the cap a tool error names the size and the cap (37.211358ms)
✔ unknown tools and methods are JSON-RPC errors and never reach the socket (34.302039ms)
✔ a missing argument is a usage error (26.611958ms)
✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (382.871241ms)
✔ pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks (318.057616ms)
✔ pi: a write through a dangling symlink is blocked, and nothing appears outside (309.8411ms)
✔ pi: a read is refused when pi would open another spelling outside (309.066895ms)
✔ pi: a relative path climbs from the real path of a workspace behind a symlink (312.53187ms)
✔ pi: a relative path climbs from the real path of a dataRoot behind a symlink (321.183468ms)
✔ pi: a missing extension refuses before any model call (6.659016ms)
✔ pi: an extension without its configuration fails pi's start (265.860224ms)
✔ founderCheck: founder variables, then a needed service without a usable token (1.653052ms)
✔ turnRequest names the sender, class, reply and decision (0.310067ms)
✔ turnRequest says a tester's message is a test and never Jason's instruction (0.298344ms)
✔ turnRequest shows the message's own sent time, a day old as well as new (0.207957ms)
✔ stateBlock names the launch state with its time and event, and the open tasks with the cap (0.336237ms)
✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (280.480981ms)
✔ launches revoked and then restored: the turn's block reads allowed and gives the restore time (300.027621ms)
✔ a task closed on the tracker is missing from the next turn's open list (337.358186ms)
✔ a message read a day after it was sent shows the time it was sent (197.32051ms)
✔ a decision the human resolves becomes a turn citing the decision and the answer (239.225677ms)
✔ a failed state read fails the turn: no adapter runs, the sender hears why, the message is read (229.132569ms)
✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (118.514125ms)
✔ typed tools carry the runner's capability; launch goes to the host's launch socket (488.216222ms)
✔ end_run and change_organization reach the broker with the tool's arguments, and refusals keep their codes (585.844312ms)
✔ a RESULT gets no automatic reply; failed turns reply with the reason (1331.387289ms)
✔ the bus's launch notice is one turn with no reply, and is not taken again (265.311336ms)
✔ SIGTERM during a turn kills the turn's process group and still exits 0 (183.984409ms)
✔ founder credentials stop before the claim (20) (214.340847ms)
✔ a refused claim exits 21; an ended run's capability exits 22 (216.076073ms)
✔ the launch ending under a running session exits 22 (148.969161ms)
✔ a broker that stays unreachable exits 23 after brokerRetries polls (283.61082ms)
✔ a broker that is down at the claim exits 23, not 21 (94.730793ms)
✔ no capability, or a malformed one, on stdin exits 2 (217.332336ms)
✔ a missing or malformed policy exits 2 before the claim (132.7317ms)
✔ every role sees read_spec, record writer follows its grant, citation schemas stay strict (17.674808ms)
✔ typed socket forwards read content/history and agent record writes, and cannot write a spec (245.454217ms)
✔ the PM gets its task verbs and the reads, and no launch (7.901106ms)
✔ the chief of staff gets launch, send_message, end_run and change_organization, and no task verbs (2.69855ms)
✔ request_staff is the pm's alone: staff.request over message.send, roles and counts bounded (13.318784ms)
✔ end_run sends role and reason, no decision; change_organization sends revision and the organization keys (2.000731ms)
✔ the organization keys change_organization takes are the broker's ORGANIZATION_KEYS (0.130629ms)
✔ a role without role.end or organization.change gets neither tool, and each tool follows its own grant (7.299053ms)
✔ the assistant gets send_message, the reads and the web tools (it holds the web grant), no launch (1.759234ms)
✔ list_requirements reads the requirement ids, for a task.create holder with a tracker (2.255349ms)
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (1.150658ms)
✔ a coder with a tracker gets its own task tools and no task_reassign (1.311912ms)
✔ launch only when the business's launch block names the instance as launcher (1.919477ms)
✔ an action outside the instance's authority has no tool (1.190732ms)
✔ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (4.347745ms)
✔ callTool: one JSON line out, the result back, a refusal rejects (10.472069ms)
✔ list_tasks takes the broker's filters and pages; get_task takes a task_ref (2.199725ms)
✔ callTool sends list_tasks done false unless the session names done; other tools as given (2.627958ms)
✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.58142ms)
✔ web_search's query maxLength is the web layer's SEARCH_MAX_QUERY_CHARS (2.431986ms)
ℹ tests 105
ℹ suites 0
ℹ pass 105
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 12685.052118
@@ -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,15 @@
land-work rc=0 start=2026-10-16T19:06:10Z end=2026-10-16T19:06:22Z
bus rc=0 start=2026-10-16T19:06:22Z end=2026-10-16T19:06:36Z
business rc=0 start=2026-10-16T19:06:36Z end=2026-10-16T19:06:38Z
harness rc=0 start=2026-10-16T19:06:38Z end=2026-10-16T19:06:51Z
tasks rc=0 start=2026-10-16T19:06:51Z end=2026-10-16T19:06:56Z
test-auth rc=0 start=2026-10-16T19:06:56Z end=2026-10-16T19:06:57Z
test-conductor rc=0 start=2026-10-16T19:06:57Z end=2026-10-16T19:07:02Z
test-config rc=0 start=2026-10-16T19:07:02Z end=2026-10-16T19:07:04Z
test-discord rc=0 start=2026-10-16T19:07:04Z end=2026-10-16T19:07:10Z
test-extension-package rc=0 start=2026-10-16T19:07:10Z end=2026-10-16T19:07:12Z
test-foundation rc=0 start=2026-10-16T19:07:12Z end=2026-10-16T19:07:51Z
test-queue rc=0 start=2026-10-16T19:07:51Z end=2026-10-16T19:08:21Z
test-release rc=0 start=2026-10-16T19:08:21Z end=2026-10-16T19:08:31Z
test-task rc=0 start=2026-10-16T19:08:31Z end=2026-10-16T19:08:58Z
gate done 2026-10-16T19:08:58Z
@@ -0,0 +1,107 @@
npm notice run test
npm notice run node --test tests/*.test.mjs
✔ the boot config is checked before anything starts (101.614504ms)
✔ a business with no tracker entry refuses task verbs (129.169757ms)
✔ credential.expiring and .expired are recorded once per instance (258.744506ms)
✔ a token file that changes on disk records credential.changed (108.843789ms)
✔ autostart polls, reconciles and retries a startup the tracker was down for (131.319585ms)
✔ a refusal a restart must clear is not retried by the poll (91.58414ms)
✔ a poll that fires while two are queued is dropped (112.14761ms)
✔ close waits for a running verb and refuses one that has not started (201.693702ms)
✔ stopped bus resumes an empty task cursor after a long outage, exactly once (165.117035ms)
✔ persisted IDs avoid replay, exclude bots and bound rows to observed new comments (194.044141ms)
✔ commit followed by crash before cache advance neither loses nor duplicates a comment (182.384355ms)
✔ failed event insert rolls back cursor and a retry still emits the comment once (133.82299ms)
✔ initial cursor accepts a comment within the stated one-minute clock skew margin (112.252032ms)
✔ the bundled Vikunja is the pinned upstream image the runbook names (0.737342ms)
✔ every published port is on 127.0.0.1, and no secret is in the file (0.268227ms)
✔ the committed table loads frozen; its verbs are the broker task verbs (1.487894ms)
✔ a write names a verb the table has as its own key, not one it inherits (0.515389ms)
✔ the schema refuses any other shape (2.257847ms)
✔ the Field table in the slice 1 plan names the verbs each field is written by (0.656519ms)
✔ each verb refuses, asks for a decision and writes as the table and the role files say (1206.859109ms)
✔ the list holds the PRD's requirement ids, in its order, at its version (1.879556ms)
✔ the committed list loads frozen (1.062923ms)
✔ the schema refuses any other shape (3.847529ms)
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (366.857802ms)
✔ the project list holds recorded project objects in the recorded list envelope (130.243897ms)
✔ the recorded task bodies pass the checks S3 applies to every read (0.530815ms)
✔ the client works against the fake over real HTTP with the platform fetch (251.252198ms)
✔ a correct install starts, and the first reconcile records tasks that already exist (125.64212ms)
✔ verbs refuse while a business is starting and after startup refused it (138.778212ms)
✔ startup refuses a token that can do more than its role needs (425.629909ms)
✔ startup refuses an unsupported version and flags an untested one (294.804244ms)
✔ startup refuses a board that the runbook did not install (363.687703ms)
✔ startup refuses a project the sync bot cannot read (80.725738ms)
✔ startup refuses a configured label the pm bot cannot see (85.94252ms)
✔ startup refuses an expired credential and a missing sync credential (166.215618ms)
✔ an unreachable tracker refuses with tracker-unavailable (78.862908ms)
✔ an edit in the UI is recorded once, with the fields that changed (220.782451ms)
✔ a move between open buckets is seen on the board, though updated does not change (170.365661ms)
✔ a person's comment is counted and a bot's is not (310.03269ms)
✔ the hourly reconcile catches a comment through comment_count (217.734051ms)
✔ a task closed in the UI leaves the open view with its done bucket (390.263176ms)
✔ a task that leaves the board is recorded as deleted, moved or out of reach (271.654553ms)
✔ a poll that read before a verb wrote does not overwrite the verb (169.009731ms)
✔ a tracker fault during a tick is reported and the next tick catches up (151.074632ms)
✔ a malformed answer refuses the tick with tracker-shape (129.862107ms)
✔ no token value reaches the database, the log or a refusal (281.41211ms)
✔ the first look at a task counts only comments inside the window (160.249442ms)
✔ with descendants off the adapter reads no project list and no child, as in v0 (134.082974ms)
✔ a three-level tree is read per project, and a project outside it is not (117.71905ms)
✔ a watch-only child keeps the business ready, and a failed child read refuses as transient (243.016961ms)
✔ a root without the installed buckets refuses with descendants on (101.068442ms)
✔ the project list refuses without read_all, and skips archived projects and saved filters (186.867704ms)
✔ descendants must be true or false (86.834067ms)
✔ a move inside the tree is a moved tombstone plus a snapshot under the new ref (180.774658ms)
✔ a child made after startup joins the tree at the next reconcile (149.913669ms)
✔ a child that leaves the tree has its tasks recorded missing, and the ledger keeps their history (169.055109ms)
✔ a child deleted or hidden between reconciles drops out at the next tick, and the root still refuses (202.203564ms)
✔ a reconcile that fails keeps the previous tree (126.932815ms)
✔ a closed task the cursor missed takes its own project's done bucket (127.759705ms)
✔ closed tasks read at startup take their own project's done bucket (108.338935ms)
✔ a closed task the cursor returns takes its own project's done bucket (122.787774ms)
✔ a task moved out of a child that leaves the tree at the same time is moved, not no-access (126.559396ms)
✔ a task in a bucket outside the five keeps its raw bucket id (108.963783ms)
✔ bucket 0 on a watch-only task never becomes a bucket or a state (199.832645ms)
✔ a child whose kanban view is deleted still refuses the tick, and the reconcile reads it watch-only (133.857834ms)
✔ a child dropped at a tick comes back at the next reconcile once the bot reads it again (116.1668ms)
✔ task.create needs a recorded human request and a requirement id (195.701596ms)
✔ task.create takes only an approved requirement id (212.69243ms)
✔ task.create cites the request a received message carries, not the message's id (212.867319ms)
✔ only labels named in the business file can be written (217.617703ms)
✔ task.schedule sets and clears a due date and relations (263.303296ms)
✔ assign and reassign move the role bots and record task.assigned (299.807552ms)
✔ task.update.assigned is for the assignee and records task.state (307.667371ms)
✔ a wrong expected digest records task.conflict and writes nothing (182.599541ms)
✔ a cross-role verb needs a resolved decision, used once (242.684397ms)
✔ task.close needs a verdict; after it every verb refuses with task-done (230.518282ms)
✔ a task with pushed work closes only citing an approve verdict for its latest push, and a refusal writes nothing (248.906897ms)
✔ a lost answer is settled by a re-read and never retried (179.231835ms)
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (113.131411ms)
✔ a task the sync bot cannot read refuses and records nothing (92.848671ms)
✔ verbs and polls for one business run one at a time (170.344696ms)
✔ a due date with milliseconds is written to the second (152.704046ms)
✔ every write landed and the final read failed: the verb succeeds and records what it wrote (105.256561ms)
✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (79.164523ms)
✔ a create whose final read fails succeeds and records task.created (88.880903ms)
✔ an edit between the last write and the final read shows as external on the next poll (105.351739ms)
✔ task.created is recorded when a later label write fails (114.438271ms)
✔ every verb acts in an installed child, on the child's own board (249.28884ms)
✔ task.create refuses a project outside the tree, a watch-only one and one that is not an id (154.758275ms)
✔ with descendants off, task.create takes only the root project (78.733091ms)
✔ a verb on a watch-only task, or a relation to one, refuses with project-not-installed (104.40691ms)
✔ a related task is read before authority: in another project than its ref names, task-moved; missing, task-not-found (133.846352ms)
✔ a task moved to another project of the tree refuses its old ref with task-moved and takes its new one (90.400152ms)
✔ a verb and a create in the child record the final read of the child's board (83.363919ms)
✔ a create in the child whose final read fails records the child's todo bucket (91.569991ms)
✔ a lost answer in the child is settled by a re-read of the child's board (111.50291ms)
✔ every related task is read before authority, not only the first (91.890504ms)
ℹ tests 97
ℹ suites 0
ℹ pass 97
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5121.076756
@@ -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: 839691 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
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
# Row 139 round 2 mutants. 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-r2"
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 && !this.#hardWait(s, a.action, a.target))", "(cls === 'gated' && !this.#hardWait(s, a.action, a.target))", "bus"),
("m2-no-hard-wait-check", "packages/bus/src/broker.mjs",
"(cls === 'gated' && spec !== null && !this.#hardWait(s, a.action, a.target))", "(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"),
("m6-no-assistant-check", "packages/bus/src/broker.mjs",
" return action === 'role.revoke' && END_PROTECTED.includes(", " return false && action === 'role.revoke' && END_PROTECTED.includes(", "bus"),
("m7-no-chief-exemption", "packages/bus/src/broker.mjs",
" if (b.roles[s.role]?.definition === STAFF_TO || r?.via === 'cli') return;", " if (r?.via === 'cli') return;", "bus"),
("m8-no-human-exemption", "packages/bus/src/broker.mjs",
" if (b.roles[s.role]?.definition === STAFF_TO || r?.via === 'cli') return;", " if (b.roles[s.role]?.definition === STAFF_TO) return;", "bus"),
("m9-no-revoke-protection", "packages/bus/src/broker.mjs",
" if (action === 'role.revoke') this.#protectedEnd(s, target, r);\n", "", "bus"),
]
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,548 @@
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.209063ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (232.505499ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (324.388036ms)
✔ agents: working wins over waiting, and Input needed still shows (172.528886ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (209.523371ms)
✔ agents: state reads only its own business, even for a run name both use (183.478748ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (206.357381ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (212.441478ms)
✔ agents: stranded is oldest first and capped at 50 for a role (942.201583ms)
✔ agents: stranded is per role and per business (346.719632ms)
✔ agents: a message routed to a held role and not yet received is not stranded (140.263019ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (302.806447ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (195.118055ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (267.957302ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (249.253042ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (249.715887ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (333.54321ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (173.546237ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (272.457926ms)
✖ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (235.608694ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (196.163378ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (173.648315ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (283.751342ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (170.350529ms)
✔ task action subjects and linked decision trail are complete and ordered (185.315389ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (114.991209ms)
✔ business isolation includes inherited object names and cross-business message references (206.795008ms)
✖ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (313.470183ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (173.405043ms)
✖ revocation permanently bars the old run from reclaiming first, including after broker restart (173.175877ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (159.074829ms)
✔ both arbiters require human resolution when their cross-role route is themselves (236.89661ms)
✖ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (130.347354ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.460588ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (265.837334ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (326.788662ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (254.5635ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (232.617093ms)
✖ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (260.419626ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (285.94329ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (283.098115ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (268.90461ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (339.547857ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (315.133348ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (376.857077ms)
✔ 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 (545.962216ms)
✔ 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 (993.26722ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (127.564877ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (121.720795ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (870.077384ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1585.367335ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (812.270382ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (808.219977ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (994.606937ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (115.452784ms)
✔ credentials refuses a role session and any argument (176.339113ms)
✔ credentials reads the status on each request, and a broker without one lists none (212.004944ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (223.09741ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.467398ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (9.124614ms)
✔ expiry refuses use and env references never become client data (1.429586ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (2.039149ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.529591ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.577009ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (489.805667ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (457.89515ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (406.149427ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (467.141019ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (511.04254ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (407.622124ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (469.211838ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (456.389471ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (415.22617ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (546.942974ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (498.733247ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (472.984736ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (432.712611ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (474.579992ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (381.548149ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (234.363038ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (269.929675ms)
✔ a human withdraw sends one message with the note (245.184852ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (289.621003ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (289.492574ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (286.161477ms)
✔ a raising role nobody holds at the close gets the message on the next claim (274.548273ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (277.321322ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (308.367184ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (294.53679ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (355.058485ms)
✔ a refused supersede withdraws nothing (280.256618ms)
✖ the assistant's within-role and gated raises still work (293.336606ms)
✔ roles lists open decisions routed to a role, and never the human inbox (221.751062ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (304.399911ms)
✔ recent gives the closing event as closed when the decision was seen first (292.545317ms)
✔ recent stops at RECENT rows (818.117529ms)
✔ every row is what trail already gives the same session for that id (283.023233ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (467.326804ms)
✔ decisions is a read: one business, strict arguments, nothing written (419.325023ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (145.903096ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (157.248133ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (144.942168ms)
✔ endLaunch leaves a claim another run took alone (176.914671ms)
✔ refuse records action.refused against the caller with the code only (136.925229ms)
✔ launches off refuses role.launch with launch-revoked until launches on (195.267658ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (315.938458ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.256965ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (113.648864ms)
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (738.583674ms)
✖ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (193.817365ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (258.630492ms)
✖ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (286.741959ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (262.117067ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (281.453665ms)
✔ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (276.657622ms)
✔ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (243.567843ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (233.921991ms)
✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (289.946983ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.500416ms)
✔ process reader gets own kernel identity without exposing environment values (0.454078ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.654305ms)
✔ real pid 1 remains inspectable when its environment is protected (0.238882ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (216.220629ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (171.140743ms)
✔ a message to a running role sends no notice and its result is unchanged (146.223371ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (262.073494ms)
✔ a run launched but not yet claimed counts as no holder (143.05645ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (270.62302ms)
✔ a business with no launch block tells nobody and says there is no launcher (128.169775ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (334.792065ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (237.637821ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (293.235811ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (432.96204ms)
✖ within-role sends cite an open gated launch decision without spending it or naming it in grants (175.749813ms)
✔ missing and foreign-business citations refuse and roll back message and grant (186.291548ms)
✔ cross-role sends still need a matching resolved decision and consume it once (258.017308ms)
✔ messages pages all human messages newest first without writes; read state is history (2644.748568ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (258.843013ms)
✔ views messages passes only its paging arguments through (0.441454ms)
✔ messages isolates businesses even with a valid foreign cursor (184.679882ms)
✔ the assistant sends to the user and to the chief of staff only (316.720813ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (359.969881ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (369.193178ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (365.448155ms)
✔ a sender-side list alone refuses a send to a role that has no list (343.143681ms)
✔ a target-side list alone refuses a send from a role that has no list (347.621413ms)
✔ the sender list is checked before the target list (304.416027ms)
✔ roles without the field behave exactly as before (447.891191ms)
✔ a business with no role definitions at all is unlimited (364.823196ms)
✔ a malformed messaging field refuses at boot (123.494233ms)
✖ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (350.68425ms)
✖ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (369.812726ms)
✔ review requests and verdicts between roles without the field are unchanged (385.523649ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (206.548693ms)
✔ 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 (477.805657ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (447.341676ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (202.18202ms)
✔ startup token refusal returns safe code without value or partial listening broker (66.825949ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (216.296994ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (226.086374ms)
✔ trusted host registers later launches; socket clients never have a registration verb (257.640639ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (66.06471ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (215.929258ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (139.034596ms)
✔ D2: spec.read refuses without content if its role is released during the read (158.609043ms)
✔ D3: spec.read secret check refuses flagged content without content (151.207735ms)
✔ D3: spec.read secret check refuses flagged subject without content (172.629218ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.88414ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (213.559974ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (198.383577ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (234.496094ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (246.031484ms)
✔ a message with no reply gives an empty list (195.981932ms)
✔ replies come oldest first and stop at REPLIES (755.004109ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (316.027772ms)
✔ strict arguments; a role session gets human-required (378.9497ms)
✔ views passes replies through as one call with the id (0.247546ms)
✔ requirements returns the committed list to a holding role session, and no one else (264.574238ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (163.526616ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (174.098873ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (254.952073ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (246.9214ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (278.979795ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (385.900582ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (236.649101ms)
✔ review.verdict answers one request once, to the role that asked (325.437278ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (425.856773ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (286.214937ms)
✔ v3b prototype refusals, views and append-only mutations (1516.925731ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (290.04078ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (226.914715ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (152.977202ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (302.485627ms)
✔ class drift gated to cross-role refuses before consumption (271.045375ms)
✔ class drift cross-role to gated refuses before consumption (238.007153ms)
✔ class drift gated to within-role refuses before consumption (284.101977ms)
✔ class drift cross-role to within-role refuses before consumption (301.421002ms)
✔ class drift within-role to gated refuses before consumption (227.140635ms)
✔ class drift within-role to cross-role refuses before consumption (239.48818ms)
✖ message.send consumes approval and prevents a later send or authorize (205.901701ms)
✖ role.revoke consumes approval and prevents a later revoke or authorize (236.47759ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1398.536488ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (259.527997ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (187.212586ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (227.107554ms)
✔ client disconnect during await tolerates late reply (171.717201ms)
✔ client disconnect during await tolerates late refusal (176.23351ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (383.048233ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (296.584587ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (309.670781ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (216.73386ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (282.225965ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (257.935884ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (319.807841ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (178.224726ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (497.574518ms)
✔ 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 (438.753089ms)
✔ 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 (475.590201ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (254.616913ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (601.145905ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (450.112043ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (239.234805ms)
✔ a business that never switched launches reads allowed with no time and no event (136.287741ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (161.325042ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (151.77424ms)
✔ a tombstone is not open (154.23953ms)
✔ the open list stops at the cap and says so; total still counts every open task (152.194077ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (177.818609ms)
✔ a human session reads the same state, with the same cap (168.46382ms)
✔ reading state writes nothing: every table keeps its row count (160.876594ms)
✔ creates private WAL store and excludes a second writer until explicit close (142.067892ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (171.440566ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (187.276377ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (179.302485ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (126.822002ms)
✔ async transactions refuse before invoking their function (136.531271ms)
✔ the session default fits under the reply cap and pages to the end with next (322.126509ms)
✔ the short form: no description, assignees by role, every listed key (277.724369ms)
✔ each filter, alone and together, and total counts every match (345.177804ms)
✔ task_ref reads one task in full, as the whole board has it (310.620894ms)
✔ no arguments is the read the Console and mosaic make, unchanged (333.778934ms)
✔ an unknown argument or a bad value refuses invalid-request (914.67991ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (356.74357ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (370.384186ms)
✔ an empty business and one with snapshots but no task.created read cleanly (136.913047ms)
✔ recordTask keeps sync reads and a role write apart (217.840172ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (117.527317ms)
✔ a bad entry refuses the whole record (123.660141ms)
✔ taskView reads the projection for one business (129.229054ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (283.022691ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (474.559224ms)
✔ without an adapter the server refuses every task verb (240.281ms)
✔ the runtime refuses an invalid adapter and closes a valid one (195.453159ms)
✔ the process loads the S3 adapter from plain-data trackers (318.958349ms)
✔ the tester binds only when the business sets bus.tester to exactly true (881.569305ms)
✔ a business may not name a role tester, so the tester address is never a role (280.21088ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (584.595068ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (226.863369ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (288.309212ms)
✔ the tester reads the human views a reader reads (178.790923ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.387354ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.388386ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.214661ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (120.544353ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (898.798192ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1089.297009ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (836.370984ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (879.330806ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (337.594942ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1232.744824ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (361.485022ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3539.320929ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (100.523538ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (146.197415ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (156.856414ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (218.460097ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (168.951096ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (138.408792ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (166.829984ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (119.239329ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (156.514508ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (503.026555ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (177.13793ms)
✔ two wire claims serialize; a lost reply never automatically retries (191.245605ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (130.956871ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.795244ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (151.219501ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (163.859346ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (191.894449ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (210.648802ms)
✔ a broker with no web service refuses a granted role (184.794892ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (230.050386ms)
✔ a session that is not the role holder is refused (118.583963ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (372.50072ms)
✔ web.search with no search source configured is a refused result, not an error (168.043802ms)
✔ a search source that fails with its own refusal, or that throws, is handled (196.281873ms)
✔ a slow search source ends in a refusal at the deadline (192.972609ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (142.414716ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (255.215933ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.303537ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (215.209951ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (177.278167ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (448.014738ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (173.774003ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (197.670636ms)
✔ 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 (169.60216ms)
ℹ tests 306
ℹ suites 0
ℹ pass 293
ℹ fail 13
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 11668.717492
✖ failing tests:
test at tests/broker.test.mjs:96:1
✖ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (235.608694ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (313.470183ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (173.175877ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (130.347354ms)
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 (260.419626ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (293.336606ms)
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 (193.817365ms)
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 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 (286.741959ms)
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 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 (175.749813ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (350.68425ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (369.812726ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (205.901701ms)
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:1321:38)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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 (236.47759ms)
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:1321:38)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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'
}
@@ -0,0 +1,414 @@
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 (207.045576ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (240.730618ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (400.426309ms)
✔ agents: working wins over waiting, and Input needed still shows (241.186783ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (227.640425ms)
✔ agents: state reads only its own business, even for a run name both use (217.459729ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (339.147567ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (234.443411ms)
✔ agents: stranded is oldest first and capped at 50 for a role (1160.988975ms)
✔ agents: stranded is per role and per business (444.425324ms)
✔ agents: a message routed to a held role and not yet received is not stranded (204.048929ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (400.817556ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (252.91469ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (356.823746ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (261.714086ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (301.070873ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (494.982357ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (227.476714ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (331.496759ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (317.475304ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (252.855236ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (186.308451ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (396.530822ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (209.610641ms)
✔ task action subjects and linked decision trail are complete and ordered (212.960747ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (138.621254ms)
✔ business isolation includes inherited object names and cross-business message references (212.659127ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (473.802074ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (200.683688ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (290.652462ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (181.833296ms)
✔ both arbiters require human resolution when their cross-role route is themselves (360.118604ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (225.364973ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.029828ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (351.786782ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (408.372062ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (325.461509ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (261.273231ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (391.30168ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (326.909409ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (299.613149ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (294.947088ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (507.98134ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (393.185243ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (537.288404ms)
✔ 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 (686.321836ms)
✔ 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 (1360.340377ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (190.539112ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (163.646697ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (1125.781028ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (2001.428264ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (1087.48955ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (1122.506421ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (1179.1195ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (157.842189ms)
✔ credentials refuses a role session and any argument (198.543334ms)
✔ credentials reads the status on each request, and a broker without one lists none (266.291789ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (233.135179ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (3.379668ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (1.984961ms)
✔ expiry refuses use and env references never become client data (0.628421ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.700217ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.450596ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.563657ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (635.067474ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (571.117528ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (548.887439ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (532.651948ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (661.101475ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (496.084534ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (646.570057ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (572.868141ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (606.54118ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (685.520341ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (569.851264ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (435.295121ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (500.592822ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (450.150636ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (367.830553ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (320.875593ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (310.500453ms)
✔ a human withdraw sends one message with the note (324.916561ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (318.320735ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (406.530029ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (339.605242ms)
✔ a raising role nobody holds at the close gets the message on the next claim (311.599134ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (304.854037ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (402.535541ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (373.325698ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (423.433208ms)
✔ a refused supersede withdraws nothing (340.082598ms)
✔ the assistant's within-role and gated raises still work (438.814506ms)
✔ roles lists open decisions routed to a role, and never the human inbox (329.815276ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (349.403564ms)
✔ recent gives the closing event as closed when the decision was seen first (393.6446ms)
✔ recent stops at RECENT rows (999.382826ms)
✔ every row is what trail already gives the same session for that id (319.022347ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (620.404494ms)
✔ decisions is a read: one business, strict arguments, nothing written (479.927691ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (210.229485ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (158.828482ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (205.471246ms)
✔ endLaunch leaves a claim another run took alone (221.047633ms)
✔ refuse records action.refused against the caller with the code only (215.973884ms)
✔ launches off refuses role.launch with launch-revoked until launches on (210.477091ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (418.973783ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.261835ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (178.006801ms)
✖ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (263.442703ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (545.20383ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (283.185111ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (414.840923ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (290.166818ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (276.660988ms)
✖ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (192.873554ms)
✖ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (258.475823ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (384.554335ms)
✖ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (296.16808ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.331486ms)
✔ process reader gets own kernel identity without exposing environment values (0.41676ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.528474ms)
✔ real pid 1 remains inspectable when its environment is protected (0.225566ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (296.051901ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (208.176123ms)
✔ a message to a running role sends no notice and its result is unchanged (155.191391ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (337.597194ms)
✔ a run launched but not yet claimed counts as no holder (140.127411ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (308.673504ms)
✔ a business with no launch block tells nobody and says there is no launcher (225.165733ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (402.877986ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (308.306468ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (339.112302ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (497.887477ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (265.010064ms)
✔ missing and foreign-business citations refuse and roll back message and grant (278.272304ms)
✔ cross-role sends still need a matching resolved decision and consume it once (262.376033ms)
✔ messages pages all human messages newest first without writes; read state is history (3329.052496ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (347.644247ms)
✔ views messages passes only its paging arguments through (0.279254ms)
✔ messages isolates businesses even with a valid foreign cursor (239.800494ms)
✔ the assistant sends to the user and to the chief of staff only (443.645663ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (411.797456ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (408.68946ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (511.78699ms)
✔ a sender-side list alone refuses a send to a role that has no list (418.215222ms)
✔ a target-side list alone refuses a send from a role that has no list (495.486368ms)
✔ the sender list is checked before the target list (366.343571ms)
✔ roles without the field behave exactly as before (573.116118ms)
✔ a business with no role definitions at all is unlimited (548.468314ms)
✔ a malformed messaging field refuses at boot (144.278541ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (536.179968ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (468.12324ms)
✔ review requests and verdicts between roles without the field are unchanged (364.159869ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (213.018608ms)
✔ 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 (651.574035ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (466.516339ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (219.661513ms)
✔ startup token refusal returns safe code without value or partial listening broker (66.243192ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (230.51212ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (247.045555ms)
✔ trusted host registers later launches; socket clients never have a registration verb (278.257435ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (64.483107ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (264.588454ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (181.810841ms)
✔ D2: spec.read refuses without content if its role is released during the read (173.763999ms)
✔ D3: spec.read secret check refuses flagged content without content (159.768582ms)
✔ D3: spec.read secret check refuses flagged subject without content (301.299358ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.255795ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (234.04527ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (224.621171ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (247.288411ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (250.412597ms)
✔ a message with no reply gives an empty list (315.000599ms)
✔ replies come oldest first and stop at REPLIES (1016.568152ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (349.864971ms)
✔ strict arguments; a role session gets human-required (507.859858ms)
✔ views passes replies through as one call with the id (0.242664ms)
✔ requirements returns the committed list to a holding role session, and no one else (397.470438ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (194.352227ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (224.41021ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (421.254809ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (289.856036ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (412.094484ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (464.423566ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (349.195038ms)
✔ review.verdict answers one request once, to the role that asked (445.385593ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (526.363102ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (403.655774ms)
✔ v3b prototype refusals, views and append-only mutations (2025.616638ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (349.661865ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (258.563455ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (255.360944ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (389.762074ms)
✔ class drift gated to cross-role refuses before consumption (263.836532ms)
✔ class drift cross-role to gated refuses before consumption (356.624434ms)
✔ class drift gated to within-role refuses before consumption (367.311167ms)
✔ class drift cross-role to within-role refuses before consumption (380.225974ms)
✔ class drift within-role to gated refuses before consumption (334.50848ms)
✔ class drift within-role to cross-role refuses before consumption (301.497996ms)
✔ message.send consumes approval and prevents a later send or authorize (289.099547ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (289.306581ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1780.691754ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (352.14265ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (232.673616ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (253.520418ms)
✔ client disconnect during await tolerates late reply (237.213828ms)
✔ client disconnect during await tolerates late refusal (217.052799ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (436.882636ms)
✖ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (321.389474ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (372.416051ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (243.914158ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (383.343444ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (368.887325ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (362.933366ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (177.758426ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (647.859234ms)
✔ 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 (537.288283ms)
✔ 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 (594.041065ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (292.446429ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (667.554132ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (464.7086ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (241.68112ms)
✔ a business that never switched launches reads allowed with no time and no event (153.125155ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (176.013629ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (179.77928ms)
✔ a tombstone is not open (229.713252ms)
✔ the open list stops at the cap and says so; total still counts every open task (182.432589ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (242.703994ms)
✔ a human session reads the same state, with the same cap (256.244377ms)
✔ reading state writes nothing: every table keeps its row count (178.756336ms)
✔ creates private WAL store and excludes a second writer until explicit close (143.477187ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (258.573876ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (231.834738ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (288.285703ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (207.736974ms)
✔ async transactions refuse before invoking their function (111.731127ms)
✔ the session default fits under the reply cap and pages to the end with next (491.117221ms)
✔ the short form: no description, assignees by role, every listed key (330.19306ms)
✔ each filter, alone and together, and total counts every match (498.028131ms)
✔ task_ref reads one task in full, as the whole board has it (380.21512ms)
✔ no arguments is the read the Console and mosaic make, unchanged (277.583684ms)
✔ an unknown argument or a bad value refuses invalid-request (1074.438213ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (526.625893ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (471.998528ms)
✔ an empty business and one with snapshots but no task.created read cleanly (163.215908ms)
✔ recordTask keeps sync reads and a role write apart (293.63358ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (206.434274ms)
✔ a bad entry refuses the whole record (135.463527ms)
✔ taskView reads the projection for one business (138.928333ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (423.371193ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (453.500886ms)
✔ without an adapter the server refuses every task verb (243.915917ms)
✔ the runtime refuses an invalid adapter and closes a valid one (243.228487ms)
✔ the process loads the S3 adapter from plain-data trackers (374.782334ms)
✔ the tester binds only when the business sets bus.tester to exactly true (1224.497663ms)
✔ a business may not name a role tester, so the tester address is never a role (313.523652ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (616.382488ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (243.239663ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (279.433626ms)
✔ the tester reads the human views a reader reads (187.979755ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.221122ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.346209ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.2688ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (106.602819ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (905.337721ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1445.039198ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (894.699658ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (861.661835ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (408.407803ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1464.806819ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (368.924366ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3519.48498ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (103.080666ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (159.525411ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (151.320171ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (176.345598ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (154.795097ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (153.028598ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (150.623104ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (113.436568ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (149.259666ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (476.796882ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (264.280236ms)
✔ two wire claims serialize; a lost reply never automatically retries (251.788081ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (138.603902ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.873074ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (177.495406ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (261.491453ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (227.668823ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (260.721527ms)
✔ a broker with no web service refuses a granted role (154.365746ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (226.79117ms)
✔ a session that is not the role holder is refused (134.972672ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (427.046192ms)
✔ web.search with no search source configured is a refused result, not an error (154.16875ms)
✔ a search source that fails with its own refusal, or that throws, is handled (197.614408ms)
✔ a slow search source ends in a refusal at the deadline (194.401441ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (131.48511ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (233.003066ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.257227ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (208.520521ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (171.788083ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (458.283677ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (176.63443ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (185.719793ms)
✔ 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 (155.707523ms)
ℹ tests 306
ℹ suites 0
ℹ pass 301
ℹ fail 5
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 12897.612493
✖ 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 (263.442703ms)
AssertionError [ERR_ASSERTION]: spend {"project":"stack","spec_path":"spec/north-star.md","spec_commit":"94ad77f623676dff49a4274d13b388b71807c333"}
+ 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'
}
test at tests/hard-waits.test.mjs:126:1
✖ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (192.873554ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ 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:129:10)
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: 'reviewer' },
expected: { class: 'gated', route_to: 'human' },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at tests/hard-waits.test.mjs:141:1
✖ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (258.475823ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:144:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
test at tests/hard-waits.test.mjs:160:1
✖ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (296.16808ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ 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:165:10)
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: '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 (321.389474ms)
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:297:18)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1320:22)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966: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,384 @@
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 (146.701078ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (260.817711ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (396.009553ms)
✔ agents: working wins over waiting, and Input needed still shows (222.326641ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (257.542184ms)
✔ agents: state reads only its own business, even for a run name both use (219.807958ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (271.458603ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (242.177599ms)
✔ agents: stranded is oldest first and capped at 50 for a role (1012.576722ms)
✔ agents: stranded is per role and per business (451.131479ms)
✔ agents: a message routed to a held role and not yet received is not stranded (155.583777ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (335.627353ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (240.774445ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (321.684873ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (301.698893ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (303.937334ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (448.161807ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (188.238239ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (320.649862ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (322.512957ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (228.3145ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (221.499745ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (323.336826ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (228.075274ms)
✔ task action subjects and linked decision trail are complete and ordered (222.907906ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (146.672571ms)
✔ business isolation includes inherited object names and cross-business message references (224.911518ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (335.730502ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (181.229717ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (322.097238ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (182.335926ms)
✔ both arbiters require human resolution when their cross-role route is themselves (295.542121ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (225.243693ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.117548ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (309.141378ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (392.552929ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (295.018572ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (299.418368ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (301.459962ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (363.316282ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (304.892067ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (262.665582ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (382.794871ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (422.190803ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (456.995316ms)
✔ 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 (596.34087ms)
✔ 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 (1190.003732ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (167.954579ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (146.621789ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (1022.194563ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1790.247313ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (1046.323465ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (876.244466ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (1168.422563ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (119.075652ms)
✔ credentials refuses a role session and any argument (220.331551ms)
✔ credentials reads the status on each request, and a broker without one lists none (273.316901ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (242.983749ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.649569ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.869215ms)
✔ expiry refuses use and env references never become client data (1.17271ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.736197ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.2805ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (2.338512ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (573.840426ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (561.631226ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (483.672031ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (589.269609ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (514.576772ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (506.780869ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (599.350485ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (514.538746ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (465.526314ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (688.610395ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (551.277085ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (431.318732ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (599.285506ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (489.417021ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (396.19236ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (266.258785ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (291.665624ms)
✔ a human withdraw sends one message with the note (281.497502ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (341.842355ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (334.791501ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (380.371441ms)
✔ a raising role nobody holds at the close gets the message on the next claim (306.434621ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (266.780996ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (346.835636ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (352.628669ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (437.375646ms)
✔ a refused supersede withdraws nothing (341.643056ms)
✔ the assistant's within-role and gated raises still work (374.80254ms)
✔ roles lists open decisions routed to a role, and never the human inbox (256.872522ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (362.964659ms)
✔ recent gives the closing event as closed when the decision was seen first (360.006717ms)
✔ recent stops at RECENT rows (985.913621ms)
✔ every row is what trail already gives the same session for that id (299.46152ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (474.885195ms)
✔ decisions is a read: one business, strict arguments, nothing written (547.655836ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (152.948705ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (172.805643ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (177.806429ms)
✔ endLaunch leaves a claim another run took alone (214.194146ms)
✔ refuse records action.refused against the caller with the code only (165.603353ms)
✔ launches off refuses role.launch with launch-revoked until launches on (244.192895ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (375.864401ms)
✖ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.836942ms)
✖ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (135.023841ms)
✖ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (457.569248ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (501.359059ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (296.774298ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (441.192039ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (301.481105ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (277.831999ms)
✔ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (262.723342ms)
✔ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (268.099577ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (317.444058ms)
✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (340.457331ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.404841ms)
✔ process reader gets own kernel identity without exposing environment values (0.413017ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.534074ms)
✔ real pid 1 remains inspectable when its environment is protected (0.214436ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (239.443588ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (213.328084ms)
✔ a message to a running role sends no notice and its result is unchanged (164.536694ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (292.498322ms)
✔ a run launched but not yet claimed counts as no holder (158.331957ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (319.23462ms)
✔ a business with no launch block tells nobody and says there is no launcher (148.598027ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (437.384832ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (270.019252ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (347.886503ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (506.349775ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (247.877894ms)
✔ missing and foreign-business citations refuse and roll back message and grant (229.254516ms)
✔ cross-role sends still need a matching resolved decision and consume it once (313.043452ms)
✔ messages pages all human messages newest first without writes; read state is history (3105.389445ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (294.507617ms)
✔ views messages passes only its paging arguments through (0.269292ms)
✔ messages isolates businesses even with a valid foreign cursor (217.137588ms)
✔ the assistant sends to the user and to the chief of staff only (383.867818ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (446.13424ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (412.302115ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (361.402724ms)
✔ a sender-side list alone refuses a send to a role that has no list (450.882483ms)
✔ a target-side list alone refuses a send from a role that has no list (404.810806ms)
✔ the sender list is checked before the target list (372.074531ms)
✔ roles without the field behave exactly as before (487.694474ms)
✔ a business with no role definitions at all is unlimited (378.524097ms)
✔ a malformed messaging field refuses at boot (145.716305ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (535.728346ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (474.351151ms)
✔ review requests and verdicts between roles without the field are unchanged (355.567133ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (251.58783ms)
✔ 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 (554.789818ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (481.914398ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (292.352104ms)
✔ startup token refusal returns safe code without value or partial listening broker (79.090713ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (239.661442ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (248.099518ms)
✔ trusted host registers later launches; socket clients never have a registration verb (216.41706ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (67.862701ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (229.256427ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (174.375688ms)
✔ D2: spec.read refuses without content if its role is released during the read (183.652447ms)
✔ D3: spec.read secret check refuses flagged content without content (157.590525ms)
✔ D3: spec.read secret check refuses flagged subject without content (163.586873ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.583628ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (224.005804ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (222.481064ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (275.117042ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (213.95472ms)
✔ a message with no reply gives an empty list (213.705103ms)
✔ replies come oldest first and stop at REPLIES (965.369732ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (356.67467ms)
✔ strict arguments; a role session gets human-required (431.702471ms)
✔ views passes replies through as one call with the id (0.322395ms)
✔ requirements returns the committed list to a holding role session, and no one else (261.382848ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (200.934435ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (238.169394ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (278.692135ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (311.826032ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (371.230923ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (448.57168ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (269.936227ms)
✔ review.verdict answers one request once, to the role that asked (364.239883ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (451.4654ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (305.842065ms)
✔ v3b prototype refusals, views and append-only mutations (1804.5363ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (314.461871ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (313.406501ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (188.335345ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (395.473465ms)
✔ class drift gated to cross-role refuses before consumption (241.040352ms)
✔ class drift cross-role to gated refuses before consumption (299.663888ms)
✔ class drift gated to within-role refuses before consumption (274.166484ms)
✔ class drift cross-role to within-role refuses before consumption (295.902318ms)
✔ class drift within-role to gated refuses before consumption (250.142589ms)
✔ class drift within-role to cross-role refuses before consumption (383.320383ms)
✔ message.send consumes approval and prevents a later send or authorize (302.38901ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (299.270966ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1670.879947ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (300.592584ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (232.067683ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (249.83309ms)
✔ client disconnect during await tolerates late reply (194.581701ms)
✔ client disconnect during await tolerates late refusal (198.061224ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (419.080223ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (329.781006ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (364.371467ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (240.027776ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (285.351153ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (294.309225ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (349.893145ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (200.336126ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (506.098614ms)
✔ 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 (576.803313ms)
✔ 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 (471.511499ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (262.340739ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (775.714446ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (480.702883ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (253.138321ms)
✔ a business that never switched launches reads allowed with no time and no event (172.869528ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (164.507433ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (166.473176ms)
✔ a tombstone is not open (167.182076ms)
✔ the open list stops at the cap and says so; total still counts every open task (166.797616ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (168.221518ms)
✔ a human session reads the same state, with the same cap (154.475643ms)
✔ reading state writes nothing: every table keeps its row count (201.593172ms)
✔ creates private WAL store and excludes a second writer until explicit close (144.305665ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (210.554834ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (228.566791ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (190.162036ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (126.298188ms)
✔ async transactions refuse before invoking their function (132.333672ms)
✔ the session default fits under the reply cap and pages to the end with next (341.534823ms)
✔ the short form: no description, assignees by role, every listed key (310.616891ms)
✔ each filter, alone and together, and total counts every match (407.867897ms)
✔ task_ref reads one task in full, as the whole board has it (350.360734ms)
✔ no arguments is the read the Console and mosaic make, unchanged (277.722281ms)
✔ an unknown argument or a bad value refuses invalid-request (1075.231076ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (362.661245ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (410.369429ms)
✔ an empty business and one with snapshots but no task.created read cleanly (258.849606ms)
✔ recordTask keeps sync reads and a role write apart (210.563369ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (114.883197ms)
✔ a bad entry refuses the whole record (149.69772ms)
✔ taskView reads the projection for one business (137.066258ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (427.470856ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (442.43973ms)
✔ without an adapter the server refuses every task verb (251.00903ms)
✔ the runtime refuses an invalid adapter and closes a valid one (225.620203ms)
✔ the process loads the S3 adapter from plain-data trackers (440.234043ms)
✔ the tester binds only when the business sets bus.tester to exactly true (1022.58458ms)
✔ a business may not name a role tester, so the tester address is never a role (290.950337ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (617.480066ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (365.526365ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (285.645179ms)
✔ the tester reads the human views a reader reads (210.447206ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.1717ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.373205ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.272355ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (112.47102ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (936.730574ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1282.857782ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (999.406449ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (888.216713ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (405.904062ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1537.616599ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (376.387463ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3721.326824ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (138.109062ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (154.570829ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (161.163826ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (191.856734ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (153.34101ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (131.797885ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (152.316703ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (114.444995ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (153.917425ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (498.95702ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (167.064414ms)
✔ two wire claims serialize; a lost reply never automatically retries (305.994102ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (161.95357ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.762413ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (168.109489ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (180.29961ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (287.127644ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (244.267741ms)
✔ a broker with no web service refuses a granted role (165.59634ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (237.448263ms)
✔ a session that is not the role holder is refused (146.458483ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (551.372051ms)
✔ web.search with no search source configured is a refused result, not an error (170.011308ms)
✔ a search source that fails with its own refusal, or that throws, is handled (202.22383ms)
✔ a slow search source ends in a refusal at the deadline (204.450616ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (156.728323ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (255.64178ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.261037ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (261.882637ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (202.409957ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (454.856955ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (172.383216ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (195.328175ms)
✔ 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.732615ms)
ℹ tests 306
ℹ suites 0
ℹ pass 303
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 12913.120511
✖ 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 (1.836942ms)
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 (135.023841ms)
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 (457.569248ms)
AssertionError [ERR_ASSERTION]: account.create {"project":"stack","spec_path":"spec/north-star.md","spec_commit":"b3d963f03ea0ed293d885c0fc05abaec14aa6b28"}
+ 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 (10.548834ms)
✔ the shipped file is exactly the approved list (1.228386ms)
✔ the assistant's authority is message.send and record.write within role (3.683682ms)
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.986042ms)
✔ a business grant through limits does not widen the role (2.3382ms)
✔ a launch block whose by is the assistant is refused (2.807141ms)
✔ the instance takes no credential references and no tracker (4.304246ms)
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (6.914945ms)
✔ an arbiter without messaging lists is accepted beside the limited assistant (4.614251ms)
✔ the contract passes work to the chief of staff and directs no one (1.214654ms)
✔ REQ-HARN-2: the assistant holds the web grant, and no other role file does (2.941348ms)
✔ config directory and file path follow MOSAIC_CONFIG (2.412464ms)
✔ the fixture business validates and comes back frozen (7.422565ms)
✔ two instances may share a definition (2.961836ms)
✔ top-level refusals (6.601931ms)
✔ arbiters and projects (9.336956ms)
✔ role instances (4.94913ms)
✔ Vikunja bots (15.102483ms)
✔ a role without Vikunja takes no tracker block (3.56508ms)
✔ credential references match the definition's services (3.6885ms)
✔ launch (8.83128ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (5.026916ms)
✔ loadBusiness: file checks (2.295721ms)
✔ loadBusiness: not a regular file (43.612314ms)
✔ loading writes nothing (1.139752ms)
✔ names that are Object.prototype properties don't count as declared (2.811079ms)
✔ the shipped example refuses as written and validates once filled in (0.733766ms)
✔ the role file loads and resolves through loadBusiness (10.746005ms)
✔ the shipped file is exactly the approved list (0.329656ms)
✔ the resolved record matches the approved list, so a widened file fails (2.787363ms)
✔ the chief of staff holds role.launch, message.send, role.end, organization.change and record.write; every other action is gated (2.840727ms)
✔ the pm does not hold role.launch (3.035954ms)
✔ a business grant of a gated action through limits does not widen the role (3.588575ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.972003ms)
✔ a launch block whose by is the pm instance is refused (4.341036ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.492973ms)
✔ the instance takes no credential references and no tracker (3.267876ms)
✔ usage errors exit 4 (298.214236ms)
✔ validate: a good business exits 0 and prints instance digests (66.746097ms)
✔ validate: project files (314.816895ms)
✔ validate: missing files and a broken system config (234.673827ms)
✔ validate: credential reference problems exit 2 and name each one (60.381722ms)
✔ validate: a token file inside the repository is refused (65.808188ms)
✔ validate: role definitions come from MOSAIC_ROLES_DIR (183.164798ms)
✔ resolve: prints one instance's record (250.632231ms)
✔ resolve: refusals (369.496569ms)
✔ parse: exactly one of file or env, plus the service's date (3.640872ms)
✔ check: a good file has no problems (1.634815ms)
✔ check never opens the file: a write-only token passes (0.776707ms)
✔ check: file problems (1.311251ms)
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.348772ms)
✔ check: dates and environment references (0.61056ms)
✔ each definition loads, with a contract file, and is exactly the approved list (5.90182ms)
✔ an organization file that declares them passes, and each resolves to least privilege (9.339357ms)
✔ the chief of staff launches each one; no other role does (5.598148ms)
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (6.437049ms)
✔ limits can narrow a C-suite role but never widen it (4.068034ms)
✔ any unknown key, tool, action or version in a role file is refused (1.355569ms)
✖ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (3.020026ms)
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.532982ms)
✖ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (1.700591ms)
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (24.214488ms)
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.322064ms)
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (257.203488ms)
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (246.21555ms)
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (3.270633ms)
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (285.573299ms)
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.913163ms)
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (115.323092ms)
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (18.165732ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (297.382164ms)
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (433.217065ms)
✔ 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.471241ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (36.15546ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.583682ms)
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (106.753984ms)
✔ optional project repo is preserved and must be a normalized absolute path (15.503723ms)
✔ loaded business project repo cannot be nested in any declared code checkout (4.411439ms)
✔ path and load (3.459081ms)
✔ refusals (1.708272ms)
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (4.335237ms)
✔ systemVars flattens the validated config (2.07101ms)
✔ precedence: system, business, project, project role, agent (7.774974ms)
✔ limits narrow the definition and never widen it (3.116111ms)
✔ role.launch stays within-role only for the instance the launch block names (6.943515ms)
✔ limits.authority without role.launch leaves the launcher with no launch block (2.629068ms)
✔ limits.authority narrows cross-role actions too (1.089462ms)
✔ classify (1.05187ms)
✔ the record carries what the broker and launcher need (1.133199ms)
✔ digest: key order doesn't matter, any value change does (7.942315ms)
✔ refusals (2.160973ms)
✔ the four shipped version 2 roles load (2.811367ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.822279ms)
✔ shipped authority follows the note's table (0.604429ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (1.474942ms)
✔ version 1 files keep loading with no authority (1.16582ms)
✔ the conductor policy isn't a role (0.270789ms)
✔ a missing role file is exit 4, a symbolic link too (0.510698ms)
✔ version 2 refusals (2.091691ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (2.802027ms)
✔ messaging: optional, role definitions and human, never the tester (2.275603ms)
✔ credentials: Gitea scopes (1.453728ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.044153ms)
✔ credentials: services (0.471709ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.572541ms)
✔ every key names known layers and a merge rule (0.925215ms)
✔ unknown keys and wrong layers refuse (0.712562ms)
✔ types (2.119973ms)
✔ merge: defaults, then the most specific layer wins (0.284313ms)
✔ merge: limits only narrow, and provenance lists each source (0.329155ms)
✔ merge doesn't change its inputs (0.129315ms)
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.182226ms)
ℹ tests 112
ℹ suites 0
ℹ pass 110
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1938.82275
✖ 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 (3.020026ms)
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.700591ms)
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 (9.182992ms)
✔ the shipped file is exactly the approved list (1.152574ms)
✔ the assistant's authority is message.send and record.write within role (2.977925ms)
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.968134ms)
✔ a business grant through limits does not widen the role (2.169662ms)
✔ a launch block whose by is the assistant is refused (2.679489ms)
✔ the instance takes no credential references and no tracker (3.774824ms)
✔ an arbiter whose role definition declares messaging lists is refused, for each kind (4.851884ms)
✔ an arbiter without messaging lists is accepted beside the limited assistant (2.066296ms)
✔ the contract passes work to the chief of staff and directs no one (0.61057ms)
✔ REQ-HARN-2: the assistant holds the web grant, and no other role file does (4.467397ms)
✔ config directory and file path follow MOSAIC_CONFIG (2.307066ms)
✔ the fixture business validates and comes back frozen (11.974391ms)
✔ two instances may share a definition (3.442413ms)
✔ top-level refusals (8.884424ms)
✔ arbiters and projects (10.299561ms)
✔ role instances (5.523865ms)
✔ Vikunja bots (8.624167ms)
✔ a role without Vikunja takes no tracker block (2.286207ms)
✔ credential references match the definition's services (2.520946ms)
✔ launch (8.557491ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (4.015711ms)
✔ loadBusiness: file checks (2.672263ms)
✔ loadBusiness: not a regular file (37.162926ms)
✔ loading writes nothing (1.07061ms)
✔ names that are Object.prototype properties don't count as declared (2.87897ms)
✔ the shipped example refuses as written and validates once filled in (0.767844ms)
✔ the role file loads and resolves through loadBusiness (11.492997ms)
✔ the shipped file is exactly the approved list (0.444023ms)
✔ the resolved record matches the approved list, so a widened file fails (3.128816ms)
✔ the chief of staff holds role.launch, message.send, role.end, organization.change and record.write; every other action is gated (2.801299ms)
✔ the pm does not hold role.launch (2.521604ms)
✔ a business grant of a gated action through limits does not widen the role (2.688532ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.923605ms)
✔ a launch block whose by is the pm instance is refused (2.348896ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.436535ms)
✔ the instance takes no credential references and no tracker (3.435653ms)
✔ usage errors exit 4 (295.488364ms)
✔ validate: a good business exits 0 and prints instance digests (60.7352ms)
✔ validate: project files (314.530158ms)
✔ validate: missing files and a broken system config (218.418175ms)
✔ validate: credential reference problems exit 2 and name each one (63.122562ms)
✔ validate: a token file inside the repository is refused (70.411519ms)
✖ validate: role definitions come from MOSAIC_ROLES_DIR (205.411846ms)
✔ resolve: prints one instance's record (292.246308ms)
✔ resolve: refusals (408.099272ms)
✔ parse: exactly one of file or env, plus the service's date (3.748124ms)
✔ check: a good file has no problems (1.130474ms)
✔ check never opens the file: a write-only token passes (0.686343ms)
✔ check: file problems (1.7551ms)
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.159406ms)
✔ check: dates and environment references (0.622726ms)
✔ each definition loads, with a contract file, and is exactly the approved list (5.14694ms)
✔ an organization file that declares them passes, and each resolves to least privilege (9.085095ms)
✔ the chief of staff launches each one; no other role does (4.212439ms)
✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (3.711809ms)
✔ limits can narrow a C-suite role but never widen it (2.804768ms)
✔ any unknown key, tool, action or version in a role file is refused (1.114697ms)
✔ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (2.701303ms)
✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (1.04501ms)
✖ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (3.8427ms)
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (20.622251ms)
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.118047ms)
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (256.939708ms)
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (268.325082ms)
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (2.972161ms)
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (231.086199ms)
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.767747ms)
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (144.24011ms)
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (18.763876ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (286.192557ms)
✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (432.920012ms)
✔ 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.522601ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (35.549874ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.460305ms)
✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (118.100792ms)
✔ optional project repo is preserved and must be a normalized absolute path (14.705191ms)
✔ loaded business project repo cannot be nested in any declared code checkout (4.211096ms)
✔ path and load (3.257977ms)
✔ refusals (1.908847ms)
✔ record.write is within-role for the eleven approved v2 agents, never gated-only (7.274862ms)
✔ systemVars flattens the validated config (2.094836ms)
✔ precedence: system, business, project, project role, agent (5.988353ms)
✔ limits narrow the definition and never widen it (2.285408ms)
✔ role.launch stays within-role only for the instance the launch block names (4.910713ms)
✔ limits.authority without role.launch leaves the launcher with no launch block (2.228051ms)
✔ limits.authority narrows cross-role actions too (1.052243ms)
✔ classify (1.100298ms)
✔ the record carries what the broker and launcher need (1.174469ms)
✔ digest: key order doesn't matter, any value change does (9.318176ms)
✔ refusals (2.025622ms)
✔ the four shipped version 2 roles load (3.517722ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.965816ms)
✔ shipped authority follows the note's table (0.678908ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (1.134579ms)
✔ version 1 files keep loading with no authority (0.863968ms)
✔ the conductor policy isn't a role (0.177668ms)
✔ a missing role file is exit 4, a symbolic link too (0.3644ms)
✔ version 2 refusals (1.251328ms)
✖ authority: closed vocabulary, no gated-only action, no overlap (0.851276ms)
✔ messaging: optional, role definitions and human, never the tester (1.857997ms)
✔ credentials: Gitea scopes (0.705841ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.373919ms)
✔ credentials: services (0.483228ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.659945ms)
✔ every key names known layers and a merge rule (1.295719ms)
✔ unknown keys and wrong layers refuse (0.806985ms)
✔ types (2.157826ms)
✔ merge: defaults, then the most specific layer wins (0.385441ms)
✔ merge: limits only narrow, and provenance lists each source (0.587947ms)
✔ merge doesn't change its inputs (0.156159ms)
✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.214077ms)
ℹ tests 112
ℹ suites 0
ℹ pass 109
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2006.532699
✖ failing tests:
test at tests/cli.test.mjs:109:1
✖ validate: role definitions come from MOSAIC_ROLES_DIR (205.411846ms)
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 (3.8427ms)
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.851276ms)
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'
}
@@ -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 (155.063053ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (241.161916ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (386.125503ms)
✔ agents: working wins over waiting, and Input needed still shows (163.903846ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (182.024141ms)
✔ agents: state reads only its own business, even for a run name both use (182.053417ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (218.271027ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (206.387417ms)
✔ agents: stranded is oldest first and capped at 50 for a role (840.00376ms)
✔ agents: stranded is per role and per business (335.111849ms)
✔ agents: a message routed to a held role and not yet received is not stranded (126.222066ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (317.681569ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (254.037449ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (267.828221ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (227.11609ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (238.307829ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (374.288404ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (186.153833ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (339.67942ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (284.749245ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (172.349092ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (173.305516ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (262.351953ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (182.98293ms)
✔ task action subjects and linked decision trail are complete and ordered (195.436311ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (115.49538ms)
✔ business isolation includes inherited object names and cross-business message references (188.893912ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (272.741063ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (141.665115ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (212.78449ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (148.38983ms)
✔ both arbiters require human resolution when their cross-role route is themselves (242.280803ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (177.665931ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.254263ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (282.150019ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (382.262373ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (231.012782ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (230.335107ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (258.59892ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (292.476378ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (277.485257ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (225.173554ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (282.571549ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (302.437385ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (369.686947ms)
✔ 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 (484.819769ms)
✔ 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 (866.926907ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (142.510365ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (145.188906ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (952.118513ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1479.319731ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (778.825666ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (727.87393ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (884.222469ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (109.903193ms)
✔ credentials refuses a role session and any argument (215.063763ms)
✔ credentials reads the status on each request, and a broker without one lists none (283.443085ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (220.634815ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.135556ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.592616ms)
✔ expiry refuses use and env references never become client data (0.84642ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.7251ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.408721ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.757083ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (576.327926ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (439.738931ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (394.312982ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (480.85762ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (423.354874ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (367.382568ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (460.916944ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (420.169636ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (384.305028ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (472.737934ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (456.090922ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (398.974323ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (447.662673ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (658.881965ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (445.546194ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (260.961261ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (301.931392ms)
✔ a human withdraw sends one message with the note (249.901581ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (266.533682ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (277.258648ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (301.193179ms)
✔ a raising role nobody holds at the close gets the message on the next claim (260.5422ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (230.781981ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (338.272398ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (347.229538ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (330.730868ms)
✔ a refused supersede withdraws nothing (276.242067ms)
✔ the assistant's within-role and gated raises still work (315.017397ms)
✔ roles lists open decisions routed to a role, and never the human inbox (231.544874ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (380.725493ms)
✔ recent gives the closing event as closed when the decision was seen first (290.178456ms)
✔ recent stops at RECENT rows (805.878584ms)
✔ every row is what trail already gives the same session for that id (274.984506ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (379.824921ms)
✔ decisions is a read: one business, strict arguments, nothing written (365.917385ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (148.364242ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (165.008836ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (210.854714ms)
✔ endLaunch leaves a claim another run took alone (171.322773ms)
✔ refuse records action.refused against the caller with the code only (125.264633ms)
✔ launches off refuses role.launch with launch-revoked until launches on (178.884004ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (290.943232ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.297941ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (118.573806ms)
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (813.597368ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (470.360961ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (235.93233ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (300.233053ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (269.678465ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (224.702003ms)
✖ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (181.453798ms)
✖ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (200.735158ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (228.410963ms)
✖ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (219.247219ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.456517ms)
✔ process reader gets own kernel identity without exposing environment values (0.422936ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.597234ms)
✔ real pid 1 remains inspectable when its environment is protected (0.243308ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (239.495944ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (230.025402ms)
✔ a message to a running role sends no notice and its result is unchanged (152.390967ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (235.28249ms)
✔ a run launched but not yet claimed counts as no holder (131.160579ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (256.494853ms)
✔ a business with no launch block tells nobody and says there is no launcher (127.328381ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (351.437225ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (253.925887ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (341.81361ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (407.738334ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (261.457856ms)
✔ missing and foreign-business citations refuse and roll back message and grant (194.42556ms)
✔ cross-role sends still need a matching resolved decision and consume it once (239.587992ms)
✔ messages pages all human messages newest first without writes; read state is history (2502.977465ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (255.490303ms)
✔ views messages passes only its paging arguments through (0.282315ms)
✔ messages isolates businesses even with a valid foreign cursor (165.047918ms)
✔ the assistant sends to the user and to the chief of staff only (295.350649ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (380.520656ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (351.440636ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (280.083873ms)
✔ a sender-side list alone refuses a send to a role that has no list (306.050185ms)
✔ a target-side list alone refuses a send from a role that has no list (330.810486ms)
✔ the sender list is checked before the target list (300.112592ms)
✔ roles without the field behave exactly as before (393.979552ms)
✔ a business with no role definitions at all is unlimited (324.861592ms)
✔ a malformed messaging field refuses at boot (122.169231ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (346.470675ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (386.825012ms)
✔ review requests and verdicts between roles without the field are unchanged (332.424448ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (186.447239ms)
✔ 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 (513.96096ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (386.911504ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (220.314318ms)
✔ startup token refusal returns safe code without value or partial listening broker (77.8626ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (223.046802ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (248.098695ms)
✔ trusted host registers later launches; socket clients never have a registration verb (206.555652ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (66.843146ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (196.003471ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (152.547841ms)
✔ D2: spec.read refuses without content if its role is released during the read (154.108826ms)
✔ D3: spec.read secret check refuses flagged content without content (133.864355ms)
✔ D3: spec.read secret check refuses flagged subject without content (135.145943ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.384394ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (215.982928ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (213.514857ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (225.99218ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (196.046207ms)
✔ a message with no reply gives an empty list (141.106508ms)
✔ replies come oldest first and stop at REPLIES (728.533198ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (295.400401ms)
✔ strict arguments; a role session gets human-required (347.706823ms)
✔ views passes replies through as one call with the id (0.229409ms)
✔ requirements returns the committed list to a holding role session, and no one else (195.081806ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (147.349301ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (163.969755ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (218.818513ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (235.940172ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (276.96395ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (361.351025ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (218.65701ms)
✔ review.verdict answers one request once, to the role that asked (284.837687ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (375.745654ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (250.675974ms)
✔ v3b prototype refusals, views and append-only mutations (1394.731586ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (246.674731ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (202.879138ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (166.04663ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (311.871319ms)
✔ class drift gated to cross-role refuses before consumption (226.657384ms)
✔ class drift cross-role to gated refuses before consumption (221.940266ms)
✔ class drift gated to within-role refuses before consumption (230.179506ms)
✔ class drift cross-role to within-role refuses before consumption (244.516533ms)
✔ class drift within-role to gated refuses before consumption (206.775752ms)
✔ class drift within-role to cross-role refuses before consumption (210.559431ms)
✔ message.send consumes approval and prevents a later send or authorize (238.62197ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (256.062602ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1294.490012ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (260.704892ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (186.883385ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (226.828515ms)
✔ client disconnect during await tolerates late reply (170.44533ms)
✔ client disconnect during await tolerates late refusal (170.088105ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (379.407083ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (307.625298ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (299.169781ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (219.412657ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (257.293598ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (242.539804ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (283.57821ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (159.30295ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (421.93901ms)
✔ 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 (415.483366ms)
✔ 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 (376.532059ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (231.377236ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (592.912315ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (705.09351ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (250.652775ms)
✔ a business that never switched launches reads allowed with no time and no event (118.671102ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (129.586926ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (151.374669ms)
✔ a tombstone is not open (141.533645ms)
✔ the open list stops at the cap and says so; total still counts every open task (138.427777ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (140.479561ms)
✔ a human session reads the same state, with the same cap (141.670688ms)
✔ reading state writes nothing: every table keeps its row count (152.345683ms)
✔ creates private WAL store and excludes a second writer until explicit close (120.240417ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (140.417371ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (178.464987ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (174.262498ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (107.541772ms)
✔ async transactions refuse before invoking their function (99.656962ms)
✔ the session default fits under the reply cap and pages to the end with next (303.167438ms)
✔ the short form: no description, assignees by role, every listed key (247.107472ms)
✔ each filter, alone and together, and total counts every match (338.558475ms)
✔ task_ref reads one task in full, as the whole board has it (288.415386ms)
✔ no arguments is the read the Console and mosaic make, unchanged (253.606623ms)
✔ an unknown argument or a bad value refuses invalid-request (1202.679797ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (291.966392ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (341.364543ms)
✔ an empty business and one with snapshots but no task.created read cleanly (108.710075ms)
✔ recordTask keeps sync reads and a role write apart (165.155423ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (121.621083ms)
✔ a bad entry refuses the whole record (106.511495ms)
✔ taskView reads the projection for one business (134.902484ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (252.616928ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (414.818522ms)
✔ without an adapter the server refuses every task verb (216.666762ms)
✔ the runtime refuses an invalid adapter and closes a valid one (211.106308ms)
✔ the process loads the S3 adapter from plain-data trackers (371.551757ms)
✔ the tester binds only when the business sets bus.tester to exactly true (801.74922ms)
✔ a business may not name a role tester, so the tester address is never a role (238.873967ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (553.600587ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (209.690599ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (232.973455ms)
✔ the tester reads the human views a reader reads (437.467494ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.471376ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.427071ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.269203ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (156.746246ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1003.445536ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (972.326919ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (792.457401ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (1123.063281ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (319.379545ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1136.072091ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (315.813803ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (4016.142566ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (105.418404ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (155.767903ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (170.031384ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (182.966719ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (166.515417ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (137.219197ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (160.232131ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (114.174233ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (150.810125ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (499.410487ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (148.196072ms)
✔ two wire claims serialize; a lost reply never automatically retries (176.44724ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (120.205787ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.674113ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (137.251559ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (168.161312ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (164.223423ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (193.49248ms)
✔ a broker with no web service refuses a granted role (136.594302ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (206.224707ms)
✔ a session that is not the role holder is refused (115.376197ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (369.21533ms)
✔ web.search with no search source configured is a refused result, not an error (131.535458ms)
✔ a search source that fails with its own refusal, or that throws, is handled (466.863549ms)
✔ a slow search source ends in a refusal at the deadline (201.995415ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (134.524125ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (435.001125ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.293252ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (191.0647ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (163.429343ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (466.704151ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (217.083088ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (197.483571ms)
✔ 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 (157.682941ms)
ℹ tests 306
ℹ suites 0
ℹ pass 303
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 11948.297073
✖ failing tests:
test at tests/hard-waits.test.mjs:126:1
✖ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (181.453798ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ 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:129:10)
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: 'reviewer' },
expected: { class: 'gated', route_to: 'human' },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at tests/hard-waits.test.mjs:141:1
✖ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (200.735158ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:144: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: false,
code: 'ERR_ASSERTION',
actual: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
test at tests/hard-waits.test.mjs:160:1
✖ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (219.247219ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
+ class: 'cross-role',
+ route_to: 'reviewer'
- class: 'gated',
- route_to: 'human'
}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:165: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: 'cross-role', route_to: 'reviewer' },
expected: { class: 'gated', route_to: 'human' },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,377 @@
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 (150.53971ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (253.146637ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (375.915259ms)
✔ agents: working wins over waiting, and Input needed still shows (184.270677ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (246.39718ms)
✔ agents: state reads only its own business, even for a run name both use (211.634356ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (249.572115ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (253.49504ms)
✔ agents: stranded is oldest first and capped at 50 for a role (958.691031ms)
✔ agents: stranded is per role and per business (425.056538ms)
✔ agents: a message routed to a held role and not yet received is not stranded (155.295537ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (343.644412ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (215.708048ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (314.082492ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (272.591392ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (295.874986ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (423.426796ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (205.772925ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (300.615099ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (319.177506ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (201.811382ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (204.95441ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (293.283777ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (207.709469ms)
✔ task action subjects and linked decision trail are complete and ordered (234.267297ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (138.199463ms)
✔ business isolation includes inherited object names and cross-business message references (211.633117ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (326.806049ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (181.922853ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (286.649879ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (183.585797ms)
✔ both arbiters require human resolution when their cross-role route is themselves (298.135063ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (220.738737ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.534906ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (308.859902ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (373.323749ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (277.174244ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (275.168041ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (288.561826ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (353.522702ms)
✖ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (262.67111ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (254.258284ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (355.252638ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (392.586763ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (446.299123ms)
✔ 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 (615.507819ms)
✔ 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 (1211.774148ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (163.804918ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (151.858401ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (989.729627ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1736.233248ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (994.100946ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (947.581809ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (1133.555523ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (122.794233ms)
✔ credentials refuses a role session and any argument (205.872062ms)
✔ credentials reads the status on each request, and a broker without one lists none (251.740726ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (229.889972ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (3.158163ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.218531ms)
✔ expiry refuses use and env references never become client data (0.790217ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.674926ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.392003ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.658315ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (554.581189ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (513.230919ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (451.559828ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (567.750543ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (497.022134ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (452.587545ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (573.093563ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (533.265886ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (540.178021ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (659.980572ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (555.452389ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (444.570466ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (487.399425ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (496.430015ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (442.133421ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (261.842099ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (290.272426ms)
✔ a human withdraw sends one message with the note (270.96814ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (319.120529ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (326.205624ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (335.661764ms)
✔ a raising role nobody holds at the close gets the message on the next claim (306.30383ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (267.898593ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (354.660563ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (328.986943ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (390.382446ms)
✔ a refused supersede withdraws nothing (337.314626ms)
✔ the assistant's within-role and gated raises still work (358.349298ms)
✔ roles lists open decisions routed to a role, and never the human inbox (266.115219ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (342.841641ms)
✔ recent gives the closing event as closed when the decision was seen first (341.190011ms)
✔ recent stops at RECENT rows (933.70091ms)
✔ every row is what trail already gives the same session for that id (296.406885ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (445.807223ms)
✔ decisions is a read: one business, strict arguments, nothing written (484.627241ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (164.562727ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (166.603625ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (168.691018ms)
✔ endLaunch leaves a claim another run took alone (205.281743ms)
✔ refuse records action.refused against the caller with the code only (160.54503ms)
✔ launches off refuses role.launch with launch-revoked until launches on (216.156002ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (362.872816ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.275778ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (130.48479ms)
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (806.489198ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (475.367296ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (283.942801ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (343.021097ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (286.474461ms)
✖ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (247.786671ms)
✔ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (284.108169ms)
✔ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (277.416337ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (265.764864ms)
✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (376.767198ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.177383ms)
✔ process reader gets own kernel identity without exposing environment values (0.651014ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.932695ms)
✔ real pid 1 remains inspectable when its environment is protected (0.345103ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (269.491472ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (189.56362ms)
✔ a message to a running role sends no notice and its result is unchanged (178.439498ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (277.118536ms)
✔ a run launched but not yet claimed counts as no holder (145.217233ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (300.648984ms)
✔ a business with no launch block tells nobody and says there is no launcher (153.181261ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (404.532695ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (258.774609ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (334.834619ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (496.252096ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (230.414585ms)
✔ missing and foreign-business citations refuse and roll back message and grant (209.826855ms)
✔ cross-role sends still need a matching resolved decision and consume it once (295.955381ms)
✔ messages pages all human messages newest first without writes; read state is history (2963.150294ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (295.297057ms)
✔ views messages passes only its paging arguments through (0.294628ms)
✔ messages isolates businesses even with a valid foreign cursor (206.311874ms)
✔ the assistant sends to the user and to the chief of staff only (348.539977ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (437.711498ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (399.538007ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (349.254541ms)
✔ a sender-side list alone refuses a send to a role that has no list (376.531728ms)
✔ a target-side list alone refuses a send from a role that has no list (421.104695ms)
✔ the sender list is checked before the target list (371.312761ms)
✔ roles without the field behave exactly as before (499.302005ms)
✔ a business with no role definitions at all is unlimited (488.301777ms)
✔ a malformed messaging field refuses at boot (199.003881ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (436.430095ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (459.079096ms)
✔ review requests and verdicts between roles without the field are unchanged (382.252865ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (230.699598ms)
✔ 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 (581.932483ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (463.301671ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (236.577901ms)
✔ startup token refusal returns safe code without value or partial listening broker (66.073571ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (226.25913ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (237.635454ms)
✔ trusted host registers later launches; socket clients never have a registration verb (225.389103ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (69.980147ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (234.843393ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (181.330702ms)
✔ D2: spec.read refuses without content if its role is released during the read (176.634905ms)
✔ D3: spec.read secret check refuses flagged content without content (146.030767ms)
✔ D3: spec.read secret check refuses flagged subject without content (145.627021ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.231688ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (234.289362ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (228.125602ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (253.413221ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (208.243915ms)
✔ a message with no reply gives an empty list (192.459602ms)
✔ replies come oldest first and stop at REPLIES (896.547812ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (352.723562ms)
✔ strict arguments; a role session gets human-required (434.216246ms)
✔ views passes replies through as one call with the id (0.255721ms)
✔ requirements returns the committed list to a holding role session, and no one else (227.236239ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (188.728067ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (189.908397ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (276.660966ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (272.015988ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (350.908264ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (437.768241ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (276.677716ms)
✔ review.verdict answers one request once, to the role that asked (375.824154ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (526.835329ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (350.721976ms)
✔ v3b prototype refusals, views and append-only mutations (1735.577546ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (287.842199ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (252.469262ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (198.923547ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (387.356391ms)
✔ class drift gated to cross-role refuses before consumption (245.748169ms)
✔ class drift cross-role to gated refuses before consumption (290.09119ms)
✔ class drift gated to within-role refuses before consumption (341.410006ms)
✔ class drift cross-role to within-role refuses before consumption (353.348825ms)
✔ class drift within-role to gated refuses before consumption (299.163798ms)
✔ class drift within-role to cross-role refuses before consumption (278.956426ms)
✔ message.send consumes approval and prevents a later send or authorize (267.528666ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (302.56145ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1649.819362ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (305.579649ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (228.915332ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (259.197063ms)
✔ client disconnect during await tolerates late reply (211.176637ms)
✔ client disconnect during await tolerates late refusal (181.230566ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (469.782253ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (328.288606ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (353.761156ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (260.243056ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (315.593753ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (300.105648ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (361.848996ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (193.535111ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (638.805874ms)
✔ 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 (518.575555ms)
✔ 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 (453.847993ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (250.881455ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (677.544112ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (488.674613ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (293.460146ms)
✔ a business that never switched launches reads allowed with no time and no event (149.201838ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (175.471314ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (174.501268ms)
✔ a tombstone is not open (242.066413ms)
✔ the open list stops at the cap and says so; total still counts every open task (193.156206ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (231.225234ms)
✔ a human session reads the same state, with the same cap (182.850198ms)
✔ reading state writes nothing: every table keeps its row count (187.22773ms)
✔ creates private WAL store and excludes a second writer until explicit close (143.720037ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (191.653645ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (258.295115ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (266.180719ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (149.625751ms)
✔ async transactions refuse before invoking their function (170.275972ms)
✔ the session default fits under the reply cap and pages to the end with next (410.945665ms)
✔ the short form: no description, assignees by role, every listed key (366.739661ms)
✔ each filter, alone and together, and total counts every match (409.027574ms)
✔ task_ref reads one task in full, as the whole board has it (338.24313ms)
✔ no arguments is the read the Console and mosaic make, unchanged (278.945074ms)
✔ an unknown argument or a bad value refuses invalid-request (1033.610956ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (457.679255ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (486.618739ms)
✔ an empty business and one with snapshots but no task.created read cleanly (148.294307ms)
✔ recordTask keeps sync reads and a role write apart (248.415573ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (147.393748ms)
✔ a bad entry refuses the whole record (192.647682ms)
✔ taskView reads the projection for one business (167.638435ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (303.960544ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (447.331489ms)
✔ without an adapter the server refuses every task verb (245.62628ms)
✔ the runtime refuses an invalid adapter and closes a valid one (232.443013ms)
✔ the process loads the S3 adapter from plain-data trackers (366.262735ms)
✔ the tester binds only when the business sets bus.tester to exactly true (1102.488539ms)
✔ a business may not name a role tester, so the tester address is never a role (289.865273ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (628.502625ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (254.529862ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (280.872426ms)
✔ the tester reads the human views a reader reads (205.642838ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.296926ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.382275ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.219689ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (128.305986ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (896.501651ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1287.678829ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (891.338339ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (937.323728ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (488.22708ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1315.423759ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (365.946245ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3832.819306ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (629.605014ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (158.063659ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (168.556546ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (186.290588ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (158.160688ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (173.720785ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (169.663486ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (113.189467ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (158.38447ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (525.641881ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (200.625463ms)
✔ two wire claims serialize; a lost reply never automatically retries (232.674796ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (145.395808ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.643431ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (161.92408ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (211.208759ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (175.752716ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (237.95069ms)
✔ a broker with no web service refuses a granted role (156.630567ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (226.826393ms)
✔ a session that is not the role holder is refused (123.252645ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (439.847419ms)
✔ web.search with no search source configured is a refused result, not an error (155.089396ms)
✔ a search source that fails with its own refusal, or that throws, is handled (212.517637ms)
✔ a slow search source ends in a refusal at the deadline (195.046244ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (187.295757ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (226.757038ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.247989ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (213.096708ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (183.558633ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (461.597094ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (177.132623ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (201.657347ms)
✔ 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 (233.062906ms)
ℹ tests 306
ℹ suites 0
ℹ pass 304
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 13354.078019
✖ failing tests:
test at tests/chief-of-staff.test.mjs:174:1
✖ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (262.67111ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /not-held/. Input:
'Error: role-end-refused'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/chief-of-staff.test.mjs:181: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: Error: role-end-refused
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
at #protectedEnd (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:503:5)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1173:29)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966:26)
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/chief-of-staff.test.mjs:73:44)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/chief-of-staff.test.mjs:181:23
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24),
expected: /not-held/,
operator: 'throws',
diff: 'simple'
}
test at tests/hard-waits.test.mjs:115:1
✖ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (247.786671ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /not-held/. Input:
'Error: role-end-refused'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:120: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: Error: role-end-refused
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
at #protectedEnd (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:503:5)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1173:29)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966:26)
at Object.call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/spec-fixtures.mjs:33:49)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:120:25
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24),
expected: /not-held/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,376 @@
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 (162.656333ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (311.326216ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (586.313308ms)
✔ agents: working wins over waiting, and Input needed still shows (210.138972ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (568.436289ms)
✔ agents: state reads only its own business, even for a run name both use (227.417652ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (240.806636ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (301.827966ms)
✔ agents: stranded is oldest first and capped at 50 for a role (1147.397191ms)
✔ agents: stranded is per role and per business (426.173669ms)
✔ agents: a message routed to a held role and not yet received is not stranded (161.924506ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (321.032566ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (330.099323ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (503.433535ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (559.722352ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (355.318526ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (448.364677ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (203.139216ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (373.844242ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (531.040246ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (378.641465ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (393.278946ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (305.974216ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (202.32357ms)
✔ task action subjects and linked decision trail are complete and ordered (271.12818ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (137.634596ms)
✔ business isolation includes inherited object names and cross-business message references (259.527001ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (425.660984ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (203.338222ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (291.878269ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (203.209347ms)
✔ both arbiters require human resolution when their cross-role route is themselves (278.697397ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (199.160109ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.359642ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (280.53919ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (671.990924ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (363.410562ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (553.583214ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (295.131701ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (371.384579ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (304.692ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (332.086673ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (446.050913ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (392.452038ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (449.71483ms)
✔ 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 (681.293037ms)
✔ 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 (1140.615492ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (156.651258ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (136.638564ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (1419.209234ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (2126.897507ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (997.428909ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (968.09569ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (1135.608242ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (128.933762ms)
✔ credentials refuses a role session and any argument (195.868676ms)
✔ credentials reads the status on each request, and a broker without one lists none (490.709334ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (303.459339ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.353233ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.586367ms)
✔ expiry refuses use and env references never become client data (0.864621ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.826687ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.439284ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.575984ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (712.633405ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (934.992842ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (529.269864ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (584.958107ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (631.088496ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (508.904929ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (572.151262ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (589.497571ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (501.538131ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (586.581685ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (609.52592ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (437.714633ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (490.336835ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (668.957438ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (400.991192ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (276.89821ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (516.349784ms)
✔ a human withdraw sends one message with the note (339.035589ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (672.506077ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (340.496551ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (380.515655ms)
✔ a raising role nobody holds at the close gets the message on the next claim (311.688232ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (341.329161ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (372.847853ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (574.042251ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (697.886476ms)
✔ a refused supersede withdraws nothing (396.381838ms)
✔ the assistant's within-role and gated raises still work (375.18159ms)
✔ roles lists open decisions routed to a role, and never the human inbox (246.258436ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (579.458592ms)
✔ recent gives the closing event as closed when the decision was seen first (465.403653ms)
✔ recent stops at RECENT rows (1254.494946ms)
✔ every row is what trail already gives the same session for that id (327.4302ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (570.018801ms)
✔ decisions is a read: one business, strict arguments, nothing written (510.77069ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (160.830509ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (195.444371ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (221.246282ms)
✔ endLaunch leaves a claim another run took alone (410.40013ms)
✔ refuse records action.refused against the caller with the code only (180.309684ms)
✔ launches off refuses role.launch with launch-revoked until launches on (504.98191ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (415.187723ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.265445ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (126.266539ms)
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (1035.07429ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (809.404998ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (264.738675ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (421.925211ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (313.842212ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (376.472254ms)
✔ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (316.912233ms)
✖ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (256.467001ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (271.66062ms)
✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (322.239677ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.368351ms)
✔ process reader gets own kernel identity without exposing environment values (0.401173ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.555017ms)
✔ real pid 1 remains inspectable when its environment is protected (0.213865ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (230.728349ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (267.01477ms)
✔ a message to a running role sends no notice and its result is unchanged (345.978999ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (351.907713ms)
✔ a run launched but not yet claimed counts as no holder (431.418817ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (387.676736ms)
✔ a business with no launch block tells nobody and says there is no launcher (137.976761ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (443.864626ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (277.181832ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (527.692835ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (882.516818ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (463.868013ms)
✔ missing and foreign-business citations refuse and roll back message and grant (274.615506ms)
✔ cross-role sends still need a matching resolved decision and consume it once (486.893095ms)
✔ messages pages all human messages newest first without writes; read state is history (3527.830964ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (295.01453ms)
✔ views messages passes only its paging arguments through (0.289281ms)
✔ messages isolates businesses even with a valid foreign cursor (260.965405ms)
✔ the assistant sends to the user and to the chief of staff only (463.132211ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (445.106525ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (426.151148ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (444.159408ms)
✔ a sender-side list alone refuses a send to a role that has no list (430.698376ms)
✔ a target-side list alone refuses a send from a role that has no list (429.893156ms)
✔ the sender list is checked before the target list (353.009379ms)
✔ roles without the field behave exactly as before (555.991745ms)
✔ a business with no role definitions at all is unlimited (411.103865ms)
✔ a malformed messaging field refuses at boot (126.079979ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (440.666255ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (542.115757ms)
✔ review requests and verdicts between roles without the field are unchanged (371.246899ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (233.322998ms)
✔ 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 (606.033713ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (553.734549ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (318.040339ms)
✔ startup token refusal returns safe code without value or partial listening broker (75.690942ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (242.497325ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (239.563515ms)
✔ trusted host registers later launches; socket clients never have a registration verb (262.407795ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (65.767383ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (249.263055ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (190.359124ms)
✔ D2: spec.read refuses without content if its role is released during the read (186.426623ms)
✔ D3: spec.read secret check refuses flagged content without content (191.607814ms)
✔ D3: spec.read secret check refuses flagged subject without content (210.653785ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.396024ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (233.410378ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (262.927226ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (286.835218ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (295.675966ms)
✔ a message with no reply gives an empty list (244.579515ms)
✔ replies come oldest first and stop at REPLIES (916.939328ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (341.847782ms)
✔ strict arguments; a role session gets human-required (500.222824ms)
✔ views passes replies through as one call with the id (0.266846ms)
✔ requirements returns the committed list to a holding role session, and no one else (285.722503ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (213.944112ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (222.087958ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (319.01033ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (298.278182ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (365.811523ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (434.33185ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (285.217243ms)
✔ review.verdict answers one request once, to the role that asked (446.913277ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (468.301749ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (316.99201ms)
✔ v3b prototype refusals, views and append-only mutations (1886.058075ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (347.713552ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (255.928687ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (215.205922ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (365.556533ms)
✔ class drift gated to cross-role refuses before consumption (274.869573ms)
✔ class drift cross-role to gated refuses before consumption (376.673934ms)
✔ class drift gated to within-role refuses before consumption (267.911871ms)
✔ class drift cross-role to within-role refuses before consumption (319.543287ms)
✔ class drift within-role to gated refuses before consumption (285.713668ms)
✔ class drift within-role to cross-role refuses before consumption (300.602953ms)
✔ message.send consumes approval and prevents a later send or authorize (322.765354ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (306.344432ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1683.519979ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (319.463469ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (217.002344ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (256.469103ms)
✔ client disconnect during await tolerates late reply (198.303579ms)
✔ client disconnect during await tolerates late refusal (281.351469ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (416.406033ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (314.519403ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (329.524057ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (261.20438ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (339.1566ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (301.095554ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (428.993203ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (196.817763ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (550.062614ms)
✔ 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 (520.271054ms)
✔ 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 (468.197147ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (268.356849ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (837.084262ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (494.720133ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (241.112563ms)
✔ a business that never switched launches reads allowed with no time and no event (159.031856ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (177.940514ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (252.846007ms)
✔ a tombstone is not open (175.665225ms)
✔ the open list stops at the cap and says so; total still counts every open task (197.636796ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (177.529549ms)
✔ a human session reads the same state, with the same cap (188.409803ms)
✔ reading state writes nothing: every table keeps its row count (200.751701ms)
✔ creates private WAL store and excludes a second writer until explicit close (145.860236ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (254.712699ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (226.885886ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (229.006551ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (119.614264ms)
✔ async transactions refuse before invoking their function (115.342936ms)
✔ the session default fits under the reply cap and pages to the end with next (358.502094ms)
✔ the short form: no description, assignees by role, every listed key (297.46197ms)
✔ each filter, alone and together, and total counts every match (385.991916ms)
✔ task_ref reads one task in full, as the whole board has it (330.628066ms)
✔ no arguments is the read the Console and mosaic make, unchanged (305.335797ms)
✔ an unknown argument or a bad value refuses invalid-request (1168.801997ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (385.031986ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (412.421557ms)
✔ an empty business and one with snapshots but no task.created read cleanly (183.005655ms)
✔ recordTask keeps sync reads and a role write apart (237.132073ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (132.359623ms)
✔ a bad entry refuses the whole record (145.097288ms)
✔ taskView reads the projection for one business (165.598278ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (335.88327ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (432.111544ms)
✔ without an adapter the server refuses every task verb (257.927406ms)
✔ the runtime refuses an invalid adapter and closes a valid one (223.8076ms)
✔ the process loads the S3 adapter from plain-data trackers (366.707767ms)
✔ the tester binds only when the business sets bus.tester to exactly true (1041.973108ms)
✔ a business may not name a role tester, so the tester address is never a role (313.610793ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (612.705535ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (240.52631ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (482.082285ms)
✔ the tester reads the human views a reader reads (193.953556ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.159076ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.373438ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.195533ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (138.304962ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (914.609196ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1276.105708ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (871.170845ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (1116.447683ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (413.169724ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1361.477877ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (549.898865ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3596.090145ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (95.549902ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (161.12138ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (156.658782ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (173.350493ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (147.129681ms)
✖ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (130.878424ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (149.672749ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (108.753649ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (145.862807ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (507.286444ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (170.074348ms)
✔ two wire claims serialize; a lost reply never automatically retries (248.494351ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (149.955281ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.592676ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (164.768712ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (227.662154ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (181.284276ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (239.68938ms)
✔ a broker with no web service refuses a granted role (164.352135ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (223.547339ms)
✔ a session that is not the role holder is refused (129.599803ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (606.259062ms)
✔ web.search with no search source configured is a refused result, not an error (175.155244ms)
✔ a search source that fails with its own refusal, or that throws, is handled (191.337824ms)
✔ a slow search source ends in a refusal at the deadline (221.151328ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (135.115199ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (229.690316ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.298526ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (197.596665ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (156.910681ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (461.21964ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (176.173633ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (190.253215ms)
✔ 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 (158.324249ms)
ℹ tests 306
ℹ suites 0
ℹ pass 304
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 13459.004011
✖ failing tests:
test at tests/hard-waits.test.mjs:141:1
✖ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (256.467001ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /decision-mismatch/. Input:
'Error: role-end-refused'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:148:10)
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: Error: role-end-refused
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
at #protectedEnd (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:503:5)
at #checkAuthority (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:681:53)
at #consumeAuthority (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:689:40)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1176:33)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966:26)
at Object.call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/spec-fixtures.mjs:33:49)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:148:25,
expected: /decision-mismatch/,
operator: 'throws',
diff: 'simple'
}
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 (130.878424ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /decision-mismatch/. Input:
'Error: role-end-refused'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/threads.test.mjs:411: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: Error: role-end-refused
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
at #protectedEnd (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:503:5)
at #checkAuthority (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:681:53)
at #consumeAuthority (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:689:40)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1176:33)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:972: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:966:26)
at call (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/threads.test.mjs:50:44)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/threads.test.mjs:411:23,
expected: /decision-mismatch/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,346 @@
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 (142.309873ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (231.201892ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (367.803044ms)
✔ agents: working wins over waiting, and Input needed still shows (259.798746ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (397.235171ms)
✔ agents: state reads only its own business, even for a run name both use (212.02064ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (230.831747ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (216.60349ms)
✔ agents: stranded is oldest first and capped at 50 for a role (963.843246ms)
✔ agents: stranded is per role and per business (374.286349ms)
✔ agents: a message routed to a held role and not yet received is not stranded (154.105087ms)
✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (301.650289ms)
✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (202.787714ms)
✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (312.824123ms)
✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (432.648317ms)
✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (339.267456ms)
✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (386.665924ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (168.387278ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (281.576631ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (309.051654ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (260.623363ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (368.078554ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (281.304982ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (183.937178ms)
✔ task action subjects and linked decision trail are complete and ordered (203.012845ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (117.076817ms)
✔ business isolation includes inherited object names and cross-business message references (265.740593ms)
✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (299.378572ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (157.033714ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (231.052303ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (161.53335ms)
✔ both arbiters require human resolution when their cross-role route is themselves (263.180123ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (237.437295ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.662861ms)
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (267.786792ms)
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (360.49961ms)
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (326.513773ms)
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (436.237107ms)
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (290.987227ms)
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (311.667144ms)
✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant gets past the protection (320.097107ms)
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (249.134553ms)
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (343.47293ms)
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (323.745355ms)
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (440.91704ms)
✔ 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 (570.161417ms)
✔ 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 (1034.702314ms)
✔ comment cursors isolate businesses, move only forward and remain append-only (130.710467ms)
✔ only trusted sync writes validated cursors; events and cursor share rollback (126.238819ms)
✔ explicit 3b copy migration preserves evidence and opens as 3f; normal startup never migrates (981.018335ms)
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (1846.682878ms)
✔ named migration registry chains 3b through 3f to a fixture-only future version in order (914.705075ms)
✔ migration refuses mismatched source metadata without changing schema or evidence (873.90628ms)
✔ offline command migrates only the supplied fixture root and reports usage errors (959.061595ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (116.849709ms)
✔ credentials refuses a role session and any argument (183.716297ms)
✔ credentials reads the status on each request, and a broker without one lists none (241.477878ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (267.35318ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.841626ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.375213ms)
✔ expiry refuses use and env references never become client data (1.410739ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.864275ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.47774ms)
✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (2.46137ms)
✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (516.024271ms)
✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (692.495881ms)
✔ chief-executive-officer: another C-suite role isn't on the lists either (494.513961ms)
✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (501.732584ms)
✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (517.19919ms)
✔ chief-financial-officer: another C-suite role isn't on the lists either (412.467321ms)
✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (537.983819ms)
✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (480.40045ms)
✔ chief-information-officer: another C-suite role isn't on the lists either (495.078512ms)
✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (530.385133ms)
✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (481.380012ms)
✔ chief-information-security-officer: another C-suite role isn't on the lists either (435.154144ms)
✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (445.391814ms)
✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (442.247177ms)
✔ chief-marketing-officer: another C-suite role isn't on the lists either (376.630317ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (256.416022ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (263.845341ms)
✔ a human withdraw sends one message with the note (319.714821ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (443.359587ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (368.603134ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (307.830785ms)
✔ a raising role nobody holds at the close gets the message on the next claim (307.533623ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (292.212853ms)
✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (302.501868ms)
✔ a gated raise by a role that does not hear from the human is refused and stores nothing (317.897026ms)
✔ a route the raiser hears from is allowed, checked from the route to the raiser (495.946425ms)
✔ a refused supersede withdraws nothing (431.790648ms)
✔ the assistant's within-role and gated raises still work (324.778931ms)
✔ roles lists open decisions routed to a role, and never the human inbox (252.393829ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (313.889133ms)
✔ recent gives the closing event as closed when the decision was seen first (378.582693ms)
✔ recent stops at RECENT rows (1051.827553ms)
✔ every row is what trail already gives the same session for that id (319.899108ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (447.915408ms)
✔ decisions is a read: one business, strict arguments, nothing written (415.51024ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (157.673954ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (150.377271ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (171.035152ms)
✔ endLaunch leaves a claim another run took alone (201.988658ms)
✔ refuse records action.refused against the caller with the code only (208.690888ms)
✔ launches off refuses role.launch with launch-revoked until launches on (325.591384ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (400.14439ms)
✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.383913ms)
✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (116.094019ms)
✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (888.603613ms)
✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (633.272316ms)
✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (264.993121ms)
✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (316.161815ms)
✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (314.106828ms)
✔ Q169: the chief of staff's end of the assistant is not refused role-end-refused; its own run and another ender's are (266.282267ms)
✖ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (229.485912ms)
✔ Q169: a revoke of the assistant citing a decision the human resolved is not refused by the protection (240.76101ms)
✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (264.547675ms)
✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (341.174075ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.601705ms)
✔ process reader gets own kernel identity without exposing environment values (0.464613ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.660976ms)
✔ real pid 1 remains inspectable when its environment is protected (0.283166ms)
✔ a message to a stopped role sends launch.by one notice, and the result says so (218.531313ms)
✔ a message to a role outside launch.instances says so in its result and in the notice (173.858032ms)
✔ a message to a running role sends no notice and its result is unchanged (147.575196ms)
✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (346.114755ms)
✔ a run launched but not yet claimed counts as no holder (239.599115ms)
✔ no notice for a message to launch.by, from it, from a tester, to the human, or in a thread to a thread instance (359.579029ms)
✔ a business with no launch block tells nobody and says there is no launcher (146.317742ms)
✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (365.662836ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (217.892093ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (313.059179ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (685.224956ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (221.358863ms)
✔ missing and foreign-business citations refuse and roll back message and grant (214.294871ms)
✔ cross-role sends still need a matching resolved decision and consume it once (385.612912ms)
✔ messages pages all human messages newest first without writes; read state is history (3018.395084ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (261.012792ms)
✔ views messages passes only its paging arguments through (0.254699ms)
✔ messages isolates businesses even with a valid foreign cursor (212.26414ms)
✔ the assistant sends to the user and to the chief of staff only (422.215888ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (385.353863ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (440.796041ms)
✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (318.289243ms)
✔ a sender-side list alone refuses a send to a role that has no list (335.545256ms)
✔ a target-side list alone refuses a send from a role that has no list (377.557884ms)
✔ the sender list is checked before the target list (363.151065ms)
✔ roles without the field behave exactly as before (448.547393ms)
✔ a business with no role definitions at all is unlimited (423.075891ms)
✔ a malformed messaging field refuses at boot (119.671095ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (381.325815ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (410.741928ms)
✔ review requests and verdicts between roles without the field are unchanged (366.044928ms)
✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (209.124271ms)
✔ 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 (517.643083ms)
✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (475.892132ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (218.819659ms)
✔ startup token refusal returns safe code without value or partial listening broker (66.986171ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (220.480367ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (264.099594ms)
✔ trusted host registers later launches; socket clients never have a registration verb (215.306984ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (67.867886ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (192.541157ms)
✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (173.428009ms)
✔ D2: spec.read refuses without content if its role is released during the read (177.673555ms)
✔ D3: spec.read secret check refuses flagged content without content (180.81081ms)
✔ D3: spec.read secret check refuses flagged subject without content (140.357003ms)
✔ Broker keeps the existing sandbox client import closure: no new imports or lazy project import (1.288023ms)
✔ without a trusted project adapter all three async paths refuse project-not-configured (235.988712ms)
✔ real startBroker injects the project adapter for socket reads and bound-author record writes (236.378394ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (280.418741ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (204.95764ms)
✔ a message with no reply gives an empty list (184.844268ms)
✔ replies come oldest first and stop at REPLIES (811.37612ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (355.279473ms)
✔ strict arguments; a role session gets human-required (383.705107ms)
✔ views passes replies through as one call with the id (0.256315ms)
✔ requirements returns the committed list to a holding role session, and no one else (232.798166ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (162.494811ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (174.61228ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (248.094559ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (242.82092ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (310.307381ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (448.980334ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (246.57512ms)
✔ review.verdict answers one request once, to the role that asked (357.446501ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (470.767628ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (279.341688ms)
✔ v3b prototype refusals, views and append-only mutations (1594.869488ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (283.040113ms)
✔ another run of the same instance consumes an approval once; a failed check leaves it usable (224.657459ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (172.666658ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (396.904502ms)
✔ class drift gated to cross-role refuses before consumption (241.703687ms)
✔ class drift cross-role to gated refuses before consumption (236.475931ms)
✔ class drift gated to within-role refuses before consumption (285.197496ms)
✔ class drift cross-role to within-role refuses before consumption (325.021228ms)
✔ class drift within-role to gated refuses before consumption (231.422614ms)
✔ class drift within-role to cross-role refuses before consumption (250.952844ms)
✔ message.send consumes approval and prevents a later send or authorize (266.66055ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (284.183249ms)
✔ 3e copy migrates to 3f preserving every old column/row and adding null spec citations (1517.976505ms)
✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (283.007428ms)
✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (211.603937ms)
✔ two real socket writes to one repository yield one commit and one project-busy refusal (246.520136ms)
✔ client disconnect during await tolerates late reply (185.518208ms)
✔ client disconnect during await tolerates late refusal (196.536129ms)
✔ N-a: slow admitted record.write returns its committed result after the idle deadline (393.853726ms)
✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (329.634523ms)
✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (355.084027ms)
✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (228.7702ms)
✔ record writes bind the session author, require authority and refuse spec and impersonation paths (292.323439ms)
✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (302.42164ms)
✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (317.676289ms)
✔ REQ-COS-1: a pm without message.send is refused like any other gated action (185.908016ms)
✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (536.226685ms)
✔ 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 (421.211175ms)
✔ 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 (431.296622ms)
✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (252.770273ms)
✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (611.932547ms)
✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (452.412719ms)
✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (225.026499ms)
✔ a business that never switched launches reads allowed with no time and no event (150.925805ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (144.239155ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (183.717526ms)
✔ a tombstone is not open (177.800365ms)
✔ the open list stops at the cap and says so; total still counts every open task (202.838706ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (151.210144ms)
✔ a human session reads the same state, with the same cap (155.142841ms)
✔ reading state writes nothing: every table keeps its row count (169.588689ms)
✔ creates private WAL store and excludes a second writer until explicit close (132.415244ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (188.599482ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (227.479788ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (224.306007ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (125.586787ms)
✔ async transactions refuse before invoking their function (115.886513ms)
✔ the session default fits under the reply cap and pages to the end with next (338.514121ms)
✔ the short form: no description, assignees by role, every listed key (289.375787ms)
✔ each filter, alone and together, and total counts every match (348.297857ms)
✔ task_ref reads one task in full, as the whole board has it (307.676935ms)
✔ no arguments is the read the Console and mosaic make, unchanged (276.309848ms)
✔ an unknown argument or a bad value refuses invalid-request (931.051792ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (398.585507ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (363.807012ms)
✔ an empty business and one with snapshots but no task.created read cleanly (134.77228ms)
✔ recordTask keeps sync reads and a role write apart (263.742439ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (119.166747ms)
✔ a bad entry refuses the whole record (116.552772ms)
✔ taskView reads the projection for one business (129.320852ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (249.853068ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (432.680447ms)
✔ without an adapter the server refuses every task verb (234.850492ms)
✔ the runtime refuses an invalid adapter and closes a valid one (211.533809ms)
✔ the process loads the S3 adapter from plain-data trackers (329.425875ms)
✔ the tester binds only when the business sets bus.tester to exactly true (904.907886ms)
✔ a business may not name a role tester, so the tester address is never a role (267.844483ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (571.467494ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (227.510853ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (264.789573ms)
✔ the tester reads the human views a reader reads (182.312101ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.091283ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.339985ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.196834ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (118.235665ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (906.907804ms)
✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (1111.812707ms)
✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (840.29248ms)
✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (846.342797ms)
✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (364.91389ms)
✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (1201.307549ms)
✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (346.287626ms)
✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (3581.476413ms)
✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (104.052747ms)
✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (153.478556ms)
✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (156.706006ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (198.6723ms)
✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (153.217928ms)
✔ REQ-ROLE-2 REQ-THREAD-1: an approved role.revoke never reaches a thread run, and the thread stays held (144.957507ms)
✔ REQ-USER-2: the human verbs refuse another owner's thread (142.841126ms)
✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (106.34141ms)
✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (717.650965ms)
✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (513.631219ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (175.885318ms)
✔ two wire claims serialize; a lost reply never automatically retries (184.196596ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (115.427156ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.824489ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (156.279664ms)
✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (145.989984ms)
✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (172.092623ms)
✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (212.208249ms)
✔ a broker with no web service refuses a granted role (147.221811ms)
✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (210.059542ms)
✔ a session that is not the role holder is refused (122.600863ms)
✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (399.420573ms)
✔ web.search with no search source configured is a refused result, not an error (160.227129ms)
✔ a search source that fails with its own refusal, or that throws, is handled (177.042197ms)
✔ a slow search source ends in a refusal at the deadline (189.97177ms)
✔ a secret in the arguments is refused before anything is trailed or fetched (130.606701ms)
✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (231.343444ms)
✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.266379ms)
✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (212.244347ms)
✔ an ungranted call is refused at once: no admission, no wait, nothing touched (155.516111ms)
✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (447.626574ms)
✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (168.079457ms)
✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (190.484769ms)
✔ 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 (157.092837ms)
ℹ tests 306
ℹ suites 0
ℹ pass 305
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 12767.820872
✖ failing tests:
test at tests/hard-waits.test.mjs:126:1
✖ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (229.485912ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /role-end-refused/. Input:
'Error: decision-mismatch'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:138: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: Error: decision-mismatch
at fail (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:104:9)
at #checkAuthority (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:682:7)
at #consumeAuthority (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:688:40)
at #dispatch (file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:1175:33)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/src/broker.mjs:971: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:965:26)
at file:///home/jwoltje/dewey-scratch/r139/wt/packages/bus/tests/hard-waits.test.mjs:138:29
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24),
expected: /role-end-refused/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,9 @@
m1-no-spec-check packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:09:11Z end=2026-10-16T19:09:23Z
m2-no-hard-wait-check packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:09:23Z end=2026-10-16T19:09:36Z
m3-new-action-not-hard-wait packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:09:36Z end=2026-10-16T19:09:49Z
m4-hard-wait-routed packages/business/src/vocabulary.mjs suite=business rc=1 KILLED restored=True start=2026-10-16T19:09:49Z end=2026-10-16T19:09:51Z
m5-validator-refusal packages/business/src/role.mjs suite=business rc=1 KILLED restored=True start=2026-10-16T19:09:51Z end=2026-10-16T19:09:53Z
m6-no-assistant-check packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:09:53Z end=2026-10-16T19:10:06Z
m7-no-chief-exemption packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:10:06Z end=2026-10-16T19:10:19Z
m8-no-human-exemption packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:10:19Z end=2026-10-16T19:10:33Z
m9-no-revoke-protection packages/bus/src/broker.mjs suite=bus rc=1 KILLED restored=True start=2026-10-16T19:10:33Z end=2026-10-16T19:10:46Z