From dc8f5528197cfa37a9c5e8de707ebc6dd0cecc2f Mon Sep 17 00:00:00 2001 From: Steelbeak Date: Sat, 24 Oct 2026 10:37:41 -0500 Subject: [PATCH] Row 287: grant library collaborators with declared scopes --- BUILD-LOG.md | 3 + agents/steelbeak/work/queue-287/BUILD.md | 396 ++++++ .../steelbeak/work/queue-287/evidence/M1.log | 40 + .../steelbeak/work/queue-287/evidence/M10.log | 35 + .../steelbeak/work/queue-287/evidence/M11.log | 30 + .../steelbeak/work/queue-287/evidence/M2.log | 29 + .../steelbeak/work/queue-287/evidence/M3.log | 30 + .../steelbeak/work/queue-287/evidence/M4.log | 31 + .../steelbeak/work/queue-287/evidence/M5.log | 25 + .../steelbeak/work/queue-287/evidence/M6.log | 33 + .../steelbeak/work/queue-287/evidence/M7.log | 40 + .../steelbeak/work/queue-287/evidence/M8.log | 35 + .../steelbeak/work/queue-287/evidence/M9.log | 25 + .../work/queue-287/evidence/bus-suite.log | 1070 ++++++++++++++++ .../queue-287/evidence/business-suite.log | 291 +++++ .../work/queue-287/evidence/cleanup-audit.txt | 7 + .../work/queue-287/evidence/cli-suite.log | 1110 +++++++++++++++++ .../queue-287/evidence/docker-facts-r2.log | 27 + .../work/queue-287/evidence/docker-facts.log | 13 + .../work/queue-287/evidence/docker-whole.log | 28 + .../work/queue-287/evidence/install-whole.log | 35 + .../work/queue-287/evidence/mutants-run.log | 11 + .../queue-287/evidence/mutants-summary.txt | 11 + .../work/queue-287/evidence/red-new.log | 243 ++++ .../queue-287/evidence/red-replacement.log | 44 + .../evidence/shell-resume-summary.txt | 5 + .../work/queue-287/evidence/shell-summary.txt | 8 + .../work/queue-287/evidence/test-auth.log | 17 + .../queue-287/evidence/test-conductor.log | 23 + .../work/queue-287/evidence/test-config.log | 57 + .../work/queue-287/evidence/test-discord.log | 86 ++ .../evidence/test-extension-package.log | 21 + .../evidence/test-federation-acceptance.log | 10 + .../queue-287/evidence/test-foundation.log | 53 + .../evidence/test-land-work-bash.log | 88 ++ .../queue-287/evidence/test-land-work.log | 1 + .../evidence/test-push-check-bash.log | 7 + .../queue-287/evidence/test-queue-bash.log | 36 + .../queue-287/evidence/test-release-bash.log | 17 + .../queue-287/evidence/test-task-bash.log | 97 ++ .../work/queue-287/evidence/token-audit.txt | 5 + .../work/queue-287/evidence/touched-r1.log | 265 ++++ .../work/queue-287/evidence/touched-r2.log | 150 +++ .../work/queue-287/evidence/touched-r3.log | 131 ++ agents/steelbeak/work/queue-287/mutants.py | 39 + .../work/queue-287/review-manifest.txt | 56 + docs/SESSIONS.md | 3 + packages/bus/src/broker.mjs | 79 +- packages/bus/src/gitea-grant.mjs | 13 +- packages/bus/tests/fake-gitea-api.mjs | 16 +- .../bus/tests/gitea-grant-docker.test.mjs | 129 +- packages/bus/tests/gitea-grant.test.mjs | 155 +++ .../bus/tests/library-credential.test.mjs | 59 +- packages/business/src/business.mjs | 4 +- packages/business/tests/business.test.mjs | 25 + packages/cli/tests/case2-install.test.mjs | 13 + scripts/case2-install.sh | 11 +- 57 files changed, 5251 insertions(+), 70 deletions(-) create mode 100644 agents/steelbeak/work/queue-287/BUILD.md create mode 100644 agents/steelbeak/work/queue-287/evidence/M1.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M10.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M11.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M2.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M3.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M4.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M5.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M6.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M7.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M8.log create mode 100644 agents/steelbeak/work/queue-287/evidence/M9.log create mode 100644 agents/steelbeak/work/queue-287/evidence/bus-suite.log create mode 100644 agents/steelbeak/work/queue-287/evidence/business-suite.log create mode 100644 agents/steelbeak/work/queue-287/evidence/cleanup-audit.txt create mode 100644 agents/steelbeak/work/queue-287/evidence/cli-suite.log create mode 100644 agents/steelbeak/work/queue-287/evidence/docker-facts-r2.log create mode 100644 agents/steelbeak/work/queue-287/evidence/docker-facts.log create mode 100644 agents/steelbeak/work/queue-287/evidence/docker-whole.log create mode 100644 agents/steelbeak/work/queue-287/evidence/install-whole.log create mode 100644 agents/steelbeak/work/queue-287/evidence/mutants-run.log create mode 100644 agents/steelbeak/work/queue-287/evidence/mutants-summary.txt create mode 100644 agents/steelbeak/work/queue-287/evidence/red-new.log create mode 100644 agents/steelbeak/work/queue-287/evidence/red-replacement.log create mode 100644 agents/steelbeak/work/queue-287/evidence/shell-resume-summary.txt create mode 100644 agents/steelbeak/work/queue-287/evidence/shell-summary.txt create mode 100644 agents/steelbeak/work/queue-287/evidence/test-auth.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-conductor.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-config.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-discord.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-extension-package.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-federation-acceptance.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-foundation.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-land-work-bash.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-land-work.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-push-check-bash.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-queue-bash.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-release-bash.log create mode 100644 agents/steelbeak/work/queue-287/evidence/test-task-bash.log create mode 100644 agents/steelbeak/work/queue-287/evidence/token-audit.txt create mode 100644 agents/steelbeak/work/queue-287/evidence/touched-r1.log create mode 100644 agents/steelbeak/work/queue-287/evidence/touched-r2.log create mode 100644 agents/steelbeak/work/queue-287/evidence/touched-r3.log create mode 100644 agents/steelbeak/work/queue-287/mutants.py create mode 100644 agents/steelbeak/work/queue-287/review-manifest.txt diff --git a/BUILD-LOG.md b/BUILD-LOG.md index 5a4a431ca..ed1bccd27 100644 --- a/BUILD-LOG.md +++ b/BUILD-LOG.md @@ -5545,3 +5545,6 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r 2026-10-24 | Fethry | Row225 round2 resumed under sage4119/4120. Before: reviewed source94128f73f had blocking checked-message notice forgery and a misleading owner-only refusal; prepared tests7590cf633 awaited the slot. Rebased onto963303403 on new fethry/row225-r2 preserving sharing/chatGrants, upstream exports and both log histories; approved test deltas remain80/66 and3/1 with the combined bus pin. Queue start rev3946; local root/webui npm ci, no canonical dependency links. New browser RED8/3 then GREEN11/0 after only B1/N1 fixes. Editor build unchanged; whole webui442/0 and library149/0 (no skip/cancel); bus gate in progress. No old-test edit or second RED; full results and isolated-mutant evidence follow. 2026-10-24 | Fethry | Row225 round2 worktree gate and review correction complete on sourcec010dfd85 over963303403. B1/N1 RED8/3→GREEN11/0. Local editor build unchanged; webui442/0, library149/0, bus981/0, whole channel-proof2/0, all zero skips/cancels. Isolated16 unique clean assertion kills (14 originals+B1+N1); one M3 missing-file attempt timed out during a status after-hook, was not counted, was reported to Sage before any change, and passed unchanged retry with the remaining9.17 attempts total, timeout retained. Per-mutant restored hashes and final13 pins verified; scratch Node/Chromium sweep zero, no signals. New fethry/row225-r2 preserves original published branch, approved old tests80/66 and3/1, all landed sharing/exports/log histories. MANIFEST.r2.md and manifest.r2.sha256 to Dewey; source review/lead integration remain separate, no live action. +- 2026-10-24 — steelbeak — #1792 / row 287 C2-21 before: library grants still fork; creative and chief grants refuse. Recovered Megavolt's binding design on 18334d713 and documented current-code delta in agents/steelbeak/work/queue-287/BUILD.md. No tests or implementation yet; Sage holds test clearance, scope delta reported in steelbeak-0001. + +- 2026-10-24 — steelbeak — #1792 / row 287 C2-21 after: library grants use admin collaborator PUT, creative write/chief read levels, definition scopes, exact publish-wide reach and kind/level binding; business fence and chief install identity/publish updated; T7/T9 seeds replaced by real minting. Scratch Gitea F-A/F-B pass, F-C owner control negative recorded, F-D normal/force control positive. Verified bus 998, business 283, CLI 989 plus dedicated FED 1 (one expected CLI skip), Docker 2, install 27, all 12 shell gates, M1-M11 killed and restored; token audit hits0 and owned-process/container cleanup clean. Test fixture corrections approved under mos-1590/sage-4139; non-executable land-work launcher rc126 resolved by sage-4141 bash invocation. Evidence and manifest at agents/steelbeak/work/queue-287/; handing off to Boyd, then Dewey, with no live change or landing. diff --git a/agents/steelbeak/work/queue-287/BUILD.md b/agents/steelbeak/work/queue-287/BUILD.md new file mode 100644 index 000000000..881c32af2 --- /dev/null +++ b/agents/steelbeak/work/queue-287/BUILD.md @@ -0,0 +1,396 @@ +# Row 287 design notes: C2-21, the grant executor for a library project (#1792) + +Builder Megavolt, reviewer Dewey, gate owner Sage (sage-2910, Q226, mos-1310). Branch megavolt/row287, a +doc-only draft off 94c33a43c, written during Gizmoduck's slot with local git only. The build starts on the +candidates of row 285 (C2-08b, Scrooge) and row 226 (C2-13b, Gosalyn) and rebases onto their landings +(Q177). Nothing here has been run. + +The brief is "C2-21 the grant executor for a library project" in docs/plans/2026-10-20_case2-rows.md at +94c33a43c. Its gate: Dewey approves the manifest; the bus suite, the Gitea Docker test and the install test +are green on Sage's gate, along with every `scripts/test-*.sh`; a search of the test run's output for the +fixture admin and seat tokens finds nothing. + +## Sources (line cites at 94c33a43c) + +- `packages/bus/src/gitea-grant.mjs`: the admin api (`adminApi`, no collaborator call yet), the seat api + (`forkGet`, `fork`, `repos`), `identitySha256`, `REPO_PAGE`/`REPO_PAGES`. +- `packages/bus/src/broker.mjs`: `#giteaRole` :2132, `#giteaTargets` :2149, `#giteaGrantBinding` and its + block :2167-2196, `#giteaRecheck` :2200, `#giteaSteps` step 4 :2425-2441 and step 5 :2442-2473, the + `grant.applied` body and notice :2480-2488, the host refusals `host-creative-role` :1106 and :1254. +- `packages/business/src/business.mjs` :266-288: a `kind: "library"` project goes through `checkMapping`, and + :286 refuses any role whose `publish` names a library project. +- `packages/library/src/mapping.mjs`: a library entry carries `remote`, `branch` and an optional `user`. +- `packages/bus/tests/gitea-grant.test.mjs` :297-334 pins the `grant.applied` body (`forks: [...]`) and the + block; `fake-gitea-api.mjs` keeps collaborators as a list of logins with no permission. +- `packages/bus/tests/gitea-grant-docker.test.mjs`: the scratch gitea/gitea:1.27.1 container; it uses + `os.tmpdir()`. + +## 1. Rulings, blockers and questions + +- B1, the publish rule. `business.mjs:286` refuses any `publish` entry that names a library project + ("library repositories cannot be published by agents"). The brief's chief needs `publish: ["books"]`, and a + creative seat needs the library project in `publish` for `#giteaRole` and `#giteaTargets` to pass. No test + under packages/*/tests or scripts pins the :286 message (positive control: "publish names" hits + agent-identity.test.mjs). +- Ruling sage-2916 (18:42Z 10-22), files-owned extension: row 287 owns `packages/business/src/business.mjs` + :283-291 and the `packages/business/tests/business.test.mjs` cases for it. Row 285 leaves business.mjs + alone. Conditions: + - The refusal is rewritten, not deleted. A library project in `publish` loads only for a creative + instance (`CREATIVE_DEFINITIONS`) or the chief of staff, each with a gitea credential. Any other + instance still refuses, with a new message that names the rule. Tests cover both sides. The grant path + serves an allowed one with collaborator reach, never a fork. (To confirm on the base: how business.mjs + names the chief, by definition `chief-of-staff` or by `launch.by`.) + - Before in-review, grep 'packages/*/tests/*' and `scripts/test-*.sh` again for the old message text, + with a positive control. + - PRD 1.15 REQ-LIB-6 is the authority for this. If Mos changes 1.15 (sage-2914, `library.write` + routing), check this text against what lands. +- Q1 to Q4 below: Sage accepted them as stated assumptions (sage-2916). Dewey checks them in the round. +- Q1, the hash. `identitySha256` covers identity, publish and remotes, not a project's kind or the role's + definition name, and the level comes from the definition name. Proposal: add a `library` key + (`{project: level}`) to the hash input only when a publish project is a library, so every non-library + grant's hash stays byte-identical and no open grant turns `decision-mismatch` on upgrade. +- Q2, the event. `grant.applied` and its notice carry `forks`, and the test at :322 pins that body. + Proposal: `forks` stays (empty when every target is a library), and a `repositories: + [{project, repository, level}]` key appears only when a library target exists. The notice names them the + same way. +- Q3, mixed raises. A creative raise must name only library projects (mos-1320 item 2). The chief may + name a library project next to forked ones in one raise; step 4 forks the others and adds the collaborator + on the library one. Proposal: allow that, and test it. Under sage-2916, any other non-creative instance + can't hold a library project in `publish` at all, so the business file refuses it before any raise. +- Q4, the token search for the Docker test. The fake API's tokens are fixtures and can be grepped. The + Docker test's tokens are minted by the container at run time, so the gate grep can't know them in advance. + Proposal: the Docker test captures its own diagnostics and asserts that no token it minted appears in + them, and the gate greps the run's output for every fake-API fixture token. + +### Early look, Dewey (dewey-0862, 18:53Z 10-22, on 5bcfef0ad, read only), folded + +These replace the matching proposals in sections 2 and 3 where they differ. + +- Q2 accepted. `forks` stays and is `[]` when every target is a library. `repositories: + [{project, repository, level}]` appears only when a library target exists. `repository` is the remote + URL, the same form as `forks`; the `full_name` that step 5 matched is asserted in a test and doesn't go + in the body. The only src reader of `forks` is the notice (:2483). The notice drops its forks clause when + the list is empty and adds "repositories: ()". A non-library notice keeps today's text. +- The read line: "The token can read every path of this repository. It can't push to it, because the user + is a read collaborator." The seat scope is still `write:repository` (:2189). +- B-a: `\nForks:` (:2187) prints only when a fork target exists. +- B-b: "Step 5 checks that this is only its forks." (:2193) is unchanged with no library target. With one, it + reads "Step 5 checks that this is only its forks and the repositories above at their level." +- B-c: `others` (:2181, :2194) splits. An untargeted library project in publish gets its own line, "It may + also reach these repositories as a collaborator: ()", because a revoke leaves the + collaborator entry and step 5 accepts it publish-wide. Forked others keep today's line. L8 covers B-a to + B-c. +- Q1 holds. Test: a project flipped to `kind: "library"` between the raise and the recheck gives + `decision-mismatch` (L10). +- Q3 holds. Library projects stay out of step 5's fork upstream set (:2446), so a fork of her repository + refuses `gitea-reach-wider`; L4 covers it. +- Q4 holds if the assertion covers what the test prints, TAP stdout and stderr, not only an in-test + buffer. The Docker test prints one line, "minted N, hits 0", for the gate to grep. +- Step 5 compares exactly the keys `admin`, `push` and `pull`. BUILD.md records F-B's actual `permissions` + object, so an extra Gitea key can't pass or fail by accident. +- The chief is named by definition `chief-of-staff`, not `launch.by`, matching broker `STAFF_TO` (:240) and + row 286 (dewey-0860). A fired chief keeps its roles entry (instance-settings.mjs:355), so B-2 also loads a + fired chief's entry. + +### The definitions' gitea credential (dewey-0865) + +At 94c33a43c, chief-of-staff.json and all six creative definitions have `credentials: []`, so `#giteaRole` +(:2137-2143) would refuse them all `gitea-not-declared`. Row 287 doesn't add the credential. Row 285 (C2-08b) +owns those role files, and its brief (case2-rows.md :1823-1824) gives each creative role and the chief +`[{"service": "gitea"}]`, which Mos authorized in mos-1309 item 5. That is why 287 builds after 285 lands. Two +notes for 285, not for this row: +- The grant path reads only `service === 'gitea'`. The seat token's scopes come from `SEAT_SCOPES` in code, + not from the definition. coder.json adds `scopes`, and 285's validator decides whether the bare form passes. +- Five bus tests read roles/chief-of-staff.json directly (chief-of-staff, fire-revokes-grants, + instances-verb, names, routes-verb), and 285's gate names the business suite, not the bus suite. +- Settled (sage-2950, 19:06Z 10-22): 285's gate runs the bus suite whole (sage-2946), and Scrooge has the + role.mjs no-scopes check. Dewey withdrew dewey-0865 (dewey-0867). Mos withdrew the mos-1332 item 3 publish + fence (mos-1333), so business.mjs stays out of 285's files and sage-2916 stands as written. +- Correction (sage-2958, Scrooge): role.mjs refuses `{"service": "gitea"}` with no scopes + (checkGiteaScopes :100-111, util.mjs:42); only a host entry may omit them (:134). So 285's entries name + their scopes, equal to SEAT_SCOPES (sage-2915). The chief-of-staff.json readers are seven, not five: add + host-run and reload. 287's tests that build definitions give gitea entries `scopes: ["read:user", + "write:repository"]`. + +## 2. The change (proposed) + +- Order in the executor: step 2 (user) and step 3 (mint) as today. Step 4, for each target: a library + project gets `PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}` with `{permission}` through the admin + api, op `collaborator-add`, the `token` header, logged like every call. Expect 204, or anything else fails + with `gitea-failed`. A non-library project forks as today. +- The level: `write` when `CREATIVE_DEFINITIONS.includes(role.definition)`, `read` otherwise. The raise has + no field for it; `giteaGrantCheck` still refuses any key other than method, agent, rotateBy and target. +- Step 5: the upstream set stays for forks. A repository is accepted when it is the user's fork of a + non-library publish upstream (today's rule), or when its `full_name` is a library publish project's + `owner/repo` and its `permissions` are exactly `{admin: false, push: level === 'write', pull: true}`. Any + other repository, a wider permission or `admin: true` refuses `gitea-reach-wider` with the repository + named. +- `#giteaRole`: the `CREATIVE_DEFINITIONS` refusal becomes "unless every project the raise names is a + library project". `#giteaRole` gets the raise's project list from its two callers (the binding at + :2168 and the recheck at :2201), so it can decide. The comment cites mos-1320 item 2 and keeps + mos-1163 for every other project and every host grant. The host refusals at :1106 and :1254 and their + tests stay as they are. +- `#giteaTargets` returns `{project, upstream, library: true, repository, level}` for a library project and + today's `{project, upstream, fork}` otherwise. `forkProblem` still runs for both: a library remote on + another host refuses `remote-host-mismatch`, and one the seat user owns refuses `remote-not-fork`. +- The block: a library target prints `Repository: , level .` in place of its fork line, + plus the brief's line "The token can write every path of this repository. Only the bus's path check + limits it to supporting folders." for a `write` level. For a `read` level the line says the token can read + every path. (The brief gives only the write line; Dewey can rule the read wording.) +- Revoke deletes the token as today; the collaborator entry stays, as forks do. A fire sets + `prohibit_login` as today. Neither path changes. +- `scripts/case2-install.sh` (row 226's file): the chief of staff's instance gets `identity` and + `publish: ["books"]`. If `#grantableAgent` refuses the chief as its own target, the row stops and goes to + Sage. Read of :2037-2042 says it passes when the chief is `launch.by`; a test proves it. + +## 3. Tests (proposed) + +Docker (`gitea-grant-docker.test.mjs`), each fact first, before any code is trusted: +- F-A: the admin token with `ADMIN_SCOPES` adds a collaborator to a repository the admin account doesn't + own. F-B: `/user/repos` with the seat token lists that repository with the granted `permissions`. Either + failing stops the row; the output goes to Mos. +- F-C (recorded, not gating): a protected file pattern on her branch refuses a seat user's push to a story + path while her own push passes. F-D (recorded): branch protection refuses a seat user's force-push while a + normal push passes. Each logs its result as a diagnostic and doesn't fail the file; BUILD.md and a message + to Mos carry them. + +Fake API (`gitea-grant.test.mjs`, `fake-gitea-api.mjs`): collaborators carry a permission, `repoJson` +returns `permissions`, and the PUT route requires a site admin token with `write:repository`. +- L1 a creative definition gets `write`; L2 the chief gets `read`, including a mixed raise of the library + project and a forked one (Q3). +- L4 a repository with `push: true` for a read grant, or `admin: true`, or a fork of her library repository, + refuses `gitea-reach-wider` naming it. +- L5 revoke deletes the token and leaves the collaborator. +- L6 a non-library project still forks, and its `grant.applied` body is byte-identical to today's. +- L7 a creative raise naming a non-library project, or a library and a non-library one, refuses + `gitea-not-declared`. +- L8 the block holds the repository, the level and the brief's line, plus B-a to B-c (dewey-0862); the notice + has no "forks: ." on a library-only grant. +- L10 a project flipped to `kind: "library"` between the raise and the recheck gives `decision-mismatch`. +- L9 the chief raises its own grant through `credential.mint`. +- Install test: the business file gives the chief `identity` and `publish`. + +Business (`business.test.mjs`, sage-2916): B-1 a creative instance with identity, a gitea credential and a +library project in `publish` loads; B-2 so does the chief; B-3 any other instance with a library project in +`publish` refuses with the new message; B-4 a creative instance or the chief without a gitea credential +still refuses under the existing `publish needs identity and a gitea definition` rule. Mutant: M5 drop the +creative-or-chief condition (any instance loads), killed by B-3. + +Mutants: M1 drop the library-only condition from `#giteaRole` (the brief's), killed by L7. Others to list +once the code exists: the level forced to `write` (L2), step 5's exact-permission check loosened to +`push || pull` (L4), the fork path taken for a library project (L1, a fork call counted). + +## 4. Gate plan (draft) + +On Sage's CLEAR, with TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/gXXXXXX`, stopping at the first red: +the touched bus test files whole, then the mutants, the bus suite, the Docker test (checking for `^skip` +first, since a missing image skips it), the install test, every `scripts/test-*.sh` under row 281's limits, +then the token grep over the saved output. Timeouts and limits go in this file before the run. + +## Runs + +Filled in after the runs. + +## Steelbeak takeover (2026-10-24, base 18334d713) + +Sage-4124 dispatches Steelbeak as builder, Boyd first reviewer and Dewey landing reviewer. The +inherited text above is retained as design history; this section records current execution. +No test has run. Root npm ci uses this worktree only. Short TMPDIR is +`/mnt/storage/scratch/tmp/1ysAmV`; every scripts/test-*.sh invocation will use `timeout -k 10`. +Canonical queue next returned nothing while assignment was pending. Start op steelbeak-287-start +refused the sage-4122 assign lock; retry no sooner than two minutes after that refusal. + +Delta at 18334d713: broker grantableAgent 2667-2672 (launch.by self eligible), giteaRole 2760-2777 +(new chief refusal), targets 2781-2789, binding/block 2799-2830, recheck 2832-2842, step4 +3070-3085, step5 3087-3120, applied/notice 3124-3132. gitea-grant hash 75-80 and API 250-264 +unchanged. Business publish fence is 291-299. Install library helper is 109-127. Row286 +library-credential.test.mjs seeds 153-179, consumed at 219, 225 and 286, must become real minting. + +Chief scope delta raised in steelbeak-0001: row285 declares read scopes and sage-2921 fences the +existing write-scoped executor; inherited dewey-0862 retained SEAT_SCOPES and Q3 allowed mixed +chief grants. Await explicit resolution of that boundary before implementing it. Other accepted +rulings (conditional library hash, optional repositories event key, exact permission keys, business +publish fence, preserved host creative refusals) remain applicable. + +Tests planned: Docker F-A/F-B gating first; F-C/F-D recorded. Fake L1/L2/L4-L10 and business +B1-B4 from inherited plan; install chief identity/publish and row286 real-mint replacement. +Mutants M1 library-only guard, M2 forced write, M3 loose permissions, M4 library fork, +M5 missing creative-or-chief publish fence. No suite runs before Sage CLEAR. + +### Rulings + +Sage-4125 (2026-10-24 14:49:27Z), replacing Q3 and dewey-0862's chief SEAT_SCOPES line: +1. Chief grants name only library projects; mixed and fork-only chief raises refuse gitea-not-declared. +2. Library mint scopes come from the definition's gitea credential: chief read:repository/read:user, + creative its declared scopes. Fork grants retain SEAT_SCOPES. +3. Role comment retains sage-2921's reason and cites mos-1320 item 2 and sage-4125. +4. L2 pins chief read collaborator and read token scopes; L7b pins chief nonlibrary/mixed refusal; + M6 substitutes SEAT_SCOPES for library minting, M7 drops chief library-only condition. +5. Stop/report if grantableAgent or business validator still refuses chief after that condition. + +Root npm ci passed (128 packages), webui npm ci passed (2), both worktree-local with ignore-scripts, +no-audit and no-fund. Start retry met a different queue lock (louie-290-in-review); no bypass. + +Prepared tests only (not executed): Docker F-A/F-B against both creative write and chief read users; +F-B prints the actual permissions object. F-C/F-D use a private scratch repo and HTTP Git, logging +only exit codes. A parent captures the fact child's entire TAP stdout/stderr and checks actual minted +tokens before emitting diagnostics. New executor/business/install cases are drafted, including +same-definition-hash level drift. Production files and fake API remain unchanged until Docker facts +and expected RED evidence are recorded under CLEAR. + +Row286 T7/T9 fixture draft now raises chief/books, resolves the human yes and awaits the executor; +asserts repositories read level and actual token read scopes. The mapping remote is the fake's +acme/books; revoke/fire assertions are unchanged. This intentional replacement is expected RED on +the pre-C2-21 executor, separately from regressions in unchanged tests. No execution yet. + +### Implementation sequence and acceptance + +After CLEAR: run only the C2-21 Docker facts first, under a 360-second outer timeout, and stop/report +if F-A or F-B fails or skips. Then run new C2-21 tests and the intentionally replaced T7/T9 cases +against unchanged production to establish expected RED. Save the evidence and report the first RED. +No production fix until that evidence exists. Old tests retain assertions; the old chief refusal +case still targets a non-library project and should remain green. + +Implementation boundaries: definition-based scopes only for library minting, collaborator PUT through +admin token, exact admin/push/pull check publish-wide with library projects excluded from fork parents, +conditional library hash input preserving legacy hashes, optional repositories event field preserving +legacy event shape, scope-accurate block/notice. Business library publish remains fenced to creative +or chief and existing identity/credential validation. Install adds only chief identity/publish for the +library profile. Host creative refusals remain untouched. + +Builder verification after expected RED and fix: touched test files whole (600 seconds each), bus, +business and cli whole (1800 seconds each); mutants M1-M7 with targeted test filters (180 seconds +each), restoring source after each; Docker whole (600 seconds); all scripts/test-*.sh (1200 seconds +each, timeout -k 10); token search across saved output. Tests do not run concurrently. Stop on an +unexpected old-test RED or second RED and report to Sage before any remediation. The gate's cli +skip-list rules and Sage's slot ownership remain authoritative. No live host change, push to refactor, +merge or deployment is authorized. Own-branch push is authorized after applicable suites are green. + +Canonical start succeeded at 14:56Z: `ok steelbeak-287-start rev 3955 row 287 briefed→in-progress`. +Prepared all independent pre-CLEAR work; no tests, production edits, commits or pushes. Reported +ready in steelbeak-0004. The first execution is the Docker facts, never a fake or package suite. + +## CLEAR run (sage-4130, 2026-10-24 15:04Z) + +Worktree-local root npm ci passed (128 packages) and webui npm ci passed (2), ignore-scripts, +no-audit, no-fund. New short TMPDIR `/mnt/storage/scratch/tmp/HvDvQ8`. +First Docker facts invocation returned a vacuous green: inherited NODE_TEST_CONTEXT made Node skip +the nested runner, minted 0. INVALID evidence, no fact pass claimed; reported steelbeak-0006. +Corrected only the new harness child environment (delete NODE_TEST_CONTEXT) and added a requirement +that four actual tokens were minted. Second facts invocation is running. Production unchanged. + +### Docker facts (gitea/gitea:1.27.1, corrected harness, rc 0) + +Evidence: evidence/docker-facts-r2.log, 1 pass, 0 fail, 0 skip; all four tokens minted and no minted +value in complete captured TAP stdout/stderr. Container g287-* removed by t.after cleanup. +- F-A PASS: exact admin scopes write:admin/write:repository/write:user; collaborator PUT on writer/books + (admin does not own it) returned 204 for creative write and chief read. +- F-B PASS: seat /user/repos lists writer/books. Actual creative permissions + `{"admin":false,"push":true,"pull":true}`; chief permissions + `{"admin":false,"push":false,"pull":true}`. Chief token has read:repository/read:user scopes. +- F-C NOT ESTABLISHED: Story/** protection rejected both seat push (rc 1) and owner push (rc 1). + The required owner control did not pass; do not claim a useful seat-only protected-file fence. +- F-D ESTABLISHED: on protected branch normal seat supporting-file push rc 0, force-push rc 1. + +Proceeding to expected RED against untouched production, then sage-4125 implementation. + +### RED and correction record + +New-test first RED: evidence/red-new.log, 17 tests, 4 pass (refusal controls), 13 fail at missing +behavior. T7/T9 replacement expected RED: evidence/red-replacement.log, 2 fail gitea-not-declared. +After implementation, touched-r1.log: 122 tests, 115 pass, 7 fail, 0 skip, all failures new cases. +Stopped/reported steelbeak-0008 before changes. Mos-1590 via sage-4138 approved two fixture fixes: +seat-only /user/repos overrides (not admin prechecks) and real declared coder scopes for kind drift. +Dedicated-admin refusal already covered by X-G2 "admin set refuses each failed check and writes +nothing, then writes the slot once" at gitea-grant.test.mjs:276 (:293 asserts grant-admin-not-dedicated). + +Touched-r2.log: 123 tests, 122 pass, 1 fail, 0 skip. New L10's extra zero-network assertion was false: +executor correctly performs four read-only admin checks before binding recheck. Stopped/reported +steelbeak-0009. Sage-4139 approves exact GET admin checks, no /admin/users, /tokens or /collaborators, +unchanged decision-mismatch, and absent grant file/record. Add M10, an early write ahead of recheck. +The added no-declared-scopes case ran green before its mutant, an ordering error recorded honestly; +its proof will be M8 removing the scopes check, followed by restored green. No prior RED-first proof +is claimed for that added case. Add M9 dedication-check removal against existing X-G2. + +F-C follow-up (sage-4134): a protected Story/** rule refused owner and seat; the first row depending +on owner pushes to a protected branch must resolve this. Record only; no 287 behavior changes. +Install choice: books is the chief's read project when present; another library project's existing +install support is retained by using the first library project. Default identity is business-id-chief +on that project's origin, with a mosaic.invalid email; an explicit input identity is preserved. + +Touched-r3.log: 123 tests, 123 pass, 0 fail/skip after sage-4139. No unchanged assertion removed. +Mutants M1-M11: all KILLED by assertion failures (rc 1, positive fail count, zero skips), no timeout, +syntax or module-load kill. Every edited file restored byte-for-byte after each mutant; runner is +mutants.py, evidence/mutants-run.log and evidence/M*.log hold the outcomes. M8's restored proof is +the forthcoming full bus run; the added case's initial green remains recorded above. + +| Mutant | Change | Catch | +| --- | --- | --- | +| M1 | drop creative library-only guard | L7 creative nonlibrary/mixed | +| M2 | force library level write | L2 chief read | +| M3 | accept pull regardless of admin/push | L4 widened read | +| M4 | fork instead of collaborator | L1 creative write | +| M5 | remove library publish fence | business only creative/chief | +| M6 | library mint SEAT_SCOPES | L2 chief declared read scopes | +| M7 | drop chief library-only guard | L7b chief nonlibrary/mixed | +| M8 | remove declared-scopes check | no-declared-scopes refusal | +| M9 | remove admin dedication check | existing X-G2 | +| M10 | move step2 user-create write before binding recheck | L10 exact GETs, no writes/file/record | +| M11 | omit library kind/level from identity hash | L10 drift | + +### Full suites in progress + +Restored full bus: evidence/bus-suite.log, rc 0, 998/998 pass, 0 fail/skip. This includes restored M8 +proof and both Docker tests (fact child diagnostics also carry their own nested summary). +Full business: evidence/business-suite.log, rc 0, 283/283 pass, 0 fail/skip. +CLI is running sequentially after business at --test-concurrency=2. Every package invocation uses +short TMPDIR and an outer timeout -k 10; no live provider opt-in is passed. +Docker executor container prefix is now g287-executor-* (only the test's scratch name changed), +so the separately required full Docker rerun and final sweep can target only this row's g287-*. + +Full CLI: evidence/cli-suite.log, rc 0, 990 tests, 989 pass, 0 fail, 1 skip, duration 851 seconds. +The existing skip is packages/cli/tests/federation-acceptance.test.mjs, "FED R7 scratch two-instance +acceptance", runner scripts/test-federation-acceptance.sh. That runner must pass in this same gate +before the CLI skip is accepted (BRIEF-TEMPLATE mos-1583); no new skipped test was introduced. +Dedicated full Docker and install-file reruns are now running; shell gates follow sequentially. + +Dedicated Docker whole: evidence/docker-whole.log, rc 0, 2/2 pass, 0 fail/skip; actual F-A/F-B +permissions and F-C negative/F-D positive reproduced. Complete captured fact TAP: minted 4, hits 0. +Dedicated install whole: evidence/install-whole.log, rc 0, 27/27 pass, 0 fail/skip. +Docker shell wrapper adds an EXIT/INT/TERM cleanup for g287-* only, alongside each test's t.after. +Every scripts/test-*.sh is now running sequentially at timeout -k 10 1200, NO_COLOR=1 and +MOSAIC_TEST_CONCURRENCY=2, with MOSAIC_LIVE_PROVIDER and NODE_TEST_CONTEXT unset. Each script's own +scratch-project cleanup remains active. Stop at the first nonzero result; shell-summary.txt records rc. + +Shell launcher error: direct timeout invocation of scripts/test-land-work.sh returned rc 126, +Permission denied. File is tracked 100644; no assertion ran. Not a test RED. Reported steelbeak-0013; +sage-4141 approved bash invocation for this and remaining scripts, matching the landing gate. +No chmod or source change. Preserve initial shell-summary.txt and add shell-resume-summary.txt. + +## Builder verification complete (sage-4130) + +All twelve shell scripts passed (shell-summary.txt plus shell-resume-summary.txt), with the recorded +rc126 launcher error followed by the approved bash run. Counts: auth 15, conductor 17, config 55, +discord 82, extension-package 18, FED 1, foundation 44, land-work 86, push-check 38 checks plus its +six killed internal mutants, queue 28 shell checks and 156 node passes, release 15, task 92; zero +failed checks. Queue's canonical-only verify/render checks are intentionally skipped by its linked +worktree runner. Task's live-provider cases remain off as required by row244 and this assignment. + +Summary: bus 998/998; business 283/283; CLI 989 passed, zero failed and the one existing FED skip +covered by dedicated runner 1/1 with zero skips and cleanup receipt; Docker whole 2/2; install 27/27; +all 12 shell scripts rc0; M1-M11 killed without timeout/syntax kills and byte-restored. +Known fixture search: 3 values across 37 logs, hits0. Fact runner checked four actual minted values +against complete TAP stdout/stderr, hits0. Fake cases check every issued value against stored data. +Final cleanup: no owned test node/chromium processes and no g287 Docker containers; test-task and +release network removal checks passed; FED reports no groups/ports/sockets. Other containers and +preexisting harness/MCP services were untouched. git diff --check passes. + +Scope result: a creative library seat gets collaborator write; chief library read uses its declared +read token scopes; creative/chief non-library or mixed raises refuse. Business publish remains fenced +to creative/chief; legacy forks/event fields and host creative refusals retain coverage. T7/T9 now mint +real chief grants before their unchanged revoke/fire assertions. F-C owner-protection follow-up is +recorded above; F-D force-push observation is positive. No live Gitea or host change. + +Review handoff: Boyd first, Dewey landing review, Sage gate. Base 18334d713; branch steelbeak/row287. +The review manifest freezes all changed code, tests, logs and these notes (excluding only the +manifest itself). Commit/push are authorized by sage-4130; review/landing remain separate stages. diff --git a/agents/steelbeak/work/queue-287/evidence/M1.log b/agents/steelbeak/work/queue-287/evidence/M1.log new file mode 100644 index 000000000..e6f916dab --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M1.log @@ -0,0 +1,40 @@ +✖ C2-21 L7/L7b: research refuses non-library targets lib (56.821785ms) +✖ C2-21 L7/L7b: research refuses non-library targets app,lib (37.82327ms) +ℹ tests 2 +ℹ suites 0 +ℹ pass 0 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 174.924413 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1370:5 +✖ C2-21 L7/L7b: research refuses non-library targets lib (56.821785ms) + AssertionError [ERR_ASSERTION]: Missing expected rejection. + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'rejects', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1370:5 +✖ C2-21 L7/L7b: research refuses non-library targets app,lib (37.82327ms) + AssertionError [ERR_ASSERTION]: Missing expected rejection. + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'rejects', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M10.log b/agents/steelbeak/work/queue-287/evidence/M10.log new file mode 100644 index 000000000..4f7d64efa --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M10.log @@ -0,0 +1,35 @@ +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (94.748852ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 176.166886 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1416:1 +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (94.748852ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + 'GET /api/v1/version', + 'GET /api/v1/user', + 'GET /api/v1/token', + 'GET /api/v1/user/repos', + + 'POST /api/v1/admin/users' + ] + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1434:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: [ 'GET /api/v1/version', 'GET /api/v1/user', 'GET /api/v1/token', 'GET /api/v1/user/repos', 'POST /api/v1/admin/users' ], + expected: [ 'GET /api/v1/version', 'GET /api/v1/user', 'GET /api/v1/token', 'GET /api/v1/user/repos' ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M11.log b/agents/steelbeak/work/queue-287/evidence/M11.log new file mode 100644 index 000000000..224ab3212 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M11.log @@ -0,0 +1,30 @@ +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (114.899902ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 190.404699 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1416:1 +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (114.899902ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + undefined + - 'decision-mismatch' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1433:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: undefined, + expected: 'decision-mismatch', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M2.log b/agents/steelbeak/work/queue-287/evidence/M2.log new file mode 100644 index 000000000..04378d046 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M2.log @@ -0,0 +1,29 @@ +✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (56.777186ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 137.201615 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1325:3 +✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (56.777186ms) + AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value: + + assert.ok(block.includes(`Repository: ${f.gitea.url}/acme/app, level ${level}.`)) + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1338:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M3.log b/agents/steelbeak/work/queue-287/evidence/M3.log new file mode 100644 index 000000000..2019ee3c3 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M3.log @@ -0,0 +1,30 @@ +✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (119.045451ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 197.170231 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (119.045451ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.applied","method":"gitea","grantDecision":"18acf1bb-f896-4b14-9c47-a5cff85a898d","agent":"chief","user":"demo-chief","userId":7,"tokenId":8,"forks":[],"repositories":[{"project":"app","repository":"https://127.0.0.1:33421/acme/app","level":"read"}]} + + actual - expected + + + undefined + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M4.log b/agents/steelbeak/work/queue-287/evidence/M4.log new file mode 100644 index 000000000..ebdda7b08 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M4.log @@ -0,0 +1,31 @@ +✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (135.204477ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 215.545162 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1325:3 +✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (135.204477ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"0c2e03db-8b1f-4b25-b460-02d551551433","agent":"coder","code":"gitea-reach-wider","repository":"demo-coder/app","user":"demo-coder"} + + actual - expected + + + 'grant.failed' + - 'grant.applied' + ^ + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1348:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 'grant.failed', + expected: 'grant.applied', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M5.log b/agents/steelbeak/work/queue-287/evidence/M5.log new file mode 100644 index 000000000..37cb0d567 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M5.log @@ -0,0 +1,25 @@ +✖ C2-21: only creative definitions and the chief with gitea may publish a library project (17.773466ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 71.348342 + +✖ failing tests: + +test at packages/business/tests/business.test.mjs:365:1 +✖ C2-21: only creative definitions and the chief with gitea may publish a library project (17.773466ms) + AssertionError [ERR_ASSERTION]: Missing expected exception. + at refuses (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:53:10) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:380:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'throws', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M6.log b/agents/steelbeak/work/queue-287/evidence/M6.log new file mode 100644 index 000000000..6efb64e24 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M6.log @@ -0,0 +1,33 @@ +✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (117.793367ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 201.446169 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1325:3 +✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (117.793367ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + - 'read:repository', + 'read:user', + + 'write:repository' + ] + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1353:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: [ 'read:user', 'write:repository' ], + expected: [ 'read:repository', 'read:user' ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M7.log b/agents/steelbeak/work/queue-287/evidence/M7.log new file mode 100644 index 000000000..1027fe880 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M7.log @@ -0,0 +1,40 @@ +✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (52.058911ms) +✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (38.384854ms) +ℹ tests 2 +ℹ suites 0 +ℹ pass 0 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 169.051619 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1370:5 +✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (52.058911ms) + AssertionError [ERR_ASSERTION]: Missing expected rejection. + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'rejects', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1370:5 +✖ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (38.384854ms) + AssertionError [ERR_ASSERTION]: Missing expected rejection. + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1372:7) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'rejects', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M8.log b/agents/steelbeak/work/queue-287/evidence/M8.log new file mode 100644 index 000000000..9853a08fb --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M8.log @@ -0,0 +1,35 @@ +✖ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (53.012865ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 133.866141 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1455:1 +✖ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (53.012865ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'storage-refused' + - 'gitea-not-declared' + + at Object. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:40:63) + at expectedException (node:assert:547:17) + at expectsError (node:assert:672:21) + at strict.rejects (node:assert:738:3) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1460:3) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 'storage-refused', + expected: 'gitea-not-declared', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/M9.log b/agents/steelbeak/work/queue-287/evidence/M9.log new file mode 100644 index 000000000..86579679e --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/M9.log @@ -0,0 +1,25 @@ +✖ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (146.452903ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 226.323297 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:275:1 +✖ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (146.452903ms) + AssertionError [ERR_ASSERTION]: Missing expected rejection. + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:293:3) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'rejects', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/bus-suite.log b/agents/steelbeak/work/queue-287/evidence/bus-suite.log new file mode 100644 index 000000000..561da47eb --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/bus-suite.log @@ -0,0 +1,1070 @@ +✔ R4: the timer sweep after a hand edit ends only the lost user's run; its next call refuses thread-revoked (34.909881ms) +✔ R4: an expiry with no write and no request ends the run at the next timer sweep (24.303696ms) +✔ R4: a downgrade to viewer is a loss (26.614652ms) +✔ R4: another user's request after a hand edit ends the lost user's run, and its own run stays (29.27994ms) +✔ R4: a request reads the access file only when its stamp changed, once per change (23.47076ms) +✔ R4: access.reload ends the lost runs at once for the human; the tester, a reader, an agent and a user are refused (26.519699ms) +✔ R4: threadsState marks revoked: true on the lost user's thread only, a pending message included (18.238541ms) +✔ R4: threadsState after an expiry with no write and no sweep ends the claim before it marks the row revoked (26.762601ms) +✔ R4: a new run of a revoked thread refuses thread-revoked at bind and at claim; a rebind is allowed (22.340679ms) +✔ R4 runtime: a hand edit ends the run at the first timer period, intervalMs is checked, and close stops the timer (52.163399ms) +✔ REQ-LIMIT-4 3h copy migrates to 3i: every row keeps its seq, the sequence carries over, and only the account CHECK changes (92.025728ms) +✔ REQ-LIMIT-4 a 3h→3i step that fails partway leaves the store at 3h with every row, the sequence and the schema intact (117.759508ms) +✔ REQ-LIMIT-4 the 3h engine refuses a migrated store, migrating or not, and leaves it as it was (99.793336ms) +✔ parseAddress: one (1.124642ms) +✔ parseAddress: many (0.102582ms) +✔ parseAddress: repeat (0.079437ms) +✔ parseAddress: plain (0.074262ms) +✔ parseAddress: stop (0.083933ms) +✔ parseAddress: EOF tag (0.071821ms) +✔ parseAddress: EOF last tag (0.067962ms) +✔ parseAddress: leading whitespace (0.106096ms) +✔ parseAddress: tags only (0.086168ms) +✔ parseAddress: empty (0.130152ms) +✔ parseAddress: whitespace (0.07306ms) +✔ parseAddress: bare at (0.049987ms) +✔ parseAddress: punctuation is a name (0.049555ms) +✔ parseAddress rejects non-text without coercion (0.277299ms) +✔ the mounted address parser has no import dependency (0.20472ms) +✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (20.996603ms) +✔ agents: working counts what the holder received and has not read; routed alone is not working (20.013196ms) +✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (37.264004ms) +✔ agents: working wins over waiting, and Input needed still shows (17.389268ms) +✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (19.757453ms) +✔ agents: state reads only its own business, even for a run name both use (17.941307ms) +✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (21.541613ms) +✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (19.240324ms) +✔ agents: stranded is oldest first and capped at 50 for a role (81.77078ms) +✔ agents: stranded is per role and per business (33.502357ms) +✔ agents: a message routed to a held role and not yet received is not stranded (15.294111ms) +✔ REQ-DEC an approval outlives its run and a broker restart; a new run of the instance consumes it once (34.380112ms) +✔ REQ-DEC another instance of the same definition cannot consume an approval, and its refusal consumes nothing (18.851731ms) +✔ REQ-DEC REQ-THREAD-1 a thread approval is consumed only by a run holding the same thread (24.905425ms) +✔ REQ-DEC a later run of the same instance supersedes an open decision; another instance cannot (23.072276ms) +✔ REQ-DEC REQ-THREAD-1 a thread decision is superseded only from the same thread (22.981219ms) +✔ REQ-DEC REQ-THREAD-1 a thread instance decision raised before row 121 is not consumed or superseded by any thread run (32.417159ms) +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (23.534803ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (25.909729ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (24.794356ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (15.037415ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (15.273824ms) +✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (23.17287ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (14.755543ms) +✔ task action subjects and linked decision trail are complete and ordered (22.453165ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (12.114057ms) +✔ business isolation includes inherited object names and cross-business message references (17.445753ms) +✔ authority never transfers between action, target, unresolved or replaced role holder; a later run of the instance keeps it (25.580558ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (13.869614ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (20.030198ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (13.779051ms) +✔ both arbiters require human resolution when their cross-role route is themselves (21.072713ms) +✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (20.796795ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (1.221013ms) +✔ channel conversion preserves the four proof/transport byte baselines and modules cannot import a proof caller (1.957535ms) +✔ channel recovery still requires the same shim path and rejects an absent or forged proof (370.912283ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (28.811552ms) +✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (29.400787ms) +✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (21.442026ms) +✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (23.088597ms) +✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (26.068044ms) +✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (25.153426ms) +✔ REQ-COS-1: the chief of staff cannot end its own run; its end of the assistant stops the assistant's thread run (25.170751ms) +✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (20.659403ms) +✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (26.119207ms) +✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (29.081375ms) +✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (30.490687ms) +✔ REQ-COS-1: organization.revision answers the revision alone to the chief of staff and spends nothing (25.886563ms) +✔ REQ-COS-1, REQ-LIMIT-1: organization.revision is refused for every caller whose role file lacks organization.change (28.224901ms) +✔ 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 (84.818312ms) +✔ REQ-COS-1: the chief of staff reads the revision, changes the file with it, and reads again after a stale one is refused (74.783928ms) +✔ Q169: the chief of staff's change of the assistant is validated, written and logged; a stale or protection-stripping one is refused (72.579155ms) +✔ row 65, Q169: the chief's change of the assistant's route runs the routing checks, and a valid one saves as the chief (53.11627ms) +✔ 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 (77.584348ms) +✔ row 194 T10: the chief's reason end of a kept instance refuses instance-kept, holds the claim and spends nothing (19.742573ms) +✔ row 194 T10 (Q2): a role.revoke citing a decision the human resolved still ends a kept run (21.642814ms) +✔ row 194 T10 (B7): the chief's organization.set of a kept instance's model refuses before the writer; the human's limits.set does not (21.413284ms) +✔ row 194 T13: organization.set and limits.set refuse the launch, retired, keep and noHire keys before the writer (25.511839ms) +✔ row 194 T18 (D2.4): from a chief session veto.set and instances.set refuse, and nothing reaches the writer (24.175842ms) +✔ row 194 T8: the chief hires with a reason and no decision, the writer gets the reason as context, and the log names it (20.541583ms) +✔ row 194 T11: on a bus with two businesses, the chief of one hires and fires only in its own (18.388371ms) +✔ row 194 T8: hire and fire need a reason of 1 to 500 characters and refuse a decision key (30.032523ms) +✔ row 194 T8 (sage-1471): only the chief hires or fires; every other role refuses role-not-chief, even one granted the actions (26.556345ms) +✔ row 194 T8 (sage-1471): a chief whose authority lacks role.hire or role.fire refuses decision-required before the writer (45.440264ms) +✔ row 194 T8a: a re-hire of a fired name goes to the writer like a hire, and the writer's refusals pass through by name (24.25945ms) +✔ row 194 T8b: a definition is hireable only when every action is on HIRE_ALLOWED, it is version 2, the chief lists it and noHire doesn't (700.179178ms) +✔ row 194 T8b: a hire without the role files configured refuses not-configured (20.398815ms) +✔ row 194 T9: a fire revokes the claim, cancels open requests, asks the launcher to kill the runs, then delists and logs (23.193063ms) +✔ row 194 T9a (bus): a fire end's done writes lifecycle.end.killed and lifecycle.end.alive with run and pid, and an unknown stop's reason; a terminate's done or a bad stop refuses (28.222265ms) +✔ row 194 T9: fire refusals come before step A and leave the claim, the requests and the file alone (22.418053ms) +✔ row 194 T9c: a writer failure at B leaves the instance firing, and the repeat delists and logs once (25.168813ms) +✔ row 194 T9c: a failure at C leaves the fire unlogged and the instance firing, and the repeat logs it (26.236208ms) +✔ row 194 W-N1: a fire.done before a later fire.stopped of the same instance leaves it firing; another instance's done does not end it (23.580914ms) +✔ row 194 T10: a hire or fire of a kept name refuses instance-kept before the writer (28.544816ms) +✔ row 194 T10: a fire the writer's check refuses from the file stops nothing and leaves the instance launchable (20.015513ms) +✔ row 194 T18: hire and fire take only their own keys, and a refused call writes nothing (33.4674ms) +✔ row 194 T8, T9, T18: through the runtime the chief hires and fires with the real writer, the receipt carries the reason, and a refusal leaves the file alone (71.90735ms) +✔ row 194 W-N1: the launcher's take of a launch or regenerate of a firing instance closes it as instance-firing (24.315468ms) +✔ C2-06 held library lock gives the agent citation-unavailable and its retry reason (5146.847687ms) +✔ C2-06 malformed sync state is availability failure, never a fabricated invalid cite (91.017758ms) +✔ C2-06 no mirror or freshly initialized empty mirror still means no served citation (74.899112ms) +✔ C2-06 coder send_message guard covers every table refusal; inserts nothing (104.891492ms) +✔ C2-06 research send_message guard covers every table refusal; inserts nothing (92.634445ms) +✔ C2-06 stored check carries the file author trailer at an unrelated cited tip (58.086655ms) +✔ C2-06 supporting cite cannot state canon, including a folder also marked playground (39.432924ms) +✔ C2-06 supporting file stores supporting scope and its real agent trailer (46.345741ms) +✔ C2-06 check evidence and insert are atomic when storing evidence fails (40.888569ms) +✔ C2-06 live limits change both directions; unreadable file keeps checks and exact fixed texts (53.49631ms) +✔ C2-06 page store is session-local, caps 20 fetches and 200 KiB, drops at disconnect (81.723004ms) +✔ C2-06 transport passes safe capped guard reason; ordinary refusal envelope is byte-identical (53.423454ms) +✔ C2-06 non-library businesses retain role.claim shape and unguarded free sends (25.837596ms) +✔ C2-06 thread messages carry file/last evidence with one read per distinct path (75.337236ms) +✔ C2-06 guarded request snapshot cannot change between check and insert (23.309318ms) +✔ C2-06 library.limits.set is human-only and invalid settings leave file and receipts unchanged (38.990605ms) +✔ C2-06 runtime reads changed and unreadable reply limits without restarting (118.181053ms) +✔ comment cursors isolate businesses, move only forward and remain append-only (15.821466ms) +✔ only trusted sync writes validated cursors; events and cursor share rollback (11.506676ms) +✔ explicit 3b copy migration preserves evidence and opens as 3g; normal startup never migrates (65.375404ms) +✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (83.495369ms) +✔ named migration registry chains 3b through 3g to a fixture-only future version in order (80.059628ms) +✔ migration refuses mismatched source metadata without changing schema or evidence (33.12335ms) +✔ offline command migrates only the supplied fixture root and reports usage errors (164.107665ms) +✔ N4: the console verbs are exactly the preview and the console resolve, and decision.resolve is not one (1.068294ms) +✔ bindConsole takes only the business human and a three-string owner subject (37.775518ms) +✔ J1, Q188: only a console cap reaches the console verbs, and a console cap reaches nothing else (36.720565ms) +✔ preview: the two real answers, a grant summary and the digest, for an open grant request only (26.223895ms) +✔ J1, J2: a decision that is not an eligible grant request refuses both console verbs (31.129759ms) +✔ N2, J13: a Console approve records via console and the owner, before the executor starts, and the grant runs once (100.255226ms) +✔ J15: a Console deny closes with the other key and starts nothing (134.091936ms) +✔ J2: approve and deny come from approvalChoice and the options, not a fixed yes and no (171.368715ms) +✔ J11: a stale digest or a key outside the two refuses decision-stale and writes nothing (101.332974ms) +✔ J14: a via console close with no matching human.input approves nothing (131.423755ms) +✔ recordEvent keeps human.input reserved, so no agent forges a console answer (72.907673ms) +✔ J7: a console proof over the socket binds a cap for its one call and the reply releases it (130.351209ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ M6: a creative raise refuses decision-not-allowed, cited or not, and no decision row appears; the assistant still raises (132.310823ms) +✔ a creative instance sends only to the user and hears only from the user (161.830537ms) +✔ M10 through the broker: the chief and the tester are refused on bean, the human's limits.set of its models key works (167.057521ms) +✔ row 256 T6 the human deletes a creative instance through the broker; the chief and the tester are refused at the verb (174.790133ms) +✔ X4: register adds a store reference to the running bus, and issue then writes the key (21.619264ms) +✔ X4: register runs the constructor's reference checks (0.618159ms) +✔ X4: register reads no file, so a key file left at the path stays unused, and a live token refuses (20.40591ms) +✔ X3, X4: unregister drops an entry with no token and refuses one with a live token (15.701542ms) +✔ X5: a claim holds the slot, so a second claim and a host.run refuse host-busy until release (11.342573ms) +✔ X5: a claim made while a host.run is in flight refuses host-busy (11.149409ms) +✔ X5: a stale release leaves a later claim in place (0.393147ms) +✔ X5: abortClaim aborts a live claim and says pending, and revoke's abort reaches a claim too (10.986872ms) +✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (72.717527ms) +✔ credentials refuses a role session and any argument (88.547233ms) +✔ credentials reads the status on each request, and a broker without one lists none (84.968307ms) +✔ the broker process answers credentials without a token, path or variable in its output, log or store (217.631075ms) +✔ T1: a store reference with no file yet starts, and the agent is not-issued until issue (14.401814ms) +✔ T1: a store path the reference does not own, or no store root, refuses the reference (0.486037ms) +✔ T1: a wrong mode on the store root, a business dir, an agent dir or the file refuses start (12.580852ms) +✔ T1: a missing store root refuses start; a missing {file} reference still refuses start (0.478133ms) +✔ T1: a store file that is a directory or a symlink refuses start (0.680102ms) +✔ T2: useFor keys from the session only (12.69759ms) +✔ T5: issue, rotate, revoke and delete are live with no restart, and revoke holds across one (35.313027ms) +✔ T5: a failed commit leaves the token on disk but not in memory, and a bad token or date writes nothing (13.728376ms) +✔ T5: revoke drops the token from memory before the unlink, so a failed unlink still leaves it revoked (15.059819ms) +✔ T5: revoke of a {file} reference forgets the token and never unlinks the file (0.710644ms) +✔ T5: delete refuses an agent dir holding anything but tokens (12.413614ms) +✔ D5: every credentials use( call site is on the allowlist (36.866222ms) +✔ row 199: the lifecycle verbs refuse every session but the human, and the tester revokes only (140.161827ms) +✔ T5: issue, rotate, revoke and delete are live, rewrite the business file and leave one receipt each, with no value anywhere (400.748908ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (2.400268ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (1.301476ms) +✔ expiry refuses use and env references never become client data (0.447046ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.038699ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.277232ms) +✔ each C-suite role file carries the same two lists: human, chief-of-staff, pm, cto (1.198751ms) +✔ chief-executive-officer: messages to and from the user, chief of staff, pm and cto pass (260.61647ms) +✔ chief-executive-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (181.571441ms) +✔ chief-executive-officer: another C-suite role isn't on the lists either (205.711684ms) +✔ chief-financial-officer: messages to and from the user, chief of staff, pm and cto pass (233.087536ms) +✔ chief-financial-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (228.181065ms) +✔ chief-financial-officer: another C-suite role isn't on the lists either (226.379557ms) +✔ chief-information-officer: messages to and from the user, chief of staff, pm and cto pass (258.033178ms) +✔ chief-information-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (244.927756ms) +✔ chief-information-officer: another C-suite role isn't on the lists either (224.375342ms) +✔ chief-information-security-officer: messages to and from the user, chief of staff, pm and cto pass (211.398689ms) +✔ chief-information-security-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (246.901921ms) +✔ chief-information-security-officer: another C-suite role isn't on the lists either (224.299547ms) +✔ chief-marketing-officer: messages to and from the user, chief of staff, pm and cto pass (255.20302ms) +✔ chief-marketing-officer: a message to or from the coder, reviewer or assistant is refused and logged, and nothing is stored (218.355414ms) +✔ chief-marketing-officer: another C-suite role isn't on the lists either (223.481697ms) +limits: {"event":"limit-reached","business":"demo","check":"daily-quota","export":"mayLaunch","subject":{"instance":"coder","provider":"anthropic","model":"opus","account":"jason-max"},"path":["tokens","daily","jason-max"],"limit":0,"count":0,"unknownTurns":0} +usage: {"event":"usage-unknown","business":"demo","run":"run1","instance":"coder","turn":"3914b3ab-6fd8-4505-8dc6-7d8f717bed2a","source":"@none","code":"baseline-unknown"} +limits: {"event":"limit-reached","business":"demo","check":"daily-quota","export":"mustStop","subject":{"instance":"coder","provider":"anthropic","model":"opus","account":"jason-max"},"path":["tokens","daily","jason-max"],"limit":0,"count":0,"unknownTurns":1} +usage: {"event":"usage-unknown","business":"demo","run":"run1","instance":"coder","turn":"4244520e-8bc8-4b1f-a7b0-390867789071","source":"@none","code":"baseline-unknown"} +✔ REQ-LIMIT-2 runtime: limits counts the daily quota, and mayLaunch refuses only the account at it (176.563478ms) +✔ REQ-LIMIT-4 runtime: a run bound with the account writes it on its usage records and stops at the quota (147.474202ms) +✔ REQ-LIMIT-4 runtime: a run bound without an account records null and the daily quota never stops it (136.018943ms) +✔ REQ-LIMIT-4 bindLaunch keeps the account off session.launched, refuses one that is not an identifier, and takes null as none (78.557197ms) +✔ a human resolve sends the raising role one message from the human, and its request is the answer (132.886172ms) +✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (141.438924ms) +✔ a human withdraw sends one message with the note (127.547192ms) +✔ a human seen before the close leaves the request on the close's answer, not the seen (159.960055ms) +✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (158.892491ms) +✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (155.635154ms) +✔ a raising role nobody holds at the close gets the message on the next claim (149.095826ms) +✔ a human DECISION send keeps its own instruction as the request, not a close's answer (138.535257ms) +✔ a cross-role raise whose arbiter the raiser does not hear from is refused and stores nothing (173.34113ms) +✔ a gated raise by a role that does not hear from the human is refused and stores nothing (158.221663ms) +✔ a route the raiser hears from is allowed, checked from the route to the raiser (179.435293ms) +✔ a refused supersede withdraws nothing (169.53675ms) +✔ the assistant's within-role and gated raises still work (160.903568ms) +✔ roles lists open decisions routed to a role, and never the human inbox (119.939415ms) +✔ recent lists closed decisions of every route, newest close first, with how each closed (160.62131ms) +✔ recent gives the closing event as closed when the decision was seen first (148.284261ms) +✔ recent stops at RECENT rows (435.640294ms) +✔ every row is what trail already gives the same session for that id (165.613384ms) +✔ a role session can't list decisions, so it can't read another role's open decisions (196.708995ms) +✔ decisions is a read: one business, strict arguments, nothing written (167.309182ms) +✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (86.333613ms) +✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (75.326168ms) +✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (86.586569ms) +✔ endLaunch leaves a claim another run took alone (101.77052ms) +✔ refuse records action.refused against the caller with the code only (79.182053ms) +✔ launches off refuses role.launch with launch-revoked until launches on (104.846135ms) +✔ broker process: launch ops authorize role.launch, record refusals and end runs (228.874873ms) +✔ row 281 T2: a fire with host grants on two hosts and a Gitea grant answers with a job, then revokes all three (596.421763ms) +✔ row 281 T3 (sage-2575): a delete with grants takes two requests: the first starts the job, the next completes (546.08069ms) +✔ row 281 T3b (Q2): a delete whose job refuses on host B; the retry refuses grant-revoke-failed and revokes only B and Gitea (547.217141ms) +✔ row 281 T3b: B cleared by hand with hosts.revoke; the next delete starts a job for Gitea and the leftover line only (542.570884ms) +✔ row 281 T3c (sage-2575 condition 4): a delete of an instance with no grant completes in one request (156.681863ms) +✔ row 281 T4: host B unreachable in a fire: the job refuses, A stays revoked, B and Gitea untouched; a repeat completes (509.742163ms) +✔ row 281 T4b (Q1): a line a manual hosts.revoke left on C is removed by the fire, with its close (254.409939ms) +✔ row 281 T4b (Q1): with C unreachable, the fire completes and names the line in grantsLeft (247.020526ms) +✔ row 281 T5: Gitea's admin token can't be checked: the job refuses with the Gitea grant intact (312.003353ms) +✔ row 281 T5b (N1): a failed token DELETE refuses gitea-left; the repeat retries only this agent's entries (695.44442ms) +✔ row 281 T6 (sage-2574): a host decision approved before the fire and run after it ends revoked, with nothing applied (181.181692ms) +✔ row 281 T6b (sage-2574): a Gitea decision approved before the fire and run after it ends revoked before any Gitea write (217.069091ms) +✔ row 281 T6c: a host executor in flight aborts and ends revoked; a Gitea executor in flight is awaited and leaves no token (520.986022ms) +✔ row 281 T6d (B1): undecided host and Gitea decisions are in the set, and approved after the fire both end revoked (235.606944ms) +✔ row 281 T6e (B1): a grant raise refuses instance-fired while firing and after the fire, goes through after a rehire, and refuses grant-revoke-running during a delete job (313.65987ms) +✔ row 281 T7: an instance with no grant fires as today, synchronously, with the same events (153.101877ms) +✔ row 281 T9 (B3): one step G per agent; the manual verbs refuse while it runs, and sweep names it running (542.893142ms) +✔ row 281 T10 (B2): the fire and the delete answer while their jobs are held, and the broker answers other verbs (492.287879ms) +✔ row 281 T11 (C1): a fire's job completes after the chief's run ends and processing halts (217.584684ms) +✔ row 281 T12 (C1, N7): a step B refusal in the job is fire.failed, and the repeat runs B and C synchronously (212.804898ms) +✔ row 281 T13 (N4): a restart records each held job interrupted once, with one message each; an ended job gets none (367.359461ms) +✔ row 281 T14 (N1, sage-2653): a host removed under an undecided decision stops the fire on host-unknown; the human's no closes it, and the repeat fire has no job (180.005359ms) +✔ row 239 T4: a refusal after the writer's revoke is named -revoked, in the job's fire.failed and notice, a synchronous fire and a delete (257.396064ms) +✔ row 239 T4b: an unmarked step B refusal keeps its code, and the notice says nothing of the tokens (208.951942ms) +✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (11605.98971ms) +ℹ ✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (11486.636498ms) +ℹ F-A creative write: PUT status 204 +ℹ F-B creative: permissions {"admin":false,"push":true,"pull":true} +ℹ F-A chief read: PUT status 204 +ℹ F-B chief: permissions {"admin":false,"push":false,"pull":true} +ℹ F-C protected Story/**: seat rc 1, owner rc 1; established false +ℹ F-D protected branch: normal rc 0, force rc 1; established true +ℹ minted 4, hits 0 +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 11577.32902 + +ℹ minted 4, hits 0 (complete TAP stdout/stderr) +✔ X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token (12346.736798ms) +✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.122316ms) +✔ X-G1: a raise that fails a check refuses and stores nothing (73.603067ms) +✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (180.000473ms) +✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (36.243518ms) +✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (143.838629ms) +✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (103.637864ms) +✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (316.130712ms) +✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (102.22647ms) +✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (82.627409ms) +✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (148.608034ms) +✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (125.071633ms) +✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (150.342551ms) +✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (65.50195ms) +✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (175.216955ms) +✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (151.001152ms) +✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (169.008369ms) +✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (175.830887ms) +✔ X-G6: a restart settles an open rotation back when the file still holds the old token (139.029794ms) +✔ X-G6: a restart settles an open rotation forward when the file holds the new token (138.216382ms) +✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (142.757801ms) +✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1091.433398ms) +✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (162.232841ms) +✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (134.554848ms) +✔ N9: a 404 on a delete by id counts as done (132.878837ms) +✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (408.303337ms) +✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (55.604509ms) +✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (119.491117ms) +✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (112.234142ms) +✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (66.804682ms) +✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (48.893832ms) +✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (103.10314ms) +✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (108.827202ms) +✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (108.23663ms) +✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (116.067701ms) +✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (159.604481ms) +✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (349.855412ms) +✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (191.390358ms) +✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (168.84658ms) +✔ X-G12: a token that reaches wider than its forks fails with the repository named (374.745975ms) +✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (241.869183ms) +✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (42.272869ms) +✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (265.693353ms) +✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (149.923992ms) +✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (253.437483ms) +✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (265.488335ms) +✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (134.51154ms) +✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (104.863826ms) +✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (127.458222ms) +✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (44.991992ms) +✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (90.334074ms) +✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (124.37379ms) +✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (104.989653ms) +✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (103.246453ms) +✔ C2-21 L7/L7b: research refuses non-library targets lib (42.526463ms) +✔ C2-21 L7/L7b: research refuses non-library targets app,lib (40.482915ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (39.371869ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (39.922526ms) +✔ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (88.345886ms) +✔ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (87.031462ms) +✔ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (86.351778ms) +✔ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (84.915793ms) +✔ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (104.605286ms) +✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (85.687367ms) +✔ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (143.522108ms) +✔ C2-21 L4: creative write requires push true but ignores unrelated permission keys (191.524651ms) +✔ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (42.318949ms) +✔ 3j copy migrates to 3k: every row kept, and sqlite_master gains only the two tables and their keys (78.630365ms) +✔ a 3j→3k step that fails partway leaves the store at 3j with every row and the schema intact (96.861459ms) +✔ the 3j engine refuses a migrated store, migrating or not, and leaves it as it was (80.916201ms) +✔ gitea_users: one user per business and agent, an id above 0, a 16-hex nonce and a real decision (10.127811ms) +✔ gitea_grants: one grant per agent, a projects array, a token id above 0, and only for a recorded user (11.887622ms) +✔ G1, A1: only the chief raises a grant, and a refused raise stores nothing and runs no keyscan (31.867269ms) +✔ G1: grant goes only with credential.mint, row a takes only host-enroll, and E1 refuses each bad field (46.197078ms) +✔ G5, A5, A6: the scanned key set must equal the requested set, or host-key-mismatch and nothing stored (31.398279ms) +✔ G4 (host-enroll block, M1): the broker appends the keys, fingerprints and sudo line, and the cap counts the block (27.491417ms) +✔ G6, X1: the human's cli yes starts enrollment, which writes the record, the key file and the events in order (50.456248ms) +✔ A2, A4: a credential.mint without grant, and a no, start nothing (234.766653ms) +✔ A3, X1: a resolve by the tester or an agent starts nothing (237.33494ms) +✔ G6 (duckworth-0081 B3): a withdraw by the chief or the human, and an arbiter resolve, start nothing (240.494535ms) +✔ G10 (duckworth-0081 B1): a second start for a spent decision refuses decision-consumed, and an unresolved or agent-resolved one not-authorized (52.338264ms) +✔ A8, S1: two approved enrollments for one host: the second ends host-enrolled and the pin stays (70.114688ms) +✔ A9: the hosts primary key refuses a second record for (business, host) (24.283038ms) +✔ X3: a failure at step 2 deletes the record again and ends grant.failed, with no key file (46.24405ms) +✔ A10, X3: at start a resolved, unspent enrollment is spent and ends interrupted, and never runs late (26.231352ms) +✔ X3: at start an enrollment that stopped after its registry step loses its record and key file (45.960734ms) +✔ G24 first sentence, M2, A12: tags are mosaic- and 32 hex, and differ by business, agent and host (0.2514ms) +✔ E4: hosts.check sets the record ready and records grant.host.checked; a non-zero list leaves it pending (51.129859ms) +✔ G20 (duckworth-0081 B3): the human's hosts.check marks the host ready, and the reader refuses (47.394471ms) +✔ G18 (duckworth-0081 B2), broker half: a check refused host-key-mismatch leaves the host pending-install with no grant.host.checked (47.340819ms) +✔ E4: a check whose installer fingerprint differs from the record refuses host-changed (46.405097ms) +✔ E4, G21: hosts.remove is the human's or the tester's, deletes the key and the record, and records grant.host.removed (49.657098ms) +✔ G21, F1, F2, M4, A22: hosts.remove refuses host-line-left on a guarded hostLineLeft, and ignores look-alikes (49.570622ms) +✔ hosts.remove refuses host-in-use while a grant record names the host (46.156677ms) +✔ the grant writer refuses a body outside section 7's shape (20.654259ms) +✔ recordEvent refuses reserved-event for a grant.* action or operation (20.442522ms) +✔ row 280: an occupied TCP port is attempted, refused, and retried with our sshd (85.99113ms) +✔ row 280: every injected picker sample is unprivileged and outside the ephemeral range (0.325667ms) +✔ G7: an Approve installs the tagged line, logs in with the new key, and writes the key file, record and grant.applied (685.593378ms) +✔ G7: a commands change on a live grant updates the record in one transaction and keeps the key and line (541.295692ms) +✔ G8: a failure at install or verify removes the line and leaves no record, key file or reference (1501.889501ms) +✔ G8: a failed remove adds hostLineLeft and tells the human to sweep (629.161154ms) +✔ G8: a failure in step 5 before commit unlinks the key file, drops the reference and removes the line (668.571337ms) +✔ G8: a store fault in the commit transaction ends host-failed with no record, key file, reference or line (658.374518ms) +✔ G8: a failed step 1 check spends the decision and runs nothing remote (86.176158ms) +✔ G9: a registry, definition or commands change between raise and Approve ends decision-mismatch before any remote step (528.768991ms) +✔ G9: an agent no longer a role or no longer declaring host ends with its code, spent (76.899464ms) +✔ G10: a second start refuses decision-consumed, and an unresolved or agent-resolved decision not-authorized, writing nothing (521.168348ms) +✔ G15: no event, message or decision holds the private key (512.962357ms) +✔ G23: a revoke during install ends grant.failed revoked with no key file, no grant and no line (528.351626ms) +✔ G23 (row 249): an add that writes after an abort's remove leaves hostLineLeft until the sweep (890.392885ms) +✔ G23 (row 249): an add that writes after a timed-out install's remove leaves hostLineLeft until the sweep (865.529249ms) +✔ G23: a revoke recorded after the raise and before the start ends step 1 revoked, and a held slot ends host-busy (518.971983ms) +✔ G24: names at 64 characters with . and _ give distinct tags of the fixed length (0.391124ms) +✔ G11: a restart after install removes the line, then ends interrupted with the tag, holding the slot until then (232.581779ms) +✔ G11: a restart after a new key's commit, with the key file written, ends with no key file, record, reference or line (223.350048ms) +✔ G11: a restart after a commands change's commit leaves the record as it was and asks nothing of the host (514.750672ms) +✔ G11 (B2): a restart before step 1, spent or not yet spent, ends interrupted and nothing runs later (70.969428ms) +✔ G11: a remote undo that fails ends interrupted with hostLineLeft and the sweep hint (277.824357ms) +✔ G11: a restart leaves a finished grant alone, and the reconcile adds nothing for it (490.472292ms) +✔ N1 (G27): the start sweep unlinks host. files with no record in loaded businesses only, and refuses a store dir that is not private (6.330668ms) +✔ G12: the human's hosts.revoke deletes the record, the key file, the reference and the line, with a closed marker (663.152604ms) +✔ G12 (N4): a revoke with no record still removes the computed tag's line, with granted false; bad args refuse (217.13799ms) +✔ G13: a revoke during install aborts the executor, which ends revoked, and the line goes (532.66538ms) +✔ G26: the human, the assistant and the tester revoke; the chief and a pm refuse; sweep is the human's or the tester's (511.587979ms) +✔ row 281 T8: a tester hosts.revoke refuses tester-dev-only once bus.tester is off, and a human revoke writes no job records (640.249827ms) +✔ G27: a restart after step 1 sweeps the key file and runs step 3 once the bus is up; status shows the revoke until then (664.763112ms) +✔ G27: after step 2 with the host down, the restart closes the marker with hostLineLeft; a marker without a revoke id is not reopened (964.459555ms) +✔ G27: a key file a revoke could not unlink is reported left and goes at the next start (656.883433ms) +✔ G28: a commands change, then a revoke, then a restart leave no record, key, reference or line (686.055875ms) +✔ G24 (B4): hosts.sweep removes only this business's recorded tags no live grant holds, and records what it left (957.992693ms) +✔ G12: the local steps finish before the host step, so a host that hangs leaves the agent no access (732.654031ms) +✔ G23 (X5): a revoke right after the spend reaches the executor's claim, so the grant ends revoked (211.233039ms) +✔ G23 (X5): a revoke that lands as verify returns ends revoked with no record, key file or line (633.185683ms) +✔ G13: a revoke whose host remove fails still drops the record and key, and reports the line left for a sweep (652.840708ms) +✔ G22: enroll, check, grant, host.run, a commands change the next host.run uses, and a revoke that kills a run in flight (1163.800031ms) +✔ mos-1163: a grant whose agent became a creative role after the raise ends host-creative-role at step 1, spent, with nothing remote (64.015364ms) +✔ sage-1908: once the agent of an applied grant is a creative role, host.run refuses host-creative-role, runs nothing remote and keeps the record (538.418197ms) +✔ G1 (host): only the chief raises a host grant, a grant on any other action refuses, and nothing is stored (32.710479ms) +✔ G1, R4: the grant takes exactly method, agent, host, commands and rotateBy, and an unknown method refuses (33.674894ms) +✔ row 236 (#1742): a gitea grant on a broker with no Gitea path refuses gitea-not-configured, and nothing is stored (19.46278ms) +✔ G2: an agent whose definition lacks host refuses host-not-declared, and an unknown agent unknown-role (22.91918ms) +✔ mos-1163: a grant whose agent is a creative role refuses host-creative-role, even when its definition declares host (19.211988ms) +✔ G3: wildcard, shell, empty and other 210 D6 argv prefixes refuse invalid-request (42.311518ms) +✔ R4 rotateBy (sage-1782): a real calendar day after today UTC, or invalid-request (31.997666ms) +✔ G19: a grant naming a host that is pending-install refuses host-not-ready, and an unknown host host-unknown (26.820413ms) +✔ R4, G5: the raise routes to the human and its event body binds the eight fields with their hashes (20.631504ms) +✔ G4 (host block): the broker appends the host, each prefix, the rotate date and new key or change (35.252323ms) +✔ 3i copy migrates to 3j: every row kept, and sqlite_master gains only the two tables, their keys and the guard (84.521716ms) +✔ a 3i→3j step that fails partway leaves the store at 3i with every row and the schema intact (104.741219ms) +✔ the 3i engine refuses a migrated store, migrating or not, and leaves it as it was (92.51188ms) +✔ A22b, sage-1703: a session-written grant.* row from before the step survives it, and the grant readers ignore it (83.685563ms) +✔ the guard: a grant.* action or operation on action.allowed needs the (NULL, grant) actor (16.450444ms) +✔ hosts: one record per business and host, a ready host has its installer and check, grants need the host (14.053678ms) +organization: {"event":"reload-needs-restart","business":"demo"} +✔ row 298 runtime: human scope approval writes the file and organization history at the supplied revision (72.294299ms) +✔ row 298 (Q3): only the human at the cli reads or sets the scope; agents, the tester, a reader and the Console refuse (73.794369ms) +✔ row 298 (Q1): organization.set and limits.set refuse a grantScope key, and nothing is written (49.773859ms) +✔ row 298 (Q3): the human verb writes the scope and a grant.scope record with the human proof and both sha256 (49.823082ms) +✔ row 298 (Q4): an in-scope Gitea grant is issued with no inbox item, logged with 236 records, and revocable (137.425955ms) +✔ row 298: an out-of-scope Gitea grant waits for the human, whose Approve still issues it (112.667194ms) +✔ row 298: with no scope, a bad scope or an unreadable file, an in-scope-looking grant waits (147.492667ms) +✔ row 298 (Q6): a chief-of-staff instance that is not launch.by never raises in scope (52.576787ms) +✔ row 298 (Q5): a scope narrowed between the raise and the spend ends the grant scope-changed (78.994152ms) +✔ row 298 (Q2): an in-scope host grant is spent with no Approve; ls never covers lsblk, nor a scope command extra arguments (77.189686ms) +✔ row 298 (Q5): a host scope narrowed before the spend ends scope-changed (68.854486ms) +✔ row 298 Q4: missing scope evidence or a bad scope digest cannot authorize execution (91.801425ms) +✔ row 298 T4/T8: an in-scope host grant issues, records and revokes through the existing executor (78.319582ms) +✔ row 298 T6: host enrollment stays open for human Approve even with both methods in scope (54.560875ms) +✔ row 298: scope without an organization revision never closes automatically (46.657264ms) +✔ REQ-WAIT-1: the broker hard waits are exactly the list, and match the business vocabulary (1.301609ms) +✔ REQ-WAIT-2: a business granting a hard wait at either class is refused; the actions that left are grantable (22.43529ms) +✔ REQ-DEC-2: every hard wait goes to the human, cited or not, and the arbiter cannot resolve it (417.442267ms) +✔ REQ-DEC-2: a gated raise citing the current spec goes to the arbiter for its domain; uncited, to the human (169.152331ms) +✔ REQ-DEC-2: an arbiter-approved cited gated decision authorizes the raiser, and the human cannot resolve it (54.601619ms) +✔ REQ-DEC-2: a cited merge raised by merge-gate goes to the technical arbiter; a merge raised without the grant routes by citation (73.589128ms) +✔ REQ-DEC-2: a stale or unconfirmed citation refuses without a decision; an arbiter raising its own goes to the human (101.852574ms) +✔ Q169: the chief of staff's end of the assistant stops every thread run; its own run and another ender's are refused (48.853609ms) +✔ Q169: a revoke of the assistant the chief of staff raises goes to the human, cited or not, and no arbiter resolves it (123.78714ms) +✔ Q169: another role's cited revoke of the assistant, resolved by an arbiter, is refused (67.291578ms) +✔ Q169: a revoke of the assistant citing a decision the human resolved stops its thread run (54.511275ms) +✔ Q169: a cited revoke of a role not in END_PROTECTED still goes to the arbiter, and executes (59.078555ms) +✔ Q169: a role granted role.revoke at either class still sends a revoke of the assistant to the human (71.658678ms) +✔ row 280: an occupied TCP port is attempted, refused, and retried with our sshd (92.325255ms) +✔ row 280: every injected picker sample is unprivileged and outside the ephemeral range (0.33723ms) +✔ the executor tests need sshd, ssh, ssh-agent, ssh-add, ssh-keygen and setpriv: a missing one fails, never skips (0.111256ms) +✔ T3: organization.set, limits.set and instances.set refuse hosts, and the writer never runs (36.018182ms) +✔ T4: the value check takes one unencrypted ed25519 key and nothing else (157.480478ms) +✔ T5 to T8: registry, grant, argv shape and allowlist refusals (506.80221ms) +✔ T9: under 1.7 every call needs a decision bound to its host, argv and stdin, spent once (289.921802ms) +✔ T10, T11: argv reaches sudo unchanged after sudo -n -- (283.383739ms) +✔ T12, T13: exit code passes through, the timeout kills the group, output is capped (1496.034031ms) +✔ T14: a host key not in the registry refuses and nothing runs (172.819199ms) +✔ G18: hosts.check with a wrong pin refuses host-key-mismatch and runs nothing on the host (303.804395ms) +✔ N3: a remote exit 255 that prints the pin refusal text is the command result, and an ssh failure keeps its message (277.858768ms) +✔ T15: ssh reads no config file and no user known_hosts (252.154743ms) +✔ T16, T17: key material in output or stdin refuses credential-leak, and no event holds it (585.209612ms) +✔ T18, T19, T20: one call at a time, no key in any argv or env, and nothing left after (4644.627941ms) +✔ T21: revoke kills the call in flight, and the next call refuses (287.340808ms) +✔ T22: events carry host, address, user and argv in clear, and sizes and hashes only (260.462073ms) +✔ T24: no literal gitea and vikunja kind list outside vocabulary.mjs, and the credential use sites are known (16.409898ms) +✔ T25: no role definition lists host.sudo while it is a hard wait (0.491673ms) +✔ T27: the bus child dying takes ssh and ssh-agent with it (313.366843ms) +✔ T27: ssh-add -t drops the key from an agent the test keeps alive (3554.030464ms) +✔ N2: a hung ssh-add is killed at its limit or on abort, and nothing is left running (427.663419ms) +✔ T27: the start sweep keeps a live bus dir and kills only a matching live process (26.684212ms) +✔ T27: the bus start sweeps /secrets/run before it listens, and a stray entry stops the start (50.331235ms) +✔ M3: host.run refuses host-not-granted unless the grant record has an approved, spent host grant (140.19724ms) +✔ M3: a host_grants record gives Credentials a store reference at start, and a restart reads its key (97.96694ms) +✔ G16: the writer refuses each user, tag and key the design names, before any key file (3977.539022ms) +✔ G16: from= comes from the host file, a privileged user skips the group and sudo checks, and the line is exact (979.888629ms) +✔ G16: list shows tagged lines only, and add and remove never touch a line outside the tag pattern (493.882809ms) +✔ G25: the writer refuses a symlinked dir or user file, a group-writable dir and an odd tmp, and writes 0644 (710.868641ms) +✔ G16: mosaic-keys passes the three requests to the writer through sudo and refuses anything else without it (61.876758ms) +✔ E3: the sudoers line and the sshd drop-in are the design lines and parse (11.711145ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.353031ms) +✔ process reader gets own kernel identity without exposing environment values (0.388645ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.547471ms) +✔ real pid 1 remains inspectable when its environment is protected (0.206743ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ row 165: instances and instances.set refuse every non-human session but the tester, the chief of staff included, and pass the verified actor (32.498874ms) +✔ row 165 runtime: an add and a change reach the organization file with the human as actor; each refusal keeps the bytes and logs nothing (121.096486ms) +✔ row 194 T12: agent and reader sessions refuse veto.set; the tester gets add and change but not delete or veto.set; the human passes the hook for each listed verb (24.124925ms) +✔ system Python is a resolved root-owned ELF, never a shim or home interpreter (1.9319ms) +✔ real system interpreter is available and safely inspected (18.242837ms) +✔ restart receipt appends actual replacement identity and refuses unsafe files (2.475451ms) +✔ activation gate compares fresh startup receipt to the current interpreter identity (1.248406ms) +✔ non-regular startup receipt refuses promptly without waiting on a FIFO peer (25.996937ms) +✔ REQ-DEC-2: a merge-gate raise its arbiter resolved lands through the host op: checkLand answers the facts and a ticket, records nothing, and recordLand spends the decision once (35.977406ms) +✔ checkLand refuses on the decision and the review (33.456523ms) +✔ REQ-LAND-1: a gated merge decision refuses not-authorized, cited and approved by the arbiter or uncited and approved by the human (80.645552ms) +✔ REQ-LAND-1: a within-role merge grant refuses not-authorized at the land (22.708726ms) +✔ a cross-role merge that routes to the human, because merge-gate is its own arbiter, lands on the human's CLI answer (26.458106ms) +✔ a merge option with no first line refuses no-subject, as land-work does (23.667437ms) +✔ checkLand refuses with land-work's codes past the authority check, and a decision raised for refactor never lands (60.919472ms) +✔ a ticket outlives the run that asked, and recordLand checks the task again (26.392813ms) +✔ two tickets on one decision: the first recordLand lands, the second refuses decision-consumed and records nothing (27.568719ms) +✔ landRefused files the code on the task, tells the role only with notify, and ends the ticket (27.981073ms) +✔ landHeld is true only for an approved, unspent cross-role merge decision this role raised for a task in this thread (29.339521ms) +✔ landHolders lists a merge decision exactly while landHeld is true for its non-thread instance, in decision order (38.112086ms) +✔ landHolders leaves out a thread instance's decision, even one raised before its role became one (22.491181ms) +✔ landRelease releases the holding run by land-relaunch once it has asked message.receive and nothing delivered to it is unread, and waiting messages go to the next holder (35.869767ms) +✔ a landing run started after the resolution passes checkLand for its held decision before any message.receive, and lands it (28.347495ms) +✔ the bus process answers landHolders and landRelease from their own lines, not as credentialStatus (134.707636ms) +✔ usage.begin with a decision id needs it held for the run's instance, usage.end needs the begin, and the limit checks see the landing turn (35.280909ms) +✔ no run may write the action.allowed for git.merge.protected itself (16.923407ms) +✔ a message to a stopped role sends launch.by one notice, and the result says so (26.156287ms) +✔ a message to a role outside launch.instances says so in its result and in the notice (16.29963ms) +✔ a message to a running role sends no notice and its result is unchanged (14.087546ms) +✔ a re-route at a claim, a release and claim, and a broker restart send no second notice (20.513066ms) +✔ a run launched but not yet claimed counts as no holder (12.812418ms) +✔ no notice for a message to launch.by, from it, a refused one from a tester, to the human, or in a thread to a thread instance (24.54525ms) +✔ a business with no launch block tells nobody and says there is no launcher (10.73999ms) +✔ the notice sender stays internal: no role is named _bus and no caller can send as it or to it (31.032892ms) +✔ row 302 a run threadIdle released answers launcher-released on receive, state, read and send, logs each, and claims nothing again (41.106585ms) +✔ row 302 with a run launcher-released, a run that lost its claim another way still answers not-holder (30.291331ms) +✔ row 302 a run landRelease released answers launcher-released, and claims nothing again (32.373917ms) +✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (30.715403ms) +✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (292.95116ms) +✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (32.575353ms) +✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (29.824761ms) +✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (15.072635ms) +✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (752.680526ms) +✔ C2-03a library.sync: every agent, the tester and a reader are refused before the adapter runs (26.51115ms) +✔ C2-03a library.sync: the human fetches, accept passes through, and a held sync is a recorded refusal (17.098619ms) +✔ C2-03a library.sync: a LibraryError keeps its code and a host without sync is not configured (23.769243ms) +✔ C2-03a runtime: a librarySync from a boot config, with no function, is refused (1.254133ms) +✔ C2-03a runtime: the timer syncs each entry with a user at start and on each tick, logs codes only, and stops on close (550.732605ms) +✔ row 254 human-cli: library.sync outlasts the 5 s client default, and library.status does not (6078.452204ms) +✔ C2-04 M4: both verbs reject every agent before arguments or adapter access (34.091081ms) +✔ C2-04 bus: human reads validate keys and return adapter results without path leakage (14.059665ms) +✔ C2-04 socket: each async library verb resolves its result rather than serializing {} (73.249665ms) +✔ C2-04 runtime: protectedRoots skips library but retains real code-root credential protection (99.841905ms) +✔ C2-04 runtime: unsafe existing mirror refuses boot and never publishes a socket (23.208522ms) +✔ C2-04 runtime: real library adapters reach the socket and lock waits leave other bus reads responsive (209.058898ms) +organization: {"event":"reload-needs-restart","business":"demo"} +✔ T20 fire: a revoke that fails stops the fire with the file unchanged; one that succeeds revokes, keeps the directory and names the services outside the store (88.853855ms) +✔ T20 fire: an instance already delisted still has its tokens revoked and the file is left alone (50.531732ms) +✔ T20 fire: with no entry in the running bus, a token on disk refuses and an empty store goes on (55.130931ms) +✔ T20 delete: a revoke that fails keeps the file; one that succeeds clears the store, deletes, and names the tracker bot (68.365056ms) +✔ T20 delete: with no entry in the running bus, a token file is cleared and the delete goes on; a file reference refuses (56.027802ms) +✔ T21 delete: a write between the raise and the take makes the delete refuse revision-conflict before it revokes anything (56.216714ms) +✔ row 281 T1 fire: a host. or granted gitea file in the store refuses step B credential-revoke-failed, file unchanged (54.006454ms) +✔ row 281 T1 start: a host grant record whose key file is gone starts and reads not-issued (49.819101ms) +✔ row 194 T12: agent and reader sessions refuse the lifecycle verbs before any effect; the tester gets launch, end and the request read, never regenerate (37.100946ms) +✔ row 194 V2 and V4 at the verb: the request is open, by the human, with a human.input; thread, retired, unknown and revoked refuse with no event (21.059473ms) +✔ row 194 D1.1 take: a request is taken once, closes once, and lifecycleRequests lists only what is not closed (15.857664ms) +✔ row 194 D1.1 take: a launch.revoke after the request closes it launch-revoked, and a request past 60 s closes request-expired (17.74144ms) +✔ row 194 T15 (bus): while halted, launch and regenerate refuse processing-halted at the verb and at the take, hire and fire refuse, terminate goes; an unreadable switch refuses with its code (27.832162ms) +✔ row 194 T12a: the tester launches and ends on its own business with actor tester; bus.tester off in the file refuses the call and the take tester-off (22.055326ms) +✔ row 194 V3 terminate: claims revoked, open requests cancelled, bound runs named, taken requests listed; nothing at all answers not-held (26.881819ms) +✔ row 194 V3 terminate of a thread instance: the human ends every thread, the tester only its own (20.386641ms) +✔ row 194 T6 V5 order at the verb: the writer's named refusals, then instance-running, instance-starting, the last-listed refusal and the revision (31.547178ms) +✔ row 194 V5: an open launch past its 60 s is not starting, as it can only close request-expired (12.396323ms) +✔ row 194 V5 after the take: lifecycleDelete re-checks running and starting, then commits through the writer with the human as actor (22.156679ms) +✔ row 194 V5 retry: a retired name deletes again only after workspace-rename-failed, and that request commits nothing (18.666363ms) +✔ row 194 V5 runtime: a delete request, the take and lifecycleDelete commit the file with retired, log the human and reload so the name no longer binds (126.153299ms) +✔ REQ-LIMIT-3 limitLeaves finds every limit object and skips the agent numbers (1.162999ms) +✔ REQ-LIMIT-3 limitLeaves takes token limits at schema depth only, whatever their names (0.203324ms) +✔ REQ-LIMIT-3 with no check switched on a launch is allowed, a turn never stops, and every limit is not enforced (13.102895ms) +✔ REQ-LIMIT-3 a fixture check that refuses refuses the launch and stops the turn, logged with the limit and the count (13.166517ms) +✔ REQ-LIMIT-3 a report that omits a limit shows it not enforced; a counted entry and a reason come through (12.243368ms) +✔ REQ-LIMIT-4 each check gets the business, a copy of the limits, the subject, the records and the time (13.606659ms) +✔ REQ-LIMIT-4 fail closed: a throw refuses, stops and fails the report, naming the check and the code (12.124149ms) +✔ REQ-LIMIT-4 fail closed: a malformed answer, a promise, a rejected promise, a non-boolean or a bad hit is malformed-answer (14.891103ms) +✔ REQ-LIMIT-4 fail closed: a check switched on but not loaded refuses, stops and fails every limit as not-loaded (11.465207ms) +✔ REQ-LIMIT-4 state(), never-loaded case: before any load the report shows not enforced, or not-loaded for a switched-on check (10.057669ms) +✔ REQ-LIMIT-4 state(), first-load-refused case: a check refused on its first load is not-loaded in mayLaunch, mustStop and report (10.580753ms) +✔ REQ-LIMIT-4 fail closed: an unreadable organization file refuses and stops with check null; the report throws (10.490638ms) +✔ REQ-LIMIT-4 fail closed: unreadable records refuse and stop with check null and fail the report (0.460895ms) +✔ REQ-LIMIT-3 the shipped token-limit, through the real loader, counts known records only since the limit was set (23.694781ms) +✔ REQ-LIMIT-3 a token limit for a provider named limit is counted and refuses, beside an agent limit of the same name (10.220552ms) +✔ REQ-LIMIT-3 core reaches limit checks only through the plug-in loader (1.040782ms) +✔ REQ-LIMIT-4 a role run at its limit stops at the end of its turn: RESULT and read land first, the claim is revoked by limit-check (32.864475ms) +✔ REQ-LIMIT-4 the stop also lands on state; a run with no stop ends without a limit (33.557805ms) +✔ REQ-LIMIT-4 fail closed: a mustStop throw or a malformed answer stops the run, naming the bus and the code (79.740548ms) +✔ REQ-LIMIT-4 a thread run at its limit is released with the reason, and its end record names the limit (21.553652ms) +✔ REQ-LIMIT-4 a broker with no limit checks configured never stops a run (14.515456ms) +limits: {"event":"limit-reached","business":"demo","check":"token-limit","export":"mayLaunch","subject":{"instance":"coder","provider":"zai","model":"glm-5.3-flash","account":null},"path":["tokens","instances","coder"],"limit":0,"count":0,"unknownTurns":0} +organization: {"event":"reload-needs-restart","business":"demo"} +usage: {"event":"usage-unknown","business":"demo","run":"run1","instance":"coder","turn":"43909b03-4723-4284-a034-99a4124dbcb0","source":"@none","code":"baseline-unknown"} +limits: {"event":"limit-reached","business":"demo","check":"token-limit","export":"mustStop","subject":{"instance":"coder","provider":"zai","model":"glm-5.3-flash","account":null},"path":["tokens","instances","coder"],"limit":0,"count":0,"unknownTurns":1} +usage: {"event":"usage-unknown","business":"demo","run":"run1","instance":"coder","turn":"8a1d0ce8-a5fa-48de-8fbb-1cdf3dc48590","source":"@none","code":"baseline-unknown"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ REQ-LIMIT-3 REQ-LIMIT-4 runtime: limits carries the token count, mayLaunch refuses at the limit, a reset moves since (140.464509ms) +✔ REQ-LIMIT-3 runtime: with no limit check switched on, the limit shows not enforced and every launch is allowed (36.115812ms) +✔ REQ-LIMIT-4 runtime: the broker asks the shipped token-limit after a turn, and the run at its limit stops on its next receive (47.278576ms) +✔ REQ-LIMIT-4 runtime: with no usage source a turn is unknown, counts zero and stops nothing under a limit of 1 (45.942529ms) +✔ REQ-LIMIT-3 runtime: switching a limit check on with plugins.set loads it without a launch or a turn (62.507225ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ limits binds actor from human proof and rejects role, reader, a tester set and forged actor (21.593699ms) +✔ runtime wires verified human settings to the actual shared file writer and actor receipt (99.140556ms) +✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (52.138762ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (26.250026ms) +✔ missing and foreign-business citations refuse and roll back message and grant (17.242715ms) +✔ cross-role sends still need a matching resolved decision and consume it once (22.002598ms) +✔ trail on a hidden message id answers exactly as on an unknown id, for the owner, a reader and the tester (23.889175ms) +✔ replies on a hidden message id answers exactly as on an unknown id, for the owner, a reader and the tester (24.508447ms) +✔ trail on a decision keeps the decision and drops the user thread messages, their deliveries and their events (32.346527ms) +✔ replies leaves out other users thread messages and still fills a page (184.882748ms) +✔ messages in the business human and tester threads are not hidden (row 177 unchanged) (39.691877ms) +✔ governance pin: the owner sees decisions, inbox entries, claims, tasks and project files, but not the user messages (94.974349ms) +✔ messages pages all human messages newest first without writes; read state is history (312.035947ms) +✔ messages refuses role callers, invalid paging and cursors outside this human feed (25.427401ms) +✔ views messages passes only its paging arguments through (0.29833ms) +✔ messages isolates businesses even with a valid foreign cursor (19.73316ms) +✔ the assistant sends to the user and to the chief of staff only (39.943656ms) +✔ an assistant send to any other role, or to the tester, is refused and logged (35.125523ms) +✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (35.65141ms) +✔ REQ-ASSIST-2 REQ-THREAD-1 thread.send to an instance that does not hear from the user is refused and stores nothing (29.51424ms) +✔ a sender-side list alone refuses a send to a role that has no list (35.364857ms) +✔ a target-side list alone refuses a send from a role that has no list (30.232288ms) +✔ the sender list is checked before the target list (27.746412ms) +✔ roles without the field behave exactly as before (37.182898ms) +✔ a business with no role definitions at all is unlimited (36.052441ms) +✔ a malformed messaging field refuses at boot (12.939412ms) +✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (36.273426ms) +✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (38.805485ms) +✔ review requests and verdicts between roles without the field are unchanged (29.174459ms) +✔ 3f copy migrates to 3g preserving every old column/row, with an empty instance_names and null to_name (105.607849ms) +✔ REQ-MSG-2: the chief of staff names an instance under its within-role grant, and the write is logged (24.072688ms) +✔ REQ-MSG-2: the human names an instance through the CLI (17.152307ms) +✔ REQ-MSG-2: a role without the grant, or holding it cross-role, is refused not-authorized (19.837744ms) +✔ REQ-MSG-2: a name is unique in the business and is never an instance key or a role name (33.20881ms) +✔ REQ-MSG-2: a send by name reaches the instance and records both; a send by instance key is unchanged (26.543837ms) +✔ REQ-MSG-2: an instance key beats a name that equals it, as when a later instance takes the key (19.1142ms) +✔ REQ-MSG-2: a role name beats a name that equals it, as when a later definition takes the word (sage-0590) (20.246676ms) +✔ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (5.945018ms) +✔ proof helper error and disconnect during verification never dispatch or bind (1.335433ms) +✔ Client receives explicit proof refusal codes without dispatch, not outcome-unknown (40.219483ms) +✔ asynchronous proof calls release their transient session on success, failure and timeout (27.28081ms) +✔ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (241.913234ms) +✔ helper output and failures fail closed including unsafe gid (1.789038ms) +✔ helper timeout, capacity and shutdown refuse without leaving children (25.891393ms) +✔ missing or non-regular peer helper refuses at construction before opening the bus (0.275339ms) +✔ shutdown waits for killed helper close before returning (5.702594ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ plugins and plugins.set refuse every non-human session and a forged actor, and pass only the patch with the verified actor (22.360736ms) +✔ 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 (116.945782ms) +✔ vikunja:19/309 through the broker, a patch switches the kinds it names and keeps the rest; null removes one kind and is trailed (52.495923ms) +✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.634426ms) +✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.488687ms) +✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.283427ms) +✔ human leaf identity is reread after walking the parents (0.177504ms) +✔ console requires its own wrapper, nonce and serve parent, and a configured owner from the spawn env (1.209335ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (167.618481ms) +✔ startup token refusal returns safe code without value or partial listening broker (111.434172ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (149.146693ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (226.167472ms) +✔ trusted host registers later launches; socket clients never have a registration verb (146.754409ms) +✔ REQ-LIMIT-4 the broker process answers the launcher's mayLaunch op with the limits' awaited decision (137.78561ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (110.088958ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (141.045437ms) +✔ real runtime injects the coordinator, stops its workload and mirrors stable stop events across restart (161.460072ms) +✔ invalid state still starts the control bus and human halt then resume repairs it (66.670261ms) +✔ processing halt and resume extend only the socket timeout while ordinary status retains the explicit timeout (183.263369ms) +✔ a root halt aborts the active search-source signal before the normal web deadline (32.057129ms) +✔ processing.halt refuses agent reader and tester before any transition (22.636878ms) +✔ processing.resume refuses agent reader and tester before any transition (15.50733ms) +✔ human halt and clear each have one event with verified actor; launch.restore cannot clear (17.853224ms) +✔ all ordinary work verbs refuse before dispatch while pure reads remain available (33.949042ms) +✔ task web authorization and bind paths refuse before external work (16.159868ms) +✔ recordEvent cannot forge processing.halt (12.415293ms) +✔ recordEvent cannot forge processing.resume (11.874061ms) +✔ recordEvent cannot forge processing.stop (11.525125ms) +✔ recordTask poll cannot forge a processing action through its kinds filter (11.84918ms) +✔ recordTask self cannot forge a processing action through its kinds filter (11.831458ms) +✔ spec read remains a read but cannot release new write work after an await (14.877873ms) +✔ the real socket awaits halt and resume beyond the ordinary idle deadline (54.334018ms) +✔ a malformed processing reader never silently admits agent work (16.506656ms) +✔ halt refuses the newly landed host executor path before credentials or effects (13.81699ms) +✔ D1: committed write with a failed receipt refuses outcome-unknown without action.allowed (21.787055ms) +✔ D2: spec.read refuses without content if its role is released during the read (14.028599ms) +✔ D3: spec.read secret check refuses flagged content without content (13.193692ms) +✔ D3: spec.read secret check refuses flagged subject without content (13.124306ms) +✔ a human lists, opens and saves; the save is one commit by the business human (108.415416ms) +✔ an agent and a reader refuse every file verb and nothing is written (44.58563ms) +✔ a stale base refuses stale-base and writes nothing (70.369417ms) +✔ paths outside the spec and docs refuse, as do unknown projects, missing files and odd arguments (58.352895ms) +✔ without a project adapter the file verbs refuse project-not-configured (27.871003ms) +✔ on the socket a save may carry 1 MiB of text; every other request keeps the 64 KiB limit (239.263433ms) +✔ a request that names the save verb first and another verb last is refused at 64 KiB (60.752335ms) +✔ two saves racing for one repository make one commit (145.077196ms) +✔ Broker keeps the sandbox client import closure: only the listed imports, no lazy project import (2.1882ms) +✔ without a trusted project adapter all three async paths refuse project-not-configured (41.362532ms) +✔ real startBroker injects the project adapter for socket reads and bound-author record writes (197.810953ms) +✔ a raise with no recorded push refuses no-push; a raise binds the latest push and shows it (30.218324ms) +✔ happy path: the fork is the upstream repo under the agent's own user, the destination is host-built (24.328796ms) +✔ a hard wait: no decision refuses, and a decision for another task refuses (23.988872ms) +✔ r4 B4: approve while the latest push is A, push B, publish refuses decision-mismatch (48.315616ms) +✔ grant and fork refusals (43.020094ms) +✔ remote-not-fork, compared without case, and remote-host-mismatch, at push (87.447671ms) +✔ two agents granted one project each get their own fork (42.780725ms) +✔ the request cannot name the fork, the user or the destination (22.259842ms) +✔ one decision, one open publish: a second ticket refuses publish-busy, whatever its branch (50.799841ms) +✔ publishRefused files the code on the task's trail and drops the ticket (22.38654ms) +✔ git.push.remote is in neither ROUTED list and no shipped role grants it (0.860928ms) +✔ runtime: withGitea keys from the open ticket only, and the store root is the organization dir (100.519131ms) +✔ the bus process answers the publish ops from their own lines, not launch-op-refused (146.1383ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ T14 a create goes live with no restart: the new instance binds at once and the answer says restart false (70.159586ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ T14 every committed verb reloads once, limits.set, routes.set, plugins.set and veto.set included, and a refused one does not (86.328062ms) +✔ T14 a definition change on a running instance refuses instance-running; a vars change still goes live (63.230389ms) +✔ T14 a file whose credential references differ from the boot copy is not reloaded and the write answers restart true (44.653579ms) +✔ T14 a date-only change to a credential reference still reloads: row 199 issue rewrites the date outside the reload (48.475051ms) +✔ T19 a credentialed create answers restart true; issue works only after a restart, and a later vars change goes live (100.390558ms) +✔ T14 after a refused reload veto.set, instance.hire and instance.fire answer restart true, and a fire of a name kept since refuses before step A (109.376836ms) +✔ T14 a file whose project root differs from the boot copy is not reloaded, because the Credentials store protects the boot roots (44.096944ms) +organization: {"event":"reload-needs-restart","business":"demo"} +✔ T14 a file whose library mapping differs from the boot copy is not reloaded, because the mirror is ensured at start (189.738655ms) +✔ T14 reloadBusiness refuses an unknown business, a changed human and a document the boot check refuses (11.304065ms) +✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (36.651964ms) +✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (20.631222ms) +✔ a message with no reply gives an empty list (15.636847ms) +✔ replies come oldest first and stop at REPLIES (74.216981ms) +✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (29.192959ms) +✔ strict arguments; a role session gets human-required (36.068993ms) +✔ views passes replies through as one call with the id (0.207603ms) +✔ requirements returns the committed list to a holding role session, and no one else (26.25782ms) +✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (16.796201ms) +✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (17.421124ms) +✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (28.789156ms) +✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (25.486714ms) +✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (28.912854ms) +✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (34.30615ms) +✔ reviewFetch answers only the addressed reviewer, for the request it cites (23.644803ms) +✔ review.verdict answers one request once, to the role that asked (27.842286ms) +✔ task.close on a pushed task needs an approve verdict for the latest push (39.722438ms) +✔ a refusal the host decided is filed on the task it names, and only a task ref counts (23.421088ms) +✔ row 306 T1: a run the timer sweep ended answers thread-revoked on every claim-needing call, logged, and its claim still refuses run-revoked (35.785927ms) +✔ row 306 T1: a user's request and access.reload end the run the same way, and it answers thread-revoked (48.507316ms) +✔ row 306 T2: after the sweep, a run the human stopped and a run the limit stop ended still answer not-holder (32.878038ms) +✔ room.open sorts all threaded instances, creates defaults, and reuses caller-owned threads (24.04021ms) +✔ room.open preflights instance collisions atomically (17.697738ms) +✔ room.open preflights owner collisions atomically (16.560622ms) +✔ room verbs require the human, and none is a tester verb (20.121838ms) +✔ room verbs refuse missing projects and zero member businesses (31.554402ms) +✔ send/read refuse a missing canonical member without effects (16.945683ms) +✔ send/read refuse a foreign-owner canonical member without effects (18.195759ms) +✔ send/read refuse a wrong-instance canonical member without effects (18.505374ms) +✔ room.send targets a key, broadcasts without tags, and preserves original text and ordinary route/input rows (21.040868ms) +✔ current display names and repeated tags resolve once; a replaced display name is unknown (20.155731ms) +✔ a legacy name colliding with another member key is ambiguous; its own key resolves once (19.240203ms) +✔ unknown or non-member addressees and tags-only bodies leave no message effects (22.291877ms) +✔ messaging denial on the last target rolls back the entire broadcast; allowed subset still works (19.855713ms) +✔ a clearing last member fences the whole send; another active target still accepts (23.636491ms) +✔ room.read groups one send, pages by the last grouped id, and rejects foreign or absent cursors (37.928347ms) +✔ room.read keeps identical sends at the same timestamp separate and includes every session (30.290914ms) +✔ room.read defaults and caps the number of merged entries, not recipient rows (477.577237ms) +✔ room.read includes an agent reply with its thread and instance without merging it with human rows (25.777396ms) +organization: {"event":"reload-needs-restart","business":"demo"} +✔ row 65: routes and routes.set refuse every non-human session, the chief of staff included, the tester may read and set, and each passes the verified actor (31.357671ms) +✔ row 65: a new launch binds only on a switched-on harness with a module name, and a run an earlier broker bound binds again after its harness is switched off (25.656568ms) +✔ row 65 runtime: harnessLevels answers each module in the tree, and null for a name with no module (43.697888ms) +✔ row 65 runtime: routes and routes.set reach the organization file with the human as actor, a new bind reads plugins.harness, and each refusal keeps the bytes and logs nothing (82.8458ms) +✔ v3b prototype refusals, views and append-only mutations (102.475261ms) +✔ T4 D7: a clear of a held default session fences it, drains at the next receive, retires at the exit proof and moves the default (43.380779ms) +✔ T4 D7: an unheld session drains at accept, its proof is run null, and a non-default clear leaves the default (18.869118ms) +✔ T4 D7: a run that ends before its next receive drains at its end, with its generation (19.667189ms) +✔ T4 D7: a run whose turn is running (delivered, unread) stays clearing; uncertain is recorded once and nothing more (28.869618ms) +✔ T4 D7 B1: undelivered inputs are cancelled with receipts at accept, never delivered to the successor, and a routed reply is cancelled at arrival (31.446986ms) +✔ T4 D7: repeat, conflict, a second op, a stale revision, confirm and a halt (27.784595ms) +✔ T4 Q4: the successor takes -N, the next N free, and a bound name stays taken (23.135391ms) +✔ T4 D8: the successor start is held, refused or started, each report on its own counter (22.221136ms) +✔ T4 D7 N4: a halt at retirement waits and writes nothing; retirement goes once processing resumes (18.665399ms) +✔ T4 sage-2828: a cancel before retirement makes the session active again, keeps the claim and the cancelled messages, and never makes the successor (28.487376ms) +✔ T4 sage-2828 sage-2830: a cancel after retirement ends the startup only, and a later thread-only send makes the successor pending as new input (21.84549ms) +✔ T4 N4: a user's thread whose grant is gone by retirement ends the clear failed, writes no successor, and leaves the session as it was (20.393413ms) +✔ T4 N4: a user's thread whose grant is live at retirement retires the session and makes its successor (20.463324ms) +✔ T4 sage-2828: a cancel after the successor started refuses session-clear-complete (18.951498ms) +✔ T4 B2: a run bound to a clearing session refuses its claim, and claims again once the clear is cancelled (21.433308ms) +✔ T4 mos-1325: the tester clears its own thread; its clear, cancel and operation on a human-owned thread refuse thread-not-owned and write nothing (25.414577ms) +✔ T4 schema: session_ops and message_cancels are append-only, and a second open clear of a session is refused below the broker (17.484562ms) +✔ T4 schema: a 3m store migrates to 3n, session_ops and message_cancels are new and empty, and every other row stays as it was (98.806472ms) +✔ T4 schema: the 3m engine refuses a 3n store, migrating or not, and leaves it as it was (184.194142ms) +✔ session.list rows carry the reader count for each session, with its store identity (25.940947ms) +✔ a legacy session is asked for with legacy true (18.334885ms) +✔ without a reader the count is unknown, never 0; a reader that throws is store-unreadable (29.057023ms) +✔ compactions must be a function (8.46073ms) +✔ T1 REQ-ROLE-2: two sessions of one thread keep their own history, delivery and replies (44.864321ms) +✔ T2 REQ-ROLE-2: one holder per session, generations count its claims, and a sibling session's message never reaches the other holder (36.407197ms) +✔ T2 sage-2257: a released or revoked run's retired generation refuses its approval, delivery and read, and writes nothing (46.356474ms) +✔ session.create is idempotent on its op, refuses a reused op or a taken name, and reaches only the owner's threads (33.383099ms) +✔ the 3l triggers keep sessions in their thread and session ids apart from thread ids (79.670566ms) +✔ T7: a 3k store migrates to 3l, each thread's default session has the thread's id, and old rows keep their seqs (114.877142ms) +✔ T7 D9: the 3k engine refuses a 3l store, migrating or not, and leaves it as it was (174.674664ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (30.729071ms) +✔ another run of the same instance consumes an approval once; a failed check leaves it usable (21.000747ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (16.878861ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (32.096496ms) +✔ class drift gated to cross-role refuses before consumption (24.402435ms) +✔ class drift cross-role to gated refuses before consumption (28.531683ms) +✔ class drift gated to within-role refuses before consumption (23.793663ms) +✔ class drift cross-role to within-role refuses before consumption (27.324824ms) +✔ class drift within-role to gated refuses before consumption (22.43571ms) +✔ class drift within-role to cross-role refuses before consumption (20.570478ms) +✔ message.send consumes approval and prevents a later send or authorize (24.53433ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (25.915514ms) +✔ 3e copy migrates through 3f to 3g preserving every old column/row and adding null spec citations (116.763397ms) +✔ a human spec save sends one notice to each pm and cto, naming the commit, and each commit sends its own (117.51131ms) +✔ no notice for a docs save, an agent record commit, a git commit outside the Console, or a refused save (137.799774ms) +✔ a notice to a role no run holds waits for a claim, and launches nobody and tells nobody (72.261863ms) +✔ a business without a pm or a cto saves the spec and sends nothing (59.660702ms) +✔ a notice that cannot be stored answers outcome-unknown, and the commit stands (71.598382ms) +✔ a BLOCK reaches the chief of staff, the chief relays it to the assistant in the user's thread, and the commit stands (79.678632ms) +✔ with no thread a BLOCK still reaches the chief of staff, and the relay to the assistant is refused (69.212106ms) +✔ within the notice's project the relay lands in the user's most recently opened thread (75.967033ms) +✔ a more recently opened thread in another project does not take the relay (71.06828ms) +✔ a BLOCK is refused from anyone but a pm or cto, to anyone but the chief of staff, and outside a reply to its own notice (82.201405ms) +✔ a role cannot forge a spec notice: its text opens neither a BLOCK nor the assistant's thread (72.560087ms) +✔ the bus receives nothing: a reply to a notice addressed to _bus is refused (71.348381ms) +✔ real socket awaits spec reads, record writes and cited raises; other requests still return normally (110.272344ms) +✔ real socket preserves asynchronous refusal codes and disconnects its transient proof (66.419246ms) +✔ two real socket writes to one repository yield one commit and one project-busy refusal (63.660403ms) +✔ client disconnect during await tolerates late reply (62.404269ms) +✔ client disconnect during await tolerates late refusal (47.630195ms) +✔ N-a: slow admitted record.write returns its committed result after the idle deadline (213.548744ms) +✔ current citation is recorded; docs commits do not stale it and a new spec never reopens settlement (99.70433ms) +✔ stale, unconfirmed, missing and partial spec citations refuse without inserting a decision (98.435252ms) +✔ uncited raises retain their synchronous path and spec read is available to any held role in its business (45.936283ms) +✔ record writes bind the session author, require authority and refuse spec and impersonation paths (51.783046ms) +✔ REQ-COS-1: the pm asks the chief of staff for roles and counts; the chief receives a STAFF-REQUEST and answers it (28.809874ms) +✔ REQ-COS-1: only the pm can send a staff request; every other session is refused and the chief of staff hears nothing (29.717637ms) +✔ REQ-COS-1: a pm without message.send is refused like any other gated action (16.471437ms) +✔ REQ-COS-1: the request goes to the business launcher, and only when that is a chief of staff (45.421105ms) +✔ 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 (38.45695ms) +✔ 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 (35.265872ms) +✔ REQ-COS-1: a refused staff request leaves no staff.request action.allowed event (19.837119ms) +✔ REQ-ASSIST-2: the role definitions' messaging lists hold for a staff request, sender and recipient (54.582196ms) +✔ a staff request names a task, 1 to 10 distinct roles and counts of 1 to 20; nothing else is accepted (38.507277ms) +✔ REQ-COS-1: a staff request grants nothing: the pm still can't launch and the chief of staff launches as before (21.163504ms) +✔ a business that never switched launches reads allowed with no time and no event (19.951743ms) +✔ launches revoked and then restored read allowed, with the restore time and event id (17.546917ms) +✔ a task closed on the tracker leaves the open list, and total counts the open ones (19.861595ms) +✔ a tombstone is not open (14.428562ms) +✔ the open list stops at the cap and says so; total still counts every open task (16.923813ms) +✔ a role and a reader session read state; arguments, and an unbound cap, refuse (16.978669ms) +✔ a human session reads the same state, with the same cap (16.341884ms) +✔ reading state writes nothing: every table keeps its row count (17.14111ms) +✔ creates private WAL store and excludes a second writer until explicit close (13.766027ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (13.86732ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (15.583414ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (62.607335ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (9.992754ms) +✔ async transactions refuse before invoking their function (7.565437ms) +✔ usage_records refuses non-canonical timestamps atomically (8.405944ms) +✔ the session default fits under the reply cap and pages to the end with next (121.698139ms) +✔ the short form: no description, assignees by role, every listed key (76.552074ms) +✔ each filter, alone and together, and total counts every match (149.215176ms) +✔ task_ref reads one task in full, as the whole board has it (89.971437ms) +✔ no arguments is the read the Console and mosaic make, unchanged (70.220766ms) +✔ an unknown argument or a bad value refuses invalid-request (329.388812ms) +✔ tasks rows carry priority and requirement for every session that reads tasks (37.654063ms) +✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (40.73565ms) +✔ an empty business and one with snapshots but no task.created read cleanly (13.638238ms) +✔ recordTask keeps sync reads and a role write apart (22.280189ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (10.268182ms) +✔ a bad entry refuses the whole record (10.291184ms) +✔ taskView reads the projection for one business (12.345512ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (22.441883ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (296.15995ms) +✔ without an adapter the server refuses every task verb (40.867817ms) +✔ the runtime refuses an invalid adapter and closes a valid one (52.245609ms) +✔ the process loads the S3 adapter from plain-data trackers (270.807564ms) +✔ row 177 T1: a tester thread is owned by tester, and neither side reaches or lists the other (30.949997ms) +✔ row 177 T2: the tester thread.send is from tester-cli with no human.input; its open is by tester (17.472534ms) +✔ row 177 T3: the lists read the tester as the human in its own thread, and nowhere else (14.766871ms) +✔ row 177 T3: an assistant that would not hear the human refuses the tester too (14.08949ms) +✔ row 177 T4: the tester thread run answers the tester, never the human (27.631899ms) +✔ row 177 T5: each stop revokes only its own threads, recorded by its own actor (20.919215ms) +✔ row 177 T5: the human stop never ends a tester run (16.137318ms) +✔ row 177 T6: a business whose human is tester refuses invalid-business (10.335025ms) +✔ row 177 T7: the human trail shows a tester thread message as the tester (20.108898ms) +✔ row 177 T8: a tester-thread run never raises a decision to the human; a human-thread run still does (20.624108ms) +✔ row 177 T8: a tester-thread raise routed to the arbiter stays allowed (16.346561ms) +✔ row 182 T9: a tester reply into the human's thread refuses thread-not-owned and stores nothing (19.771995ms) +✔ row 182 T9: a tester reply into its own thread lands there (20.71573ms) +✔ row 182 T9: the chief's reply up a chain into a tester's thread lands there (22.587899ms) +✔ row 182 T9: a tester reply to a spec notice refuses thread-not-owned; the chief's relay still lands (22.004483ms) +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +organization: {"event":"reload-needs-restart","business":"demo"} +✔ row 175: TESTER_VERBS and the organization branch tester set are pinned (1.02212ms) +✔ row 175: each of the six reads as the tester deep-equals the human read (66.509295ms) +✔ row 175: W1-W3 as the tester land with receipt actor tester; the human still logs the human (73.343201ms) +✔ row 175: W2 as the tester refuses credentials, protected definitions, human, tester and bus.tester, and changes nothing (60.932445ms) +✔ row 175: the human-only actions refuse a tester tester-refused, and the file and store stay as they were (58.60777ms) +✔ row 175: W4 as the tester writes by tester and keeps every name check (19.093146ms) +✔ row 175: W6 as the tester records created_by tester; the human records the human (14.414726ms) +✔ row 175: with bus.tester unset or not exactly true no tester binds, so no read or write runs (53.860243ms) +✔ the tester binds only when the business sets bus.tester to exactly true (84.645572ms) +✔ a business may not name a role tester, so the tester address is never a role (26.820913ms) +✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (41.162007ms) +✔ a tester message is from tester, needs no prefix, and writes no human.input or authority (18.36157ms) +✔ a tester message to a role no run holds refuses tester-target-not-running and stores nothing (12.528114ms) +✔ a tester message is refused while the run is only launched, and routes once it claims (16.864575ms) +✔ a human message to a role no run holds still waits and tells launch.by (14.511743ms) +✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (27.855205ms) +✔ the tester reads the human views a reader reads (28.520745ms) +✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.123485ms) +✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.343143ms) +✔ testerNonce reads only the tester's variable and gives null on any read error (0.17693ms) +✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (99.165089ms) +✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1023.697975ms) +✔ T1: the human and the reader see a held thread claim; agents still lists the role only (25.990047ms) +✔ T2: a released or revoked thread claim is not listed, though its thread has an earlier claim row (23.485805ms) +✔ T3: a role session refuses human-required and the tester refuses tester-refused (21.878007ms) +✔ T4: agents answers each session the same before and after a thread run claims (20.893236ms) +✔ T5: the result carries neither the thread name a user wrote nor a message body (24.309149ms) +✔ T6: two held sessions of one thread list as two rows, each its session's latest claim (20.744632ms) +✔ REQ-THREAD-1 REQ-ORG-2: the 3d→3e migration adds the thread tables and leaves messages from before threads as written (101.405352ms) +✔ REQ-THREAD-1: a 3d bus with a changed schema is refused and keeps its version and tables (42.179733ms) +✔ REQ-THREAD-1: the offline command prints the kept-message counts before the ready line (118.00854ms) +✔ a share lets the sharee read the thread and nothing else: every write verb stays the owner's (mutants 7, 9) (37.388418ms) +✔ the business human shares too, and a `*` share reaches every covered principal but the tester (25.933798ms) +✔ mutant 2: a share never widens a grant; when the sharee's grant stops covering the thread, its reads end (24.797841ms) +✔ mutant 3: a thread whose user owner has lost coverage is hidden from its sharees, the owner's board included (34.089015ms) +✔ mutant 4: a share's state is its latest row, so an unshare ends it and a share after it starts it again (30.368769ms) +✔ mutant 8: a user target needs a live grant of any level covering the thread, else share-not-covered (20.57025ms) +✔ a viewer reads a thread shared with it, and share, unshare and shares refuse a viewer access-refused (design 4.3) (22.926681ms) +✔ a share target is `user:`, the business human or `*`, and never the sharer (44.206185ms) +✔ a user whose collaborator grant no longer covers its own thread can't share, unshare or list its shares (22.363335ms) +✔ the tester and a reader session never share: each verb refuses and writes nothing (35.04066ms) +✔ the owner's trail and replies show a user's message only while that user's thread is shared with the owner (29.324705ms) +✔ a share covers every session of the thread (21.887081ms) +✔ a sharee can't list the thread's sessions: session.list stays owner-only (Q7, sage-2818 Q1) (21.165954ms) +✔ only a human principal matches a share: an agent bound to the thread and the tester match none (sage-2818 Q2) (34.022208ms) +✔ a 3l store migrates to 3m: thread_shares is new and empty, and every other row stays as it was (91.805061ms) +✔ the 3l engine refuses a 3m store, migrating or not, and leaves it as it was (182.672228ms) +✔ REQ-ROLE-2 REQ-THREAD-1: one holder per thread, through the broker and the trigger (40.064323ms) +✔ REQ-ROLE-2 REQ-THREAD-1: the 3e triggers refuse writes that skip the broker (76.889789ms) +✔ REQ-MSG-1: a message to or from the assistant names its thread, or is refused (33.545088ms) +✔ REQ-MSG-1: the chief of staff's reply comes back to the thread that sent the work (601.10295ms) +✔ REQ-MSG-1: a reply can't carry one thread instance's thread to another thread instance (25.657181ms) +✔ REQ-THREAD-1 REQ-USER-2: one thread's messages never reach another thread's holder (29.553108ms) +✔ REQ-LAUNCH-1: a message to a thread with no holder waits, and the next holder gets it (32.380669ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting, and only for its holder (31.947038ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1: a message delivered to a run that ended unread doesn't hold up the next holder (29.220743ms) +✔ REQ-ROLE-2 REQ-THREAD-1 Q169: a role.revoke the human approved ends every thread run of the assistant (29.148707ms) +✔ Q169 REQ-USER-2: assistant.stop ends the human's own assistant thread runs, and only the human's (40.715017ms) +✔ REQ-USER-2: the human verbs refuse another owner's thread (30.514779ms) +✔ REQ-THREAD-1: thread.create refuses when the business has no thread instance or more than one (26.361854ms) +✔ REQ-ORG-2: projects and threads are named once, and the repo names a business project (27.673282ms) +✔ REQ-THREAD-2: thread.messages returns the thread's history in pages and writes nothing (92.574224ms) +✔ project.create refuses a repository name without its matching repo before any write (20.483784ms) +✔ project.create keeps unrelated names with and without a repository (12.761945ms) +✔ C2-09a: explicit creative instances bind independent threads, default sessions and holders (50.445176ms) +✔ C2-09a: explicit missing, non-thread and malformed selectors create no thread or session (22.463949ms) +✔ C2-09a: creative bindLaunch without a thread refuses thread-required (10.395003ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (47.192665ms) +✔ two wire claims serialize; a lost reply never automatically retries (41.519613ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (31.889257ms) +✔ client preserves UTF-8 when a response divides a multibyte character (11.334002ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (34.091281ms) +✔ REQ-LIMIT-4 usage protocol binds run identity, requires a delivered turn and isolates businesses (27.220238ms) +✔ REQ-LIMIT-4 3g copy migrates through 3h, 3i, 3j and 3k to 3l preserving every old column, row and sequence; new usage table is empty (104.10737ms) +✔ REQ-PLUG-1 runtime reaches transcript only through loader, over the real bus socket (65.117133ms) +✔ REQ-LIMIT-4 loader source records new turn kinds, excludes history and retries exactly once (14.949326ms) +✔ REQ-LIMIT-4 cut: unknown is durable and logged next to every total (9.994772ms) +✔ REQ-LIMIT-4 disabled: unknown is durable and logged next to every total (10.421247ms) +✔ REQ-LIMIT-4 missing-begin: unknown is durable and logged next to every total (8.838196ms) +✔ REQ-LIMIT-4 no-new-usage: unknown is durable and logged next to every total (9.691449ms) +✔ REQ-LIMIT-4 rewrite: unknown is durable and logged next to every total (9.913817ms) +✔ REQ-LIMIT-4 unfinished turn remains visibly unknown after accounting restarts (8.620203ms) +✔ usage records are append-only (9.340387ms) +✔ REQ-LIMIT-4 parse failure logs its safe code, never transcript content (9.593424ms) +✔ REQ-LIMIT-4 resumed run baselines history and exact totals do not overflow (12.855536ms) +✔ REQ-PLUG-1 core has no transcript import and disabled sources do not read any transcript (10.119763ms) +✔ REQ-LIMIT-4 a resumed thread bills only new assistant messages to its next run (10.325626ms) +✔ REQ-LIMIT-4 every enabled source is called; failure is unknown and a partial total stays labelled (15.23575ms) +✔ REQ-LIMIT-4 a turn whose reported sum exceeds safe precision is unknown, not rounded (10.276476ms) +✔ row 65 a run bound without a transcript directory records no-transcript (9.833659ms) +✔ R3: bindUser binds a granted user and refuses one with no live grant (14.45214ms) +✔ R3: a user named like the business human, the tester or a principal word is refused, whatever the grants say (11.38617ms) +✔ R3: each collaborator verb is allowed to a granted user (21.401137ms) +✔ R3: every verb off the collaborator list refuses a user access-refused before its own checks (114.038408ms) +✔ R3: task, web and host verbs refuse a user access-refused (23.999905ms) +✔ R3 leak: project.list shows granted projects only (13.738746ms) +✔ R3 leak: thread.create needs the project and the thread instance granted, and an ungranted id reads as one that does not exist (17.962442ms) +✔ R3 leak: thread.list shows the user its own threads that are still granted, never others (40.410567ms) +✔ R3 leak: names shows granted instances only (17.386966ms) +✔ R3: thread.send is from user: and writes no human.input (13.798915ms) +✔ R3: a reply-chain message.send into another user thread is refused (17.618371ms) +✔ R3: the run reply to a user message reaches that user thread.messages, with no waiting and no notice (19.577163ms) +✔ R3: a reply to: user without a matching in_reply_to is refused user-reply-required (30.870142ms) +✔ R3: a non-thread run, the human and the tester cannot send to: user (20.323494ms) +✔ R3: a user thread run cannot message the owner, while its uncited gated raise reaches only the owner inbox, B9 (21.51814ms) +✔ R3: a removed or expired grant refuses the next request access-revoked, and the file is read per request (21.679586ms) +✔ R3: a user refusal is recorded as action.refused by user: (13.866664ms) +✔ R3: a business with a role keyed user refuses invalid-business (9.786135ms) +✔ R3: name.set refuses the name user (12.018116ms) +✔ R3 r4: while halted, a granted user's thread.send refuses processing-halted and thread.messages still answers (16.772512ms) +✔ R3 r4: the chief's hire refuses an instance named user with the code it gives human and tester (16.985622ms) +✔ R3 r4: a non-function access throws before the store is read (0.130685ms) +✔ C2-09a: naming an instance cannot bypass its collaborator grant or disclose an ungranted project (18.468701ms) +✔ 264e deferred B6: a reply cannot cite the same user in a sibling session (21.119095ms) +✔ 275: VIEWER_VERBS is the ruled read list, inside USER_VERBS (0.934884ms) +✔ 275: a viewer binds, and each viewer verb answers with what its grant covers (30.2724ms) +✔ 275: a viewer is refused thread.create, thread.open and thread.send access-refused, and nothing is stored (21.187135ms) +✔ 275: every verb off the user list refuses a viewer access-refused before its own checks (95.63512ms) +✔ 275: mixed grants, collaborator on one project and viewer on another, take the level per project (23.595183ms) +✔ 275: board reads refuse users and viewers access-refused (80.714186ms) +✔ governance pin, user side: decisions, inbox entries, claims, tasks and project files are the owner's, the user reads its messages (42.819366ms) +✔ web.search: a granted role gets labelled results, and the call is trailed with role, run, verb and query (22.372015ms) +✔ web.fetch: a granted role gets the page as labelled text, and the call is trailed with the url (20.357549ms) +✔ a role without the grant is refused, the refusal is logged with the verb, and nothing is fetched (19.261654ms) +✔ a broker with no web service refuses a granted role (13.772346ms) +✔ bad requests are refused: unknown verb, extra or missing argument, not a string, a human or reader session (18.518736ms) +✔ a session that is not the role holder is refused (12.82222ms) +✔ web.mjs's fences hold on the broker side: refusals are normal results with the fixed reason (39.733323ms) +✔ web.search with no search source configured is a refused result, not an error (13.7816ms) +✔ a search source that fails with its own refusal, or that throws, is handled (17.185417ms) +✔ a slow search source ends in a refusal at the deadline (63.963889ms) +✔ a secret in the arguments is refused before anything is trailed or fetched (13.560133ms) +✔ over the socket: the server routes web verbs to the service; other roles are refused; no service is web-not-configured (67.391944ms) +✔ busBusiness: the web grant comes from "web" in the resolved tool limit (0.258211ms) +✔ webServices: the business's search-source plug-in and web.searxng var make web.search work; without either it is refused as not configured (44.194628ms) +✔ an ungranted call is refused at once: no admission, no wait, nothing touched (17.221814ms) +✔ over the socket an ungranted caller is dropped by the server timeout and a granted one gets the longer wait (347.928815ms) +✔ a search that never answers is aborted at the deadline, the module sees the signal, and the call is trailed as refused (timeout) (74.920064ms) +✔ a fetch that stalls is cut off at the deadline: the request is destroyed, not left running (95.396318ms) +✔ 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 (18.32829ms) +✔ merge-gate's technical raise is cross-role, goes to the cto, and only merge-gate can use the approval (34.073045ms) +✔ merge-gate has no message.send, and without a domain its raise goes to the delivery arbiter (25.272505ms) +✔ the cto must be able to message merge-gate, or the technical raise is refused (row 133) (24.053078ms) +✔ security-reviewer answers a review with its verdict alone; it holds no message.send (27.586325ms) +ℹ tests 998 +ℹ suites 0 +ℹ pass 998 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 75769.365313 diff --git a/agents/steelbeak/work/queue-287/evidence/business-suite.log b/agents/steelbeak/work/queue-287/evidence/business-suite.log new file mode 100644 index 000000000..52172633c --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/business-suite.log @@ -0,0 +1,291 @@ +✔ a grant's level is collaborator or viewer (1.2135ms) +✔ any other level refuses with the key path named (0.705817ms) +✔ covers tells a viewer grant from a collaborator grant (0.16459ms) +✔ covers refuses a call with no level, so a viewer grant can't slip past a collaborator check (0.123619ms) +✔ a viewer grant still needs its business, project, instance and expiry to cover (0.12482ms) +✔ a valid document passes and comes back frozen (2.021061ms) +✔ checkAccess does not freeze or alias the caller's document (0.237983ms) +✔ LEVELS is the single allowed level set, collaborator and viewer (row 193a) (0.098348ms) +✔ accessFilePath is access.json in the config directory (0.319126ms) +✔ top level: object, unknown keys, accessVersion, revision, arrays (0.858299ms) +✔ users: unknown keys and the id rule (0.249483ms) +✔ users: human, tester and any business's human are refused (0.24794ms) +✔ users: home is a given peer or local (0.203275ms) +✔ users: subjects, provider, sub, uniqueness and owner subjects (0.531512ms) +✔ grants: unknown keys, id, user and business (0.431077ms) +✔ grants: projects is ["*"] or distinct lowercase UUIDs (0.609265ms) +✔ grants: instances is ["*"] or distinct declared instances of that business (0.320727ms) +✔ grants: any level other than collaborator or viewer refuses, naming grants[i].level (0.367521ms) +✔ grants: expires is optional ISO time, and a past expiry is not refused (0.747486ms) +✔ the context is required and checked (0.336112ms) +✔ loadAccess reads a valid file and checks it (0.501525ms) +✔ loadAccess refuses a missing file, a symlink and a directory with exit 4 (0.324663ms) +✔ loadAccess refuses another owner and group or other write (0.341248ms) +✔ loadAccess refuses bad JSON and a bad document with exit 2 (0.328302ms) +✔ covers: * and listed ids match (0.194537ms) +✔ covers: an unlisted project, instance or business doesn't match (0.116729ms) +✔ covers: an expired grant doesn't match, at and after the instant (0.118575ms) +✔ covers: level is the least level asked for (row 193a) (0.108766ms) +✔ covers: a missing or unknown level throws (row 193a, N1) (0.235393ms) +✔ covers: a grant with an unknown level covers nothing (row 193a) (0.095664ms) +✔ covers: now must be a valid Date (0.111546ms) +✔ today's files load unchanged: identity, publish and remote are optional (4.289784ms) +✔ a granted agent loads with its identity, grant and the project's upstream (1.918421ms) +✔ N10: publish needs identity and a gitea definition, and the error names the grant (2.193949ms) +✔ row 285: a chief with its gitea reference, identity and publish loads (2.646658ms) +✔ publish names declared projects that have a remote, each once (2.695792ms) +✔ identity fields are checked (2.941449ms) +✔ D9c: the upstream must be its own normalized form, exactly /OWNER/REPO (7.397104ms) +✔ D9c: one .git is stripped, and the fork is built from the stripped name (0.736416ms) +✔ D9c: gitea.url names the instance only (3.765187ms) +✔ the upstream's host and port must be the agent's Gitea's (1.274431ms) +✔ remote-not-fork: an upstream the agent's own user owns, compared without case (1.27873ms) +✔ store references: own path only (design D1, D3a) (0.253823ms) +✔ the role file loads and resolves through loadBusiness (5.248984ms) +✔ the shipped file is exactly the approved list (0.568801ms) +✔ the assistant's authority is message.send and record.write within role (1.643851ms) +✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.468874ms) +✔ a business grant through limits does not widen the role (2.103126ms) +✔ a launch block whose by is the assistant is refused (1.227137ms) +✔ the instance takes no credential references and no tracker (1.712585ms) +✔ an arbiter whose role definition declares messaging lists is refused, for each kind (3.414519ms) +✔ an arbiter without messaging lists is accepted beside the limited assistant (1.570128ms) +✔ the contract passes work to the chief of staff and directs no one (0.485429ms) +✔ REQ-HARN-2: the assistant, companion and research hold the web grant, and no other role file does (0.988738ms) +✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (11.206347ms) +✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.732724ms) +✔ config directory and file path follow MOSAIC_CONFIG (0.227073ms) +✔ the fixture business validates and comes back frozen (0.994283ms) +✔ two instances may share a definition (1.013991ms) +✔ top-level refusals (2.939224ms) +✔ arbiters and projects (4.586351ms) +✔ role instances (2.30027ms) +✔ Vikunja bots (5.245177ms) +✔ a role without Vikunja takes no tracker block (2.857595ms) +✔ credential references match the definition's services (2.272475ms) +✔ launch (8.361716ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (4.793795ms) +✔ loadBusiness: file checks (2.040605ms) +✔ loadBusiness: not a regular file (41.081919ms) +✔ loading writes nothing (1.077813ms) +✔ names that are Object.prototype properties don't count as declared (3.161448ms) +✔ the shipped example refuses as written and validates once filled in (0.811309ms) +✔ persona vars: agent layer, creative definition only, both or neither (8.817701ms) +✔ row 285: a chief or creative instance without credentials.gitea refuses at load (6.447728ms) +✔ row 285: a creative instance with identity and publish loads (0.774254ms) +✔ row 194 keep, noHire and retired validate, and a file without them loads as before (1.57886ms) +✔ row 194 each bad keep, noHire or retired entry refuses (5.865272ms) +✔ C2-21: only creative definitions and the chief with gitea may publish a library project (10.140247ms) +✔ row 323: committed Case 1 document loads unchanged through the business loader (4.882073ms) +✔ row 323: real business validate accepts the Case 1 fixture without tracker credentials (65.86572ms) +✔ row 323: real factory install accepts the document's scratch input and writes it unchanged (161.468205ms) +✔ row 323: the Case 1 business boots without a tracker or @sync credential (50.794373ms) +✔ row 323: factory install refuses bus.tester false before writing (34.17225ms) +✔ row 323: literal library key refuses through final business validation (178.477589ms) +✔ row 323: a library project refuses through the factory profile plan (33.530043ms) +✔ row 325: the Case 1 project's sole key matches the repository project id (4.587873ms) +✔ row 325: real business validate reports the copied repository project valid (66.272939ms) +✔ row 325: the old mosaic-stack project key refuses against those same project bytes (66.102296ms) +✔ row 285 amendment 1: no library loads with empty chief credentials (3.385712ms) +✔ row 285 amendment 1: a library without chief gitea refuses by name (2.200618ms) +✔ row 285 amendment 1: no library with chief gitea refuses the extra reference (0.708673ms) +✔ the role file loads and resolves through loadBusiness (8.538323ms) +✔ the shipped file is exactly the approved list (0.339503ms) +✔ the resolved record matches the approved list, so a widened file fails (2.406238ms) +✔ the chief of staff holds role.launch, message.send, role.end, organization.change, record.write, instance.name, role.hire and role.fire; every other action is gated (2.024787ms) +✔ the pm does not hold role.launch (2.017621ms) +✔ a business grant of a gated action through limits does not widen the role (1.216462ms) +✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.161866ms) +✔ a launch block whose by is the pm instance is refused (1.3663ms) +✔ the contract names no duty to talk to the human and the pm contract no launch (0.368325ms) +✔ the instance takes no credential references and no tracker (1.990093ms) +✔ C2-04: business validate and resolve skip a library before code projects and credential checks (200.876248ms) +✔ usage errors exit 4 (277.707279ms) +✔ validate: a good business exits 0 and prints instance digests (70.09797ms) +✔ validate: project files (308.414394ms) +✔ validate: missing files and a broken system config (237.80596ms) +✔ validate: credential reference problems exit 2 and name each one (62.694272ms) +✔ validate: a not-issued store agent dated today passes, and an issued one with a past expiry fails (137.769151ms) +✔ validate: a token file inside the repository is refused (67.194357ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (189.791235ms) +✔ resolve: prints one instance's record (258.920062ms) +✔ resolve: refusals (383.486673ms) +✔ parse: exactly one of file or env, plus the service's date (1.863836ms) +✔ check: a good file has no problems (0.63731ms) +✔ check never opens the file: a write-only token passes (0.259065ms) +✔ check: file problems (0.654057ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.666643ms) +✔ check: dates and environment references (0.302686ms) +✔ each definition loads, with a contract file, and is exactly the approved list (2.422404ms) +✔ an organization file that declares them passes, and each resolves to least privilege (4.82324ms) +✔ the chief of staff launches each one; no other role does (2.43161ms) +✔ a C-suite role can't be made the launcher: launch.by must hold role.launch (1.392659ms) +✔ limits can narrow a C-suite role but never widen it (1.344546ms) +✔ any unknown key, tool, action or version in a role file is refused (0.395675ms) +✔ row 298: checkGrantScope takes the D2 shape and refuses anything else (1.345091ms) +✔ row 298: a missing scope is empty and a bad one falls back to empty, which covers nothing (0.239287ms) +✔ row 298: a Gitea grant is in scope only when its agent and every project are listed (0.120581ms) +✔ row 298 (sage-3594 Q2): host commands match whole argv tokens, and enrollment is never in scope (0.172913ms) +✔ row 298: a bad grantScope in the file still loads, and the read shows the fallback (9.548945ms) +✔ row 298: the writer checks the scope, logs the change and removes the key for {} (29.2548ms) +✔ row 298 (sage-3594 Q1): the general writers can't write grantScope (6.509756ms) +✔ REQ-WAIT-1: GATED_ONLY is exactly the hard waits; the new actions are in the vocabulary (0.953961ms) +✔ REQ-ROLE-2: END_PROTECTED includes the assistant's thread definition (0.140081ms) +✔ REQ-COS-3: role.hire and role.fire are routed, not hard waits (0.086622ms) +✔ REQ-WAIT-3: library.write is routed under REQ-LIB-6 (1.16), not a hard wait (0.079125ms) +✔ REQ-WAIT-3: every action is routed or a hard wait, never both, and the routed list names only actions (0.142718ms) +✔ REQ-WAIT-3: instance.name is routed under REQ-MSG-2 (1.5), not a hard wait (row 141, sage-0589) (0.070496ms) +✔ REQ-WAIT-2: a role listing a hard wait at either class is refused; the actions that left can be granted (2.750593ms) +✔ S1: the business file refuses hosts at the top level and in a role, and host. references (12.095424ms) +✔ T1: address, user and host key checks, which the enrollment raise uses (4.457254ms) +✔ T26: no wildcard and no shell, runner or interpreter as a prefix (1.428718ms) +✔ D6: argv shape and the literal prefix match (0.265339ms) +✔ row 285 P: creative equal-service changes preserve every existing credential reference (23.443808ms) +✔ row 285 P: creative declared-service mismatch refuses even when host has no reference (4.777219ms) +✔ row 285 P: an equal-service non-creative target keeps the base credential refusal (4.051484ms) +✔ row 285 P: business validation precedes the creative credential fence (4.080403ms) +✔ row 285 P: listing derives creative from authority and accepts only existing gitea minting (2.940761ms) +✔ C2-04: instance settings skip a library while checking a later code project (24.897209ms) +✔ row 165 read lists each instance and the definitions add accepts (2.526881ms) +✔ row 165 add: two instances join, validate exits 0, the log names the human, and the writer asks for no restart (row 194, D3) (82.478108ms) +✔ row 165 change: definition, vars and launch membership log before and after; none asks for a restart, the bus reloads (row 194, D3) (100.666633ms) +✔ row 165 refuses each validation failure and leaves the file's bytes alone (35.076218ms) +✔ T19 add of a credentialed definition writes store references dated today, names its bot, and validate exits 0 (85.693778ms) +✔ T19 add of a definition that declares host writes references for its token services only (12.728252ms) +✔ row 165 refuses a definition change to or from one that declares credentials (5.117931ms) +✔ row 165 a vars or launch change on an instance with its binding succeeds and keeps the binding (75.996979ms) +✔ row 165 refuses an END_PROTECTED definition and every change to a protected instance (8.113739ms) +✔ row 165 refuses while a project file doesn't resolve, as validate would (16.000833ms) +✔ row 165 refuses a stale revision and leaves the file's bytes alone (11.161353ms) +✔ row 65 an instance write refuses harness-off, thinking-unsupported, an unknown model and a partial fallback (30.579803ms) +✔ row 213 a creative instance is the human's: add, vars and definition changes, persona digest only (85.63908ms) +✔ row 256 T1-T3, T5 the human deletes a creative instance, no other actor does, and the assistant stays (99.46135ms) +✔ row 256 T4 the human's delete of the only listed instance, a creative one, is the last-listed refusal (3.143347ms) +✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (11.58859ms) +✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (0.653762ms) +✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (17.075687ms) +✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (13.840934ms) +✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (3.72795ms) +✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (19.857864ms) +✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.748236ms) +✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (9.132518ms) +✔ Q169 assistants: vars and definition change and log the actor; tracker null leaves no tracker (16.727332ms) +✔ Q169 assistants: refuses other instances, other definitions, holder and credentials, empty entries, a tracker the definition has no credential for and a stale revision (18.030778ms) +✔ Q169 assistants: a definition with a vikunja credential takes a tracker change and refuses its removal or a used bot (11.719638ms) +✔ row 65 routes lists every instance with harness, model, thinking and fallback, the switched-on harnesses, the model keys and each harness's levels (2.212163ms) +✔ row 65 setRoutes saves a harness, model, thinking and whole fallback, reads each back from the file, logs the actor, and a null fallback clears all three (14.340575ms) +✔ row 65 setRoutes refuses a partial fallback, a switched-off harness, a level the harness doesn't apply, an unloaded module, an unknown model or instance, a malformed route, no change and a stale revision, with the file unchanged (19.4324ms) +✔ row 65 a file with harness vars and no plugins.harness loads and its views read; a model change still saves, and a route change refuses harness-off (9.559348ms) +✔ B7 kept instance: the models keys it names and the instances key are the human's (25.258332ms) +✔ B7 kept instance: its route is the human's (8.773053ms) +✔ M10 creative instance: models keys it names, the instances key and its route are the human's (31.644759ms) +✔ C2-04 M9: changed playground and hybrid entries leave business and changes.jsonl byte-identical (20.984808ms) +✔ C2-04 M9: code projects, additions, deletions and array changes are protected too (15.600225ms) +✔ C2-04 M9: canonical key order is allowed and a non-project setter still records its change (12.997939ms) +✔ C2-04 M9: a serialization hook cannot change projects after the canonical comparison (9.845749ms) +✔ M7/M8: library seats come from library businesses, creative seats from their creative definitions (6.566752ms) +✔ M8: a business with no library project puts no seat in either set (1.612265ms) +✔ M7/M8: a business file that is not valid JSON throws; no empty set (1.658908ms) +✔ M7/M8: an absent businesses directory gives empty sets (0.32243ms) +✔ M7/M8: a businesses path that is not a directory throws; no empty set (0.725625ms) +✔ M7/M8: a businesses symlink to a missing target throws; no empty set (0.382107ms) +✔ M7/M8: a config directory under a regular file throws (lstat fails with ENOTDIR); no empty set (0.297168ms) +✔ M7/M8: a business file that can't be read throws; no empty set (0.755072ms) +✔ T6 delete refuses in the design's order and leaves the file's bytes alone (28.611933ms) +✔ T6 delete refuses the last listed instance (3.226085ms) +✔ T6 a clean delete removes the entry and its own references, adds retired, and the name can't come back (83.48393ms) +✔ T7 veto: keep, unkeep, nohire and hire each write once, a repeat is no change, and the log names the human (112.086534ms) +✔ T7 veto refuses an unknown or END_PROTECTED keep, a bad request, a stale revision and a business without launch (17.584597ms) +✔ D2.2 hire adds and lists a new instance, logs the chief's context, and sets only model and limits (78.115614ms) +✔ D2.2 hire refuses noHire, a kept name and a listed name, and re-lists a fired one only as it was (22.285503ms) +✔ D2.2 fire delists and keeps the entry, a repeat is done with no write, and each refusal holds (79.084361ms) +✔ row 256 checkDelete passes the human's creative delete and refuses another actor's; fire still refuses (5.186556ms) +✔ row 239 T1 a fire on a revision that goes stale before the writer's lock refuses there and revokes nothing (4.342942ms) +✔ row 239 T2 a write after the revoke refused before publication carries revoked, and the file keeps the edit (7.275516ms) +✔ row 239 T3 any error after the revoke carries revoked, for a fire and a delete; a revoke that throws carries nothing (24.024395ms) +✔ Q247: omitting tracker loads as null and preserves independent role bots (4.88928ms) +✔ Q247: real business validate passes without a sync credential reference or file (67.498247ms) +✔ Q247: present tracker retains its values and validation without mutating the input (2.36343ms) +✔ Q247: present null, undefined and malformed trackers still refuse (3.735298ms) +✔ Q247: absent business tracker does not waive a role's Vikunja bot requirement (0.785091ms) +✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (10.412775ms) +✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (21.608828ms) +✔ vikunja:19/309 a patch changes only the kinds it names; null removes one kind, explicitly and in the trail (22.123619ms) +✔ 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.61091ms) +✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (3.81691ms) +✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.453096ms) +✔ vikunja:19/305 with an exists check, the setter refuses a module no tree file answers to, before anything is written (6.975244ms) +✔ optional project repo is preserved and must be a normalized absolute path (8.248464ms) +✔ loaded business project repo cannot be nested in any declared code checkout (1.924299ms) +✔ path and load (1.814342ms) +✔ refusals (0.952563ms) +✔ record.write is within-role for the eleven approved v2 agents, never gated-only (2.278902ms) +✔ C2-06 M11 human replacement works; omitted settings return to defaults (27.784744ms) +✔ C2-06 M11 agents and tester cannot change settings, including by changing human (24.473091ms) +✔ C2-06 M11 serialization cannot replace settings after the first fence (8.808556ms) +✔ C2-06 M11 an unrelated actor cannot appoint itself human and change reply limits (7.973319ms) +✔ C2-06 invalid setter values preserve the business and changes.jsonl (8.939774ms) +✔ C2-06 validator accepts hand-edited settings; raw reader fails closed on invalid files (10.916335ms) +✔ C2-04: a library first in projects does not hide a later project layer (6.843821ms) +✔ systemVars flattens the validated config (0.836289ms) +✔ precedence: system, business, project, project role, agent (1.614161ms) +✔ limits narrow the definition and never widen it (2.069759ms) +✔ role.launch stays within-role only for the instance the launch block names (4.437162ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (1.556099ms) +✔ limits.authority narrows cross-role actions too (0.872466ms) +✔ classify (0.882709ms) +✔ the record carries what the broker and launcher need (0.73785ms) +✔ digest: key order doesn't matter, any value change does (5.81602ms) +✔ refusals (1.779158ms) +✔ the four shipped version 2 roles load (2.378087ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (0.811462ms) +✔ shipped authority follows the note's table (4.746427ms) +✔ every role file in roles/ loads through the validator, and a v2 contract exists (1.129712ms) +✔ version 1 files keep loading with no authority (0.820814ms) +✔ the conductor policy isn't a role (0.145369ms) +✔ a missing role file is exit 4, a symbolic link too (0.319514ms) +✔ version 2 refusals (0.929286ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (1.548788ms) +✔ messaging: optional, role definitions and human, never the tester (1.049825ms) +✔ row 64 skills: optional, distinct skill directory names, none on version 1 (1.019457ms) +✔ credentials: Gitea scopes (0.605626ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.6735ms) +✔ credentials: services (0.330683ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.469895ms) +✔ the six creative roles load: read tools, message.send and library.write, gitea, messaging with the human only (0.353918ms) +✔ M1: a creative role file wider than read and answer is refused (2.717807ms) +✔ hireable: HIRE_ALLOWED is exactly the six plain work actions, and every one is routed (0.08884ms) +✔ hireable: a list naming a definition that holds any refused action refuses at load, one case per action (6.677373ms) +✔ hireable: a mixed definition refuses, a clean list loads, and the named file's own hireable isn't read (0.544957ms) +✔ hireable: a missing, version 1, duplicate or badly named entry refuses, and the key is optional (1.724693ms) +✔ the pm and the cto check a saved spec against settled decisions and work in flight, and answer with a BLOCK or an accept (4.171158ms) +✔ the chief of staff relays a BLOCK or an accept to the assistant as a reply to the message that carried it (0.402275ms) +✔ the assistant summarizes a spec review in its own words and doesn't repeat the chief of staff's text (0.368389ms) +✔ REQ-LIMIT-3 schema: each subject kind, limit 0 and no ceiling are accepted; bad limits, times, keys and undeclared instances refuse (12.866323ms) +✔ REQ-LIMIT-3 the writer stamps since: a new limit gets now, a changed one keeps its time, a different time is refused (38.335671ms) +✔ REQ-LIMIT-3 only tokens.* at schema depth holds token limits: agent limits named `limit` save as sent, and a token limit named `limit` is stamped and reset (39.769908ms) +✔ REQ-LIMIT-3 a reset moves since to now, is logged with its actor, and refuses a path that is not a limit (21.162763ms) +✔ REQ-LIMIT-2 schema: launch.account names a credential set; a daily quota needs a reset and an IANA zone and names the account (22.640957ms) +✔ REQ-LIMIT-2 the writer stamps a daily quota's since, keeps its reset and zone, and a reset moves since (22.016802ms) +✔ every key names known layers and a merge rule (0.630788ms) +✔ unknown keys and wrong layers refuse (0.532995ms) +✔ types (1.62428ms) +✔ merge: defaults, then the most specific layer wins (0.217143ms) +✔ merge: limits only narrow, and provenance lists each source (0.286677ms) +✔ merge doesn't change its inputs (0.115663ms) +✔ web.searxng takes https anywhere and http on loopback, so Jason's 127.0.0.1:8888 passes (0.148911ms) +✔ both definitions load, with a contract file, and are exactly the approved lists (1.885538ms) +✔ security-reviewer has no write or shell tool and classifies only the verdict as its own (5.091977ms) +✔ merge-gate's only grant is git.merge.protected cross-role, and it has no write tool (1.67274ms) +✔ neither role can be an arbiter: its messaging lists refuse the business file (row 133) (3.925963ms) +✔ every role contract carries the proactive line in its Authority section (0.562759ms) +✔ pm and chief-of-staff say where a raise goes under REQ-DEC-2 (0.479526ms) +✔ the merge landing takes two runs: merge-gate, cto and chief-of-staff each say their part (0.372532ms) +ℹ tests 283 +ℹ suites 0 +ℹ pass 283 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3476.671216 diff --git a/agents/steelbeak/work/queue-287/evidence/cleanup-audit.txt b/agents/steelbeak/work/queue-287/evidence/cleanup-audit.txt new file mode 100644 index 000000000..684af2b51 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/cleanup-audit.txt @@ -0,0 +1,7 @@ +owned test node/chromium processes found: 0 +owned test processes remaining: 0 +owned g287 Docker containers removed in final sweep: 0 +owned g287 Docker containers remaining: 0 +FED runner: no child groups, ports or sockets remain. +Task/release scripts: owned compose network removal checks pass. +Preexisting Codex/MCP runtime services excluded from the test-process sweep; other containers untouched. diff --git a/agents/steelbeak/work/queue-287/evidence/cli-suite.log b/agents/steelbeak/work/queue-287/evidence/cli-suite.log new file mode 100644 index 000000000..398cf69e0 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/cli-suite.log @@ -0,0 +1,1110 @@ +✔ R6 a configured peer id validates as a user's home and an unconfigured one refuses (8.708181ms) +✔ R6 B1 a boot with access.json binds a granted user, whose home is a configured peer (73.690687ms) +✔ R6 B2 a boot without access.json has no access and binds no user (58.777893ms) +✔ R6 B3 a boot with a file R1's loader refuses still boots, binds no user and says so once (183.634474ms) +✔ list prints the revision and the users (25.814043ms) +✔ grant writes one revision up, a receipt line, and the id in the file (52.471497ms) +✔ a viewer grant writes the viewer level, one revision up (48.487944ms) +✔ a second write appends a second receipt and leaves the first intact (102.108177ms) +✔ a stale revision refuses with revision-conflict and writes nothing (78.237724ms) +✔ a live holder's lock refuses and leaves the file and the lock alone (27.955005ms) +✔ an empty or unreadable lock refuses, since its holder may still be writing (53.438153ms) +✔ a lock left by a dead pid refuses, names the pid, and is not reclaimed (46.361078ms) +✔ a failed rename leaves the old bytes, removes the temp file, and writes no receipt (26.436931ms) +✔ a group-writable access file refuses with exit 3, names the file, and changes nothing (24.323639ms) +✔ an agent or tester caller is refused before the context or the access file is read (1.091758ms) +✔ the lock is mode 0600 while held and removed after a refused write (26.693428ms) +✔ a grant naming an unknown instance is refused before any write (26.742275ms) +✔ a project name resolves to its id at grant time (54.299463ms) +✔ an unknown project name refuses before any write (32.459405ms) +✔ revoke removes the user's grants and leaves a revision behind (105.892244ms) +✔ revoke of a user with no grant refuses and writes nothing (26.423754ms) +✔ an agent caller is refused before the bus is asked (0.786195ms) +✔ --tester is refused on every verb before the bus is asked (0.894402ms) +✔ a bus that refuses the human proof blocks the write (26.528865ms) +✔ the context reads console.sso subjects and the business files, and peers stay empty (7.610513ms) +✔ the real human-cli, run from agent ancestry, refuses and writes nothing (5156.310734ms) +✔ T1 user add writes the user one revision up, prints it and appends a user.add receipt (27.584254ms) +✔ T2 a bad user add refuses before any write (315.113003ms) +✔ T3 user remove of a user with no grant drops them one revision up, with a user.remove receipt (32.193798ms) +✔ T4 user remove refuses with user-has-grants while the user holds a grant, and writes nothing (77.801213ms) +✔ T5 user remove of an id not in the file refuses and writes nothing (27.450233ms) +✔ T6 a stale revision refuses both user verbs with revision-conflict (52.237178ms) +✔ T7 --tester and an agent caller are refused on both user verbs before the bus is asked (1.240839ms) +✔ T8 a user verb of the wrong shape prints the help (0.909971ms) +✔ T9 a bus that refuses the human proof blocks both user verbs (46.986468ms) +✔ R4 grant and revoke call access.reload after the write, and nothing else does (153.347993ms) +✔ R4 a refused access.reload after a good write exits 0, stdout reports the write, stderr names the code and the 30 s timer (56.069422ms) +✔ R4 the help keeps the provider-removal order and says what a refused reload means (0.838331ms) +✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (1206.284557ms) +✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (62.819288ms) +✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (139.325127ms) +✔ NO_COLOR: no escape codes, except reverse video on the selected row (211.541353ms) +✔ 1-9 shows the decide flow's own lines and question; only y resolves (58.319741ms) +✔ the app shows the same decide text as mosaic decide (25.704237ms) +✔ any key but y or Y at the question answers No; nothing is sent (230.155161ms) +✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (82.997358ms) +✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (294.695357ms) +✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (39.094278ms) +✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (25.690234ms) +✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (415.083681ms) +✔ quitting at the question answers No and sends nothing, armed or not (41.342626ms) +✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (35.188796ms) +✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (50.439734ms) +✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (316.439147ms) +✔ at every size the decide box shows each line y depends on, or says it is hidden (8123.516093ms) +✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (73.108924ms) +✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (40.376925ms) +✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (20.327264ms) +✔ keys: views, moving, trail of a selection, the command line, help and quit (96.339995ms) +✔ one option: the status line says Press 1, not 1-1 (24.444081ms) +✔ no business: the app says so and :business picks one (12.886712ms) +✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.774098ms) +✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (199.031229ms) +✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.456901ms) +✔ decide with --note still asks; a No sends nothing (15.152635ms) +✔ the app's business comes from a live bus host only (0.947678ms) +✔ runApp leaves the terminal as it found it on q, end of input, SIGTERM, SIGINT, SIGHUP and an error, the first frame's included (8.367587ms) +✔ draw is pure: same state, same frame (0.779266ms) +✔ recordBootRefused keeps the last good load across refusals; recordBootOk clears the refusal (3.241622ms) +✔ an unknown key and a JSON syntax error each leave the bus down with the reason and path in the status (715.996319ms) +✔ a missing file is recorded too, and a valid file clears the status on the next start (500.929519ms) +✔ row253 socket observation uses configured root, metadata and the Console peer without sending bytes (1865.233654ms) +✔ row253 P10 refuses unsafe metadata, wrong peers and an unsafe Python without claiming closure (29148.343866ms) +✔ row253 acceptance rejects bad config and unsupported copies before talk (1347.342078ms) +✔ row253 stalled peer observation ends within its bound without leaving a reader (3938.626751ms) +✔ row253 P8 refuses both retired TCP and board listeners (3655.458559ms) +✔ row253 C4 acceptance query inner reason holds under both sudo models (10692.868028ms) +✔ row253 C4 acceptance query outer reason discards stdout and names residual cleanup (14176.15576ms) +✔ row253 C4 acceptance query overflow is output-limit in both models (491.287919ms) +✔ row299 P10 timing copy preserves shipped bytes and isolates observer variables (0.800454ms) +✔ row299 P10 observers preserve stdout stderr status and the EXIT trap (3.207798ms) +✔ row299 P10 observer records are complete and ordered (0.491721ms) +✔ row253 C4 P10 inner reason holds under both sudo models (11551.03818ms) +ℹ {"fault":"inner","mode":"exec","baselineSeconds":1.8464614839999922,"faultSeconds":3.9080639939999964,"probeSeconds":2.24} +ℹ {"fault":"inner","mode":"hop","baselineSeconds":1.802099205999999,"faultSeconds":3.931873563000001,"probeSeconds":2.26} +✔ row253 C4 P10 outer reason holds under both sudo models (15177.008954ms) +ℹ {"fault":"outer","mode":"exec","baselineSeconds":1.8096813559999865,"faultSeconds":5.677183815000011,"probeSeconds":4} +ℹ {"fault":"outer","mode":"hop","baselineSeconds":1.8913509179999966,"faultSeconds":5.740817106000002,"probeSeconds":4} +✔ row253 C4 P10 overflow reason holds under both sudo models (3655.635581ms) +✔ row 195 accept: every check passing exits 0, with each P mark and counts that match the lines (1842.438569ms) +✔ row 195 accept: exits 1 with each F check failing alone, and its line names it (20732.874384ms) +✔ row 195 accept: F3 fails on exit 0 with no reply line, and its evidence takes talk's error (3664.923761ms) +✔ row 195 accept: F1 fails on a unit that isn't active or isn't User=mosaic (3457.591173ms) +✔ row 195 accept: F2 fails when healthz doesn't answer and when another pid holds the port (3689.697908ms) +✔ row 195 accept: F5 fails on unpriv_bwrap (enforce), on complain and on a trailing space (8670.713283ms) +✔ row 195 accept: F5 fails with no bwrap of mosaic's, and passes on comm alone (3989.6074ms) +✔ row 195 accept: F6 fails on a stub that prints nothing, on a path outside claude- and on a .. segment (5111.571363ms) +✔ row 195 accept: F7 fails on two live PM launches, one for another business, and no launch.by (5492.018714ms) +✔ row 195 accept: F8 fails on 0640, on another owner and on a symlink, and never prints the credential (5460.149839ms) +✔ row 195 accept: F9 fails on another owner, a group-writable mode and a number for the flag (5430.088646ms) +✔ row 195 accept: F9 won't run --node as root off the pin path or with a writable directory above it (5122.976514ms) +✔ row 195 accept: F11 fails on one *.json under discord/ and passes on an empty directory (3718.404323ms) +✔ row 195 accept: each P check reports unnamed on its fault, and the exit stays 0 (16360.414992ms) +✔ row 195 accept: P2 is unnamed with a supplementary group, and P5 names docker (3693.898356ms) +✔ row 195 accept: P5 is closed with no jarvis account, and P2 with an empty authorized_keys (1844.362194ms) +✔ row 195 accept: P9 is unnamed once for each item missing (9348.039582ms) +✔ row253 accept: P10 fails its observation with another peer; P11 with the unit disabled (3787.139384ms) +✔ row 195 accept: every P check unnamed together still exits 0 (1674.316746ms) +✔ row 195 accept: a bad args file refuses with exit 2 before any check, each for its own reason (98.185938ms) +✔ row 195 accept: usage errors and a run without --root as a user exit 2 (10.484713ms) +✔ row 195 accept: every in-instance call goes through sudo -u mosaic -H env PATH=:... (1965.600839ms) +✔ row 195 accept: the host check runs with --check and never --enforce (1832.577745ms) +✔ row276 legacy call inventory pins every production bound and the conversation contract (0.363629ms) +✔ row276 talk legacy inner timeout cleans both sudo models (1665.477616ms) +✔ row276 precheck legacy inner timeout cleans both sudo models (1654.328241ms) +✔ row276 claude-version legacy inner timeout cleans both sudo models (1658.253144ms) +✔ row276 pm-list legacy inner timeout cleans both sudo models (1665.534555ms) +✔ row276 node-version legacy inner timeout cleans both sudo models (1649.60191ms) +✔ row276 legacy streams and display status preserve failures without granting success (2540.112704ms) +✔ row276 trailer grammar cannot make supervisor failure pass (2110.638137ms) +✔ row276 earlier forged trailer and appended zero remain failures (552.716734ms) +✔ row276 precheck outer bound discards success status and fixture cleans residual (2165.212149ms) +✔ row276 case1 capture and legacy tools refuse unsafe system tools (76.30406ms) +✔ row276 stderr retention is bounded and keeps the final diagnostic (254.175675ms) +✔ row276 version and PM checks refuse successful-looking stdout on supervisor failure (15.908445ms) +✔ row276 full acceptance keeps all five healthy calls in both sudo models (3868.757808ms) +✔ row276 vanished pid stat read returns 3 with empty stderr in both scripts (5.280498ms) +✔ row276 legacy stdout snapshot preserves exact bytes on success and command failure (3223.947753ms) +✔ row 240 check: exits 0 with all seven items and a note that bubblewrap is the install pin (33.69171ms) +✔ row 240 check: exits 1 with each item failing alone, and its line names it (214.716262ms) +✔ row 240 check: item 1 fails when a package owns the profile, when dpkg -S errs, or with no pin (89.101025ms) +✔ row 240 check: item 2 fails on a loaded mosaic-bwrap, a missing unpriv_bwrap or a failed aa-status (87.568067ms) +✔ row 240 check: item 3 fails on a second file naming bwrap as a word, /{usr/,}bin/bwrap included (127.738057ms) +✔ row 240 check: item 4 reads the label under the units' no-new-privs and matches it exactly (122.859164ms) +✔ row 240 check: item 5 needs the statoverride and stat to agree (31.499013ms) +✔ row 240 check: item 6 fails on protected_hardlinks 0; item 7 fails with the floor at 732 and reads 1023 with no unit (179.112203ms) +✔ row 240 check: a bubblewrap version other than the install pin is a note, not a failure (31.115946ms) +✔ row 240 enforce: a pass with no DRIFT changes nothing and exits 0 (30.757167ms) +✔ row 240 enforce: each failure exits 0, unloads the profile, closes bwrap, writes DRIFT and one log line (269.973574ms) +✔ row 240 enforce: with DRIFT present and every item passing, it still closes bwrap (37.435835ms) +✔ row 240 enforce: no unload when aa-status lists neither bwrap profile; never writes the statoverride (312.052165ms) +✔ row 240 upstream: a candidate whose profile differs prints the difference and exits 0 (51.753494ms) +✔ row 240 boot unit: a oneshot after apparmor and before the bus that runs --enforce (0.43046ms) +✔ row 240 pins and usage: the shipped pins are r6's, the script reads the pins beside it, and bad usage exits 2 (44.15352ms) +✔ Vikunja 614 check: item 5 accepts dpkg's bare octal mode and leading-zero equivalents (61.347395ms) +✔ Vikunja 614 enforce: dpkg's bare 750 leaves bwrap open and writes no DRIFT (30.584552ms) +✔ Vikunja 614 check: item 5 refuses other modes, owners, groups, paths and malformed overrides (857.743387ms) +✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (240.856882ms) +✔ a library re-run passes and leaves every file as it was (410.684714ms) +✔ a re-run refuses business-file-differs and leaves the file unchanged (273.074556ms) +✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (272.734739ms) +✔ the factory install writes no library mapping, persona or slot dir (174.058688ms) +✔ the factory install refuses a library project and bus.tester off, before anything is written (71.604928ms) +✔ the library install refuses bus.tester on and a persona var in the input (75.858985ms) +✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (53.730741ms) +✔ the library install needs --personas (5.587162ms) +✔ a validate refusal removes the business file this run wrote (232.008061ms) +✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (397.357356ms) +✔ a clean library host passes the precheck with no mirror yet (407.133401ms) +✔ the library precheck refuses a persona file at 0644 (415.960127ms) +✔ the library precheck refuses a persona whose digest isn't persona.sha256 (395.738502ms) +✔ the library precheck refuses a persona dir with group bits (397.801777ms) +✔ the library precheck refuses a business file at 0644 (398.310457ms) +✔ the library precheck refuses a changes.jsonl at 0644 (561.944743ms) +✔ the library precheck refuses a Discord binding (397.862918ms) +✔ --library-status refuses library-mirror-missing before the bus has made the mirror (325.045338ms) +✔ --library-status refuses library-never-synced on a fresh mirror (346.486065ms) +✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (350.94116ms) +✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (404.730676ms) +✔ --library-status takes no --profile (5.975915ms) +✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (490.860861ms) +✔ row 322 F8: library personas install from the copy with no clone node_modules (155.528931ms) +✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (89.748123ms) +✔ C2-21: library install supplies chief identity and books publish for its real read grant (231.78426ms) +✔ stage takes each profile's SOUL.md byte for byte and prints the pairing with sizes and digests (58.537271ms) +✔ stage lists the per-channel prompts in all three places by name and byte count only (52.781917ms) +✔ a config key with a newline prints as one quoted line (52.114131ms) +✔ a read trace shows stage and the dry run open SOUL.md and config.yaml and never memory, sessions or secrets (106.787325ms) +✔ the pairing refuses anything but two existing profiles naming Bean and Chet once each, and writes nothing (415.407051ms) +✔ a profile with no persona refuses: no SOUL.md, a blank one, a symlink, not UTF-8 (374.721172ms) +✔ the import applies C2-08's exported fence at its boundaries (400.491412ms) +✔ install writes 0600 persona files in 0700 directories whose digests match, and prints the vars (101.816529ms) +✔ install refuses an open or foreign staging file, a digest off the manifest and an existing persona (514.223513ms) +✔ --dry-run writes nothing and prints only counts and the fence fault, two heading lines in a fixture counted (49.235922ms) +✔ --dry-run applies C2-08's fence: over it the fault prints, nothing is written and it exits 1 (92.605674ms) +✔ a directory made between the check and mkdir refuses by name, at stage and at install (158.969602ms) +✔ --dry-run counts three lines naming a Hermes tool or channel and prints none of the words (46.332318ms) +✔ usage: stage needs one of --staging and --dry-run, install takes only its own options (319.67261ms) +✔ row253 factory socket uses actual config and target-uid query without widening hardening (276.184155ms) +✔ row253 installer refuses unsafe location and query facts before unit writes (2896.56112ms) +✔ row253 query timeout kills its process group and leaves no running child (7050.610854ms) +✔ row253 protected homes refuse before any filesystem access (9.199633ms) +✔ row253 C4 sleep query has exact inner reason and cleans all members (10623.079485ms) +✔ row253 C4 signals query has exact inner reason and cleans all members (10605.502183ms) +✔ row253 C4 member query has exact inner reason and cleans all members (10609.70894ms) +✔ row253 C4 overflow is output-limit in both sudo modes (391.385098ms) +✔ row253 C4 stalled supervisor uses the outer reason and explicit residual cleanup (14127.784322ms) +✔ row253 C4 inline supervisor is byte-identical in both host scripts (0.432631ms) +✔ row253 C4 cleanup budget starts after inventory pass one (31.58042ms) +✔ row253 query reaps its group even when a child closes stdout before the query returns (122.104501ms) +✔ row253 config parser is strict (8.308979ms) +✔ a clean library render writes both units 0644 with the fixed lines, and enables both then starts the bus then the Console (93.001507ms) +✔ PATH starts with the --node directory, for a node outside the default list and for /usr/bin/node (101.75392ms) +✔ a failed bus start refuses with the Console not started (91.497675ms) +✔ the script never links a unit file (0.158743ms) +✔ factory renders --pm once and one MOSAIC_CLAUDE_BIN line, and without them both tokens render empty (523.3768ms) +✔ a re-install over running units says they are still on the old copy and must restart together, never that it started them (403.744186ms) +✔ a re-install to another copy re-renders both units, and an edited installed unit refuses (967.367306ms) +✔ a template with a namespace, user or system call restriction refuses (266.334273ms) +✔ the bus template's EnvironmentFile line must be there and point at the session env, and the Console template must have none (176.822963ms) +✔ a template holding an unknown token, PermissionsStartOnly or a privileged Exec prefix refuses (136.231191ms) +✔ a symlink at either unit path refuses (163.026026ms) +✔ a --node that is relative, not root-owned or under refuses (18.922635ms) +✔ a re-install with a different --node or different serve flags refuses (444.391339ms) +✔ an installed pair whose units name different copies refuses (239.000061ms) +✔ a copy id that is short, uppercase, holds / or .., or names no directory refuses (26.668559ms) +✔ library refuses a privileged-floor miss, a bad proof pair and every flag outside its set (265.118649ms) +✔ library refuses a proof file not owned by 's uid (15.956121ms) +✔ factory refuses duplicate socket, a missing --port or --business, and each proxy flag (172.626484ms) +✔ row253 factory refuses retired TCP and a value after the socket marker (26.214831ms) +✔ factory refuses a --claude that is relative, not root-owned or under , and a re-install with another --pm or --claude (1145.276416ms) +✔ the precheck passes on a good host and prints free memory, without the key (148.162996ms) +✔ the precheck refuses each failed host check (1283.122164ms) +✔ the precheck refuses a user missing from passwd, a business file that does not load, and a symlinked run root (721.52235ms) +✔ a bwrap that fails refuses and prints the sysctl and where bwrap is (146.782995ms) +✔ an unprivileged port floor at or below the Console's port refuses, and so does a missing Console unit (733.671443ms) +✔ bus.tester: library refuses it on, factory needs it on, and factory refuses a library business (565.722924ms) +✔ the claude check runs only for a claude-code instance, and refuses a claude that appends or answers wrong (1159.848055ms) +✔ the claude check runs the bus unit's MOSAIC_CLAUDE_BIN over claude on PATH (371.070086ms) +✔ the session env file refuses when missing, open to others, a symlink, empty, or emptied by a later line (903.872781ms) +✔ inside a session's sandbox a bind below 1024 fails with EACCES, and a bind on a free high port succeeds (79.001529ms) +✔ Vikunja 614 factory: scoped @anthropic-ai Claude path renders and passes installed read-back (748.602153ms) +✔ Vikunja 614 factory: plain paths still refuse every named unsafe character (87.186535ms) +✔ Vikunja 614 precheck: explicit null and named notifier decisions pass (317.960688ms) +✔ Vikunja 614 precheck: notifier config refuses missing, public, malformed and extra-key decisions (1215.360455ms) +✔ CLI off, absent and other-only channel selections refuse every project/thread flow before any effect (10.09608ms) +✔ CLI enabled project/thread lists dispatch through selection without changing output (1.131206ms) +✔ both channels off recover through the existing human CLI, socket and organization writer (2076.260444ms) +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (23.357077ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (17.035471ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (14.000447ms) +✔ decide prints a declining choice as declining (13.41423ms) +✔ an unknown outcome is reported once and never resent (12.398389ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (11.800747ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (12.506778ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (12.192618ms) +✔ every human command refuses inside an agent run before it touches the bus (11.677731ms) +✔ usage errors exit 4; no business and no host is a usage error (12.600231ms) +✔ agents and tasks print through the broker (13.1269ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.08292ms) +usage: {"event":"usage-unknown","business":"acme","run":"raa7a9a181b03","instance":"assistant","turn":"cc7f2b54-d46c-48aa-a6e3-c99971dfcb71","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rdd2fcbf492df","instance":"assistant","turn":"c515f5c2-124e-4a6a-96f3-5fa61be2f431","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T3 a run collects compactions at its start and its end, and the count holds across a resume and a host restart (2988.541128ms) +usage: {"event":"usage-unknown","business":"acme","run":"red44c81abc8a","instance":"assistant","turn":"3ae980da-01c2-4a65-96c5-0f1c8c29cd53","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r0ad574d3b2ca","instance":"assistant","turn":"18cc700b-4761-48d8-adf7-480ede2f41fe","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T3 a collection that fails is logged and recorded store-unreadable, and the run ends as one whose collections count (2834.648038ms) +usage: {"event":"usage-unknown","business":"acme","run":"r90620ac66996","instance":"assistant","turn":"c54ef413-962c-4559-88e4-fef75d6d3b7b","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rba06f3c09499","instance":"assistant","turn":"828e8e37-0178-4f18-aa69-0335495c01e5","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T3 the read budget is the system config's compactionReadBytes, and a bad value takes the default (2899.634668ms) +usage: {"event":"usage-unknown","business":"acme","run":"rbb589448e7bb","instance":"assistant","turn":"54030a69-1f9c-40a4-9d96-3b77b59e8ba6","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r8ed00226385d","instance":"assistant","turn":"cd6a7769-ad91-44a7-867c-7d5c20eaf3e8","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T3 the stream cap is the system config's compactionStreams, and a bad value takes the default (2906.768023ms) +usage: {"event":"usage-unknown","business":"acme","run":"r31956e3deb7f","instance":"assistant","turn":"79ced7bf-710c-4427-9930-b3582753939b","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T3 T4 a clear leaves the retired session's count as it was, its successor counts from 0, and a legacy session reads the thread's ledger (2750.792993ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (34.189796ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (58.029739ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (30.202294ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (30.966859ms) +✔ a business without tracker.baseUrl gets no trackers entry (28.887767ms) +✔ an unknown business and a broken system config refuse with exit 3 (57.931843ms) +✔ Row 252 (Webby N1): every console.sso subject reaches the boot as a console owner, and no sso means no consoleOwners key (27.854222ms) +✔ row 199: issue and rotate send the stdin token once to credentials.issue and print no value (5.981576ms) +✔ row 199: rotate goes ahead on each held state, rotation-due included, and refuses not-issued, revoked and an unknown entry (2.306065ms) +✔ row 199: status, revoke and delete; the tester runs status and revoke only (2.440867ms) +✔ row 210: issue of a host. kind prints public material only (1.049253ms) +✔ row 199: scripts/mosaic sends credentials to packages/cli, not the seat CLI (87.64011ms) +﹣ FED R7 scratch two-instance acceptance (0.509349ms) # run scripts/test-federation-acceptance.sh +✔ follow prints an unsolicited message after plain talk returned (32.331991ms) +✔ talk --follow continues after reply and prints a later unsolicited message (31.874108ms) +✔ inbox lists all unread human messages under decisions, does not deliver or read them (293.078728ms) +✔ follow refuses agent context and bad arguments (13.326218ms) +✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (12.776326ms) +✔ plain talk leaves the process SIGINT handlers unchanged (14.908415ms) +✔ empty views say so (0.635032ms) +✔ agents adds a thread line for each held thread claim, after the roles (0.135154ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.195242ms) +✔ tasks print the tracker fields the snapshot carries (0.146025ms) +✔ guardDocker refuses a compose run unless PI_AUTH_FILE is a regular file inside the lane temp root (3.051538ms) +✔ cleanEnv points PI_AUTH_FILE at the lane fixture and drops MOSAIC_LIVE_PROVIDER (0.470571ms) +✔ test-release.sh activates through the mock adapter with a 0600 fixture auth file (1339.205433ms) +ℹ {"script":"test-release.sh","extra":[],"wallMs":1163,"status":1} +✔ test-task.sh mounts only its fixture, uses only the mock adapter and prints the live skip twice (7361.366677ms) +ℹ {"script":"test-task.sh","extra":[],"wallMs":7291,"status":1} +✔ M-T: MOSAIC_LIVE_PROVIDER=1 runs the real-model cases, which fail under a fixture auth file (8003.025493ms) +ℹ {"script":"test-task.sh","extra":["MOSAIC_LIVE_PROVIDER","PI_AUTH_FILE"],"wallMs":7934,"status":1} +✔ parallel lease holders use only the active lane and its shared state (16.35375ms) +✔ unrelated home changes do not become lane writes, and own-root references do (0.251309ms) +✔ proof summaries refuse missing timings, non-overlap and a pair without a lane swap (0.641174ms) +✔ an exhausted single-port band refuses, and an occupied foreign listener survives the probe (6.417033ms) +✔ parallel child allocators share real lane locks; a lease remains held through child shutdown (111.631426ms) +✔ abrupt allocator death releases its holder without touching a foreign lease (47.850479ms) +✔ fixture caches survive a replacement environment with a separate tmp subdirectory (19.613371ms) +✔ an unrelated session can append while a supervised step runs without becoming an attributed write (28.040453ms) +✔ a changed COPY input or actual image ID refuses compatible image sharing (4.193929ms) +✔ warm Docker guard disables implicit build and pull and refuses image mutation (1.208659ms) +✔ exclusive image preparation records actual image ID and build wall time and requires a grant (2.484352ms) +✔ cleanup refuses a foreign Docker label and removes only an exact owned project (0.35371ms) +✔ missing receipt and missing image refuse without permitting a gate (1.67155ms) +✔ a tag changed between supervised steps fails the next boundary and suppresses gate success (23.336128ms) +✔ mutable-base rebuilds with identical files have distinct image IDs; cold and warm counters stay separate (2.965137ms) +✔ warm pinned execution records zero builds and checks the tag again after its final step (39.273624ms) +✔ installed Compose accepts rewritten run argv without starting a container (79.259425ms) +✔ installed Compose resolves the private override with no build and the pinned image (41.186332ms) +ℹ {"installedComposeConfig":{"buildPresent":false,"image":"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}} +✔ guard owns every Compose file argument and refuses caller configuration overrides (0.640143ms) +✔ changing packages/processing-control/src/state.mjs invalidates the image receipt (1.40504ms) +✔ changing packages/processing-control/src/entrypoint.mjs invalidates the image receipt (1.621325ms) +✔ unfinished dead-owner runs quarantine their lane and a recovery record clears only that run (4.545506ms) +✔ a legacy landing gate beside a lane stays legacy even with the same parent (1.485558ms) +✔ the shell status entry reads scratch receipts and reports legacy processes without mutating old slots (58.032625ms) +✔ recovery supersedes an unclean terminal without rewriting either receipt (2.191794ms) +✔ the wrapper refuses a held lane lock and preserves its inode (39.961646ms) +✔ a held kernel lock without a start receipt is BUSY rather than CLEAR (4.187703ms) +✔ SIGKILL leaves a quarantined run and locked recovery publishes a new clean receipt (209.628698ms) +✔ a malformed receipt refuses status and an identity mismatch cannot look live (1.507692ms) +✔ a symlink lock is refused before acquiring or replacing its target (32.878997ms) +✔ failed recovery keeps quarantine and never publishes a clean recovery receipt (177.052376ms) +✔ two shared image holders coexist, but exclusive preparation refuses their lock (110.738964ms) +✔ two simultaneous shell claims have one winner; supervisor retains lock FDs and ordinary children do not (107.665506ms) +✔ same checkout and a legacy land gate each refuse a claim on an otherwise free lane (3.170667ms) +✔ recovery kills a proven surviving child, preserves a foreign child and keeps quarantine if interrupted (425.353304ms) +✔ preflight refuses unsafe lane roots, missing grants and live port 9 before side effects (28.817242ms) +✔ discovery preserves all package and script steps while ignoring non-file shell names (10.016955ms) +✔ a failed step stops the gate and records its nonzero exit without running the next step (13.343223ms) +✔ a cancelled step stops its process group and records failure (113.183333ms) +✔ step receipts retain pass fail skipped counts and hashes of their actual logs (31.87673ms) +✔ preflight rejects a real 31-byte root, missing dependency tree, occupied band and missing inventory (22.236695ms) +✔ preflight refuses canonical and builder checkouts and an existing output (12.443241ms) +✔ both candidates must carry every approved launcher file (10.99204ms) +✔ the 92-byte socket example passes and an over-107-byte pathname refuses (0.164877ms) +✔ package/script duplicates stay and every supervised step clears NODE_TEST_CONTEXT (57.099225ms) +✔ scoped ss loopback and wildcard port 9 listeners refuse (22.375215ms) +✔ step children use legacy umask 022 while the supervisor retains 077 (32.552056ms) +✔ lane wrapper keeps its state directory and lock files private (83.34115ms) +✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (823.546524ms) +✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (767.45155ms) +✔ the size cap refuses a push before any byte (242.401698ms) +✔ a fetch over the cap refuses before any byte reaches the reviewer (758.049726ms) +✔ the socket refuses a request with no capability, an unknown one, and a bad request line (329.247544ms) +✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (668.956833ms) +✔ an identity with no role, or of another business, refuses not-a-role and moves no ref (441.874742ms) +✔ a review of a project the business no longer has refuses unknown-project (440.864943ms) +✔ past maxActive connections the socket refuses git-busy, and a hung-up connection frees its slot (242.744491ms) +✔ close() answers host-closing to a connection still sending its request line (231.410555ms) +✔ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (641.237658ms) +✔ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fault (730.846163ms) +✔ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (1497.808592ms) +✔ send: a close after every byte left is served; one that fails a queued write is fetch-aborted (42.611829ms) +✔ a bundle with no bytes for bodyIdle refuses body-timeout long before its deadline (557.837551ms) +✔ send: a close once the loop is past its last chunk is served, and a failed write there is fetch-aborted (23.829794ms) +✔ letGo waits for an answer to leave for at most its cap, then destroys the socket (301.935691ms) +✔ a record that fails and can't be undone refuses rollback-failed, which the host logs (608.356684ms) +✔ land: the squash runs the gate in its own worktree, then main moves and the broker records it (742.495215ms) +✔ land: an arbiter-resolved landing's squash says the arbiter resolved it through the broker (693.424273ms) +✔ land: refusals before the gate answer the role at once and leave main and no worktree (786.577977ms) +✔ land: a failed gate is refused gate-failed with its step, told to the role, and main stays (700.944103ms) +✔ land: a record the broker refuses moves main back, and the landing is refused with the broker's code (661.814928ms) +✔ land: a second landing while one runs refuses land-busy, and close() stops the gate and refuses host-closing (656.557737ms) +✔ row 298 CLI: scope reads and scope set sends JSON and the observed revision (4.432672ms) +✔ row 298 CLI: every access agent marker refuses scope set before transport (2.387245ms) +✔ row 298 CLI: --tester cannot read or change the scope (0.561327ms) +✔ row 298 CLI: missing revision and empty, invalid or oversized JSON refuse without a call (2.535273ms) +✔ row 236: grants status prints one line per entry, and --json the answer (2.924831ms) +✔ row 236: run, revoke, sweep and admin rotate and remove send their args (0.648707ms) +✔ row 236: admin set sends the stdin token once to grants.admin.set and prints no value (4.152199ms) +✔ row 236: a malformed grants command is a usage error and calls nothing (4.56052ms) +✔ row 236: a bus refusal exits 2 with its code (0.328957ms) +✔ row 236: scripts/mosaic sends grants to packages/cli, not the seat CLI (86.133637ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (937.591194ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (256.710531ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (171.377722ms) +✔ two bus starts racing for one data root leave one host running; the other refuses that a host already runs (588.706677ms) +✔ a host takes over a host.lock its dead host left, and puts back a live claim that replaced it meanwhile (1.770435ms) +✔ a host.lock that goes between the check and the read is tried again; one that can't be parsed still refuses as unreadable (0.712187ms) +✔ a live host's lock stays, however old the file is (0.501594ms) +✔ a host.lock left by a host killed with SIGKILL doesn't stop the next bus start (871.315058ms) +✔ while a host boots, a reader sees no host: the human commands still ask for --business, stop and status find none (218.720389ms) +✔ a host started before host.lock existed, holding only host.json, still refuses a second start, which leaves no lock (27.442197ms) +✔ a broker fork that throws leaves no host.lock and no host.json (29.403284ms) +✔ a broker send that throws leaves no host.lock and no host.json (31.254465ms) +✔ a broker that misses its boot wait leaves no host.lock and no host.json (35.777516ms) +✔ a notifier fork that throws stops the broker and leaves no host.lock and no host.json (169.774942ms) +✔ a notifier send that throws stops the broker and leaves no host.lock and no host.json (171.794869ms) +✔ a boot reply with no reader for the business refuses with exit 3 naming it, before the notifier starts, and leaves no host.lock and no live broker (176.260459ms) +✔ a boot reply with no reader for the business refuses with exit 3 naming it, with no notifier, and leaves no host.lock and no live broker (172.424139ms) +✔ a host.json that can't be written stops both children and leaves no host.lock (285.670337ms) +✔ an error event on the broker before it replies fails the start at once, and leaves no host.lock and no live child (31.415964ms) +✔ an error event on the notifier before it replies fails the start at once, and leaves no host.lock and no live child (170.600509ms) +✔ an error event on the broker while the notifier starts fails the start once the notifier replies (281.157922ms) +✔ an error event on the broker once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (286.296979ms) +✔ an error event on the notifier once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (291.387508ms) +✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1194.958241ms) +✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (345.488737ms) +✔ a broker reply with no request waiting breaks the channel and the host exits 1 (173.64326ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (226.64156ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (218.092999ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (171.847003ms) +✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (223.580381ms) +✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (164.67302ms) +✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (233.536415ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (20.187662ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.296732ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (318.687414ms) +✔ bus start refuses with exit 3 without a notifier config (120.633951ms) +✔ bus start runs until bus stop; status reports it while it runs (833.937952ms) +✔ bus start --no-launch says launches are disabled in its host-up line and stops cleanly; with --pm it is a usage error (693.542015ms) +✔ bus-service.sh renders the unit and installs it into a given directory (249.737584ms) +✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (396.846523ms) +✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (84.661222ms) +✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (58.049983ms) +✔ bus start --pm while halted keeps the control host up without launching the chief (424.29504ms) +✔ row 234: hosts status prints one line per host, and --json the rows (1.977151ms) +✔ row 234: hosts check and remove send the host id, as the human or the tester (0.393366ms) +✔ row 234: a malformed hosts command is a usage error and calls nothing (1.729086ms) +✔ row 234: a bus refusal exits 2 with its code (0.357785ms) +✔ row 234: scripts/mosaic sends hosts to packages/cli, not the seat CLI (85.945851ms) +✔ row 235: hosts revoke sends agent, host and reason, and sweep the host, as the human or the tester (0.440477ms) +✔ row 235 (N10): revoke without --reason, --reason on another verb, and inherited names are usage errors (2.482528ms) +✔ row 235: a status line shows the tags of a revoke in progress (1.009345ms) +usage: {"event":"usage-unknown","business":"acme","run":"r1280e31c6c40","instance":"assistant","turn":"975d3b3b-a019-49e4-9ef4-6e5653c93119","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r605d35c916fa","instance":"assistant","turn":"c56dec4b-ee69-4fef-ac1e-a2d55ec976f9","source":"@none","code":"baseline-unknown"} +✔ row 302 REQ-THREAD-2 an idle-released run whose call lands before the SIGTERM ends stopped/0; a run whose claim the human revoked in the same window still ends session-invalid/22 (1598.326978ms) +organization: {"event":"reload-needs-restart","business":"acme"} +organization: {"event":"reload-needs-restart","business":"acme"} +✔ row 165 CLI: instance add puts an instance in the file, validate exits 0, the log names the human, and the instance binds with no restart (row 194, T1) (166.206478ms) +✔ row 194 CLI: a bus whose boot credential references differ from the file answers restart needed, and doesn't bind the instance (D3 item 3) (80.958287ms) +✔ row 165 CLI: instance set passes the vars file and --no-launch, and a vars-only change needs no restart (134.931497ms) +✔ row 165 CLI: a refusal names its code and why, exits 2 and keeps the bytes (73.330281ms) +✔ row 165 CLI: usage errors exit 4 before any bus call, and an agent session is refused (58.980617ms) +✔ row 165: scripts/mosaic sends business instances, instance and (row 194) veto to packages/cli, and validate still to packages/business (208.992912ms) +✔ row 194 CLI V6: veto keep, unkeep, nohire and hire write through veto.set, the read shows kept and noHire, and a no-op refuses (112.218565ms) +✔ row 194 CLI: the read lists each instance's running run from the bus claims (68.886886ms) +✔ row 194 CLI D1.1 item 5: launch and regenerate poll instance.request and print the run; a done with a refusal exits 3 with its code and the request id (76.143738ms) +✔ row 194 CLI D1.1 item 5: an open request at --wait exits 3 with its id, and a taken one with no done is outcome-unknown (1123.407983ms) +✔ row 194 CLI: a request cancelled by a terminate exits 3 request-cancelled (64.178648ms) +✔ row 194 CLI V3: terminate prints the runs, the cancelled and the taken requests, and nothing held refuses not-held (66.213524ms) +✔ row 194 CLI V5: delete polls to the transaction and the read lists the name retired; workspace-rename-failed names the transaction (111.834792ms) +✔ row 239 T5 CLI: a delete closed -revoked says the tokens may already be revoked; a plain revision-conflict doesn't (85.108245ms) +✔ row 194 CLI: lifecycle and veto usage errors exit 4 before any bus call (61.070751ms) +✔ REQ-LIMIT-2 a claude-code launch asks mayLaunch with launch.account.name (16.913906ms) +✔ REQ-LIMIT-2 each login set the account can name is accepted when the bus host holds exactly it (25.068985ms) +✔ REQ-LIMIT-2 account-credential-missing: the named set is not a login set, or the bus host lacks part of it (25.668376ms) +✔ REQ-LIMIT-2 account-credential-ambiguous: the bus host sets a credential variable outside the named set, ANTHROPIC_BASE_URL included (18.548137ms) +✔ REQ-LIMIT-2 account-is-credential: launch.account.name is the value of a credential variable the bus host sets (11.643731ms) +✔ REQ-LIMIT-2 a pi launch carries account null and skips both account checks (13.03738ms) +✔ REQ-LIMIT-2 with no launch.account a claude-code launch carries null and the environment is not checked for ambiguity (7.031925ms) +✔ REQ-LIMIT-4 the launcher asks mayLaunch with the instance, provider, model and account (row 126), and goes on to authorization on an allow (16.52215ms) +✔ REQ-LIMIT-3 REQ-LIMIT-4 a launch whose subject is at its limit is refused limit-reached and logged with the check, limit and count (7.735358ms) +✔ REQ-LIMIT-4 fail closed: a refused op, a check failure or a malformed answer refuses limit-check-failed, naming the check and the code (60.80665ms) +✔ REQ-LIMIT-4 the launcher reaches token-limit only through the bus: launcher.mjs imports no limit check (0.54916ms) +✔ a launcher before trusted control initialization refuses processing-uninitialized (3.111638ms) +✔ each launch rereads limits and model selections; lowered limits keep sessions and log exact hit (26.683972ms) +✔ row 65 a passing primary is the route: its provider and model reach mayLaunch, and a stated fallback is never used (18.714768ms) +✔ row 65 a failed primary preflight takes the stated fallback, and the limit checks see the fallback route (9.92287ms) +✔ row 65 a failed primary with no fallback refuses with the preflight code and never asks mayLaunch (8.335153ms) +✔ row 65 a fallback whose own preflight fails refuses with its code and names both reasons (8.22775ms) +✔ row 65 the fallback route spends launch.account when its module counts the account (10.193811ms) +✔ row 65 a missing harness, model or thinking var refuses no-route naming it, and the system execution keys never fill it (18.758258ms) +✔ row 65 a harness that is not switched on refuses harness-off, primary or fallback, and an off primary never falls back (24.834986ms) +✔ row 65 the loader refusing a module refuses harness-refused and names it; a module that throws refuses harness-failed (20.056829ms) +✔ row 65 a thinking level the route harness does not apply refuses thinking-unsupported, on the primary or a fallback taken (27.758767ms) +✔ C2-04: launcher workspace clones code but never the library (1380.404925ms) +✔ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (1318.183823ms) +usage: {"event":"usage-unknown","business":"acme","run":"rb4f6b89ce7cc","instance":"chief","turn":"7efb4ca8-3b03-44ec-849e-2b877400dcd3","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r178f273e0097","instance":"coder","turn":"3156e1b0-867f-4eec-8f81-29c7ea7c8f4d","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rb4f6b89ce7cc","instance":"chief","turn":"160843c5-ca64-462f-b648-1c4ac1ceb30a","source":"@none","code":"baseline-unknown"} +✔ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (653.69319ms) +usage: {"event":"usage-unknown","business":"acme","run":"rfab619877e19","instance":"chief","turn":"e881a7d6-e288-4102-8af1-ae0f3caebc3e","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r716e7bb8bb95","instance":"pm","turn":"ced1726b-50ef-4a5c-ab9b-c11f823cf177","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rfab619877e19","instance":"chief","turn":"23e76601-9b1e-4d71-a8cd-d8b4849248ee","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r716e7bb8bb95","instance":"pm","turn":"07267714-cd9e-4f0c-a119-be55e897f963","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r716e7bb8bb95","instance":"pm","turn":"3424efe7-61e5-4172-a9a3-deb13e769900","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rfab619877e19","instance":"chief","turn":"8c699037-971f-4324-8c8c-ff3f8958a1c7","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r716e7bb8bb95","instance":"pm","turn":"1b6f2c59-70ec-46b3-9f35-1fb92d28021c","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rfab619877e19","instance":"chief","turn":"7aaaec78-def7-4daf-8d23-4b1a95e8a474","source":"@none","code":"baseline-unknown"} +✔ a launched pm's request_staff reaches the chief of staff; no other session has the tool and the launch limits still hold (1242.850148ms) +✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (4047.620401ms) +✔ a role the broker says holds a merge decision gets land_work, its prompt line, its landing and the git socket; without it, none (608.275787ms) +✔ REQ-LAUNCH-1 the tick starts a listed merge decision holder once, with land_work, the Landing line and its landing turn; an unlisted holder starts nothing (409.776687ms) +usage: {"event":"usage-unknown","business":"acme","run":"r24229661437a","instance":"coder","turn":"7e51c087-4571-4750-a2d2-06eee105fc58","source":"@none","code":"baseline-unknown"} +✔ a holder running since before the resolution is stopped once the broker releases it, land-relaunch, and the next tick starts it: landing turn first, then the message that waited (695.522155ms) +✔ a landing run that ended isn't started again for its decision, a spent decision starts nothing, and a new decision starts the holder (634.25079ms) +✔ after a restart, the new host's first tick starts a holder whose decision is still held (867.867249ms) +✔ a refused landing start is logged once per code with its decision, isn't a start, and the next tick with room starts the holder (619.272508ms) +✔ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (269.195824ms) +✔ a runner that stops at once ends its launch with the runner's reason (341.669818ms) +✔ a Claude Code launch with no session credential refuses before it starts (199.021796ms) +✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (326.006383ms) +usage: {"event":"plugin-refused","kind":"harness","name":"down","code":"missing","message":"/home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/plugins/harness/down is missing","kept":[]} +plugins: {"event":"plugin-refused","kind":"harness","name":"down","code":"missing","message":"/home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/plugins/harness/down is missing","kept":[]} +✔ row 65 a failed primary preflight launches the fallback on a fixture harness module, recording the route and the reason (356.299383ms) +✔ a runner killed from the host ends its launch as killed, not failed (378.785398ms) +✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (192.649873ms) +✔ row 64 a role skill that isn't enabled refuses skill-not-enabled, and one that isn't a plain directory skill-refused, before anything starts (190.922901ms) +✔ row 213 a creative instance's persona is read from /personas: off its digest the launch fails with the mismatch as its reason (248.747147ms) +✔ row 64 on Claude Code a role skill that isn't enabled refuses the same way, before anything starts (188.063207ms) +usage: {"event":"usage-unknown","business":"acme","run":"r6e0bd9592cd9","instance":"chief","turn":"a206c793-8f5d-434e-aada-91513ced6d6f","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r6e9e7b606ebc","instance":"coder","turn":"1ec649fd-14ac-4c02-8d8b-6b919104e25e","source":"@none","code":"baseline-unknown"} +✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1527.079496ms) +✔ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (1042.814868ms) +✔ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (4042.085364ms) +✔ bus start --no-launch leaves an earlier host's live session alone: no signal, no end, sessions.json as it was (257.437242ms) +✔ bus start --no-launch refuses every launch: the PM launch and a session's request, each logged, nothing spawned (204.255481ms) +✔ bus start --no-launch starts no thread tick; the same host with launches runs it (1183.549177ms) +✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (177.714887ms) +✔ an over-long launch request is refused at once, not at the 10 s idle timeout (186.229605ms) +✔ a launch client that never closes its side doesn't hold the host's close (195.662799ms) +✔ a runner that ignores SIGTERM is killed when the host closes (1367.612885ms) +✔ kill switch refuses chief and hire before launch work and a clear permits a later request (329.588606ms) +✔ kill switch stops an actual managed namespace and records the halt reason without rewriting its bundle (335.206663ms) +✔ a launch waiting across halt and resume cannot use its stale admission (202.629148ms) +✔ row 194 T2: the human's launch runs an unlisted instance and launch.by through the pm branch, and closes with the run (415.325355ms) +✔ row 194 T2: a request past its 60 s never launches, and a refused check closes the request with its code (78.591413ms) +✔ row 194 T3: a --no-launch host closes each request launches-disabled and starts nothing (75.445287ms) +✔ row 194 T3: a limit refusal closes the request with its code and the limit in the launch log (212.304673ms) +✔ row 194 T4: terminate revokes and cancels at the verb, then the pass signals the run, which ends once (273.95455ms) +✔ row 194 T5: regenerate ends the run in the registry and starts a new run on the same workspace (482.390203ms) +✔ row 194 T5: a run that outlasts the request's mark closes the regenerate regenerate-timeout and starts nothing (2758.028859ms) +✔ row 194 V5: delete retires the name, renames the workspace to .retired/I- at 0700, and a retry renames after workspace-rename-failed (101.123568ms) +✔ row 194 V5: a delete with no workspace commits and closes with the transaction (90.247981ms) +✔ row 281 T10: the delete pass closes a granted instance's first request grants-revoking and the host channel stays up (98.62482ms) +✔ row 194 D1.1 item 5: the CLI's launch, regenerate, terminate and delete end to end through the pass, and a closed refusal exits 3 with the request id (581.077497ms) +✔ row 194 T20: a store-only delete with a tracker bot closes naming the bot, and the CLI says to remove it in Vikunja (98.742584ms) +✔ row 194 T2a: a launch.revoke between the request and the pass closes it launch-revoked with no process; a done that fails once leaves the request taken and the next pass starts nothing (231.533658ms) +✔ row 194 T4: terminate signals a spawned run in the registry that the bus never bound, and not a run whose start time differs (384.063232ms) +✔ row 194 T9a: fire signals its bound run and a spawned but unbound registry run; a SIGTERM-ignoring runner gets SIGKILL and lifecycle.end.killed, and one whose kill fails gets lifecycle.end.alive (1068.967733ms) +✔ row 194 T9a: fire of a registry run with no runtime record reads lifecycle.end.alive with reason unknown, never killed (525.05459ms) +✔ row 194 T9a: a runner whose identity changed before the SIGTERM gets no signal, and one whose identity changed between the SIGTERM and the SIGKILL gets no SIGKILL (208.192395ms) +✔ row 194 T9a: V3 terminate of a SIGTERM-ignoring runner sends no SIGKILL (987.989057ms) +✔ row 194 T9b: a halt during a fire's stop sends no second SIGTERM, keeps the fire's deadline, writes one stop receipt with reason fire and the request, and counts the run; a fire while halted refuses (1689.146692ms) +✔ row 194 T15: while halted the pass closes a launch and a regenerate processing-halted and runs no thread pass, terminate signals, and after a resume a launch starts (281.651305ms) +✔ row 194 N6: a run spawned after a terminate's verb survives its pass, and the next terminate ends it (513.766372ms) +✔ row 194 T6: a delete leaves the run evidence bytes unchanged, and the retired name refuses a create and a launch (288.514717ms) +✔ row 194 T11: two stack roots with the same business id; a launch, a terminate and a delete in one leave the other byte-identical (355.592448ms) +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"dc6f47a5-535b-49f5-a568-a40f53722f75","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"d4df604a-967b-4ac1-b365-6efe324f8a80","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"43aa4d9b-3354-4db6-94ce-21a612af999a","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"3ac81fa9-315a-48fc-bab8-9c20758c5473","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"4814c034-ca1b-4d30-9cc2-fe8f3d759122","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"b2cd399b-6617-4987-9629-66f68916d506","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r504654c3daae","instance":"chief","turn":"15f09e60-bf26-4a46-9b97-4b58a36c66d2","source":"@none","code":"baseline-unknown"} +✔ row 194 D3 item 5: the chief's session hires with its tool and launches its hire in the same session, then fires it (1253.868112ms) +✔ REQ-LIMIT-3 REQ-LIMIT-4 formatTokens shows each limit counted, not enforced, or check failed, in reset path words (0.7076ms) +✔ REQ-LIMIT-2 row 126: any daily quota row adds one line after the list saying the count is this stack's own use (0.207418ms) +✔ REQ-LIMIT-3 the view prints the limits then the token lines; --json keeps tokens in the result (2.208325ms) +✔ REQ-LIMIT-3 reset reads the revision, then sends limits.set with one path per limit, a word per level (0.610244ms) +✔ REQ-LIMIT-3 reset with no path, or with --file, is refused before the bus (1.129802ms) +✔ migration needs no bus/config, prints whole converted document, is idempotent and never writes (12.607251ms) +✔ limits view/set use the human bus transport and pass only the explicit patch (1.545642ms) +✔ limits set passes an assistants patch through unchanged, and the usage names the key (0.843288ms) +✔ post-119 example and Jason fixture migrate with checked inherited models; wrong or absent config refuses (161.555803ms) +✔ plugins view/set use the human bus transport and pass only the explicit patch (vikunja:19/305) (1.730104ms) +✔ inbox --read marks one message by the id inbox prints, delivered or not, and leaves the rest unread (32.065173ms) +✔ inbox --read refuses an id that is not the human's, prints its code, still marks the rest and exits 2 (21.070486ms) +✔ inbox --read refuses an ambiguous or short prefix, names every match, reads nothing for them and marks the rest (0.770654ms) +✔ inbox --read-all marks every message the inbox walk lists, delivered or not, and inbox then shows 0 unread (672.878087ms) +✔ inbox --read stops on an error that is not about one message (0.606938ms) +✔ inbox --read and --read-all refuse bad combinations; other verbs refuse them (13.190046ms) +✔ the human names an instance, lists it, and talk's message.send reaches it by that name (23.616067ms) +✔ name and names refuse the wrong number of arguments with the usage (13.462268ms) +✔ zoned uses the IANA zone across DST (11.849424ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (23.612957ms) +✔ two blocking decisions get two DMs with different nonces (15.417007ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.167421ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (15.428413ms) +✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (17.971374ms) +✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (21.13613ms) +✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (15.379205ms) +✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (126.678706ms) +✔ a restart after the second refusal does not send before that refusal's 30 min are up (72.365321ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (18.989193ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (12.956482ms) +✔ an inbox read failure is logged and the next poll retries (0.598481ms) +✔ no Discord id reaches the journal or the log (13.487764ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (1.681739ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (1.986715ms) +✔ the journal: a whole file that is one torn line truncates to empty (1.093258ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.407473ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.479003ms) +✔ the journal: a line with a wrong type refuses with exit 3 and names the field (1.752097ms) +✔ the journal: a symlinked directory refuses and says it is a link (0.285262ms) +✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.430568ms) +✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.326819ms) +✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.424758ms) +✔ digest content stays within Discord's 2000 characters (0.237776ms) +✔ runLoop never overlaps ticks and stops after the one in flight (111.258062ms) +✔ processing halt prevents notifier reads and sends and fences the wait before delivery (23.73349ms) +✔ Q247: bootConfig omits tracker setup even with tracker variables left in the business (6.715546ms) +✔ Q247: trackerless business boots and registers no @sync credential (49.135897ms) +✔ Q247: Console business read works with tracker absent and shows no sync credential (1.689458ms) +✔ Q247: a present tracker with a missing token still refuses bus boot (10.880104ms) +✔ a human command refused with human-required prints the other-tree line and exits 3 (2.076112ms) +✔ a --tester command refused with tester-required prints the other-tree line and exits 3 (0.735069ms) +✔ other exit-3 refusals print no other-tree line (1.070266ms) +✔ the line names no path (0.091434ms) +✔ human halt/status/resume uses management transport without loading any channel (23.968003ms) +✔ halt refuses inside an agent before transport (10.829832ms) +✔ resume refuses inside an agent before transport (10.36179ms) +✔ unknown and partially stopped outcomes are never reported as a successful halt (10.843463ms) +✔ threads project resolution explains a business repository and falls back if its file cannot load (47.044149ms) +✔ project create prints the repository-name refusal and exits two without writing (17.080072ms) +✔ project init/check use declared business repo and never contact the bus (110.120849ms) +✔ project commands validate args and refuse undeclared projects and agent init (30.624238ms) +✔ project create and list validate their arguments before any bus call (34.519757ms) +✔ project dispatcher reaches the human CLI (90.774059ms) +✔ T3 end to end: publish under an approved decision sends the work commit to the agent's own fork and records it once (653.617234ms) +✔ T3 end to end: a fork the token can't reach is fork-unavailable, filed once, and nothing is recorded as allowed (403.659909ms) +✔ a bad branch or project refuses before the broker; a fork, user or ref in the request is ignored (467.894581ms) +✔ T3 end to end: two agents, one project, each lands on its own fork only; the upstream receives nothing (839.428812ms) +✔ T3: a push after checkPublish and before the lock refuses ref-moved; the fork receives nothing (520.241203ms) +✔ T3: a push recorded while the bus process pushes refuses decision-mismatch; only the decision's commit reached the fork (465.684952ms) +✔ row 237: two publishes race on one decision; the second refuses publish-busy and exactly one push reaches the fork (471.547027ms) +✔ sage-1440: the publishResult poll is bounded: publish-timeout, filed once, and nothing recorded (525.40535ms) +bus: publish 8f8d9eec-e389-4dea-8244-8b86307308cd: pushed after the git socket refused publish-timeout; recorded +✔ row 242: a retry after publish-timeout refuses publish-busy while the bus process's push runs; the late push is recorded and the decision pushes once (586.014108ms) +✔ C2-06 CLI replies reads revision and sends only the JSON settings to the human verb (1.877681ms) +✔ C2-06 CLI replies requires a file and refuses tester writes before a bus call (0.500004ms) +usage: {"event":"usage-unknown","business":"acme","run":"rad8af455b2e5","instance":"assistant","turn":"bf5a0e53-6dac-4a2a-b582-8b5d5aef735a","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"raf7902f4a26e","instance":"assistant","turn":"c9d8fe68-111b-495a-a3ee-d3f981e028e8","source":"@none","code":"baseline-unknown"} +✔ row 306 REQ-LAUNCH-1 R4 a revoked run whose call lands before the SIGTERM ends stopped/0; a run the human stopped in the same window still ends session-invalid/22 (728.891863ms) +usage: {"event":"usage-unknown","business":"acme","run":"r03cc3c216413","instance":"assistant","turn":"0eb690e0-9322-42a4-82a6-2c4176aad472","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r2cadef02ecd0","instance":"assistant","turn":"a7cf132a-0d11-4962-8fea-8f10a8aec0ed","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T4 D7 D8 N6: a clear of a running session stops its drained run once, retires it on the run's end, and starts the successor fresh in its own namespace (936.529403ms) +usage: {"event":"usage-unknown","business":"acme","run":"r38bac8a2c72e","instance":"assistant","turn":"18f0d2c6-1e46-4bda-b7c1-36dedb9fce4d","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T4 D8: a successor start refused capacity-full is held, reported and logged once, and starts when capacity frees (4658.840014ms) +✔ REQ-THREAD-2 T4 D7 Q2: after a restart, a live survivor of the cleared run holds the clear at drained with no signal, and its exit lets it retire (503.113211ms) +usage: {"event":"usage-unknown","business":"acme","run":"r387fc03d9eaa","instance":"assistant","turn":"37e84d16-efd9-49cf-bc84-8ffafd6f8405","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T4 D7: a drained run that outlives the stop timeout makes the clear uncertain once, with one signal, and it retires on the run's end (1758.985039ms) +usage: {"event":"usage-unknown","business":"acme","run":"rbab729e7115f","instance":"assistant","turn":"c20363dc-c5a0-4792-b895-89d453ed7595","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 T4 T6: a bus unavailable at the exit proof and then at retirement holds the clear with no retirement, successor or launch, and it resumes when the bus is back (3041.530049ms) +✔ session list/show/open select one session without changing the saved default (4.663738ms) +✔ create requires a reusable operation key and returns the broker identity (1.112506ms) +✔ invalid/foreign session selection never falls back to the default or opens a session (2.411417ms) +✔ unsupported clear refuses with a reason and never opens or creates a replacement (0.657159ms) +✔ clear waits by operation reads only, keeps revision, then reports pending at 30s (0.8442ms) +✔ count output distinguishes exact zero, lower bounds and unavailable collection (0.177799ms) +✔ session CLI real broker: create/list/open keeps default and selectors isolated (25.191345ms) +✔ explicit --session on thread reads, opens and talk never drops the selected id (2.167988ms) +✔ repeating clear with a recorded operation queries it and never sends a new revision (0.403453ms) +✔ new CLI surface refuses forged agent identity before reaching transport (0.298819ms) +✔ cancel requires confirmation, uses the clear op and accepts cancelled before or after retirement (0.785155ms) +✔ cancelled operation is terminal: clear polling stops without another write (0.359459ms) +✔ routed message cancellation prints its stored receipt and never waits for a reply (0.478596ms) +✔ 272 N2: reusing another session clear key gives a precise refusal without a write (0.315849ms) +usage: {"event":"usage-unknown","business":"acme","run":"r59df8d7534de","instance":"assistant","turn":"c7ec5103-89ee-4732-be26-6eb9ec8ade50","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r63b5394a7453","instance":"assistant","turn":"bafaa4c0-88a9-41e2-9c57-ea84e9b18acf","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r003519bb247d","instance":"assistant","turn":"e7258880-5ca4-4db8-bfa5-16596329997e","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"re40801281f10","instance":"assistant","turn":"753c8173-085d-4c7e-b79a-b46fbd32508d","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 two sessions of one thread, and another thread's default, run at once, each with its own store and workspace; a restart of one resumes only that one (5978.201713ms) +usage: {"event":"usage-unknown","business":"acme","run":"r19f414e484ea","instance":"assistant","turn":"86a233d2-0adb-4adc-80e7-e5f79e5085aa","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 a legacy session resumes the thread's old store at the old in-sandbox cwd, in a workspace of its own (379.232472ms) +usage: {"event":"usage-unknown","business":"acme","run":"r67ca9716f6bb","instance":"assistant","turn":"f28a722b-7031-4c2f-90aa-67605f55b397","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 a live lock on a session workspace refuses session-workspace-busy before any checkout; a stale one is taken over (1508.28271ms) +usage: {"event":"usage-unknown","business":"acme","run":"ree1df7823835","instance":"assistant","turn":"03155160-73ce-4b04-9509-a720e61c1768","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 a session start refuses legacy-run-live while an earlier host's sessionless run lives, and starts once it has exited (1627.173824ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 a symlinked session workspace root refuses session-workspace-unsafe and launches nothing (188.69083ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 a session workspace that holds the legacy workspace root refuses session-workspace-overlap and launches nothing (201.845872ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 a symlinked session store refuses thread-store-refused and makes no workspace (198.689528ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 a named session's start refuses processing-generation-changed, then processing-halted, after check() and before any spawn (336.039267ms) +✔ a copy carries every file the harness modules read from it (680.511069ms) +✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (533.568651ms) +✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (715.600615ms) +✔ row 190 N6: check --instance passes only when the wrapper exports the instance's MOSAIC_CONFIG (662.972158ms) +✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (807.243585ms) +✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (382.176921ms) +✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (3827.72625ms) +✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (620.099537ms) +✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (406.944375ms) +✔ install never shows a template name on systemd 261; template drop-ins still refuse (363.964176ms) +✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (337.529126ms) +✔ inherited mosaic-.service.d override refuses without changing the unit (402.794986ms) +✔ inherited service.d override refuses without changing the unit (394.558818ms) +✔ inherited drop-in unreadable directory or file refuses before writing (345.566139ms) +✔ row 232: copy --bundle copies a SHA the bundle's refactor reaches, byte-identical to copy SHA, and refuses the rest (2250.760496ms) +✔ row 232: wrapper --create writes the first wrapper only, the same bytes wrapper ID writes, and refuses over any file (810.434767ms) +✔ row 232: check --units external reads the system units, the default reads the user units (601.915366ms) +✔ Vikunja 614: bundle and checkout copy leave an unreadable caller directory before find (1052.793746ms) +▶ T-I1 bootstrap --instance creates each instance's config, root and marker once, and refuses what it must not claim + ✔ alpha and beta get a 0700 config dir, a config naming ~/.mosaic-NAME, a 0700 root and a 0644 marker (62.702884ms) + ✔ under umask 077 the config and marker are 0600: the source honors the operator's umask (29.296712ms) + ✔ a second bootstrap of each changes no byte, mode or mtime (113.994174ms) + ✔ a non-empty root without its own marker refuses before anything is written, worktrees included (115.687924ms) + ✔ an empty existing root is claimed, and an existing config whose root lost its marker refuses, not repairs (59.542183ms) + ✔ a dangling symlink at the config path is never written through, named or default (56.591979ms) + ✔ bad names and stray arguments exit 4 and write nothing (381.183257ms) + ✔ --instance dev writes exactly what no flag writes (53.640419ms) +✔ T-I1 bootstrap --instance creates each instance's config, root and marker once, and refuses what it must not claim (874.370243ms) +▶ T-I2 selection: the four rows of D1 through the instance op and through an entry point + ✔ row 1: no flag and no MOSAIC_CONFIG is the default; a missing default config leaves the data root empty (48.236489ms) + ✔ row 2: MOSAIC_CONFIG alone is that config with the default names (24.672104ms) + ✔ row 3: --instance NAME alone is NAME's config, root and suffix (51.545574ms) + ✔ row 4: both must name one file; a symlink to it agrees, any other file exits 4 (340.85859ms) + ✔ a named instance with no config exits 3 with its own bootstrap hint (25.840276ms) + ✔ an entry point takes --instance in any position, once, and refuses a mismatch before it prints (168.181902ms) + ✔ copy takes no --instance in either script: every instance shares the copies (15.900765ms) +✔ T-I2 selection: the four rows of D1 through the instance op and through an entry point (733.2392ms) +▶ T-I7 claims: the instance op refuses a root another instance claims, and a named root with no marker + ✔ alpha's config pointing at beta's root refuses instance-root-mismatch in every op (122.780839ms) + ✔ MOSAIC_CONFIG naming alpha's config with no flag refuses: the root is alpha's (26.158341ms) + ✔ a default config whose root carries alpha's marker refuses; one naming dev, or none, passes (76.787909ms) + ✔ a named root whose marker is missing, a symlink, unparsable or the wrong shape refuses instance-root-unclaimed (212.606594ms) +✔ T-I7 claims: the instance op refuses a root another instance claims, and a named root with no marker (497.564551ms) +✔ T-I5 two real bus hosts, one per instance, run at once and each answers from its own socket under its own root (1356.98967ms) +✔ T-I10 the guard protects every instance's config dir and data roots, even under a corrupt config (83.355511ms) +✔ row 232: confirm, revert, gate, --to and --expect-from need --units external; the user-only flags are usage under it, and so are row 273's --send and --no-sso (896.605115ms) +✔ row 232: pm-on and activate --pm-after-harness refuse external-pm (924.052884ms) +✔ row 232: units-running (N4) refuses while a unit is active and leaves the store, the log and TAKEN; failed counts as down (1851.562675ms) +✔ row 232: activate stages: the pointer stays, AWAITING-UNITS names TO, a backup of the stopped store, no directory in the set, no --user, no journal (1558.20053ms) +✔ row 232: A3 order: capture-failed and marker-failed refuse with exit 3 and leave no marker or FAILED (1642.339138ms) +✔ row 232: confirm and revert refuse open-activation with no marker, two, the wrong copy, or one the log holds closed; refusals name no activation (3367.392109ms) +✔ row 232: confirm polls: bus status good after 3 s fails with --timeout 1, writes FAILED and leaves the rest; FAILED then refuses activation-failed (2055.598212ms) +✔ row 232: confirm polls to a pass with --timeout 10: the pointer, an ok line that reads as the started run, no marker (5468.635824ms) +✔ row 232: stale-start: @1792540799 fails a start floor of 1792540800 with stale-start (2935.793963ms) +✔ row 232: stale-start is a gate code: an enter time that turns to @1792540800 after 2 s passes with --timeout 10 (4250.628822ms) +✔ row 232: gate (N2, A2f): --since 1792540800 passes @1792540800 and fails @1792540799; it writes nothing (2605.137268ms) +✔ row 232: the external PM check reads launches list: one live run of launch.by since the start passes (10691.16757ms) +✔ row 232: revert puts the wrapper back and leaves the store when nothing migrated (1427.536597ms) +✔ row 232: confirm --rollback refuses rollback-mismatch, and logs rollback-failed with exit 2 when OLD fails its gate (3453.927243ms) +✔ row 232: confirm --rollback after a --restore swap logs rollbackCode not-attempted; rollback --restore finds an external backup (3154.760774ms) +✔ row 232: sequence 1: activate, confirm times out, revert, confirm --rollback --cause A5: gate-failed rollback ok (4721.992669ms) +✔ row 232: sequence 2: activate --migrate, the wrapper install fails, revert restores TAKEN, confirm --rollback --cause A3 (2950.81541ms) +✔ row 232: sequence 3: activate, root's A4 fails, revert, confirm --rollback --cause A4: failed code units-start (2764.50291ms) +✔ row 232: sequence 4: a capture failure leaves nothing open, then A2f's gate OLD writes nothing (1851.579549ms) +✔ row 232: sequence 5: a confirm of the wrong copy is refused, and revert and confirm --rollback still close the activation (3245.942365ms) +✔ row 232: sequence 6: a revert that fails leaves the activation open, and the next --plan refuses interrupted-run (1729.249136ms) +✔ row 232: sequence 7: adopt, two confirmed activates, rollback --to the previous copy, confirmed, logs op rollback (7148.576104ms) +✔ row 232: sequence 8: a migration that writes and fails: revert restores the backup byte for byte, confirm --rollback --cause A3 (2790.712744ms) +✔ row 232: user mode: every systemctl call carries --user, and activate, rollback and check make the base's calls (6298.161023ms) +✔ row 232: user mode: a default activate --plan prints the base's bytes (2163.141107ms) +✔ T1 adopt writes the pointer from the units once; a failing check refuses check-failed (1223.375223ms) +✔ T2 activate installs exactly the pointer's pm, claude and Console flags (1868.891695ms) +▶ T3 a hand-edited unit refuses unit-flags-drift, names the difference and changes nothing + ✔ pm (811.558039ms) + ✔ claude (881.802811ms) + ✔ console (811.560619ms) +✔ T3 a hand-edited unit refuses unit-flags-drift, names the difference and changes nothing (2505.69486ms) +▶ T4 each item 3 refusal changes nothing and logs one line with its code + ✔ copy-missing (701.876924ms) + ✔ copy-unreachable (708.244659ms) + ✔ copy-schema-unknown (718.830913ms) + ✔ already-active (724.278564ms) + ✔ instance-busy (another business active) (728.62484ms) + ✔ instance-busy (this business activating) (752.537703ms) + ✔ drop-in-override (764.281067ms) + ✔ no-pointer (399.325731ms) +✔ T4 each item 3 refusal changes nothing and logs one line with its code (5499.359167ms) +✔ T5 an activate with no migration stops the Console then the bus, installs, starts the bus then the Console (1851.855632ms) +✔ T6 a migration without --migrate rolls back with migration-needs-flag (1726.61819ms) +✔ T7 --migrate takes the backup before the migration runs (1905.603872ms) +✔ T8 a failed migration restores the backup and keeps the store it replaced (1916.096027ms) +✔ T9 a store outside the target's chain rolls back with store-newer (1724.296806ms) +▶ T10 each gate check failing alone rolls back and puts every file back + ✔ units-inactive (5200.081875ms) + ✔ bus-status (5588.784926ms) + ✔ healthz (4709.683354ms) + ✔ copy-check (4923.419495ms) + ✔ pm-launch-unconfirmed (5078.800679ms) +✔ T10 each gate check failing alone rolls back and puts every file back (25501.690506ms) +▶ T10b the PM check needs exactly one launched line for a live run of this start + ✔ no launched line (5091.504822ms) + ✔ two lines (5052.951049ms) + ✔ a changed word (5135.880426ms) + ✔ run not in launches list (5305.033059ms) + ✔ run started before the start (6498.218055ms) + ✔ a human launch with no journal line (5013.702191ms) + ✔ run for another business (5198.809355ms) + ✔ run not live (5221.714485ms) + ✔ one well-formed line and a live run pass (1915.02132ms) +✔ T10b the PM check needs exactly one launched line for a live run of this start (44434.370675ms) +✔ T11 a rollback whose gate fails too: rollback-failed, exit 2, both units stopped, no retry (7014.319781ms) +✔ T12 rollback takes the previous copy from the log (3284.316748ms) +✔ T13 a rollback across a migration needs --restore, then restores and goes live (3731.426254ms) +✔ T14 events written after the backup need --events N with the right N (4212.508257ms) +✔ T14b a rollback --restore whose gate fails stops both units and skips item 6 (5633.353942ms) +✔ T14c a rollback without --restore whose gate fails runs item 6 (5905.838533ms) +✔ T15 --plan names the migration and the restore and changes nothing (2451.026322ms) +✔ T26 --plan lists the commits an activate adds, after the migration lines (1404.317015ms) +✔ T27 --plan names each migration step, or says the steps cannot be read (1094.803331ms) +✔ T25 --plan refuses copies-unrelated when neither copy is an ancestor of the other (522.065691ms) +▶ T23 --plan reads the store without changing a file, stopped or held + ✔ stopped store (606.641691ms) + ✔ held store (625.595577ms) +✔ T23 --plan reads the store without changing a file, stopped or held (1232.771999ms) +✔ T24 business-mismatch and capture-failed refuse with exit 3; a short id is a usage error (1375.11563ms) +✔ T16 status prints the pointer, the log, the check, bus status and drift, and writes nothing (849.946449ms) +✔ T19 a started line with no final line: status shows it and every changing verb refuses (1186.6703ms) +✔ T20 a held run lock refuses run-locked without a log line; status and --plan still run (1295.84212ms) +✔ T21 a bus unit with no MOSAIC_CLAUDE_BIN: adopt and activate refuse no-claude-pin (1425.129783ms) +▶ T22 the stopped store read: a -wal or -shm refuses store-unclean and is left alone; a clean read adds no file + ✔ bus.sqlite-wal (1753.751105ms) + ✔ bus.sqlite-shm (1754.327081ms) + ✔ clean (1912.188236ms) +✔ T22 the stopped store read: a -wal or -shm refuses store-unclean and is left alone; a clean read adds no file (5420.936241ms) +▶ T28 a unit path, drop-in directory or drop-in file that cannot be read, or a relative unit path, refuses drop-in-override before any stop + ✔ unit path (788.92044ms) + ✔ drop-in directory (811.414297ms) + ✔ drop-in file (797.678707ms) + ✔ relative unit path (903.178544ms) +✔ T28 a unit path, drop-in directory or drop-in file that cannot be read, or a relative unit path, refuses drop-in-override before any stop (3301.981298ms) +▶ T29 a bus that stops during the held-store read: --plan says so, rollback --restore refuses restore-mismatch + ✔ --plan (854.887599ms) + ✔ rollback --restore (2408.544894ms) +✔ T29 a bus that stops during the held-store read: --plan says so, rollback --restore refuses restore-mismatch (3263.91623ms) +✔ T30 a copy check that agrees on another copy fails the gate with copy-check and rolls back (4691.74826ms) +▶ T31 a quoted or escaped Environment= or ExecStart= is unreadable: refusals by name, nothing read from /a + ✔ Environment double quotes (1662.681326ms) + ✔ Environment single quotes (1673.508397ms) + ✔ Environment backslash (1675.149649ms) + ✔ ExecStart quotes (1654.001027ms) + ✔ Console ExecStart quotes (1664.84021ms) +✔ T31 a quoted or escaped Environment= or ExecStart= is unreadable: refusals by name, nothing read from /a (8331.079522ms) +▶ T32 item 1: --pm-after-harness refuses a target without harness or a file that sets it, and passes without the PM + ✔ usage: not with --pm, and only for activate (626.737914ms) + ✔ pm-after-harness-target (890.656491ms) + ✔ pm-after-harness-set (1260.203883ms) + ✔ a pointer without pm (822.163134ms) + ✔ phase 1 installs without --pm, skips pm_launched and writes the pending pointer (1945.410181ms) +✔ T32 item 1: --pm-after-harness refuses a target without harness or a file that sets it, and passes without the PM (5546.046813ms) +▶ T33 item 2: read_pointer takes pmPending only as true beside pm false; a pending pointer refuses activate and shows in status + ✔ read_pointer (1490.110552ms) + ✔ pm-pending, already-adopted and the status line (2744.998743ms) +✔ T33 item 2: read_pointer takes pmPending only as true beside pm false; a pending pointer refuses activate and shows in status (4235.600355ms) +▶ T34 item 3: pm-on refuses before any stop, passes with the PM, or puts the phase-1 bus back + ✔ not-pending (706.432544ms) + ✔ harness-unset (2316.91372ms) + ✔ a launched line passes: the bus alone restarts with --pm (3227.480497ms) + ✔ a failed pm_launched puts the phase-1 unit back: exit 1, still pending (5971.874376ms) + ✔ a failed phase-1 gate too: pm-on-down, exit 2, both units stopped (10222.77064ms) +✔ T34 item 3: pm-on refuses before any stop, passes with the PM, or puts the phase-1 bus back (22446.448213ms) +▶ T35 item 4: rollback from pending installs --pm; a plug-in kind the target lacks refuses by name + ✔ rollback from pending gates pm_launched on the old copy (3251.735778ms) + ✔ after the harness set, rollback refuses until harness is gone (4621.937194ms) + ✔ activate names every kind the target lacks (1165.433939ms) +✔ T35 item 4: rollback from pending installs --pm; a plug-in kind the target lacks refuses by name (9039.729543ms) +✔ T36 --plan shows the pending state and lists the row 169 refusals it can see (3281.667352ms) +✔ T37 tester-port activate writes pointer and unit, replaces the port, and supported re-pin and rollback keep it (6259.024745ms) +✔ T38 tester-port rollback to unsupported target drops flag in plan, steps, unit and pointer (3396.589967ms) +✔ T39 tester-port explicit unsupported target reads target copy and refuses before stopping either unit (718.571466ms) +✔ T40 tester-port invalid values refuse exit 4 before stopping, including tunnel and main ports (756.477153ms) +✔ T41 tester-port inherited activate to unsupported target drops it and retains drift refusal and failed-run recovery (8125.594722ms) +✔ T42 tester-port pm-on preserves pointer Console flags (2950.495624ms) +▶ T43 row 180 item 1: runs in one second get distinct activation ids, rollback sets and post-attempt stores + ✔ activate then pm-on on the same copy (3019.812017ms) + ✔ two restores: an old-form id and a suffixed one, each with its own post-attempt store (6148.990777ms) + ✔ adopt takes the next free id too (593.324669ms) + ✔ all ten names taken: capture-failed before anything stops, and the log line names no activation (923.41709ms) + ✔ --restore takes an id with or without -1 to -9 and nothing else (963.68755ms) +✔ T43 row 180 item 1: runs in one second get distinct activation ids, rollback sets and post-attempt stores (11650.209576ms) +▶ T44 row 180 item 2: --plan lists tester-port-invalid and tester-port-target with its other refusals + ✔ a bad tester port (2076.249675ms) + ✔ without --plan a bad port is usage before any pointer read (350.092997ms) + ✔ a target Console without --tester-port (965.5447ms) +✔ T44 row 180 item 2: --plan lists tester-port-invalid and tester-port-target with its other refusals (3392.541847ms) +▶ T45 row 181 items 2 and 3: the restore guard counts events and every COUNTED table + ✔ both modes count events and each listed table, and a table the store lacks counts 0 (1121.525931ms) + ✔ one instance_names row and no event refuses before any stop; --events 1 restores (4232.351272ms) + ✔ one usage_records row and no event refuses before any stop; --events 1 restores (4220.912393ms) + ✔ a listed table that cannot be read refuses restore-mismatch before any stop (2341.218249ms) + ✔ the in-run recount counts the listed tables too (3253.783813ms) +✔ T45 row 181 items 2 and 3: the restore guard counts events and every COUNTED table (15170.761279ms) +✔ T46 row 181 item 4: every table in the bus schema is in exactly one of four lists (3.849039ms) +✔ T47 row 185 test 1: a same-schema activate leaves a whole backup and the log line names it (1870.524076ms) +✔ T48 row 185 test 2: a gate failure on a same-schema activate rolls back without a restore (5887.667015ms) +▶ T49 row 185 test 3: a same-schema rollback --restore restores, or refuses rows written after STAMP + ✔ no rows after STAMP: restores and goes live (3322.518318ms) + ✔ one instance_names row after STAMP refuses post-backup-events; --events 1 restores (3742.071036ms) +✔ T49 row 185 test 3: a same-schema rollback --restore restores, or refuses rows written after STAMP (7065.114075ms) +✔ T50 row 185 test 4: activate --plan prints the backup line and writes nothing (2416.184248ms) +✔ T51 row 185 test 5: an activate with no store file takes no backup (2005.837116ms) +▶ T52 row 186: the activate plan's backup line says what the run will do + ✔ same schema (2194.145777ms) + ✔ same schema with --migrate (2196.817061ms) + ✔ in chain with --migrate (2428.502495ms) + ✔ in chain without --migrate (2033.088187ms) + ✔ outside the chain (2067.892947ms) + ✔ outside the chain with --migrate (2234.893255ms) +✔ T52 row 186: the activate plan's backup line says what the run will do (13156.564467ms) +✔ T178 proxy proof header and path survive re-pin and rollback unchanged in the pointer and unit (3436.305599ms) +✔ T178 proxy proof pair survives a tester-port replacement as well (2056.970377ms) +▶ T53 row 187: activate sets, carries and drops the proof pair + ✔ tests 1 and 9: the pair goes after the preview flags, before --send and the kept --tester-port, as the unit renders it; a new pair replaces it (5734.035686ms) + ✔ tests 2 and 3: an activate of the live copy writes the pair; a later activate and a rollback carry it (5081.12075ms) + ✔ test 2: only a pair change may name the live copy; the log keeps from and to, and rollback still goes to the earlier copy (6615.392064ms) + ✔ test 2: a live-copy activate says only the Console flags change, and backs up the stopped store before it installs (4041.516975ms) + ✔ test 3: pm-on keeps the pair (3081.392781ms) + ✔ test 4: --no-proof drops the pair from the pointer and the unit and says so; with no pair it says so and passes (5658.277766ms) + ✔ test 5: each refusal exits 3 before anything stops (3810.791622ms) + ✔ tests 5 and 9: --plan lists every proof refusal with the others and writes nothing (886.091937ms) + ✔ test 6: usage errors (802.216249ms) + ✔ test 7: a rollback to a copy without proof support drops the pair and says so (3435.49001ms) + ✔ test 7: an activate to a copy without proof support refuses proof-target without --no-proof and passes with it (3872.856718ms) +✔ T53 row 187: activate sets, carries and drops the proof pair (43021.284803ms) +▶ T54 row 188: a carried pair gets the proof file checks, and a proof file inside the repository refuses + ✔ test 1: an activate that carries the pair refuses after chmod 644, in the plan and the run, before anything stops (2370.347465ms) + ✔ test 1: an activate that carries the pair refuses after a symlink to a mode-600 file, in the plan and the run, before anything stops (2452.089225ms) + ✔ test 1: an activate that carries the pair refuses after removal, in the plan and the run, before anything stops (2385.550707ms) + ✔ test 2: a rollback that carries the pair refuses after chmod 644, in the plan and the run, before anything stops (2419.803919ms) + ✔ test 3: a proof file inside the repository refuses, in the plan and the run, before anything stops (1084.199654ms) + ✔ test 4: a path outside the repository through a directory symlink into it refuses on its realpath (1065.385411ms) +✔ T54 row 188: a carried pair gets the proof file checks, and a proof file inside the repository refuses (11778.59175ms) +▶ T55 row 189: activate --no-send drops --send, and rollback and pm-on carry it off + ✔ test 1: a live-copy --no-send drops --send from the pointer and the unit, keeps the rest in order, logs the step and backs up the stopped store (2188.910782ms) + ✔ test 2: a plain live-copy activate still refuses already-active, and so does --no-send once --send is gone (3201.18874ms) + ✔ test 3: --plan prints one send: line in each case, with the live-copy note on a same-copy run (4721.558919ms) + ✔ test 4: after --no-send, a rollback to the earlier copy leaves --send out of the pointer and the unit (3382.70946ms) + ✔ test 4: pm-on after --no-send keeps --send off (3220.467059ms) + ✔ test 5: --no-send on rollback, pm-on, status, adopt or rehearse is a usage error (664.378295ms) + ✔ test 6: with both a pair and --send in the pointer, --no-send and --no-proof each drop what they name and keep the rest (10421.842413ms) +✔ T55 row 189: activate --no-send drops --send, and rollback and pm-on carry it off (27802.341188ms) +▶ T57 row 200: activate --sso, its refusals before any stop, and the rollback to a Console without --sso + ✔ item 1: --sso drops the preview set and the pair, puts --sso before --send and the tester port, and the plan names what it drops and carries (3920.659872ms) + ✔ item 1: --sso may re-pin the live copy once; later activates carry it; --no-send still drops --send (9133.916663ms) + ✔ item 1: --sso with a pair or --no-proof, or on any verb but activate, is a usage error (740.714867ms) + ✔ item 2: a target without --sso, a missing or bad console.sso, and each slot problem refuse before any stop (5815.60056ms) + ✔ item 3: a config whose console key the target loader refuses stops an activate or a rollback before any stop, naming the first step (4459.830493ms) + ✔ row 203: a config whose federation key the target loader refuses stops an activate or a rollback before any stop, naming the first step (3805.910465ms) + ✔ item 3: a rollback from --sso to a Console without it takes the flags from the set of the activation onto the copy, past a same-copy re-pin (sage-1389) (11479.71026ms) + ✔ item 3: a rollback set that is missing, fails its sums, holds no argv or already ran --sso refuses sso-rollback-flags (4482.959767ms) + ✔ round 2 (webby-0028 N1): run through a symlinked repository path, a slot or a proof file inside the repository still refuses (2315.663344ms) + ✔ round 2 (webby-0028 N2): a config.json that does not parse stops the run before anything is read or stopped (2084.526004ms) + ✔ round 3 (sage-1556): a named instance turns --sso on with its own config, slot and Console unit, and rolls back from its own set (4327.3678ms) + ✔ row 238 (#1744): on a named instance the sso-config first step names --instance NAME in both commands (1064.476645ms) +✔ T57 row 200: activate --sso, its refusals before any stop, and the rollback to a Console without --sso (53633.555899ms) +▶ T58 row 273: activate --send and --no-sso + ✔ T1: --sso --send turns send on after --sso and before the tester port, and the plan says so (2675.104985ms) + ✔ T1: --send beside a carried --sso re-pins the live copy once, and the plan names the carried --sso (4399.406557ms) + ✔ T2: --send without --sso, given or carried, refuses before any stop, and --plan lists it (2002.689338ms) + ✔ T2 (sage-2315 B1): a rollback from an --sso --send pointer to a Console without --sso ends with neither flag (3861.326335ms) + ✔ T2 and T4: --send with --no-send, --no-sso with --sso, --send, a pair or --no-proof, and either flag on another verb, is a usage error (2215.124087ms) + ✔ T3: --no-sso on the same copy returns the unit and the pointer to the flags from before --sso, without --send (5383.0107ms) + ✔ T3: the set walk passes later re-pins that ran with --sso, and the tester follows the pointer (5253.1263ms) + ✔ T3 (sage-2305 a): after --sso, two later re-pins that carry it, then --no-sso restores the flags from before --sso byte for byte (8019.672253ms) + ✔ T4 (sage-2305 b): a log in which every set holds a Console with --sso refuses before any unit stops (3770.702351ms) + ✔ T4: --no-sso refuses before any stop on a pointer without --sso, a tampered or missing set, a set that ran with --sso, an ok line with no activation, and a log with no set (5678.144025ms) +✔ T58 row 273: activate --send and --no-sso (43260.177035ms) +✔ R1 rehearse passes on the same schema; the scratch root holds its files and the live files stay byte-identical (2434.307557ms) +✔ R2 rehearse runs the migration checks when the schema differs; the live store stays at 3g (3553.003378ms) +✔ R3 a failing migration gives REHEARSAL_FAIL migrate and starts nothing (370.752708ms) +✔ R4 a host that does not start, does not answer status, reports another pid, launches anyway, makes a launches directory or keeps its lock each fails by name (25510.620694ms) +✔ R5 a host that ignores SIGTERM gets SIGKILL and FAIL host-stopped (4445.236727ms) +✔ R6 a Console that does not answer /healthz gives REHEARSAL_FAIL healthz (3530.801053ms) +✔ R7 a host on another copy fails copy-match (5644.146772ms) +✔ R8 a scratch port in use or reserved refuses, and the rehearsal takes no other port (513.409036ms) +✔ R9 mos-0963: a role reference left outside placeholders/, or left on an env variable, fails credential-ref-live before the host starts (738.100338ms) +✔ R10 mos-0962: a scratch business that still sets a tracker fails tracker-configured (353.076219ms) +✔ R11 mos-0961: a scratch config on the live data root or on any root but the scratch one, or a sessions.json naming a running pid, refuses by name (1432.469121ms) +✔ R12 mos-0962: a notifier config with a binding fails notify-off (352.627439ms) +✔ R13 the pre-checks: an old copy, a backup that fails its sums, a missing backup or pointer, and usage (488.322887ms) +✔ R14 a host that writes the live config, the live business file or the backup fails the end check by name (10319.676712ms) +✔ R15 a rehearsal stopped by TERM, INT or HUP fails signal and still stops the host and Console and runs the end checks (9999.254442ms) +✔ row 233: --backup last takes the newest backup the log records; a later line with backup null does not hide it, and the store line names it (2452.292573ms) +✔ row 233: --backup last with no backup in the log, or no log, exits 3 with no-backup and makes no rehearsal directory (201.075279ms) +✔ row 233: --backup last refuses a newest backup that is not an activation id, with exit 1 (119.92266ms) +▶ T56 row 190: named instances side by side on one unit directory, $BIN and set of stand-ins + ✔ T-I3 each instance renders its own config, session env and Description, and nothing of another (1534.453269ms) + ✔ T-I4 a Console port another installed Console takes refuses port-claimed, and a named instance keeps off the default ports (2012.693636ms) + ✔ T-I6 alpha's activate and rollback reach nothing of beta's (4068.498285ms) + ✔ T-I6 swapped: beta's reach nothing of alpha's (4065.562783ms) + ✔ T-I7 MOSAIC_CONFIG naming alpha's config with no flag refuses instance-root-mismatch and changes nothing (687.673555ms) + ✔ T-I9 another instance's bus active refuses nothing; one of this instance's does (6019.50343ms) +✔ T56 row 190: named instances side by side on one unit directory, $BIN and set of stand-ins (18389.716915ms) +✔ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (6103.352377ms) +✔ row196 SSO socket conversion pins pointer and unit argv order without send (2303.327026ms) +▶ row196 interpreter activation checks receipt, identity, plan and startup log + ✔ plan and started log preserve the same interpreter JSON (2257.063649ms) + ✔ interpreter-receipt-refuse fails at the gate and restores the pointer (3466.644996ms) + ✔ interpreter-drift fails at the gate and restores the pointer (3383.653673ms) + ✔ missing-verifier refuses before any start or stop (1060.181161ms) + ✔ interpreter-unsafe refuses before any start or stop (1063.621128ms) +✔ row196 interpreter activation checks receipt, identity, plan and startup log (11232.520867ms) +▶ row196 rehearsal selects socket only for marked copies and uses scratch dataRoot + ✔ socket copy (3432.798578ms) + ✔ legacy copy (3435.642968ms) +✔ row196 rehearsal selects socket only for marked copies and uses scratch dataRoot (6869.079186ms) +✔ config-any-key: a non-console target refusal stops activate and rollback before any stop (2853.001844ms) +✔ config-any-key: a pre-270 target refuses each row 270 key on a full activate and rollback, naming the key (4547.170039ms) +✔ config-any-key: accepting loaders run validate with the selected config on activate and rollback (3141.470618ms) +✔ config-any-key: missing-loader copies accept only the four oldest keys (126.998602ms) +✔ config-any-key: missing config passes but malformed JSON refuses without calling the loader (46.157654ms) +✔ row 233: sequence 1: confirm fails healthz, the driver reverts, renders the old copy and confirm --rollback --cause A5 passes: exit 1 (6381.994653ms) +✔ row 233: sequence 2: activate --migrate fails at the wrapper, revert restores the store, confirm --rollback --cause A3: exit 1 (3397.721959ms) +✔ row 233: sequence 3: root's render of the new copy fails, the driver rolls back with --cause A4, and holds the lock to the end: exit 1 (3183.127607ms) +✔ row 233: sequence 4: a capture failure is exit 3 from the stage, and A2f restarts the old units and gates them: exit 1 (2385.565595ms) +✔ row 233: N1: a bus that outlives A2's timeout keeps its old start, so A2f's gate fails stale-start: exit 2, both units stopped (7142.144987ms) +✔ row 233: sequence 5: confirm of the wrong copy is refused, and the driver rolls back with --cause A5: exit 1 (3687.405189ms) +✔ row 233: sequence 6: a revert that fails stops both units at exit 2, and the next run refuses at its --plan with nothing stopped (2931.803084ms) +✔ row 233: sequence 7: two upgrades and a rollback --to the previous copy with --restore, the args file following each confirm (7521.076718ms) +✔ row 240: I3 N1: a failed args move after A5 exits 5 args-failed, the new copy live and confirmed and both args files kept (2887.532732ms) +✔ row 233: sequence 8: a migration that writes and fails: the driver reverts to the backup byte for byte, --cause A3: exit 1 (3276.448791ms) +✔ row 233: a second run while the lock is held refuses upgrade-running with one log line and no call (740.888427ms) +✔ row 233: A1 refuses open-activation on any AWAITING-UNITS file, through the stack's user's --plan, with nothing stopped (1143.099486ms) +✔ row 233: a target that migrates refuses migrate-required without --migrate, with nothing stopped (1126.679444ms) +✔ row 233: the rehearsal runs after the plan and before A2; a failed one refuses rehearsal-failed with nothing stopped (1176.144705ms) +✔ row 233: the rehearsal's BACKUP_STORE line goes to the step log after a match, a malformed one as none, the rest of its output never (3051.142631ms) +✔ row 233: a real rehearsal with no backup in the log refuses no-backup, and goes on only with --no-rehearsal, which logs the skip (3631.136331ms) +✔ row 233: a real rehearsal after an upgrade names the log's newest backup; --no-rehearsal with a backup refuses rehearsal-required (4020.960953ms) +✔ row 233: the args file must be the running user's, with no group or other write, one --copy, and the copy the units run (904.183444ms) +✔ row 233: hostcheck (row 240): a nonzero --check or a missing file refuses profile-drift before the plan (1033.25899ms) +✔ row 233: the checkout must be at --sha (head-mismatch), and a commit the clone can't fast-forward to refuses fetch-failed (1129.965812ms) +✔ row 233: usage exits 4 before any log or lock (706.460112ms) +✔ tester HTTP helper uses instance socket, logical origin and JSON without retrying (13.373501ms) +✔ tester HTTP timeout closes the socket and does not retry (23.520707ms) +organization: {"event":"reload-needs-restart","business":"acme"} +✔ row 175: each read with --tester inside an agent prints the human form's output, and refuses without --tester (85.257923ms) +✔ row 175: --tester on limits set, reset and migrate and every write form outside W1-W7 exits 4 before any call (34.313027ms) +✔ row 175: scripts/mosaic sends plugins, name, names, follow, threads and thread to packages/cli, not the seat CLI (523.048083ms) +✔ row 175 W7: project init --tester makes the repository under the project area, committed by tester (383.444485ms) +✔ row 175 W7: a repo outside the project area refuses tester-project-area before any bus call, and nothing changes (146.758239ms) +✔ row 175 W7: the tester bind refuses tester-off and tester-required before any directory (1008.065537ms) +✔ row 184: with --tester, thread create, threads, open, talk --thread and read run on the tester's own thread (44.946502ms) +organization: {"event":"reload-needs-restart","business":"acme"} +✔ row 184: talk --thread --follow and follow --thread with --tester print and read only that thread's messages, from message.receive (83.392017ms) +✔ row 184: with --tester a human thread refuses with the bus's code, and nothing is written (39.999704ms) +✔ row 184: without --tester the thread verbs stay the human's and never call the tester transport (28.232091ms) +✔ row 184: scripts/mosaic runs thread create, threads, talk --thread and thread read as the tester through tester-cli.mjs (2281.710179ms) +✔ talk --tester sends a message as tester from inside an agent and prints the role's reply (24.763753ms) +✔ talk --tester sends text with no prefix, and follow --tester reads (13.940906ms) +✔ the read verbs take --tester inside an agent, and the trail shows the tester as the sender (16.907268ms) +✔ decide, stop and launches take no --tester (13.01372ms) +✔ the tester transport runs tester-cli.mjs, and the tester's refusals exit 3 (0.305711ms) +✔ with no stand-in transport, --tester runs tester-cli.mjs on the data root's socket (0.473921ms) +✔ agents --tester makes one call; the human's agents also reads thread.claims, and --json is the agents array (13.231543ms) +✔ a refused thread.claims still prints the role rows and the refusal's code (12.859658ms) +✔ a thread.claims error without a bus code still throws and prints nothing (12.006078ms) +✔ talk --tester to a stopped role exits 3, prints no sent line and stores nothing (12.358955ms) +✔ only a --tester send refused tester-target-not-running is rewritten (11.389794ms) +✔ REQ-CLI-1 project create and list work on the bus record and refuse what the broker refuses (28.408271ms) +✔ REQ-CLI-1 thread create, list, open and read (47.657095ms) +✔ REQ-CLI-1 thread read pages through every message, oldest first (284.825295ms) +✔ REQ-CLI-1 the business needs exactly one threaded instance to create a thread (25.539583ms) +✔ REQ-CLI-1 a thread is its id, an id prefix of 8 characters or more, or /; anything else is refused before any write (41.184498ms) +✔ REQ-CLI-2 REQ-THREAD-2 talk --thread sends to the thread and prints only the reply to it; the rest stays unread (30.052254ms) +✔ REQ-CLI-2 talk --thread --wait gives up with exit 1 and leaves the thread's messages unread (1028.902099ms) +✔ REQ-CLI-2 talk --thread --follow and follow --thread print and read only that thread's messages (81.533493ms) +✔ REQ-CLI-2 plain talk to the assistant asks for a thread and exits 2 without a write (21.358017ms) +✔ REQ-USER-2 a thread of another user is refused for talk, open, read and follow, and nothing is written (39.212551ms) +✔ REQ-CLI-1 the new verbs refuse inside an agent run, as every human verb does (11.888346ms) +usage: {"event":"usage-unknown","business":"acme","run":"re71ac086fdb2","instance":"assistant","turn":"0ca3c603-0b40-434d-84db-424ee16ba934","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r868d441e8ef8","instance":"assistant","turn":"58c01145-43db-4d33-b920-35774cb38953","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"re855baecb87c","instance":"assistant","turn":"62d6eeae-5b26-4fe9-b753-39866ada8576","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r472ba742010b","instance":"assistant","turn":"c46aef04-a188-4d78-bc24-a1e755d7382e","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 REQ-MSG-1 a thread message starts the thread's session, which stops when idle; its history survives the stop and a restart and no other thread sees it (5855.709849ms) +usage: {"event":"usage-unknown","business":"acme","run":"r525712ad7e52","instance":"assistant","turn":"9f35f63d-454a-4e15-9d5a-64b403580b8f","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r59c1c9781c19","instance":"assistant","turn":"c5d67849-e7d8-4185-b672-a38a6d5d3bc1","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 a running thread counts against its model's limit and an idle-stopped one doesn't (1694.027782ms) +usage: {"event":"usage-unknown","business":"acme","run":"r06d0800bbcf5","instance":"assistant","turn":"3991bd4b-8402-40a1-a0af-7e6ddcf5153c","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 a turn longer than the idle time isn't stopped, and the thread idles out after it (4412.92545ms) +usage: {"event":"usage-unknown","business":"acme","run":"rc54b0b1bab75","instance":"assistant","turn":"4a2be7d5-474b-4f3e-804f-4727ad17fe53","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rc54b0b1bab75","instance":"assistant","turn":"7dcf5492-a1c1-4261-8556-4435fe9e0354","source":"@none","code":"baseline-unknown"} +✔ REQ-THREAD-2 REQ-LAUNCH-1 a message sent after the idle snapshot keeps the session, which answers it (2846.884253ms) +✔ REQ-ROLE-2 REQ-LAUNCH-1 a thread held by a run the launcher didn't start gets no second session (80.125841ms) +usage: {"event":"usage-unknown","business":"acme","run":"rcad4932a66cb","instance":"assistant","turn":"0e274309-df2e-4332-8411-9831de429e02","source":"@none","code":"baseline-unknown"} +✔ REQ-LAUNCH-1 a thread run's session file has no landing, even when landHolders names its instance (371.734895ms) +✔ REQ-LAUNCH-1 a run that ends without taking its thread's message isn't started again for that message (226.680688ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 a thread id that isn't a safe segment, or a thread of a non-thread instance, launches nothing (226.257836ms) +✔ REQ-THREAD-1 REQ-LAUNCH-1 the chief can't launch the assistant outside a thread (81.515416ms) +✔ C2-09a: fromSession refuses a creative instance outside a thread without a sandbox (75.793407ms) +limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mayLaunch","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":0} +limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mayLaunch","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":0} +✔ REQ-LIMIT-3 REQ-LIMIT-4 a thread start whose instance is at its token limit is refused, logged once with the limit and the count (192.454937ms) +usage: {"event":"usage-unknown","business":"acme","run":"rba5faa10b26d","instance":"assistant","turn":"cc9b2106-da24-4acd-a2e9-e3dda9eb6e97","source":"@none","code":"baseline-unknown"} +limits: {"event":"limit-reached","business":"acme","check":"token-limit","export":"mustStop","subject":{"instance":"assistant","provider":"zai","model":"claude-sonnet-5-5","account":null},"path":["tokens","instances","assistant"],"limit":0,"count":0,"unknownTurns":1} +✔ REQ-LIMIT-4 a thread run at its token limit stops at the end of its turn: the reply lands, and the end record names the limit (352.015672ms) +usage: {"event":"usage-unknown","business":"acme","run":"rf0d9acda4532","instance":"bean","turn":"e44cb5c1-a88e-4dd9-8fc7-8441fbcf7175","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"r0ea0488d7646","instance":"chet","turn":"0b2c98bd-861a-4c49-a0fb-4ff5d8075d77","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rf0d9acda4532","instance":"bean","turn":"12234a5c-278c-4e86-9cd2-8e7ba17b5aa8","source":"@none","code":"baseline-unknown"} +✔ C2-09a: creative thread messages start distinct runs and keep each instance history separate (748.018715ms) +usage: {"event":"usage-unknown","business":"acme","run":"r655b0591752d","instance":"bean","turn":"a1284737-ffa4-4156-8b00-faed5fd68239","source":"@none","code":"baseline-unknown"} +usage: {"event":"usage-unknown","business":"acme","run":"rf1978e4ea89e","instance":"bean","turn":"91e83e93-7584-43fb-955c-e55bf343c2d6","source":"@none","code":"baseline-unknown"} +✔ C2-09a: terminating one creative thread does not signal an unselected peer of the same instance (650.073745ms) +✔ REQ-LAUNCH-1 R4: a pending message on a revoked thread starts no run after a launcher restart (155.078302ms) +usage: {"event":"usage-unknown","business":"acme","run":"r83ead46a8fb2","instance":"assistant","turn":"cbc28309-9600-49b3-8634-a2cbe11d0a36","source":"@none","code":"baseline-unknown"} +✔ REQ-LAUNCH-1 R4: a running thread's run gets SIGTERM at the first tick after the hand edit, with no idle check (343.510659ms) +✔ REQ-LAUNCH-1 R4 D1: recover() rebinds an earlier host's run on a revoked thread and ends it, which ends its claim (135.824166ms) +✔ C2-04: a library-only business with tracker.baseUrl boots without a project tracker (229.001495ms) +task.close {} answered: invalid-request; fake saw 18 requests, first GET /info 200, GET /projects/1 200, GET /projects/1/views 200 +task.close on a missing task answered: task-not-found; it made GET /tasks/999 404 +✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (368.613278ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (30.360053ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2124.457321ms) +✔ busExit and refuseInsideAgent (0.495401ms) +✔ REQ-LIMIT-4 mosaic usage shows each token kind and unknown turns beside every total (54.339312ms) +✔ usage is dispatched by scripts/mosaic and the shared read adapter (86.217281ms) +✔ talk sends a REQUEST, prints and reads only the reply to it, and leaves other messages unread (52.292037ms) +✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1029.878037ms) +✔ a late reply the Console has listed stays unread for mosaic inbox, and the next talk skips it (2049.110572ms) +✔ talk says when the role isn't running, whether launch.by was told, and how to start launch.by (33.095926ms) +✔ talk to a role launch.by can't launch says it isn't in the launch block (15.585126ms) +✔ talk to a stopped role prints the waiting line once, skips a non-reply and prints the reply (43.46065ms) +✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (14.121483ms) +✔ launches off and on go to the broker and change the business's launch state (14.349887ms) +✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (13.590244ms) +ℹ tests 990 +ℹ suites 0 +ℹ pass 989 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 1 +ℹ todo 0 +ℹ duration_ms 851062.776023 diff --git a/agents/steelbeak/work/queue-287/evidence/docker-facts-r2.log b/agents/steelbeak/work/queue-287/evidence/docker-facts-r2.log new file mode 100644 index 000000000..2917a7fc3 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/docker-facts-r2.log @@ -0,0 +1,27 @@ +✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (11763.04751ms) +ℹ ✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (11616.122395ms) +ℹ F-A creative write: PUT status 204 +ℹ F-B creative: permissions {"admin":false,"push":true,"pull":true} +ℹ F-A chief read: PUT status 204 +ℹ F-B chief: permissions {"admin":false,"push":false,"pull":true} +ℹ F-C protected Story/**: seat rc 1, owner rc 1; established false +ℹ F-D protected branch: normal rc 0, force rc 1; established true +ℹ minted 4, hits 0 +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 11727.640162 + +ℹ minted 4, hits 0 (complete TAP stdout/stderr) +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 11858.472832 diff --git a/agents/steelbeak/work/queue-287/evidence/docker-facts.log b/agents/steelbeak/work/queue-287/evidence/docker-facts.log new file mode 100644 index 000000000..1a4425967 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/docker-facts.log @@ -0,0 +1,13 @@ +✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (40.800678ms) +ℹ (node:3897534) Warning: node:test run() is being called recursively within a test file. skipping running files. +(Use `node --trace-warnings ...` to show where the warning was created) + +ℹ minted 0, hits 0 (complete TAP stdout/stderr) +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 148.828093 diff --git a/agents/steelbeak/work/queue-287/evidence/docker-whole.log b/agents/steelbeak/work/queue-287/evidence/docker-whole.log new file mode 100644 index 000000000..58715566b --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/docker-whole.log @@ -0,0 +1,28 @@ +✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (12203.540198ms) +ℹ ✔ C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations (12086.277368ms) +ℹ F-A creative write: PUT status 204 +ℹ F-B creative: permissions {"admin":false,"push":true,"pull":true} +ℹ F-A chief read: PUT status 204 +ℹ F-B chief: permissions {"admin":false,"push":false,"pull":true} +ℹ F-C protected Story/**: seat rc 1, owner rc 1; established false +ℹ F-D protected branch: normal rc 0, force rc 1; established true +ℹ minted 4, hits 0 +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 12174.33915 + +ℹ minted 4, hits 0 (complete TAP stdout/stderr) +✔ X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token (12131.714969ms) +ℹ tests 2 +ℹ suites 0 +ℹ pass 2 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 24419.085201 diff --git a/agents/steelbeak/work/queue-287/evidence/install-whole.log b/agents/steelbeak/work/queue-287/evidence/install-whole.log new file mode 100644 index 000000000..bc069c8cd --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/install-whole.log @@ -0,0 +1,35 @@ +✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (235.765785ms) +✔ a library re-run passes and leaves every file as it was (392.367807ms) +✔ a re-run refuses business-file-differs and leaves the file unchanged (273.242685ms) +✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (272.374516ms) +✔ the factory install writes no library mapping, persona or slot dir (177.367057ms) +✔ the factory install refuses a library project and bus.tester off, before anything is written (73.809786ms) +✔ the library install refuses bus.tester on and a persona var in the input (75.800921ms) +✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (54.413413ms) +✔ the library install needs --personas (5.198673ms) +✔ a validate refusal removes the business file this run wrote (239.945383ms) +✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (386.30537ms) +✔ a clean library host passes the precheck with no mirror yet (405.96297ms) +✔ the library precheck refuses a persona file at 0644 (394.702581ms) +✔ the library precheck refuses a persona whose digest isn't persona.sha256 (390.230566ms) +✔ the library precheck refuses a persona dir with group bits (384.651188ms) +✔ the library precheck refuses a business file at 0644 (394.310416ms) +✔ the library precheck refuses a changes.jsonl at 0644 (558.803755ms) +✔ the library precheck refuses a Discord binding (390.812738ms) +✔ --library-status refuses library-mirror-missing before the bus has made the mirror (303.064191ms) +✔ --library-status refuses library-never-synced on a fresh mirror (323.253782ms) +✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (338.938239ms) +✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (383.891631ms) +✔ --library-status takes no --profile (5.425522ms) +✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (469.515908ms) +✔ row 322 F8: library personas install from the copy with no clone node_modules (139.58888ms) +✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (88.736058ms) +✔ C2-21: library install supplies chief identity and books publish for its real read grant (234.472998ms) +ℹ tests 27 +ℹ suites 0 +ℹ pass 27 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 7494.342107 diff --git a/agents/steelbeak/work/queue-287/evidence/mutants-run.log b/agents/steelbeak/work/queue-287/evidence/mutants-run.log new file mode 100644 index 000000000..37cdfce6a --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/mutants-run.log @@ -0,0 +1,11 @@ +M1: rc=1 pass=0 fail=2 skip=0 KILLED +M2: rc=1 pass=0 fail=1 skip=0 KILLED +M3: rc=1 pass=0 fail=1 skip=0 KILLED +M4: rc=1 pass=0 fail=1 skip=0 KILLED +M5: rc=1 pass=0 fail=1 skip=0 KILLED +M6: rc=1 pass=0 fail=1 skip=0 KILLED +M7: rc=1 pass=0 fail=2 skip=0 KILLED +M8: rc=1 pass=0 fail=1 skip=0 KILLED +M9: rc=1 pass=0 fail=1 skip=0 KILLED +M10: rc=1 pass=0 fail=1 skip=0 KILLED +M11: rc=1 pass=0 fail=1 skip=0 KILLED diff --git a/agents/steelbeak/work/queue-287/evidence/mutants-summary.txt b/agents/steelbeak/work/queue-287/evidence/mutants-summary.txt new file mode 100644 index 000000000..f3e65c7a7 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/mutants-summary.txt @@ -0,0 +1,11 @@ +M1 KILLED rc=1 +M2 KILLED rc=1 +M3 KILLED rc=1 +M4 KILLED rc=1 +M5 KILLED rc=1 +M6 KILLED rc=1 +M7 KILLED rc=1 +M8 KILLED rc=1 +M9 KILLED rc=1 +M10 KILLED rc=1 +M11 KILLED rc=1 diff --git a/agents/steelbeak/work/queue-287/evidence/red-new.log b/agents/steelbeak/work/queue-287/evidence/red-new.log new file mode 100644 index 000000000..ff114906a --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/red-new.log @@ -0,0 +1,243 @@ +✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (61.932385ms) +✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (45.50288ms) +✖ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (46.877421ms) +✔ C2-21 L7/L7b: research refuses non-library targets lib (41.832555ms) +✔ C2-21 L7/L7b: research refuses non-library targets app,lib (37.221112ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (36.572846ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (37.961766ms) +✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (38.226051ms) +✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (39.45444ms) +✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (39.007984ms) +✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (39.888865ms) +✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (40.565327ms) +✖ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (41.382167ms) +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (123.506544ms) +✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (38.813518ms) +✖ C2-21: only creative definitions and the chief with gitea may publish a library project (7.989289ms) +✖ C2-21: library install supplies chief identity and books publish for its real read grant (264.267351ms) +ℹ tests 17 +ℹ suites 0 +ℹ pass 4 +ℹ fail 13 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 805.736302 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1325:3 +✖ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (61.932385ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at Object.raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1336:23) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1325:3 +✖ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (45.50288ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at Object.raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1336:23) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1325:3 +✖ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (46.877421ms) + AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value: + + assert.ok(block.includes(`Repository: ${f.gitea.url}/acme/app, level ${level}.`)) + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1338:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (38.226051ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (39.45444ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (39.007984ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (39.888865ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (40.565327ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1402:1 +✖ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (41.382167ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at Object.raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1405:21) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1416:1 +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (123.506544ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + undefined + - 'decision-mismatch' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1430:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: undefined, + expected: 'decision-mismatch', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1435:1 +✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (38.813518ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at call (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:109:49) + at raise (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:111:5) + at Object.approve (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:121:21) { + code: 'gitea-not-declared' + } + +test at packages/business/tests/business.test.mjs:365:1 +✖ C2-21: only creative definitions and the chief with gitea may publish a library project (7.989289ms) + Error [BusinessError]: /mnt/storage/scratch/tmp/HvDvQ8/mosaic-business-qXfqBQ/businesses/acme.json roles.bean.publish names library project books; library repositories cannot be published by agents + at refuse (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/src/errors.mjs:12:9) + at validateBusinessDocument (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/src/business.mjs:295:49) + at check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:66:10) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/business/tests/business.test.mjs:375:22) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + exitCode: 2 + } + +test at packages/cli/tests/case2-install.test.mjs:481:1 +✖ C2-21: library install supplies chief identity and books publish for its real read grant (264.267351ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + undefined + - [ + - 'books' + - ] + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/cli/tests/case2-install.test.mjs:486:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.start (node:internal/test_runner/test:1262:17) + at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: undefined, + expected: [ 'books' ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/red-replacement.log b/agents/steelbeak/work/queue-287/evidence/red-replacement.log new file mode 100644 index 000000000..cf077f712 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/red-replacement.log @@ -0,0 +1,44 @@ +✖ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (55.83288ms) +✖ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (52.459744ms) +ℹ tests 2 +ℹ suites 0 +ℹ pass 0 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 240.265949 + +✖ failing tests: + +test at packages/bus/tests/library-credential.test.mjs:205:1 +✖ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (55.83288ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at Object.mintChiefReadGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:154:35) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:208:17) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) { + code: 'gitea-not-declared' + } + +test at packages/bus/tests/library-credential.test.mjs:265:1 +✖ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (52.459744ms) + Error: gitea-not-declared + at fail (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:219:9) + at #giteaRole (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2768:89) + at #giteaGrantBinding (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2800:52) + at file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2700:114 + at Store.transaction (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/store.mjs:289:22) + at #raiseGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:2699:28) + at Broker.request (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/src/broker.mjs:4807:88) + at Object.mintChiefReadGrant (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:154:35) + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/library-credential.test.mjs:275:17) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) { + code: 'gitea-not-declared' + } diff --git a/agents/steelbeak/work/queue-287/evidence/shell-resume-summary.txt b/agents/steelbeak/work/queue-287/evidence/shell-resume-summary.txt new file mode 100644 index 000000000..736df0f9d --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/shell-resume-summary.txt @@ -0,0 +1,5 @@ +scripts/test-land-work.sh rc=0 +scripts/test-push-check.sh rc=0 +scripts/test-queue.sh rc=0 +scripts/test-release.sh rc=0 +scripts/test-task.sh rc=0 diff --git a/agents/steelbeak/work/queue-287/evidence/shell-summary.txt b/agents/steelbeak/work/queue-287/evidence/shell-summary.txt new file mode 100644 index 000000000..6c7cb5a07 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/shell-summary.txt @@ -0,0 +1,8 @@ +scripts/test-auth.sh rc=0 +scripts/test-conductor.sh rc=0 +scripts/test-config.sh rc=0 +scripts/test-discord.sh rc=0 +scripts/test-extension-package.sh rc=0 +scripts/test-federation-acceptance.sh rc=0 +scripts/test-foundation.sh rc=0 +scripts/test-land-work.sh rc=126 diff --git a/agents/steelbeak/work/queue-287/evidence/test-auth.log b/agents/steelbeak/work/queue-287/evidence/test-auth.log new file mode 100644 index 000000000..ac2287606 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-auth.log @@ -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 diff --git a/agents/steelbeak/work/queue-287/evidence/test-conductor.log b/agents/steelbeak/work/queue-287/evidence/test-conductor.log new file mode 100644 index 000000000..dcabc3128 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-conductor.log @@ -0,0 +1,23 @@ +On branch steelbeak/row287 +Your branch is up to date with 'origin/steelbeak/row287'. + +nothing to commit, working tree clean +OK dry-run: allowed change, exit 0, nothing committed (exit 0) +OK dry-run committed nothing +OK apply: allowed change exits 0 (exit 0) +OK apply: attribution in commit subject +OK apply: target tree clean after commit +OK disallowed path refused (exit 1) +OK disallowed path: target untouched +OK syntax gate refused broken .mjs (exit 1) +OK syntax gate: target untouched +OK suite failure refused (exit 1) +OK suite failure: target reverted to clean +OK disabled policy refused (exit 2) +OK disabled policy: target untouched +OK failed run refused (exit 1) +OK failed run: target untouched +OK missing run exits 4 (exit 4) +OK invalid policy exits 2 (exit 2) + +selftest: 17 passed, 0 failed diff --git a/agents/steelbeak/work/queue-287/evidence/test-config.log b/agents/steelbeak/work/queue-287/evidence/test-config.log new file mode 100644 index 000000000..6455ecc69 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-config.log @@ -0,0 +1,57 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK valid console.sso loads and is in the output +OK console.sso HS256 alg exits 2 naming the path (exit 2) +OK console.sso alg none exits 2 naming the path (exit 2) +OK console.sso missing slot field exits 2 naming the path (exit 2) +OK console.sso slot name outside the pattern exits 2 naming the path (exit 2) +OK unknown key under console exits 2 naming the path (exit 2) +OK valid federation key loads and is in the output +OK federation unknown sub-key exits 2 naming the key (exit 2) +OK federation peer unknown sub-key exits 2 naming the key (exit 2) +OK federation peer origin over http exits 2 naming the key (exit 2) +OK federation peer origin with a path exits 2 naming the key (exit 2) +OK federation peer origin with userinfo exits 2 naming the key (exit 2) +OK federation peer id equal to self exits 2 naming the key (exit 2) +OK federation peer label empty exits 2 naming the key (exit 2) +OK federation not an object exits 2 naming the key (exit 2) +OK federation self pattern exits 2 naming the key (exit 2) +OK federation peers not an array exits 2 naming the key (exit 2) +OK federation peer not an object exits 2 naming the key (exit 2) +OK federation peer id pattern exits 2 naming the key (exit 2) +OK federation peer label over 80 characters exits 2 naming the key (exit 2) +OK federation duplicate peer id exits 2 naming the key (exit 2) +OK federation peer origin with a trailing slash exits 2 naming the key (exit 2) +OK federation self missing exits 2 naming the key (exit 2) +OK federation peer id missing exits 2 naming the key (exit 2) +OK federation peer label missing exits 2 naming the key (exit 2) +OK config without federation loads and has no federation output +OK config without console loads with unchanged output +OK compactionReadBytes 8388608 loads and is in the output as written +OK compactionReadBytes "x" loads and is in the output as written +OK compactionStreams 2048 loads and is in the output as written +OK compactionStreams "x" loads and is in the output as written +OK failed validation modified nothing + +selftest: 55 passed, 0 failed diff --git a/agents/steelbeak/work/queue-287/evidence/test-discord.log b/agents/steelbeak/work/queue-287/evidence/test-discord.log new file mode 100644 index 000000000..048c6bdd7 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-discord.log @@ -0,0 +1,86 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/notify.mjs +OK syntax: packages/discord/src/processing.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/copy-service.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/library-seat.test.mjs +OK syntax: packages/discord/tests/notify.test.mjs +OK syntax: packages/discord/tests/offline-check.test.mjs +OK syntax: packages/discord/tests/processing-stop.test.mjs +OK syntax: packages/discord/tests/processing.test.mjs +OK syntax: packages/discord/tests/recover.test.mjs +OK syntax: packages/discord/tests/rest.test.mjs +OK syntax: packages/discord/tests/setspark.test.mjs +OK syntax: packages/discord/tests/tools.test.mjs +OK syntax: packages/discord/tests/web.test.mjs +OK syntax: packages/discord/fixtures/claim-worker.mjs +OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs +OK syntax: scripts/discord.sh +OK syntax: scripts/discord-service.sh +OK packages/discord declares no dependencies +OK no bot-token-shaped string in packages/discord +OK fixture binding uses placeholder ids only +OK fixture binding validates +OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool +OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start +OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root +OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token +OK real pi with protocol vault adds reserve_id to the git verbs +OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key +OK real pi refuses a git key on a read-only root (fail closed) +OK real pi with the pilot flags (--no-tools) exposes no tool at all +OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed) +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test packages/discord/tests/ (ℹ pass 200) +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: 82 passed, 0 failed diff --git a/agents/steelbeak/work/queue-287/evidence/test-extension-package.log b/agents/steelbeak/work/queue-287/evidence/test-extension-package.log new file mode 100644 index 000000000..a89792f25 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-extension-package.log @@ -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: 383938 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 diff --git a/agents/steelbeak/work/queue-287/evidence/test-federation-acceptance.log b/agents/steelbeak/work/queue-287/evidence/test-federation-acceptance.log new file mode 100644 index 000000000..8b1a955f1 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-federation-acceptance.log @@ -0,0 +1,10 @@ +✔ FED R7 scratch two-instance acceptance (3271.695253ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3348.152983 +R7 cleanup: no child groups, ports or sockets remain; no units created diff --git a/agents/steelbeak/work/queue-287/evidence/test-foundation.log b/agents/steelbeak/work/queue-287/evidence/test-foundation.log new file mode 100644 index 000000000..6338e1e6d --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-foundation.log @@ -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 diff --git a/agents/steelbeak/work/queue-287/evidence/test-land-work-bash.log b/agents/steelbeak/work/queue-287/evidence/test-land-work-bash.log new file mode 100644 index 000000000..0027f3346 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-land-work-bash.log @@ -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 diff --git a/agents/steelbeak/work/queue-287/evidence/test-land-work.log b/agents/steelbeak/work/queue-287/evidence/test-land-work.log new file mode 100644 index 000000000..62f3ea092 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-land-work.log @@ -0,0 +1 @@ +timeout: failed to run command ‘scripts/test-land-work.sh’: Permission denied diff --git a/agents/steelbeak/work/queue-287/evidence/test-push-check-bash.log b/agents/steelbeak/work/queue-287/evidence/test-push-check-bash.log new file mode 100644 index 000000000..990d59ad4 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-push-check-bash.log @@ -0,0 +1,7 @@ +PASS push-check 38 checks +KILLED skip-hash +KILLED accept-changes +KILLED earlier-round +KILLED working-tree +KILLED no-row +KILLED skip-nul-scan diff --git a/agents/steelbeak/work/queue-287/evidence/test-queue-bash.log b/agents/steelbeak/work/queue-287/evidence/test-queue-bash.log new file mode 100644 index 000000000..7e066c1dd --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-queue-bash.log @@ -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/.t3/worktrees/mosaic-stack/steelbeak-row287) is not the queue's canonical root (/mnt/storage/src/mosaic-stack) + +queue suite: 28 passed, 0 failed diff --git a/agents/steelbeak/work/queue-287/evidence/test-release-bash.log b/agents/steelbeak/work/queue-287/evidence/test-release-bash.log new file mode 100644 index 000000000..31f8ad7b5 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-release-bash.log @@ -0,0 +1,17 @@ +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) +OK compose network made under the run's project and removed after the run + +selftest: 15 passed, 0 failed diff --git a/agents/steelbeak/work/queue-287/evidence/test-task-bash.log b/agents/steelbeak/work/queue-287/evidence/test-task-bash.log new file mode 100644 index 000000000..4ae48aa63 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/test-task-bash.log @@ -0,0 +1,97 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +OK mock adapter: gate passes on matching mock response (exit 0) +OK mock adapter: expect-mismatch recorded (exit 1) +OK mismatch reason recorded +OK unknown adapter fails closed (exit 1) +OK mission task runs via mock adapter (exit 0) +OK mission section injected into generated prompt +OK retry of mission run succeeds (exit 0) +OK retriedFrom lineage recorded +OK mission section present after retry (relative path resolved) +OK retry of missing run exits 4 (exit 4) +OK skill install bundled ms-tools (exit 0) +OK installed to skills-available +OK double install refuses (exit 1) +OK activate enables skill (exit 0) +OK enabled dir populated +OK uninstall while enabled refuses (exit 1) +OK deactivate moves back to available (exit 0) +OK uninstall removes from available (exit 0) +OK seat with enabled skill launches +OK skill path delivered to adapter +OK shipped researcher contract resolves (ceiling + network) +OK resolve-role refuses a non-role document (conductor policy) (exit 2) +OK resolve-role refuses name/filename mismatch (exit 2) +OK resolve-role refuses unknown network declaration (exit 2) +OK resolve-role refuses duplicate role tool (exit 2) +OK resolve-role refuses unsupported tool (exit 2) +OK resolve-role refuses missing contract file (exit 4) +OK version 1 role prints no contract line +OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer) +OK resolve-role accepts a minimal version 2 role (exit 0) +OK resolve-role refuses an action outside the vocabulary (exit 2) +OK resolve-role refuses a gated-only action in a role file (exit 2) +OK resolve-role refuses a Vikunja verb no role may hold (exit 2) +OK resolve-role refuses a version 2 role whose contract is missing (exit 2) +OK resolve-role refuses a contract that is a symbolic link (exit 2) +OK seat launches under role ceiling +OK role ceiling narrows seat tools (read,write,bash -> read,bash) +OK narrowing recorded loudly +OK missing role contract refuses launch (exit 2) +OK missing-contract refusal names the role +OK empty ceiling intersection -> tool-free seat +OK tool-free outcome recorded loudly +OK overridden agents dir without seat definition refuses (exit 4) +OK seat-resolution refusal names the seat +OK policy: mission only -> mission tools +OK policy: task only -> task tools +OK policy: both -> intersection (task narrowed) +OK policy: empty intersection -> tool-free +OK invalid mission capabilities rejected (exit 2) +mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls) +OK task run with user layer present (exit 0) +OK user context dispatched into generated prompt +OK workspace+tools task runs via mock (exit 0) +OK workspace path + tools delivered to adapter +OK persistent workspace created on host +OK plain task still runs (no workspace/tools) (exit 0) +OK workspace var present but empty when absent +OK tools empty when absent +OK unknown tool exits 2 (exit 2) +OK workspace traversal exits 2 (exit 2) +OK hello task succeeds with exact marker +OK result.json written in run dir +OK result.json contents are correct +OK wrong expectExact fails with exit 1 (reason: expect-mismatch) +OK each run gets a distinct run dir (no clobber) +OK list shows both runs +skip live provider cases (row 244: no provider call in the gate) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +skip live provider cases (row 244: no provider call in the gate) +OK no agent identity on headless run +OK compose network made under the run's project and removed after the run + +selftest: 92 passed, 0 failed diff --git a/agents/steelbeak/work/queue-287/evidence/token-audit.txt b/agents/steelbeak/work/queue-287/evidence/token-audit.txt new file mode 100644 index 000000000..de1003b95 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/token-audit.txt @@ -0,0 +1,5 @@ +known fixture values searched: 3 +logs searched: 37 +fixture hits: 0 +Docker fact runner separately checked all four actual minted values against complete TAP stdout/stderr: hits 0. +Fake API cases assertClean/noLeak cover every issued token against events, messages, decisions and request logs. diff --git a/agents/steelbeak/work/queue-287/evidence/touched-r1.log b/agents/steelbeak/work/queue-287/evidence/touched-r1.log new file mode 100644 index 000000000..989e588ea --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/touched-r1.log @@ -0,0 +1,265 @@ +✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.139073ms) +✔ X-G1: a raise that fails a check refuses and stores nothing (75.530438ms) +✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (193.46698ms) +✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (39.523907ms) +✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (153.731441ms) +✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (106.729946ms) +✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (363.191747ms) +✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (100.705375ms) +✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (86.135966ms) +✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (164.209877ms) +✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (125.633285ms) +✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (151.600735ms) +✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (65.645239ms) +✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (174.510424ms) +✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (148.773407ms) +✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (171.547646ms) +✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (179.655701ms) +✔ X-G6: a restart settles an open rotation back when the file still holds the old token (135.974464ms) +✔ X-G6: a restart settles an open rotation forward when the file holds the new token (135.770314ms) +✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (138.899782ms) +✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1029.94154ms) +✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (162.896203ms) +✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (138.973458ms) +✔ N9: a 404 on a delete by id counts as done (133.050723ms) +✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (414.134804ms) +✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (55.861549ms) +✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (125.765675ms) +✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (114.25118ms) +✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (66.692412ms) +✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (49.649111ms) +✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (108.282506ms) +✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (106.579322ms) +✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (107.282814ms) +✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (131.713832ms) +✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (203.032995ms) +✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (329.237353ms) +✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (194.559652ms) +✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (184.236777ms) +✔ X-G12: a token that reaches wider than its forks fails with the repository named (389.923565ms) +✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (266.358571ms) +✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (43.235444ms) +✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (281.776428ms) +✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (133.556646ms) +✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (264.851425ms) +✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (279.235184ms) +✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (133.659853ms) +✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (113.137966ms) +✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (112.990765ms) +✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (46.225594ms) +✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (113.742539ms) +✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (129.484221ms) +✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (106.138514ms) +✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (104.316693ms) +✔ C2-21 L7/L7b: research refuses non-library targets lib (42.155496ms) +✔ C2-21 L7/L7b: research refuses non-library targets app,lib (40.652006ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (40.430096ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (40.868584ms) +✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (64.643789ms) +✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (63.595938ms) +✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (64.302062ms) +✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (65.102053ms) +✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (64.826649ms) +✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (87.339871ms) +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (64.895333ms) +✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (64.965654ms) +✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (32.72782ms) +✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (296.498344ms) +✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (36.638485ms) +✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (32.149673ms) +✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (18.858172ms) +✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (782.448934ms) +✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (11.031163ms) +✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.672501ms) +✔ config directory and file path follow MOSAIC_CONFIG (0.332343ms) +✔ the fixture business validates and comes back frozen (0.995298ms) +✔ two instances may share a definition (0.963652ms) +✔ top-level refusals (2.767585ms) +✔ arbiters and projects (3.905801ms) +✔ role instances (2.3953ms) +✔ Vikunja bots (4.888461ms) +✔ a role without Vikunja takes no tracker block (8.053822ms) +✔ credential references match the definition's services (3.719872ms) +✔ launch (11.051038ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (6.346401ms) +✔ loadBusiness: file checks (2.217137ms) +✔ loadBusiness: not a regular file (44.303522ms) +✔ loading writes nothing (1.158385ms) +✔ names that are Object.prototype properties don't count as declared (3.580542ms) +✔ the shipped example refuses as written and validates once filled in (1.167705ms) +✔ persona vars: agent layer, creative definition only, both or neither (9.835969ms) +✔ row 285: a chief or creative instance without credentials.gitea refuses at load (7.574546ms) +✔ row 285: a creative instance with identity and publish loads (0.773536ms) +✔ row 194 keep, noHire and retired validate, and a file without them loads as before (2.123104ms) +✔ row 194 each bad keep, noHire or retired entry refuses (7.372634ms) +✔ C2-21: only creative definitions and the chief with gitea may publish a library project (12.000435ms) +✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (260.836427ms) +✔ a library re-run passes and leaves every file as it was (404.249515ms) +✔ a re-run refuses business-file-differs and leaves the file unchanged (279.629668ms) +✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (284.561951ms) +✔ the factory install writes no library mapping, persona or slot dir (178.261551ms) +✔ the factory install refuses a library project and bus.tester off, before anything is written (71.855815ms) +✔ the library install refuses bus.tester on and a persona var in the input (80.555838ms) +✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (54.023333ms) +✔ the library install needs --personas (5.849596ms) +✔ a validate refusal removes the business file this run wrote (233.892356ms) +✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (404.647484ms) +✔ a clean library host passes the precheck with no mirror yet (426.936455ms) +✔ the library precheck refuses a persona file at 0644 (398.6846ms) +✔ the library precheck refuses a persona whose digest isn't persona.sha256 (404.193929ms) +✔ the library precheck refuses a persona dir with group bits (411.810016ms) +✔ the library precheck refuses a business file at 0644 (404.979409ms) +✔ the library precheck refuses a changes.jsonl at 0644 (572.602427ms) +✔ the library precheck refuses a Discord binding (408.215092ms) +✔ --library-status refuses library-mirror-missing before the bus has made the mirror (324.192926ms) +✔ --library-status refuses library-never-synced on a fresh mirror (337.170027ms) +✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (354.03021ms) +✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (397.284837ms) +✔ --library-status takes no --profile (5.679559ms) +✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (477.032132ms) +✔ row 322 F8: library personas install from the copy with no clone node_modules (138.947889ms) +✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (94.497291ms) +✔ C2-21: library install supplies chief identity and books publish for its real read grant (241.629315ms) +ℹ tests 122 +ℹ suites 0 +ℹ pass 115 +ℹ fail 7 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 9691.559494 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (64.643789ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"e07beff5-0dd2-474a-987b-7f742de95dee","agent":"chief","code":"grant-admin-not-dedicated"} + + actual - expected + + + 'grant-admin-not-dedicated' + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394: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: 'grant-admin-not-dedicated', + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (63.595938ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"83dfd9a1-0794-4fb2-8fa5-a21aecaaa40a","agent":"chief","code":"grant-admin-not-dedicated"} + + actual - expected + + + 'grant-admin-not-dedicated' + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394: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: 'grant-admin-not-dedicated', + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (64.302062ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"88073d5b-3e4f-4a9a-b241-5b110d3be2c0","agent":"chief","code":"grant-admin-not-dedicated"} + + actual - expected + + + 'grant-admin-not-dedicated' + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394: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: 'grant-admin-not-dedicated', + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (65.102053ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"d2e4e251-e9f3-4ede-aa40-284f014243ab","agent":"chief","code":"grant-admin-not-dedicated"} + + actual - expected + + + 'grant-admin-not-dedicated' + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394: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: 'grant-admin-not-dedicated', + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1386:3 +✖ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (64.826649ms) + AssertionError [ERR_ASSERTION]: {"action":"grant.failed","method":"gitea","grantDecision":"676ebbc8-8397-4b63-90f4-3485fc277029","agent":"chief","code":"grant-admin-not-dedicated"} + + actual - expected + + + 'grant-admin-not-dedicated' + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1394: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: 'grant-admin-not-dedicated', + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1416:1 +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (64.895333ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'gitea-not-declared' + - 'decision-mismatch' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1430:12) + 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: 'gitea-not-declared', + expected: 'decision-mismatch', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/bus/tests/gitea-grant.test.mjs:1435:1 +✖ C2-21 L4: creative write requires push true but ignores unrelated permission keys (64.965654ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'grant-admin-not-dedicated' + - 'gitea-reach-wider' + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1444:23) + 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: 'grant-admin-not-dedicated', + expected: 'gitea-reach-wider', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/touched-r2.log b/agents/steelbeak/work/queue-287/evidence/touched-r2.log new file mode 100644 index 000000000..fa4c25ef1 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/touched-r2.log @@ -0,0 +1,150 @@ +✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.633441ms) +✔ X-G1: a raise that fails a check refuses and stores nothing (77.604936ms) +✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (240.549157ms) +✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (45.498213ms) +✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (183.346403ms) +✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (112.792295ms) +✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (377.194723ms) +✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (104.97076ms) +✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (110.564422ms) +✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (196.260428ms) +✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (133.845626ms) +✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (185.353007ms) +✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (65.526316ms) +✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (192.404202ms) +✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (174.549257ms) +✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (174.724588ms) +✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (207.75696ms) +✔ X-G6: a restart settles an open rotation back when the file still holds the old token (152.596063ms) +✔ X-G6: a restart settles an open rotation forward when the file holds the new token (139.158765ms) +✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (144.64518ms) +✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1100.789745ms) +✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (181.734467ms) +✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (146.803459ms) +✔ N9: a 404 on a delete by id counts as done (143.732923ms) +✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (469.337565ms) +✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (63.327553ms) +✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (143.161323ms) +✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (130.729706ms) +✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (77.884977ms) +✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (56.193013ms) +✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (125.223336ms) +✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (118.148549ms) +✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (132.457409ms) +✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (134.269691ms) +✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (205.166166ms) +✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (340.037735ms) +✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (196.238899ms) +✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (189.34344ms) +✔ X-G12: a token that reaches wider than its forks fails with the repository named (401.574274ms) +✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (267.846613ms) +✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (44.259928ms) +✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (284.587133ms) +✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (151.958016ms) +✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (334.678591ms) +✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (366.243469ms) +✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (134.383532ms) +✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (111.177819ms) +✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (132.34568ms) +✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (44.455018ms) +✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (94.729541ms) +✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (132.848006ms) +✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (108.726865ms) +✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (105.566834ms) +✔ C2-21 L7/L7b: research refuses non-library targets lib (43.04793ms) +✔ C2-21 L7/L7b: research refuses non-library targets app,lib (43.226528ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (40.740414ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (41.612697ms) +✔ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (106.28642ms) +✔ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (112.291928ms) +✔ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (108.82183ms) +✔ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (107.25133ms) +✔ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (111.661727ms) +✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (110.617198ms) +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (71.782699ms) +✔ C2-21 L4: creative write requires push true but ignores unrelated permission keys (216.51561ms) +✔ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (45.980982ms) +✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (33.812383ms) +✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (388.895625ms) +✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (38.05536ms) +✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (35.538194ms) +✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (16.650711ms) +✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (899.871176ms) +✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (12.126655ms) +✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.81521ms) +✔ config directory and file path follow MOSAIC_CONFIG (0.250758ms) +✔ the fixture business validates and comes back frozen (1.103053ms) +✔ two instances may share a definition (1.017458ms) +✔ top-level refusals (2.951099ms) +✔ arbiters and projects (5.522959ms) +✔ role instances (2.771534ms) +✔ Vikunja bots (6.347953ms) +✔ a role without Vikunja takes no tracker block (2.577895ms) +✔ credential references match the definition's services (2.989169ms) +✔ launch (9.573192ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (5.256373ms) +✔ loadBusiness: file checks (2.239389ms) +✔ loadBusiness: not a regular file (46.809559ms) +✔ loading writes nothing (1.257951ms) +✔ names that are Object.prototype properties don't count as declared (3.811661ms) +✔ the shipped example refuses as written and validates once filled in (0.970387ms) +✔ persona vars: agent layer, creative definition only, both or neither (11.317037ms) +✔ row 285: a chief or creative instance without credentials.gitea refuses at load (8.600878ms) +✔ row 285: a creative instance with identity and publish loads (0.949694ms) +✔ row 194 keep, noHire and retired validate, and a file without them loads as before (2.393095ms) +✔ row 194 each bad keep, noHire or retired entry refuses (7.173244ms) +✔ C2-21: only creative definitions and the chief with gitea may publish a library project (13.234306ms) +✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (319.004449ms) +✔ a library re-run passes and leaves every file as it was (470.879755ms) +✔ a re-run refuses business-file-differs and leaves the file unchanged (312.005718ms) +✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (335.01291ms) +✔ the factory install writes no library mapping, persona or slot dir (201.940121ms) +✔ the factory install refuses a library project and bus.tester off, before anything is written (83.824548ms) +✔ the library install refuses bus.tester on and a persona var in the input (76.59824ms) +✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (56.483195ms) +✔ the library install needs --personas (5.954284ms) +✔ a validate refusal removes the business file this run wrote (256.190647ms) +✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (437.854592ms) +✔ a clean library host passes the precheck with no mirror yet (441.383612ms) +✔ the library precheck refuses a persona file at 0644 (461.331192ms) +✔ the library precheck refuses a persona whose digest isn't persona.sha256 (405.1089ms) +✔ the library precheck refuses a persona dir with group bits (464.242283ms) +✔ the library precheck refuses a business file at 0644 (457.841445ms) +✔ the library precheck refuses a changes.jsonl at 0644 (635.778232ms) +✔ the library precheck refuses a Discord binding (462.22964ms) +✔ --library-status refuses library-mirror-missing before the bus has made the mirror (331.544735ms) +✔ --library-status refuses library-never-synced on a fresh mirror (347.586128ms) +✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (356.091238ms) +✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (401.900674ms) +✔ --library-status takes no --profile (5.777431ms) +✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (482.873578ms) +✔ row 322 F8: library personas install from the copy with no clone node_modules (146.255507ms) +✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (89.465774ms) +✔ C2-21: library install supplies chief identity and books publish for its real read grant (294.210478ms) +ℹ tests 123 +ℹ suites 0 +ℹ pass 122 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 10963.095817 + +✖ failing tests: + +test at packages/bus/tests/gitea-grant.test.mjs:1416:1 +✖ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (71.782699ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + 4 !== 0 + + at TestContext. (file:///home/jwoltje/.t3/worktrees/mosaic-stack/steelbeak-row287/packages/bus/tests/gitea-grant.test.mjs:1434:12) + 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: 4, + expected: 0, + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/steelbeak/work/queue-287/evidence/touched-r3.log b/agents/steelbeak/work/queue-287/evidence/touched-r3.log new file mode 100644 index 000000000..4ebaa1c69 --- /dev/null +++ b/agents/steelbeak/work/queue-287/evidence/touched-r3.log @@ -0,0 +1,131 @@ +✔ versionAtLeast compares numbers and refuses a prerelease of 1.27.0 (1.280112ms) +✔ X-G1: a raise that fails a check refuses and stores nothing (71.349417ms) +✔ X-G1: a creative role, a role without identity and a missing or due slot refuse at raise (218.719606ms) +✔ X-G1: the chief of staff refuses at raise, whatever its definition, identity and publish declare (45.959539ms) +✔ X-G2: admin set refuses each failed check and writes nothing, then writes the slot once (166.537485ms) +✔ X-G3 to X-G8: an approve makes the user, mints the token, forks, checks reach and writes the file, record and grant.applied (108.835383ms) +✔ X-G4: a user this stack did not make, an admin, a disabled user and a remade user each refuse (368.565402ms) +✔ X-G6 and X-G8: the user and mint steps are written before their calls, and each result after its call (109.068207ms) +✔ X-G4: a fork by the seat user of another repository than the project refuses fork-foreign (103.652877ms) +✔ X-G5: a failure after the mint deletes the token, unloads nothing it did not load and leaves no file (132.5244ms) +✔ X-G5: a token delete that fails is left at Gitea, shown by status and taken by sweep (125.127842ms) +✔ X-G5: a mint that fails deletes by name, and a refusal at step 0 leaves the decision unspent (147.844515ms) +✔ X-G5: an agent held for an unknown token is refused at step 0 with no Gitea call (64.597545ms) +✔ X-G5 and X-G9 (step 7): a rotation whose old-token delete fails stays applied with the old id left (190.16061ms) +✔ X-G10: in a rotation the store file holds the new token before the old one is deleted (168.111001ms) +✔ X-G6: a rotation cut before the rename settles back at once, and the old token stays live (172.49915ms) +✔ X-G6: a rotation whose step 8 write fails is settle-pending, and a restart settles it forward (189.011204ms) +✔ X-G6: a restart settles an open rotation back when the file still holds the old token (141.149461ms) +✔ X-G6: a restart settles an open rotation forward when the file holds the new token (143.41922ms) +✔ X-G6: a 401 on the file token loses the grant: both tokens go, the record goes and memory unloads (142.157997ms) +✔ X-G6: a 403, 404, 500, a hang and a dropped socket each leave the rotation settle-pending with nothing deleted (1052.205948ms) +✔ X-G6: a file token no grant minted is held; the next restart says so again; a revoke clears it and takes both ids (173.950313ms) +✔ N9: a revoke of an open rotation, then a restart: no settle, no Gitea call and no message (139.936508ms) +✔ N9: a 404 on a delete by id counts as done (135.846367ms) +✔ X-G6: a restart undoes each step a killed child left, and spends an approved unspent decision (438.925152ms) +✔ X-G6: a restart with no admin slot leaves the user an orphan and the token left (58.025191ms) +✔ X-G7: a change to the definition, identity, publish remotes or admin slot after the raise ends decision-mismatch (128.865648ms) +✔ X-G7 and X-G9: an admin rotate voids open decisions, and rotate --finish deletes what was left (122.928741ms) +✔ X-G9: a rotate whose name is taken refuses and writes nothing; remove takes the slot away (67.477228ms) +✔ X-G9: a slot past its rotate-by date refuses a run at step 0, leaving the decision unspent (51.433851ms) +✔ X-G11: run refuses every caller but the human, an unapproved, a spent and a busy decision (106.410351ms) +✔ kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses (109.111514ms) +✔ X-G11: a revoke while the executor runs aborts it and the grant ends revoked (128.36085ms) +✔ X-G11: a revoke unloads memory and removes the file before its Gitea delete (134.277671ms) +✔ X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back (184.595189ms) +✔ X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record (343.152025ms) +✔ X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not held (199.845732ms) +✔ X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost (185.12583ms) +✔ X-G12: a token that reaches wider than its forks fails with the repository named (399.290325ms) +✔ X-G12: reach is paged by X-Total-Count and a count that does not add up refuses (314.011296ms) +✔ X-G13: an agent cannot record a grant event, and the trigger refuses one from any writer but the grant writer (49.068987ms) +✔ X-G14: credentials delete fires the user: tokens go, login is prohibited, and grant.revoke.closed records it (296.702448ms) +✔ X-G14: a fire whose token delete or login PATCH fails lands it in serviceLeft (158.52798ms) +✔ X-G14: a fire whose user is gone or remade skips the fire steps and says why (302.003039ms) +✔ X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and named (351.593821ms) +✔ X-G14: a revoke Gitea cannot reach stays open, and a restart closes it (134.257555ms) +✔ row 281 T8: a tester grants.revoke refuses tester-dev-only once bus.tester is off; the human revoke keeps its records and starts no job (109.730792ms) +✔ X-G15: look-alike commit steps outside the grant writer are ignored at start, by revoke and by status (131.637028ms) +✔ X-G15: a look-alike spend under another kind with the grant writer pair is not an open grant (44.820972ms) +✔ status lists the slot, users, grants and nothing secret, and every grants verb needs service gitea (114.502261ms) +✔ C2-21 L1/L2/L9: research gets write on her repository with declared scopes through raise and yes (127.306297ms) +✔ C2-21 L1/L2/L9: chief-of-staff gets read on her repository with declared scopes through raise and yes (109.289905ms) +✔ C2-21 L1/L2/L9: coder gets read on her repository with declared scopes through raise and yes (106.048934ms) +✔ C2-21 L7/L7b: research refuses non-library targets lib (42.311032ms) +✔ C2-21 L7/L7b: research refuses non-library targets app,lib (41.695354ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets lib (41.862836ms) +✔ C2-21 L7/L7b: chief-of-staff refuses non-library targets app,lib (40.716511ms) +✔ C2-21 L4: read widened to write refuses gitea-reach-wider and deletes the minted token (87.149976ms) +✔ C2-21 L4: admin privilege refuses gitea-reach-wider and deletes the minted token (106.353171ms) +✔ C2-21 L4: missing pull refuses gitea-reach-wider and deletes the minted token (108.373202ms) +✔ C2-21 L4: missing permission object refuses gitea-reach-wider and deletes the minted token (109.736478ms) +✔ C2-21 L4: library fork refuses gitea-reach-wider and deletes the minted token (107.72263ms) +✔ C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide (113.114472ms) +✔ C2-21 L10: project kind or library level changing after raise refuses decision-mismatch (144.543711ms) +✔ C2-21 L4: creative write requires push true but ignores unrelated permission keys (214.033085ms) +✔ C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise (47.400615ms) +✔ C2-03b T6: the slot before any chief evidence, then the listed chief's live grant (31.577774ms) +✔ C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended (333.151597ms) +✔ C2-03b T7 choice checks: delisted, a fire under way and held each refuse; a rehire lists the chief again (36.808837ms) +✔ C2-03b T8: a chief grant on another project ends the fallback, and two listed chiefs refuse library-fetch-no-grant (35.425842ms) +✔ C2-03b departure (sage-2928): with no chief entry, any Gitea grant evidence refuses (18.152711ms) +✔ C2-03b T9 runtime: the chief's store file and user, then a real revoke that runs no git with the slot at 0000 (731.269457ms) +✔ C2-04: library mappings load without roots, remain frozen, and retain mapping refusals (12.697745ms) +✔ C2-04: an otherwise valid publish grant cannot name a library repo (1.895805ms) +✔ config directory and file path follow MOSAIC_CONFIG (0.218002ms) +✔ the fixture business validates and comes back frozen (1.113423ms) +✔ two instances may share a definition (1.085918ms) +✔ top-level refusals (3.201752ms) +✔ arbiters and projects (4.428142ms) +✔ role instances (2.586059ms) +✔ Vikunja bots (6.556892ms) +✔ a role without Vikunja takes no tracker block (3.259111ms) +✔ credential references match the definition's services (2.945148ms) +✔ launch (9.192439ms) +✔ REQ-THREAD-2 REQ-LAUNCH-1 launch.threadIdleSeconds: absent is the default, a positive integer is used, anything else refuses (5.144938ms) +✔ loadBusiness: file checks (1.735446ms) +✔ loadBusiness: not a regular file (43.733004ms) +✔ loading writes nothing (1.135021ms) +✔ names that are Object.prototype properties don't count as declared (3.860107ms) +✔ the shipped example refuses as written and validates once filled in (0.993456ms) +✔ persona vars: agent layer, creative definition only, both or neither (11.635685ms) +✔ row 285: a chief or creative instance without credentials.gitea refuses at load (7.937253ms) +✔ row 285: a creative instance with identity and publish loads (0.868629ms) +✔ row 194 keep, noHire and retired validate, and a file without them loads as before (1.843993ms) +✔ row 194 each bad keep, noHire or retired entry refuses (6.103965ms) +✔ C2-21: only creative definitions and the chief with gitea may publish a library project (11.142387ms) +✔ the library install writes the business file, personas and slot dir, makes the copy and starts nothing (266.122171ms) +✔ a library re-run passes and leaves every file as it was (419.422061ms) +✔ a re-run refuses business-file-differs and leaves the file unchanged (294.559417ms) +✔ a re-run refuses persona-differs when an installed persona isn't the staged one, and writes nothing (294.731302ms) +✔ the factory install writes no library mapping, persona or slot dir (189.209583ms) +✔ the factory install refuses a library project and bus.tester off, before anything is written (73.526324ms) +✔ the library install refuses bus.tester on and a persona var in the input (75.290564ms) +✔ the install refuses root, a business id mismatch, another --home and a config dir with group bits (53.415023ms) +✔ the library install needs --personas (5.673132ms) +✔ a validate refusal removes the business file this run wrote (237.469747ms) +✔ N4: a validate refusal on a re-run leaves the matching business file in place, byte for byte (430.013288ms) +✔ a clean library host passes the precheck with no mirror yet (417.064064ms) +✔ the library precheck refuses a persona file at 0644 (430.044192ms) +✔ the library precheck refuses a persona whose digest isn't persona.sha256 (395.659433ms) +✔ the library precheck refuses a persona dir with group bits (420.475466ms) +✔ the library precheck refuses a business file at 0644 (418.657123ms) +✔ the library precheck refuses a changes.jsonl at 0644 (612.437015ms) +✔ the library precheck refuses a Discord binding (405.124749ms) +✔ --library-status refuses library-mirror-missing before the bus has made the mirror (329.538375ms) +✔ --library-status refuses library-never-synced on a fresh mirror (341.974764ms) +✔ --library-status names a recorded lastError before the null lastFetch it leaves (N3) (357.309304ms) +✔ --library-status prints each R-problem's path as an escaped JSON string, then the count, and passes (394.067863ms) +✔ --library-status takes no --profile (5.871472ms) +✔ Vikunja 614: install preserves the explicit notifier decision bytes and mode and creates none when absent (536.196884ms) +✔ row 322 F8: library personas install from the copy with no clone node_modules (164.278338ms) +✔ row 322 F9: importer refusal keeps the error line rather than the Node banner (95.063494ms) +✔ C2-21: library install supplies chief identity and books publish for its real read grant (261.537958ms) +ℹ tests 123 +ℹ suites 0 +ℹ pass 123 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 10632.428445 diff --git a/agents/steelbeak/work/queue-287/mutants.py b/agents/steelbeak/work/queue-287/mutants.py new file mode 100644 index 000000000..4ec10271b --- /dev/null +++ b/agents/steelbeak/work/queue-287/mutants.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""Row287 semantic mutants. Each restoration is byte-checked; timeout/error is never a kill.""" +from pathlib import Path +import hashlib, os, re, subprocess, sys +root=Path(__file__).resolve().parents[4] +os.chdir(root) +broker='packages/bus/src/broker.mjs'; grant='packages/bus/src/gitea-grant.mjs'; business='packages/business/src/business.mjs' +bus_test='packages/bus/tests/gitea-grant.test.mjs'; biz_test='packages/business/tests/business.test.mjs' +evidence=Path('agents/steelbeak/work/queue-287/evidence') +mutants=[ + ('M1',broker,"(CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) && !libraryOnly", "role.definition === STAFF_TO && !libraryOnly",'C2-21 L7/L7b: research',bus_test), + ('M2',broker,"level: CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read'", "level: 'write'",'C2-21 L1/L2/L9: chief-of-staff',bus_test), + ('M3',broker,"repo.permissions?.admin === false && repo.permissions?.push === (level === 'write') && repo.permissions?.pull === true", "repo.permissions?.pull === true",'C2-21 L4: read widened',bus_test), + ('M4',broker," if (t.library) {", " if (false && t.library) {",'C2-21 L1/L2/L9: research',bus_test), + ('M5',business,"projects[project].kind === 'library' && !CREATIVE_DEFINITIONS.includes(role.definition) && role.definition !== 'chief-of-staff'", "false",'C2-21: only creative',biz_test), + ('M6',broker,"api.tokenCreate(user, tokenName, scopes)", "api.tokenCreate(user, tokenName)",'C2-21 L1/L2/L9: chief-of-staff',bus_test), + ('M7',broker,"(CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) && !libraryOnly", "CREATIVE_DEFINITIONS.includes(role.definition) && !libraryOnly",'C2-21 L7/L7b: chief-of-staff',bus_test), + ('M8',broker," role.publish.length === 0 ||\n (projects.some((p) => b.projects?.[p]?.kind === 'library') &&\n !definition.credentials.some((c) => c.service === 'gitea' && Array.isArray(c.scopes) && c.scopes.length > 0))", " role.publish.length === 0",'C2-21: a library grant with no declared scopes',bus_test), + ('M9',grant," if (r.json.length !== 0 || (r.total !== null && r.total !== 0)) deny('grant-admin-not-dedicated');", " // M9: omit dedicated-admin reach check.",'^X-G2:',bus_test), + ('M10',broker," // Step 1, one transaction: spend and recheck. scope-changed ends spent (D6);", " await api.userCreate({ username: 'early-write', email: 'early@example.test', full_name: 'Early write', password: 'fx287-early-q7', must_change_password: false });\n // Step 1, one transaction: spend and recheck. scope-changed ends spent (D6);",'C2-21 L10:',bus_test), + ('M11',grant,"...(Object.keys(library).length ? { library } : {})", "...(false ? { library } : {})",'C2-21 L10:',bus_test), +] +results=[] +for key,file,old,new,pattern,test in mutants: + p=Path(file); original=p.read_bytes(); digest=hashlib.sha256(original).hexdigest(); source=original.decode() + if source.count(old)!=1: raise RuntimeError(f'{key}: expected one mutation site, got {source.count(old)}') + try: + p.write_text(source.replace(old,new,1)) + out=subprocess.run(['timeout','-k','10','180','node','--test',f'--test-name-pattern={pattern}',test],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,env=os.environ) + (evidence/f'{key}.log').write_text(out.stdout) + passes=re.search(r'^ℹ pass (\d+)$',out.stdout,re.M); failures=re.search(r'^ℹ fail (\d+)$',out.stdout,re.M); skips=re.search(r'^ℹ skipped (\d+)$',out.stdout,re.M) + killed=out.returncode==1 and failures and int(failures[1])>0 and skips and int(skips[1])==0 and 'AssertionError' in out.stdout and not re.search(r'SyntaxError|ERR_MODULE_NOT_FOUND',out.stdout) + print(f'{key}: rc={out.returncode} pass={passes[1] if passes else "?"} fail={failures[1] if failures else "?"} skip={skips[1] if skips else "?"} {"KILLED" if killed else "INVALID/SURVIVED"}',flush=True) + results.append(f'{key} {"KILLED" if killed else "INVALID/SURVIVED"} rc={out.returncode}') + if not killed: sys.exit(1) + finally: + p.write_bytes(original) + if hashlib.sha256(p.read_bytes()).hexdigest()!=digest: raise RuntimeError(f'{key}: restoration mismatch') +(evidence/'mutants-summary.txt').write_text('\n'.join(results)+'\n') diff --git a/agents/steelbeak/work/queue-287/review-manifest.txt b/agents/steelbeak/work/queue-287/review-manifest.txt new file mode 100644 index 000000000..248511968 --- /dev/null +++ b/agents/steelbeak/work/queue-287/review-manifest.txt @@ -0,0 +1,56 @@ +69f91b4989a39bf09201a7b328f6c38e833e9897e310f026ce893d4caa2ec214 BUILD-LOG.md +d64255c7493e10e1d82949c2335e4fce47ad5f4130901941fdd091e224b9c2c8 agents/steelbeak/work/queue-287/BUILD.md +fb8615d02a3cdb29e96434bf97e3d6b26f447e9fa05cf26bf97c26f6c659b1c8 agents/steelbeak/work/queue-287/evidence/M1.log +df427effd5b4ce587229ccf258816b3a65fe93f25a1e3706f8f1bef9223dcc42 agents/steelbeak/work/queue-287/evidence/M10.log +777dd9500f451d9fbca215816efd5e1f94ba803ef1649a724b18ea3ec65b4d10 agents/steelbeak/work/queue-287/evidence/M11.log +08e3628a711afed00d475cb9f9b1ee2dffd3a480d8a78e91478783aaea55b8f3 agents/steelbeak/work/queue-287/evidence/M2.log +8d958f9bd3791d2e98392924ae93f335559ea2e35b72763be0847f72fc7539a9 agents/steelbeak/work/queue-287/evidence/M3.log +8a82af3e7b46206fbeb1eeb4d6976fe34be703de5fc3b09328b51d806a13fd53 agents/steelbeak/work/queue-287/evidence/M4.log +27841d51d2aa04fc8b23a2cdb0f481ef0c1b6a9ba689dc84d3fbf212276aa43a agents/steelbeak/work/queue-287/evidence/M5.log +422a05a364404dd89d0730605f637552dc7d95058e785cdb8b2916690021cbe3 agents/steelbeak/work/queue-287/evidence/M6.log +cdb11e0c732c8ee0aa6c4c7167b499fabb079a1fb3c0f64e09776efe1ce94ec9 agents/steelbeak/work/queue-287/evidence/M7.log +8c3d35bbdec28394133f17ce427122dcbb3a00859122a2ba3e220698b08c7ac6 agents/steelbeak/work/queue-287/evidence/M8.log +f697d8606f9a879b7afc16aedc722bf89cd56fb79d1947933b40354bbdcbc3cb agents/steelbeak/work/queue-287/evidence/M9.log +015595d7539fa0a3fdd8993ca5f6bb37eb3a4e90d21de9902490b68ce65790c6 agents/steelbeak/work/queue-287/evidence/bus-suite.log +1186834379a25300c4e7657dd560194346bfd960206d716d3ec80d083b874ac7 agents/steelbeak/work/queue-287/evidence/business-suite.log +fb407cecad775daeb964996f5286504478e78abb2639dfddad901da72a8299da agents/steelbeak/work/queue-287/evidence/cleanup-audit.txt +1b5994ef1fbceb0d85b7dc27b8c80c38763e9443d255f5e0ae8768a52cc49a6f agents/steelbeak/work/queue-287/evidence/cli-suite.log +b9df0f83ac9cdcbd9904d48e91c2c9b29704b6b1ed1d86bd5da8208d8ae6e5c5 agents/steelbeak/work/queue-287/evidence/docker-facts-r2.log +d0a408745b860ddcf0415f1eaa56f733117cf01eb911e89acc398d48ac6d3a12 agents/steelbeak/work/queue-287/evidence/docker-facts.log +e214557bb114e1ddcfc9fe1779664c5775e0006e1531beb42432cf994a6ee4dd agents/steelbeak/work/queue-287/evidence/docker-whole.log +67bb508ab388f24adbfb519edc22fe5438e80796e90f7c22e858a1360dca3682 agents/steelbeak/work/queue-287/evidence/install-whole.log +354a30815e25b949cba560d44dd373e7bd10a2151a7c8809a4de9eb64bbfede4 agents/steelbeak/work/queue-287/evidence/mutants-run.log +22d7df0a069ff63ebad0ae6d7d7d21f25727a85f31c5811b05d37d975c08bce9 agents/steelbeak/work/queue-287/evidence/mutants-summary.txt +5b0e61c389a337fb507f73254d0a60141ef36a8b0abd4f41e0dd20f81f1df247 agents/steelbeak/work/queue-287/evidence/red-new.log +c840276c920023dce945bcfeb3afd3b111d6abf6dcfb0ae65daa3fccbaa720ce agents/steelbeak/work/queue-287/evidence/red-replacement.log +9b5f6010e9f7c56834371b673710a95eb3bdbb256d77cc3bc8ab160c3301b40e agents/steelbeak/work/queue-287/evidence/shell-resume-summary.txt +c17fca8ffdf905a467e3a3f201876cc96719e691ff2b94c0366ecb54e8e3286f agents/steelbeak/work/queue-287/evidence/shell-summary.txt +f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 agents/steelbeak/work/queue-287/evidence/test-auth.log +d681bd46560d9f359ccd70fdb2694e8b2a4457fe5cc70796e3044447c3fb9562 agents/steelbeak/work/queue-287/evidence/test-conductor.log +92ea8ce5e66d892fefacc76b87d140bcfb8c5a51dc77bdf4a47a6213ac5d2898 agents/steelbeak/work/queue-287/evidence/test-config.log +9b482b7a3f0672c872923c06e71d5a88ff085a08170905a6cabe1d2b6fc283b6 agents/steelbeak/work/queue-287/evidence/test-discord.log +87a89b628fcebbbb3e006105de2bc5b8b583aef007051ff824b846c7de8914f4 agents/steelbeak/work/queue-287/evidence/test-extension-package.log +b56e3912fc1657a859a4ced51859ad4083825d46d932b057868eae38b9668376 agents/steelbeak/work/queue-287/evidence/test-federation-acceptance.log +83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f agents/steelbeak/work/queue-287/evidence/test-foundation.log +3e2f8024027592e77790db2bbe039803060915ebf37c7b67f04071a7e83d20dc agents/steelbeak/work/queue-287/evidence/test-land-work-bash.log +81aec0f6e855251882f3e87ac7cfbc4bd23ea3a8bd2f44577a2200fa8ae549fa agents/steelbeak/work/queue-287/evidence/test-land-work.log +5d9a170308927264d212e3dd00e87463a21d55811cf9b4cb51dce74d6a10fea5 agents/steelbeak/work/queue-287/evidence/test-push-check-bash.log +b59c448667010e92c3d2ef46ea5cb341971e31f71393d385efc63e1bd4fe0f8e agents/steelbeak/work/queue-287/evidence/test-queue-bash.log +9f9dd11e2631a24ad55aaf7becc61965c592b00da08a4e72b98e0384d3a13ec7 agents/steelbeak/work/queue-287/evidence/test-release-bash.log +a065947f07d70a4721b423da597968630bc8ec1659db14681af42bb3f974077d agents/steelbeak/work/queue-287/evidence/test-task-bash.log +851bc620c887bebf5d4458ca5ba45cd6e789f31aa1a12433c9a595de77993064 agents/steelbeak/work/queue-287/evidence/token-audit.txt +43f468e4a7b10abc8072c49aeaad78e93c38147afb7b6dbe551efed266c5df39 agents/steelbeak/work/queue-287/evidence/touched-r1.log +bbb37165d7d6e4f1d831bcb6399c2f06466525ff4d0a44c75ff1d814f193567b agents/steelbeak/work/queue-287/evidence/touched-r2.log +e87f8c8de6e58539563253962076d7b578c81b00a0dea36d5f32ad35dfe584e0 agents/steelbeak/work/queue-287/evidence/touched-r3.log +759970574c60a95dccc637284414aa5947934a100cf38114fbdc5c5d155da7df agents/steelbeak/work/queue-287/mutants.py +7a83e90c912c3cf25a119f91a59fcb10e672ad4a3199fefc6b6612b1f7574a28 docs/SESSIONS.md +901895aef8a1d4f2343c4a0e4fb1159136e1bbb61f05a15d8fcde5a7093baa59 packages/bus/src/broker.mjs +27d7d5322c2f12641e350867d3ac6f838b33832951e69f057e3a7a28b8ac4d5b packages/bus/src/gitea-grant.mjs +e18e9f4e5e28057487bf184478a97a6688c77693533ba5e8833e23163ade5da5 packages/bus/tests/fake-gitea-api.mjs +5294a667971479e4e5585a6797d5cfbe7c32698d339f7fe8fad25ab895bdc6f5 packages/bus/tests/gitea-grant-docker.test.mjs +cf7b0b8e6ee6f6c1a73ea3527df88cb43a8c129770c053abfd8963a059cff6d5 packages/bus/tests/gitea-grant.test.mjs +1e87b315d0e6676069314f37d0d7a731d76331977d1aadb17429af91239554b6 packages/bus/tests/library-credential.test.mjs +75f77b19027576f0e26b30f1c905837b0d115c9a12fc2fda53c5dac1345c7df4 packages/business/src/business.mjs +81a3b1c7b7f468b7e5db41f3a7524415ea3ed7b25a2544c999aeedc46293d312 packages/business/tests/business.test.mjs +5bfa627eed95dc7bdf6e2fc460a90db9c1cadfd6453cd9daa148c9761acac0bf packages/cli/tests/case2-install.test.mjs +e0678c27969d74d955641ee23f49f45b6152ef564db1ca05d1d7cd24cac2112b scripts/case2-install.sh diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index ab56d24fb..19c4a2ffd 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -1673,3 +1673,6 @@ are never rewritten or removed; corrections are new entries. 2026-10-23 | Fethry (14f80e3c) | Row225 T1 lane A sage-3641/3647 (#1726) | Continued Morgana handoff, fixed nested status limits and wrong-project revert receipt after RED, kept invalid-mapping diagnosis visible with RED/GREEN. Renderer9/0, routes13/0, browser9/0, full library81/0, channel-proof2/0; isolated mutants14/14, restored SHA256 checks and clean worktree sources. Old-test approval mos1510 preserved80/66 and3/1. Slot returned with no processes around19:29Z. Dewey source-review packet prepared; full webui/build and integration gate remain pending. 2026-10-24 | Fethry (14f80e3c) | Row225 resumed, sage4119/4120 CLEAR (#1726) | Canonical owner start op fethry-225-start succeeded rev3946. Rebased7590cf633 onto963303403 on new fethry/row225-r2, preserving sharing/chatGrants and both append-only histories, no force push. Replaced our old dependency symlink with local root/webui npm ci. B1/N1 browser RED8/3 then GREEN11/0; full authorized gate in progress under TMPDIR /mnt/storage/scratch/tmp/rbCdM2, MOSAIC_REQUIRE_EDITOR_BUILD=1. No old-test changes beyond approved80/66 and3/1. 2026-10-24 | Fethry (14f80e3c) | Row225 round2 gate completed, sage4119 (#1726) | B1/N1 fixed after RED; local npm ci/editor build, webui442/0/library149/0/bus981/0/channel-proof2/0 with zero skip/cancel.16 unique clean mutant kills;1 cleanup-associated timeout retained/excluded and reported, unchanged retry passed. Original branch untouched; new fethry/row225-r2 sourcec010dfd85 over963303403, approved old-test deltas80/66 and3/1.13 pins reverified; final scratch Node/Chromium sweep zero. Replacement review packet prepared for Dewey; slot returning to Sage; no merge/deploy/live action. +- 2026-10-24 — steelbeak — row 287 C2-21 (#1792), steelbeak/row287 at 18334d713: recovered inherited design and current code; canonical start initially refused assign lock; tests held pending Sage CLEAR, implementation not yet started. +- 2026-10-24 — steelbeak — row 287 C2-21 (#1792): canonical start steelbeak-287-start succeeded at queue rev 3955; inherited design and sage-4125 recorded, local root/webui npm ci passed, test drafts prepared, production untouched and all tests held pending Sage CLEAR. +- 2026-10-24 — steelbeak — row 287 C2-21 (#1792), builder complete under sage-4130: collaborator library grants and chief real read mint built; bus 998/business 283/CLI 989+FED1/Docker2/install27/all12 shell gates green, M1-M11 killed/restored, token/cleanup audits clean; preparing steelbeak/row287 push and manifest handoff to Boyd then Dewey, no live change or landing. diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs index 8a2b0caf9..8733d5a49 100644 --- a/packages/bus/src/broker.mjs +++ b/packages/bus/src/broker.mjs @@ -2756,22 +2756,24 @@ export class Broker { : `\nThis makes a new key for ${g.agent} on ${g.host}.`); return { body, block }; } - // Row 236 (#1742, #436b r5 section 2): the agent's role and definition, if the agent is a role of the - // business whose definition declares gitea and which has identity and a publish grant (199 D8). A - // creative role gets no grant, whatever its definition declares (mos-1163). Row 285 (#1790, sage-2921): - // nor does the chief of staff, whose definition declares gitea for a read grant only, since this path - // mints SEAT_SCOPES, write:repository included. - #giteaRole(business, agent) { + // Row 236: identity, publish and a Gitea declaration are required. mos-1163 keeps creative + // roles out of non-library grants and every host grant. mos-1320 item 2 and sage-4125 allow + // creative/chief targets only when all are library projects. sage-2921 refused the chief because + // SEAT_SCOPES included write:repository; library grants now use its definition's read scopes. + #giteaRole(business, agent, projects) { const b = this.#business(business); if (!Object.hasOwn(b.roles, agent)) fail('gitea-not-declared'); const role = b.roles[agent]; - if (CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) fail('gitea-not-declared'); + const libraryOnly = projects.every((p) => b.projects?.[p]?.kind === 'library'); + if ((CREATIVE_DEFINITIONS.includes(role.definition) || role.definition === STAFF_TO) && !libraryOnly) fail('gitea-not-declared'); const definition = Object.hasOwn(b.definitions ?? {}, role.definition) ? b.definitions[role.definition] : null; if ( !definition?.credentials?.some((c) => c.service === 'gitea') || !role.identity?.gitea || !Array.isArray(role.publish) || - role.publish.length === 0 + role.publish.length === 0 || + (projects.some((p) => b.projects?.[p]?.kind === 'library') && + !definition.credentials.some((c) => c.service === 'gitea' && Array.isArray(c.scopes) && c.scopes.length > 0)) ) fail('gitea-not-declared'); return { b, role, definition }; @@ -2784,6 +2786,8 @@ export class Broker { if (!role.publish.includes(project) || !upstream) fail('remote-not-granted'); const problem = forkProblem(upstream, role.identity); if (problem) fail(problem); + if (b.projects[project].kind === 'library') + return { project, upstream, library: true, repository: upstream, level: CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read' }; return { project, upstream, fork: forkUrl(upstream, role.identity.gitea.user) }; }); } @@ -2797,7 +2801,7 @@ export class Broker { // Section 2: the store checks and the raise binding. The caller owns the transaction. The chief // can't shape the block: every line comes from the business, the slot and the store. #giteaGrantBinding(business, g) { - const { b, role, definition } = this.#giteaRole(business, g.agent); + const { b, role, definition } = this.#giteaRole(business, g.agent, g.target.projects); this.#grantableAgent(business, g.agent); const targets = this.#giteaTargets(b, role, g.target.projects); const meta = this.#giteaAdminMeta(business, role.identity.gitea.url); @@ -2815,23 +2819,31 @@ export class Broker { identitySha256: this.#giteaGrant.identitySha256(role, b.projects), adminSha256: this.#giteaGrant.adminSha256(meta), }; - const others = role.publish.filter((p) => !g.target.projects.includes(p)).map((p) => b.projects?.[p]?.remote ?? p); + const others = role.publish.filter((p) => !g.target.projects.includes(p)); + const forkOthers = others.filter((p) => b.projects?.[p]?.kind !== 'library').map((p) => b.projects?.[p]?.remote ?? p); + const libraryOthers = others.filter((p) => b.projects?.[p]?.kind === 'library'); + const forks = targets.filter((t) => !t.library), repositories = targets.filter((t) => t.library); + const level = CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read'; + const scopes = repositories.length ? definition.credentials.find((c) => c.service === 'gitea').scopes : ['read:user', 'write:repository']; const { gitea, email } = role.identity; const block = `\n\nGrant ${g.agent} a Gitea token: user ${gitea.user}, email ${email}, url ${gitea.url}.` + - `\nForks:` + - targets.map((t) => `\n${t.fork}`).join('') + - `\nScopes: ${['read:user', 'write:repository'].join(', ')}.` + + (forks.length ? `\nForks:` + forks.map((t) => `\n${t.fork}`).join('') : '') + + repositories.map((t) => `\nRepository: ${t.repository}, level ${t.level}.` + (t.level === 'write' + ? `\nThe token can write every path of this repository. Only the bus's path check limits it to supporting folders.` + : `\nThe token can read every path of this repository. It can't push to it, because the user is a read collaborator.`)).join('') + + `\nScopes: ${scopes.join(', ')}.` + `\nRotate by ${g.rotateBy}.` + `\nKind: ${kind}.` + - `\nThe token can write every repository this user can write. Step 5 checks that this is only its forks.` + - (others.length ? `\nIt may also reach this user's existing forks of:` + others.map((u) => `\n${u}`).join('') : ''); + `\nThe token can write every repository this user can write. Step 5 checks that this is only its forks${repositories.length ? ' and the repositories above at their level' : ''}.` + + (forkOthers.length ? `\nIt may also reach this user's existing forks of:` + forkOthers.map((u) => `\n${u}`).join('') : '') + + (libraryOthers.length ? `\nIt may also reach these repositories as a collaborator: ` + libraryOthers.map((p) => `${b.projects[p].remote} (${level})`).join(', ') : ''); return { body, block }; } // Section 4 step 1's checks after the spend. Throws the code that ends the grant. #giteaRecheck(business, decision, g) { this.#scopeRecheck(business, decision, g); - const { b, role, definition } = this.#giteaRole(business, g.agent); + const { b, role, definition } = this.#giteaRole(business, g.agent, g.target.projects); if (this.#giteaGrant.definitionSha256(definition) !== g.definitionSha256) fail('decision-mismatch'); if (this.#giteaGrant.identitySha256(role, b.projects) !== g.identitySha256) fail('decision-mismatch'); const meta = this.#giteaAdminMeta(business, role.identity.gitea.url); @@ -3061,7 +3073,10 @@ export class Broker { tokenName = this.#giteaGrant.tokenName(business, agent, decision); step('mint', { tokenName }); minting = true; - const minted = await api.tokenCreate(user, tokenName); + const scopes = targets.some((t) => t.library) + ? b.definitions[role.definition].credentials.find((c) => c.service === 'gitea').scopes + : undefined; + const minted = await api.tokenCreate(user, tokenName, scopes); if (minted.status !== 201 || !giteaId(minted.json?.id) || typeof minted.json?.sha1 !== 'string') fail('gitea-failed'); tokenId = minted.json.id; const token = this.#giteaGrant.checkToken(minted.json.sha1); @@ -3069,9 +3084,16 @@ export class Broker { stop(); // Step 4. const seat = this.#giteaGrant.seat(meta.url, token, log); - const forks = []; + const forks = [], repositories = []; for (const t of targets) { const { owner, repo } = parseGiteaUrl(t.upstream, { repo: true }); + if (t.library) { + const added = await api.collaboratorAdd(owner, repo, user, t.level); + stop(); + if (added.status !== 204) fail('gitea-failed'); + repositories.push({ project: t.project, repository: t.repository, level: t.level }); + continue; + } const f = await seat.forkGet(user, repo); stop(); if (f.status === 200) { @@ -3088,10 +3110,14 @@ export class Broker { stop(); if (me.status !== 200) fail('gitea-reach-unknown'); if (lower(me.json?.login) !== lower(user) || me.json?.id !== userId) fail('gitea-user-foreign'); - const upstreams = new Set(); + const upstreams = new Set(), libraryReach = new Map(); for (const p of role.publish) { const u = parseGiteaUrl(b.projects?.[p]?.remote, { repo: true }); - if (u.url) upstreams.add(lower(`${u.owner}/${u.repo}`)); + if (u.url) { + const name = lower(`${u.owner}/${u.repo}`); + if (b.projects[p].kind === 'library') libraryReach.set(name, CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read'); + else upstreams.add(name); + } } let seen = 0, total = null, @@ -3105,8 +3131,11 @@ export class Broker { seen += r.json.length; for (const repo of r.json) { const parent = typeof repo?.parent?.full_name === 'string' ? lower(repo.parent.full_name) : null; - if (wider === null && (lower(repo?.owner?.login) !== lower(user) || !parent || !upstreams.has(parent))) - wider = typeof repo?.full_name === 'string' ? repo.full_name : 'unknown'; + const level = libraryReach.get(lower(repo?.full_name)); + const allowed = level + ? repo.permissions?.admin === false && repo.permissions?.push === (level === 'write') && repo.permissions?.pull === true + : lower(repo?.owner?.login) === lower(user) && parent && upstreams.has(parent); + if (wider === null && !allowed) wider = typeof repo?.full_name === 'string' ? repo.full_name : 'unknown'; } if (r.json.length < this.#giteaGrant.repoPage) break; } @@ -3122,11 +3151,13 @@ export class Broker { step('commit', { previousTokenId: previous?.tokenId ?? null, tokenId, tokenName, previous }); stop(); const applied = (extra = {}) => { - this.#grantEvent(business, { action: 'grant.applied', method: 'gitea', grantDecision: decision, agent, user, userId, tokenId, forks, ...extra }, agent); + this.#grantEvent(business, { action: 'grant.applied', method: 'gitea', grantDecision: decision, agent, user, userId, tokenId, forks, ...(repositories.length ? { repositories } : {}), ...extra }, agent); this.#grantNotice( business, decision, - `${agent} has a Gitea token as ${user} on ${meta.url} (decision ${decision}), forks: ${forks.join(', ')}.` + + `${agent} has a Gitea token as ${user} on ${meta.url} (decision ${decision})` + + (forks.length ? `, forks: ${forks.join(', ')}` : '') + + (repositories.length ? `, repositories: ${repositories.map((r) => `${r.repository} (${r.level})`).join(', ')}` : '') + '.' + (extra.serviceLeft?.length ? ` The previous token ${extra.serviceLeft.join(', ')} is still live. Run: mosaic grants sweep gitea` : ''), ); }; diff --git a/packages/bus/src/gitea-grant.mjs b/packages/bus/src/gitea-grant.mjs index 5ddae7167..07c3c3126 100644 --- a/packages/bus/src/gitea-grant.mjs +++ b/packages/bus/src/gitea-grant.mjs @@ -18,7 +18,7 @@ import { BusError, GRANT_EVIDENCE } from './broker.mjs'; import { day, privateDir } from './credentials.mjs'; import { definitionSha256, grantTag } from './host-grant.mjs'; import { canonicalJson } from '../../business/src/util.mjs'; -import { forkUrl, giteaName, ID_PATTERN, parseGiteaUrl } from '../../business/src/vocabulary.mjs'; +import { forkUrl, giteaName, ID_PATTERN, parseGiteaUrl, CREATIVE_DEFINITIONS } from '../../business/src/vocabulary.mjs'; const deny = (code) => { throw new BusError(code); @@ -72,12 +72,17 @@ export function giteaGrantCheck(grant, today) { // Section 2: over the agent's identity, its publish list and the remote of every project in it, since // step 5 checks against all of them. `projects` is the business's project map. -export const identitySha256 = (role, projects) => - sha256({ +export const identitySha256 = (role, projects) => { + const library = Object.fromEntries((role.publish ?? []).filter((p) => projects?.[p]?.kind === 'library') + .map((p) => [p, CREATIVE_DEFINITIONS.includes(role.definition) ? 'write' : 'read'])); + return sha256({ identity: role.identity ?? null, publish: role.publish ?? [], remotes: Object.fromEntries((role.publish ?? []).map((p) => [p, projects?.[p]?.remote ?? null])), + // C2-21: bind kind and level, preserving every legacy non-library binding byte for byte. + ...(Object.keys(library).length ? { library } : {}), }); +}; // Section 2: over the admin slot's metadata, never the token. export const adminSha256 = (meta) => sha256({ url: meta.url, account: meta.account, setAt: meta.setAt }); // Step 3: `mosaic--`. grantTag already starts with "mosaic-". @@ -263,6 +268,8 @@ export function giteaGrant({ configDir, ca = undefined, timeoutMs = CALL_MS, tra tokenCreate: (user, name, scopes = SEAT_SCOPES, op = 'token-create') => go(op, 'POST', `/api/v1/users/${segment(user)}/tokens`, basic, { name, scopes: [...scopes] }), tokenDelete: (user, idOrName) => go('token-delete', 'DELETE', `/api/v1/users/${segment(user)}/tokens/${segment(idOrName)}`, basic), + collaboratorAdd: (owner, repo, user, permission) => + go('collaborator-add', 'PUT', `/api/v1/repos/${segment(owner)}/${segment(repo)}/collaborators/${segment(user)}`, { token }, { permission }), }; }; return { diff --git a/packages/bus/tests/fake-gitea-api.mjs b/packages/bus/tests/fake-gitea-api.mjs index 03addb397..87d739905 100644 --- a/packages/bus/tests/fake-gitea-api.mjs +++ b/packages/bus/tests/fake-gitea-api.mjs @@ -22,9 +22,10 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic const state = { version }; const lower = (x) => String(x).toLowerCase(); const userJson = (u) => ({ id: u.id, login: u.login, email: u.email, full_name: u.full_name, is_admin: u.is_admin, prohibit_login: u.prohibit_login }); - const repoJson = (r) => ({ + const repoJson = (r, user = null) => ({ id: r.id, name: r.name, full_name: `${r.owner}/${r.name}`, owner: { login: r.owner }, fork: Boolean(r.parent), parent: r.parent ? { full_name: r.parent } : null, + permissions: { admin: lower(user) === lower(r.owner), push: lower(user) === lower(r.owner) || (r.permissions[lower(user)] ?? 'write') === 'write', pull: true }, }); const api = { url: null, @@ -52,7 +53,7 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic users.delete(lower(login)); }, addRepo(owner, name, { parent = null, collaborators = [] } = {}) { - const r = { id: nextId++, owner, name, parent, collaborators: collaborators.map(lower) }; + const r = { id: nextId++, owner, name, parent, collaborators: collaborators.map(lower), permissions: {} }; repos.push(r); return r; }, @@ -100,7 +101,7 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic const all = reachable(me); const limit = Number(query.get('limit') ?? 30); const page = Number(query.get('page') ?? 1); - return [200, all.slice((page - 1) * limit, page * limit).map(repoJson), all.length]; + return [200, all.slice((page - 1) * limit, page * limit).map((r) => repoJson(r, me.login)), all.length]; } if (p[0] === 'users' && p.length === 2 && method === 'GET') { const u = users.get(lower(p[1])); @@ -144,6 +145,15 @@ export async function fakeGiteaApi(t, dir, { version = '1.27.1', admin = 'mosaic return [204, null]; } } + if (p[0] === 'repos' && p.length === 5 && p[3] === 'collaborators' && method === 'PUT') { + if (auth.kind !== 'token' || !me.is_admin || !who.token.scopes.includes('write:repository')) return [403, { message: 'forbidden' }]; + const r = repos.find((x) => lower(x.owner) === lower(p[1]) && lower(x.name) === lower(p[2])); + if (!r || !users.has(lower(p[4]))) return [404, { message: 'not found' }]; + if (!['read', 'write'].includes(body.permission)) return [422, { message: 'invalid permission' }]; + if (!r.collaborators.includes(lower(p[4]))) r.collaborators.push(lower(p[4])); + r.permissions[lower(p[4])] = body.permission; + return [204, null]; + } if (p[0] === 'repos' && p.length === 3 && method === 'GET') { const r = repos.find((x) => lower(x.owner) === lower(p[1]) && lower(x.name) === lower(p[2])); return r ? [200, repoJson(r)] : [404, { message: 'not found' }]; diff --git a/packages/bus/tests/gitea-grant-docker.test.mjs b/packages/bus/tests/gitea-grant-docker.test.mjs index a31206a2e..622be9c30 100644 --- a/packages/bus/tests/gitea-grant-docker.test.mjs +++ b/packages/bus/tests/gitea-grant-docker.test.mjs @@ -6,11 +6,12 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { randomBytes } from 'node:crypto'; -import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs'; +import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'; import { request } from 'node:https'; import { createServer } from 'node:net'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; import { Broker, GRANT_EVIDENCE } from '../src/broker.mjs'; import { Credentials } from '../src/credentials.mjs'; @@ -58,6 +59,130 @@ function call(url, ca, method, path, { auth = null, body = null } = {}) { }); } +// C2-21: independent of the executor. F-A and F-B gate production changes; F-C/F-D are observations. +test('C2-21 F-A/F-B: admin collaborator grant on another owner and seat permission reach; F-C/F-D protection observations', { skip, timeout: 300000 }, async (t) => { + // Capture the child's complete TAP stdout/stderr before any of it reaches this runner. The private + // scratch file lets the parent search actual minted values without putting one in an assertion. + if (!process.env.C2_GRANT_FACT_CAPTURE) { + const capture = mkdtempSync(join(tmpdir(), 'g287-capture-')); + t.after(() => rmSync(capture, { recursive: true, force: true })); + const file = join(capture, 'minted'); + const childEnv = { ...process.env, C2_GRANT_FACT_CAPTURE: file }; + delete childEnv.NODE_TEST_CONTEXT; + const child = spawnSync(process.execPath, ['--test', '--test-name-pattern=^C2-21 F-A/F-B:', fileURLToPath(import.meta.url)], { + encoding: 'utf8', timeout: 280000, env: childEnv, + }); + const values = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : []; + const output = (child.stdout ?? '') + (child.stderr ?? ''); + assert.ok(values.every((value) => !output.includes(value)), 'complete TAP stdout/stderr contain no minted token'); + t.diagnostic(output); + t.diagnostic(`minted ${values.length}, hits 0 (complete TAP stdout/stderr)`); + assert.equal(child.status, 0, 'Docker facts child passed'); + assert.equal(values.length, 4, 'all four scratch tokens were minted: facts actually ran'); + return; + } + const dir = mkdtempSync(join(tmpdir(), 'g287-facts-')); + t.after(() => rmSync(dir, { recursive: true, force: true })); + const tlsDir = join(dir, 'tls'), tls = scratchCa(tlsDir); + chmodSync(tlsDir, 0o755); + chmodSync(join(tlsDir, 'server.key'), 0o644); + const ca = readFileSync(tls.ca, 'utf8'), port = await freePort(); + const url = `https://127.0.0.1:${port}`, name = 'g287-' + randomBytes(6).toString('hex'); + t.after(() => docker('rm', '-f', name)); + const started = docker('run', '-d', '--rm', '--name', name, '-p', `127.0.0.1:${port}:3000`, '-v', `${tlsDir}:/certs:ro`, + '-e', 'GITEA__security__INSTALL_LOCK=true', '-e', 'GITEA__database__DB_TYPE=sqlite3', + '-e', 'GITEA__service__DISABLE_REGISTRATION=true', '-e', 'GITEA__server__PROTOCOL=https', + '-e', 'GITEA__server__CERT_FILE=/certs/server.pem', '-e', 'GITEA__server__KEY_FILE=/certs/server.key', + '-e', `GITEA__server__ROOT_URL=${url}/`, IMAGE); + assert.equal(started.status, 0, 'scratch container starts'); + const end = Date.now() + 120000; + while ((await call(url, ca, 'GET', '/api/v1/version')).status !== 200) { + assert.ok(Date.now() < end, 'scratch Gitea answers in 120 seconds'); + await sleep(500); + } + const gitea = (...args) => docker('exec', '-u', 'git', name, 'gitea', ...args); + const secrets = [], diagnostics = []; + const say = (line) => { diagnostics.push(line); t.diagnostic(line); }; + for (const [user, admin] of [['mosaic-grant', true], ['writer', false], ['creative', false], ['chief', false]]) { + const r = gitea('admin', 'user', 'create', '--username', user, '--email', `${user}@example.test`, + '--password', randomBytes(16).toString('base64url'), '--must-change-password=false', ...(admin ? ['--admin'] : [])); + assert.equal(r.status, 0, `create scratch ${user}`); + } + const mint = (user, scopes) => { + const r = gitea('admin', 'user', 'generate-access-token', '--username', user, '--token-name', 'facts', '--scopes', scopes.join(','), '--raw'); + assert.equal(r.status, 0, `mint scratch ${user}`); + const value = r.stdout.trim(); + assert.ok(/^[0-9a-f]{40}$/.test(value), 'minted token shape'); + secrets.push(value); + writeFileSync(process.env.C2_GRANT_FACT_CAPTURE, JSON.stringify(secrets), { mode: 0o600 }); + return value; + }; + const adminToken = mint('mosaic-grant', ['write:admin', 'write:repository', 'write:user']); + const writerToken = mint('writer', ['read:user', 'write:repository']); + const creativeToken = mint('creative', ['read:user', 'write:repository']); + const chiefToken = mint('chief', ['read:repository', 'read:user']); + const made = await call(url, ca, 'POST', '/api/v1/admin/users/writer/repos', { + auth: 'token ' + adminToken, body: { name: 'books', auto_init: true, private: true }, + }); + assert.equal(made.status, 201, 'create repository owned by writer, not admin'); + for (const [user, level, value, push] of [['creative', 'write', creativeToken, true], ['chief', 'read', chiefToken, false]]) { + const added = await call(url, ca, 'PUT', `/api/v1/repos/writer/books/collaborators/${user}`, { + auth: 'token ' + adminToken, body: { permission: level }, + }); + say(`F-A ${user} ${level}: PUT status ${added.status}`); + assert.equal(added.status, 204, 'F-A admin scopes can add collaborator to another owner'); + const listed = await call(url, ca, 'GET', '/api/v1/user/repos', { auth: 'token ' + value }); + assert.equal(listed.status, 200, 'F-B seat lists reachable repos'); + const repository = listed.json?.find((r) => r.full_name === 'writer/books'); + assert.ok(repository, 'F-B seat lists the granted repository'); + say(`F-B ${user}: permissions ${JSON.stringify(repository.permissions)}`); + assert.deepEqual({ admin: repository.permissions?.admin, push: repository.permissions?.push, pull: repository.permissions?.pull }, + { admin: false, push, pull: true }, 'F-B exact admin/push/pull keys'); + } + + // No diagnostic includes a Git child result: HTTP auth is supplied only in that child's env. + const git = (args, user = 'writer', value = writerToken, cwd = dir) => spawnSync('git', args, { + cwd, encoding: 'utf8', timeout: 30000, + env: { ...process.env, GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null', GIT_TERMINAL_PROMPT: '0', + GIT_SSL_CAINFO: tls.ca, GIT_CONFIG_COUNT: '3', GIT_CONFIG_KEY_0: 'http.extraHeader', + GIT_CONFIG_VALUE_0: 'Authorization: Basic ' + Buffer.from(`${user}:${value}`).toString('base64'), + GIT_CONFIG_KEY_1: 'user.name', GIT_CONFIG_VALUE_1: 'Scratch writer', + GIT_CONFIG_KEY_2: 'user.email', GIT_CONFIG_VALUE_2: 'writer@example.test' }, + }); + const work = join(dir, 'work'); + const branch = made.json.default_branch; + const clone = git(['clone', `${url}/writer/books.git`, work]); + const protection = await call(url, ca, 'POST', '/api/v1/repos/writer/books/branch_protections', { + auth: 'token ' + writerToken, + body: { branch_name: branch, rule_name: branch, enable_push: true, enable_push_whitelist: false, + enable_force_push: false, protected_file_patterns: 'Story/**' }, + }); + if (clone.status !== 0 || protection.status !== 201) { + say(`F-C/F-D not established: clone rc ${clone.status}, protection HTTP ${protection.status}`); + } else { + const commit = (path, text) => { + mkdirSync(join(work, path.split('/')[0]), { recursive: true }); + writeFileSync(join(work, path), text); + const add = git(['add', path], 'writer', writerToken, work); + const made = git(['commit', '-m', 'scratch fact'], 'writer', writerToken, work); + return add.status === 0 && made.status === 0; + }; + const story = commit('Story/chapter.md', 'scratch story\n'); + const seatStory = story ? git(['push', 'origin', `HEAD:${branch}`], 'creative', creativeToken, work).status : null; + const ownerStory = story ? git(['push', 'origin', `HEAD:${branch}`], 'writer', writerToken, work).status : null; + say(`F-C protected Story/**: seat rc ${seatStory}, owner rc ${ownerStory}; established ${seatStory !== null && seatStory !== 0 && ownerStory === 0}`); + // Start from the actual remote tip even when the file-pattern observation failed. + const fetched = git(['fetch', 'origin', branch], 'writer', writerToken, work); + const reset = git(['reset', '--hard', 'FETCH_HEAD'], 'writer', writerToken, work); + const supporting = fetched.status === 0 && reset.status === 0 && commit('Notes/note.md', 'scratch supporting note\n'); + const normal = supporting ? git(['push', 'origin', `HEAD:${branch}`], 'creative', creativeToken, work).status : null; + const force = normal === 0 ? git(['push', '--force', 'origin', `HEAD~1:${branch}`], 'creative', creativeToken, work).status : null; + say(`F-D protected branch: normal rc ${normal}, force rc ${force}; established ${normal === 0 && force !== null && force !== 0}`); + } + assert.ok(secrets.every((value) => diagnostics.every((line) => !line.includes(value))), 'diagnostics contain no minted token'); + say(`minted ${secrets.length}, hits 0`); +}); + test('X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reach, writes the file, and revoke deletes the token', { skip, timeout: 300000 }, async (t) => { const dir = mkdtempSync(join(tmpdir(), 'g236-docker-')); t.after(() => rmSync(dir, { recursive: true, force: true })); @@ -69,7 +194,7 @@ test('X-G3: on scratch 1.27.1, a grant makes the user, mints, forks, checks reac const ca = readFileSync(tls.ca, 'utf8'); const port = await freePort(); const url = `https://127.0.0.1:${port}`; - const name = 'g236-' + randomBytes(6).toString('hex'); + const name = 'g287-executor-' + randomBytes(6).toString('hex'); t.after(() => docker('rm', '-f', name)); const started = docker('run', '-d', '--rm', '--name', name, '-p', `127.0.0.1:${port}:3000`, '-v', `${tlsDir}:/certs:ro`, '-e', 'GITEA__security__INSTALL_LOCK=true', diff --git a/packages/bus/tests/gitea-grant.test.mjs b/packages/bus/tests/gitea-grant.test.mjs index 4934a3c4e..4291a7add 100644 --- a/packages/bus/tests/gitea-grant.test.mjs +++ b/packages/bus/tests/gitea-grant.test.mjs @@ -1306,3 +1306,158 @@ test('status lists the slot, users, grants and nothing secret, and every grants await rejects(bare.request(human, { verb: 'grants.status', args: { service: 'gitea' } }), 'gitea-not-configured'); f.noLeak(); }); + +// C2-21: these catch library forking, wrong collaborator/token level, widened reach, and stale bindings. +function libraryBusiness(b, { agent = 'coder', definition = 'research', publish = ['app'], library = ['app'] } = {}) { + const original = b.roles.coder; + b.roles[agent] = { ...original, definition, publish, + identity: { ...original.identity, gitea: { ...original.identity.gitea, user: agent === 'chief' ? 'demo-chief' : USER } } }; + b.definitions[definition] = { roleVersion: 2, credentials: [{ service: 'gitea', + scopes: definition === 'chief-of-staff' ? ['read:repository', 'read:user'] : ['read:user', 'write:repository'] }] }; + for (const project of library) b.projects[project].kind = 'library'; +} + +for (const [definition, agent, level, scopes] of [ + ['research', 'coder', 'write', ['read:user', 'write:repository']], + ['chief-of-staff', 'chief', 'read', ['read:repository', 'read:user']], + ['coder', 'coder', 'read', ['read:user', 'write:repository']], +]) { + test(`C2-21 L1/L2/L9: ${definition} gets ${level} on her repository with declared scopes through raise and yes`, async (t) => { + const f = await setup(t, { business: (b) => { + libraryBusiness(b, { agent, definition }); + b.launch = { by: 'chief', instances: ['coder', 'pm'] }; + } }); + const user = agent === 'chief' ? 'demo-chief' : USER; + const puts = []; + f.gitea.rule((e) => e.method === 'PUT' && e.path.includes('/collaborators/'), (e, body) => { + puts.push({ path: e.path, as: e.as, auth: e.auth, body }); + return { pass: true }; + }); + const d = await f.raise({ agent }); + const block = f.store.get('SELECT question FROM decisions WHERE id=?', d.id).question; + assert.ok(block.includes(`Repository: ${f.gitea.url}/acme/app, level ${level}.`)); + assert.doesNotMatch(block, /\nForks:/); + assert.ok(block.includes(`Scopes: ${scopes.join(', ')}.`)); + assert.ok(block.includes(level === 'write' + ? "The token can write every path of this repository. Only the bus's path check limits it to supporting folders." + : "The token can read every path of this repository. It can't push to it, because the user is a read collaborator.")); + f.call('human', 'decision.resolve', { id: d.id, choice: 'yes' }); + await until(() => f.ended(d.id)); + await f.broker.giteaIdle(); + const end = f.ended(d.id); + assert.equal(end.action, 'grant.applied', JSON.stringify(end)); + assert.deepEqual(end.forks, []); + assert.deepEqual(end.repositories, [{ project: 'app', repository: `${f.gitea.url}/acme/app`, level }]); + assert.deepEqual(puts, [{ path: `/api/v1/repos/acme/app/collaborators/${user}`, as: 'mosaic-grant', auth: 'token', body: { permission: level } }]); + assert.equal(f.gitea.calls().filter((c) => c.endsWith('/forks')).length, 0); + assert.deepEqual(f.gitea.tokens(user)[0].scopes, scopes); + const messages = f.messages(d.id).map((m) => m.body).join('\n'); + assert.ok(messages.includes(`repositories: ${f.gitea.url}/acme/app (${level})`)); + assert.doesNotMatch(messages, /forks: \./); + const results = f.grantEvents(); + assert.ok(results.some((e) => e.action === 'grant.call' && e.op === 'collaborator-add')); + f.noLeak(); + // L5: token revocation leaves the collaborator, just as legacy forks remain. + await f.call('human', 'grants.revoke', { service: 'gitea', agent, reason: 'C2-21' }); + await f.broker.giteaIdle(); + assert.deepEqual(f.gitea.tokens(user), []); + assert.ok(f.gitea.repos.find((r) => r.owner === 'acme' && r.name === 'app').collaborators.includes(user)); + }); +} + +for (const [agent, definition] of [['coder', 'research'], ['chief', 'chief-of-staff']]) { + for (const projects of [['lib'], ['app', 'lib']]) { + test(`C2-21 L7/L7b: ${definition} refuses non-library targets ${projects.join(',')}`, async (t) => { + const f = await setup(t, { business: (b) => libraryBusiness(b, { agent, definition, publish: ['app', 'lib'] }) }); + await rejects(f.raise({ agent, target: { projects } }), 'gitea-not-declared'); + assert.equal(f.store.get('SELECT count(*) AS n FROM decisions').n, 0); + assert.equal(f.gitea.requests.length, 0); + }); + } +} + +for (const [name, permissions, fork] of [ + ['read widened to write', { admin: false, push: true, pull: true }, false], + ['admin privilege', { admin: true, push: false, pull: true }, false], + ['missing pull', { admin: false, push: false, pull: false }, false], + ['missing permission object', null, false], + ['library fork', { admin: false, push: false, pull: true }, true], +]) { + test(`C2-21 L4: ${name} refuses gitea-reach-wider and deletes the minted token`, async (t) => { + const f = await setup(t, { business: (b) => libraryBusiness(b, { agent: 'chief', definition: 'chief-of-staff' }) }); + const repository = fork ? 'demo-chief/app' : 'acme/app'; + f.gitea.rule((e) => e.as === 'demo-chief' && e.path.startsWith('/api/v1/user/repos?'), { status: 200, total: 1, json: [{ + full_name: repository, owner: { login: fork ? 'demo-chief' : 'acme' }, + parent: fork ? { full_name: 'acme/app' } : null, permissions, + }] }); + const { end } = await f.approve({ agent: 'chief' }); + assert.equal(end.code, 'gitea-reach-wider', JSON.stringify(end)); + assert.equal(end.repository, repository); + assert.deepEqual(f.gitea.tokens('demo-chief'), []); + assert.equal(f.store.get("SELECT 1 FROM gitea_grants WHERE agent='chief'"), undefined); + f.noLeak(); + }); +} + +test('C2-21 L8: untargeted library collaborator reach is disclosed and accepted publish-wide', async (t) => { + const f = await setup(t, { business: (b) => libraryBusiness(b, { publish: ['app', 'lib'], library: ['app', 'lib'] }) }); + f.gitea.repos.find((r) => r.owner === 'acme' && r.name === 'lib').collaborators.push(USER); + const d = await f.raise(); + const block = f.store.get('SELECT question FROM decisions WHERE id=?', d.id).question; + assert.ok(block.includes(`It may also reach these repositories as a collaborator: ${f.gitea.url}/acme/lib (write)`)); + assert.ok(block.includes('Step 5 checks that this is only its forks and the repositories above at their level.')); + assert.doesNotMatch(block, /existing forks of:/); + f.call('human', 'decision.resolve', { id: d.id, choice: 'yes' }); + await until(() => f.ended(d.id)); + await f.broker.giteaIdle(); + assert.equal(f.ended(d.id).action, 'grant.applied'); +}); + +test('C2-21 L10: project kind or library level changing after raise refuses decision-mismatch', async (t) => { + for (const change of ['kind', 'level']) { + const f = await setup(t, { business: (b) => { + b.definitions.coder.credentials = [{ service: 'gitea', scopes: ['read:user', 'write:repository'] }]; + if (change === 'level') libraryBusiness(b); + } }); + const d = await f.raise(); + const next = structuredClone(f.business); + if (change === 'kind') next.projects.app.kind = 'library'; + else { + next.definitions['chief-of-staff'] = structuredClone(next.definitions.research); + next.roles.coder.definition = 'chief-of-staff'; + } + f.broker.reloadBusiness('demo', next); + f.call('human', 'decision.resolve', { id: d.id, choice: 'yes' }); + await until(() => f.ended(d.id)); + await f.broker.giteaIdle(); + assert.equal(f.ended(d.id).code, 'decision-mismatch'); + assert.deepEqual(f.gitea.calls(), ['GET /api/v1/version', 'GET /api/v1/user', 'GET /api/v1/token', 'GET /api/v1/user/repos']); + assert.ok(f.gitea.requests.every((r) => r.method === 'GET' && !/\/admin\/users|\/tokens|\/collaborators/.test(r.path))); + assert.equal(existsSync(f.tokenFile), false); + assert.equal(f.record(), undefined); + } +}); + +test('C2-21 L4: creative write requires push true but ignores unrelated permission keys', async (t) => { + for (const push of [false, true]) { + const f = await setup(t, { business: (b) => libraryBusiness(b) }); + f.gitea.rule((e) => e.as === USER && e.path.startsWith('/api/v1/user/repos?'), { status: 200, total: 1, json: [{ + full_name: 'acme/app', owner: { login: 'acme' }, parent: null, + permissions: { admin: false, push, pull: true, issues: true }, + }] }); + const { end } = await f.approve(); + assert.equal(end.action, push ? 'grant.applied' : 'grant.failed'); + if (!push) assert.equal(end.code, 'gitea-reach-wider'); + } +}); + +// mos-1590: the L10 fixture must not hide the no-declared-scopes refusal. +test('C2-21: a library grant with no declared scopes refuses gitea-not-declared at raise', async (t) => { + const f = await setup(t, { business: (b) => { + libraryBusiness(b); + delete b.definitions.research.credentials[0].scopes; + } }); + await rejects(f.raise(), 'gitea-not-declared'); + assert.equal(f.store.get('SELECT count(*) AS n FROM decisions').n, 0); + assert.equal(f.gitea.requests.length, 0); +}); diff --git a/packages/bus/tests/library-credential.test.mjs b/packages/bus/tests/library-credential.test.mjs index 0a0aea2bb..23ba4aae1 100644 --- a/packages/bus/tests/library-credential.test.mjs +++ b/packages/bus/tests/library-credential.test.mjs @@ -1,8 +1,7 @@ // C2-03b (#1791, design 7.2): the credential the library fetch runs with. The chief's grant store file // once a chief has Gitea grant evidence, slot library-gitea before that, and a refusal otherwise. -// sage-4100 corrects sage-2947/dewey-0864 for the mint only: before C2-21, the real write-grant -// path must refuse the chief. T7/T9 assert that refusal, then seed a read-only fake grant. C2-21's -// real chief read executor replaces that seed when it lands. Revocation and fire use the real verbs. +// C2-21 replaces sage-4100's read fixture seed: T7/T9 raise the real chief library grant, the human +// resolves yes and the executor mints it. Revocation and fire keep using the same real verbs. // The choice function's other checks (listed, a fire under way, held, two chiefs, another project) // use fixtures: the evidence and the gitea_grants row go in by SQL, as gitea-grant.test.mjs does for // decisions. Tokens are made up or the fake's, and only ever compared. @@ -75,23 +74,23 @@ function world(t, options) { // The business for the real paths. `boss` is launch.by: instance.fire is the launch.by role's verb and // refuses its own name (organization-instance-referenced), so the fired chief is the one listed -// chief-of-staff instance, fired by boss. The grant targets project app; books is the library. +// chief-of-staff instance, fired by boss. The real read grant targets the library project books. function verbDoc(url, root) { return { id: 'acme', human: 'jason', arbiters: { technical: 'boss', delivery: 'boss' }, roles: { boss: { definition: 'pm', authority: chiefAuthority }, // sage-4097 / mos-1584: fixture projection carries the chief's own Gitea store reference. - chief: { definition: 'chief-of-staff', ...none, credentials: { gitea: giteaReference(join(root, 'secrets', 'agents'), 'acme', 'chief', '2099-01-01') }, identity: { name: 'Chief', email: 'chief@example.test', gitea: { url, user: 'acme-chief' } }, publish: ['app'] }, + chief: { definition: 'chief-of-staff', ...none, credentials: { gitea: giteaReference(join(root, 'secrets', 'agents'), 'acme', 'chief', '2099-01-01') }, identity: { name: 'Chief', email: 'chief@example.test', gitea: { url, user: 'acme-chief' } }, publish: ['books'] }, coder: { definition: 'coder', ...none }, }, definitions: { - 'chief-of-staff': { roleVersion: 2, credentials: [{ service: 'gitea' }] }, + 'chief-of-staff': { roleVersion: 2, credentials: [{ service: 'gitea', scopes: ['read:repository', 'read:user'] }] }, pm: { roleVersion: 2, credentials: [] }, coder: { roleVersion: 2, credentials: [] }, }, // sage-4054 / mos-1272: runtime validates every code project's absolute checkout root. - projects: { app: { root: join(root, 'app-checkout'), remote: `${url}/acme/app` }, books: { ...mappingEntry(), user: 'books-bot' } }, + projects: { app: { root: join(root, 'app-checkout'), remote: `${url}/acme/app` }, books: { ...mappingEntry(), remote: `${url}/acme/books`, user: 'books-bot' } }, launch: { by: 'boss', instances: ['chief', 'coder'] }, }; } @@ -108,6 +107,7 @@ async function verbWorld(t, opts = {}) { gitea = await fakeGiteaApi(t, join(dir, 'tls')); gitea.addUser('acme'); gitea.addRepo('acme', 'app'); + gitea.addRepo('acme', 'books'); } const adminDir = join(dir, 'admin'); mkdirSync(adminDir, { mode: 0o700 }); @@ -150,33 +150,22 @@ async function verbWorld(t, opts = {}) { const human = broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }); const events = () => store.all(`SELECT body FROM events WHERE business='acme' AND ${GRANT_EVIDENCE} ORDER BY seq`).map((e) => JSON.parse(e.body)); const row = () => store.get("SELECT 1 FROM gitea_grants WHERE business='acme' AND agent='chief'"); - const seedChiefReadGrant = async () => { - // sage-4100 / C2-21: the real chief read-grant executor replaces this fixture seed. - // The current executor mints write scopes and must keep refusing the chief (sage-2921). - await assert.rejects(broker.request(chief, { + const mintChiefReadGrant = async () => { + const decision = await broker.request(chief, { verb: 'decision.raise', args: { - action: 'credential.mint', question: 'Grant chief Gitea?', options, recommendation: 'yes', blocking: false, - grant: { method: 'gitea', agent: 'chief', target: { projects: ['app'] }, rotateBy: dayFrom(30) }, + action: 'credential.mint', question: 'Grant chief library read?', options, recommendation: 'yes', blocking: false, + grant: { method: 'gitea', agent: 'chief', target: { projects: ['books'] }, rotateBy: dayFrom(30) }, }, - }), { code: 'gitea-not-declared' }); - const rotateBy = dayFrom(30), user = 'acme-chief', nonce = randomBytes(8).toString('hex'); - const decision = grantDecision(store, { business: 'acme', grant: { method: 'gitea', agent: 'chief', target: { projects: ['app'] }, rotateBy } }); - const { user: account } = gitea.addUser(user, { full_name: `Chief [mosaic ${nonce}]` }); - const token = gitea.addToken(user, 'mosaic-chief-read-' + decision, ['read:repository', 'read:user']); - assert.deepEqual(token.scopes, ['read:repository', 'read:user'], 'fixture grants read scopes only (sage-4100)'); - const keys = giteaKeys({ credentials, storeRoot }); - keys.register('acme', 'chief', rotateBy); - // No exported grant-record writer exists; use the executor's credential-file writer and direct - // store inserts for the fixture-only records (sage-4100), with the fake's actual user/token IDs. - keys.issue('acme', 'chief', token.sha1, rotateBy, () => store.transaction(() => { - store.run('INSERT INTO gitea_users(business,agent,user,id,nonce,decision) VALUES(?,?,?,?,?,?)', 'acme', 'chief', user, account.id, nonce, decision); - store.run('INSERT INTO gitea_grants(business,agent,user,token_id,token_name,projects,rotate_by,decision) VALUES(?,?,?,?,?,?,?,?)', - 'acme', 'chief', user, token.id, token.name, JSON.stringify(['app']), rotateBy, decision); - writeGrantEvent(store, AT, 'acme', { action: 'grant.applied', method: 'gitea', agent: 'chief', grantDecision: decision, user, - userId: account.id, tokenId: token.id, permission: 'read' }, 'chief'); - })); - assert.ok(row(), 'with its gitea_grants row'); + }); + broker.request(human, { verb: 'decision.resolve', args: { id: decision.id, choice: 'yes' } }); + await until(() => events().some((e) => e.grantDecision === decision.id && ['grant.applied', 'grant.failed'].includes(e.action))); + await broker.giteaIdle(); + const end = events().find((e) => e.grantDecision === decision.id && ['grant.applied', 'grant.failed'].includes(e.action)); + assert.equal(end.action, 'grant.applied', JSON.stringify(end)); + assert.deepEqual(end.repositories, [{ project: 'books', repository: `${gitea.url}/acme/books`, level: 'read' }]); + assert.deepEqual(gitea.tokens('acme-chief')[0].scopes, ['read:repository', 'read:user']); + assert.ok(row(), 'real grant wrote its gitea_grants row'); }; const revoke = async () => { await broker.request(human, { verb: 'grants.revoke', args: { service: 'gitea', agent: 'chief', reason: 'test' } }); @@ -195,7 +184,7 @@ async function verbWorld(t, opts = {}) { broker.request(chief, { verb: 'role.release' }); broker.request(boss, { verb: 'role.release' }); }; - return { gitea, doc, store, broker, storeRoot, row, seedChiefReadGrant, revoke, fire, close, release, choose: () => broker.libraryFetchCredential('acme') }; + return { gitea, doc, store, broker, storeRoot, row, mintChiefReadGrant, revoke, fire, close, release, choose: () => broker.libraryFetchCredential('acme') }; } const REFUSED = { refused: 'library-fetch-no-grant', fix: LIBRARY_FETCH_FIX }; @@ -216,13 +205,13 @@ test('C2-03b T6: the slot before any chief evidence, then the listed chief\'s li test('C2-03b T7: a chief revoked through grants.revoke or fired through instance.fire refuses, and the fallback stays ended', { timeout: 60000 }, async t => { const revoked = await verbWorld(t); assert.deepEqual(revoked.choose(), { kind: 'slot' }); - await revoked.seedChiefReadGrant(); + await revoked.mintChiefReadGrant(); assert.deepEqual(revoked.choose(), { kind: 'chief', agent: 'chief', user: 'acme-chief' }); await revoked.revoke(); assert.deepEqual(revoked.choose(), REFUSED, 'revoked'); const fired = await verbWorld(t); - await fired.seedChiefReadGrant(); + await fired.mintChiefReadGrant(); assert.deepEqual(fired.choose(), { kind: 'chief', agent: 'chief', user: 'acme-chief' }); await fired.fire(); assert.equal(fired.doc.launch.instances.includes('chief'), false, 'delisted'); @@ -283,7 +272,7 @@ test('C2-03b T9 runtime: the chief\'s store file and user, then a real revoke th mkdirSync(join(root, 'secrets', 'acme'), { recursive: true, mode: 0o700 }); writeFileSync(slot, SLOT_TOKEN + '\n', { mode: 0o600 }); const granted = await verbWorld(t, { dataRoot, storeRoot }); - await granted.seedChiefReadGrant(); + await granted.mintChiefReadGrant(); granted.release(); granted.close(); const gitea = granted.gitea; diff --git a/packages/business/src/business.mjs b/packages/business/src/business.mjs index 351473c22..84b673ed2 100644 --- a/packages/business/src/business.mjs +++ b/packages/business/src/business.mjs @@ -292,7 +292,9 @@ export function validateBusinessDocument(document, file, { rolesDir }) { for (const [instance, role] of Object.entries(roles)) { for (const project of role.publish ?? []) { if (!Object.hasOwn(projects, project)) refuse(`${file} roles.${instance}.publish names ${project}, which the business file doesn't declare`); - if (projects[project].kind === 'library') refuse(`${file} roles.${instance}.publish names library project ${project}; library repositories cannot be published by agents`); + // C2-21, sage-2916: retain the fence; only creative instances and the chief may hold library reach. + if (projects[project].kind === 'library' && !CREATIVE_DEFINITIONS.includes(role.definition) && role.definition !== 'chief-of-staff') + refuse(`${file} roles.${instance}.publish names library project ${project}; library publish requires a creative definition or chief-of-staff with gitea`); if (!projects[project].remote) refuse(`${file} roles.${instance}.publish names ${project}, which has no "remote"`); const problem = forkProblem(projects[project].remote, role.identity); if (problem) refuse(`${file} roles.${instance}.publish names ${project}: ${problem}`); diff --git a/packages/business/tests/business.test.mjs b/packages/business/tests/business.test.mjs index f78789250..636c47e96 100644 --- a/packages/business/tests/business.test.mjs +++ b/packages/business/tests/business.test.mjs @@ -360,3 +360,28 @@ test("row 194 each bad keep, noHire or retired entry refuses", () => { refuses(() => check((d) => { d.retired = ["../x"]; }), /retired entry/); refuses(() => check((d) => { d.retired = {}; }), /retired must be an array/); }); + +// C2-21 B1-B4: catch a deleted/overbroad library publish fence while retaining identity/credential rules. +test('C2-21: only creative definitions and the chief with gitea may publish a library project', async (t) => { + const { mappingEntry } = await import('../../library/tests/helpers.mjs'); + for (const definition of [...CREATIVE_DEFINITIONS, 'chief-of-staff']) { + const mutate = (d) => { + d.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; + d.roles.chief.credentials = CREATIVE_GITEA; + const agent = definition === 'chief-of-staff' ? 'chief' : 'bean'; + d.roles[agent] = { ...(d.roles[agent] ?? {}), definition, credentials: CREATIVE_GITEA, + identity: { name: 'Library reader', email: 'reader@example.test', gitea: { url: 'https://git.example', user: 'acme-reader' } }, publish: ['books'] }; + }; + const business = check(mutate); + assert.deepEqual(business.roles[definition === 'chief-of-staff' ? 'chief' : 'bean'].publish, ['books']); + refuses(() => check((d) => { mutate(d); delete d.roles[definition === 'chief-of-staff' ? 'chief' : 'bean'].identity; }), /publish needs identity and a gitea definition/); + } + for (const agent of ['coder', 'reviewer', 'pm', 'cto']) { + refuses(() => check((d) => { + d.projects.books = { ...mappingEntry(), remote: 'https://git.example/writer/books' }; + d.roles.chief.credentials = CREATIVE_GITEA; + d.roles[agent].identity = { name: 'Other', email: 'other@example.test', gitea: { url: 'https://git.example', user: 'acme-other' } }; + d.roles[agent].publish = ['books']; + }), /publish.*library.*creative.*chief/); + } +}); diff --git a/packages/cli/tests/case2-install.test.mjs b/packages/cli/tests/case2-install.test.mjs index 51b606bda..7574856b0 100644 --- a/packages/cli/tests/case2-install.test.mjs +++ b/packages/cli/tests/case2-install.test.mjs @@ -477,3 +477,16 @@ test("row 322 F9: importer refusal keeps the error line rather than the Node ban assert.equal(existsSync(x.business), false); assert.equal(existsSync(x.personaDir), false); }); + +test('C2-21: library install supplies chief identity and books publish for its real read grant', (t) => { + const x = world(t); + const out = install(x); + assert.equal(out.code, 0, out.out); + const doc = JSON.parse(readFileSync(x.business, 'utf8')); + assert.deepEqual(doc.roles.chief.publish, ['books']); + assert.ok(doc.roles.chief.identity?.name); + assert.ok(doc.roles.chief.identity?.email); + assert.equal(doc.roles.chief.identity?.gitea?.url, new URL(doc.projects.books.remote).origin); + assert.ok(doc.roles.chief.identity?.gitea?.user); + assert.notEqual(doc.roles.chief.identity?.gitea?.user, 'writer'); +}); diff --git a/scripts/case2-install.sh b/scripts/case2-install.sh index 06e33dcb6..4972854ca 100755 --- a/scripts/case2-install.sh +++ b/scripts/case2-install.sh @@ -13,8 +13,8 @@ # activate, starts no unit, writes no unit file and nothing under /etc, and # never writes the library token, which is Jason's step 4. # -# FILE is the business document written for the host from the go. The -# script adds only roles.bean.vars and roles.chet.vars persona.name and +# FILE is the business document written for the host from the go. For a library, +# the script adds chief identity/publish and roles.bean.vars and roles.chet.vars persona.name and # persona.sha256, from the staging manifest, and checks the input against # the precheck's profile rules first: library needs a library project, # bus.tester off and bean and chet as companion roles with no persona var; @@ -109,6 +109,13 @@ if (profile === "factory") { } else { if (!library.length) stop("no-library-project", `${input} has no library project`); if (tester) stop("bus-tester-on", "the library profile needs bus.tester off"); + // C2-21: the chief mints its own read grant after install; no token or grant is created here. + const chief = Object.values(doc.roles ?? {}).find((r) => r?.definition === "chief-of-staff"); + if (!chief) stop("chief-missing", "the library profile needs a chief-of-staff instance"); + const project = projects.books?.kind === "library" ? "books" : library[0]; + chief.identity ??= { name: "Chief of staff", email: `${id}-chief@mosaic.invalid`, + gitea: { url: new URL(projects[project].remote).origin, user: `${id}-chief` } }; + chief.publish = [project]; for (const instance of ["bean", "chet"]) { const role = doc.roles?.[instance]; if (role?.definition !== "companion") stop("companion-roles", `roles.${instance} must have definition companion`);