From 7198d336cd14d37cbf49e5f9ff78f17d5e9fb65a Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 13 Oct 2026 09:56:33 -0500 Subject: [PATCH] researcher: row 84 round 1 packet on ac6b56ee, business 67/0, nine mutants killed (#1578) Haiku subagent build under decision 129. Candidate manifest f72730de names roles/chief-of-staff.json and .md, the business README and packages/business/tests/chief-of-staff.test.mjs. Gate and mutants ran in Sage's slot. Co-Authored-By: Claude Opus 5.5 --- agents/researcher/work/queue-84/r1/BUILD.md | 158 ++++++++++++ agents/researcher/work/queue-84/r1/base.txt | 1 + .../researcher/work/queue-84/r1/build.patch | 239 +++++++++++++++++ .../queue-84/r1/candidate-manifest.sha256 | 4 + agents/researcher/work/queue-84/r1/files.txt | 4 + agents/researcher/work/queue-84/r1/mutants.py | 240 ++++++++++++++++++ .../researcher/work/queue-84/r1/out/M01.log | 81 ++++++ .../researcher/work/queue-84/r1/out/M02.log | 87 +++++++ .../researcher/work/queue-84/r1/out/M03.log | 88 +++++++ .../researcher/work/queue-84/r1/out/M04.log | 67 +++++ .../researcher/work/queue-84/r1/out/M05.log | 130 ++++++++++ .../researcher/work/queue-84/r1/out/M06.log | 102 ++++++++ .../researcher/work/queue-84/r1/out/M07.log | 166 ++++++++++++ .../researcher/work/queue-84/r1/out/M08.log | 132 ++++++++++ .../researcher/work/queue-84/r1/out/M09.log | 45 ++++ .../work/queue-84/r1/out/baseline-M09.log | 158 ++++++++++++ .../work/queue-84/r1/out/baseline.log | 15 ++ .../work/queue-84/r1/out/step1-business.txt | 75 ++++++ .../work/queue-84/r1/out/step2-test-auth.txt | 17 ++ .../queue-84/r1/out/step2-test-conductor.txt | 55 ++++ .../queue-84/r1/out/step2-test-config.txt | 26 ++ .../queue-84/r1/out/step2-test-discord.txt | 70 +++++ .../r1/out/step2-test-extension-package.txt | 21 ++ .../queue-84/r1/out/step2-test-foundation.txt | 53 ++++ .../work/queue-84/r1/out/step2-test-queue.txt | 35 +++ .../queue-84/r1/out/step2-test-release.txt | 16 ++ .../work/queue-84/r1/out/step2-test-task.txt | 32 +++ .../work/queue-84/r1/out/summary.txt | 10 + 28 files changed, 2127 insertions(+) create mode 100644 agents/researcher/work/queue-84/r1/BUILD.md create mode 100644 agents/researcher/work/queue-84/r1/base.txt create mode 100644 agents/researcher/work/queue-84/r1/build.patch create mode 100644 agents/researcher/work/queue-84/r1/candidate-manifest.sha256 create mode 100644 agents/researcher/work/queue-84/r1/files.txt create mode 100644 agents/researcher/work/queue-84/r1/mutants.py create mode 100644 agents/researcher/work/queue-84/r1/out/M01.log create mode 100644 agents/researcher/work/queue-84/r1/out/M02.log create mode 100644 agents/researcher/work/queue-84/r1/out/M03.log create mode 100644 agents/researcher/work/queue-84/r1/out/M04.log create mode 100644 agents/researcher/work/queue-84/r1/out/M05.log create mode 100644 agents/researcher/work/queue-84/r1/out/M06.log create mode 100644 agents/researcher/work/queue-84/r1/out/M07.log create mode 100644 agents/researcher/work/queue-84/r1/out/M08.log create mode 100644 agents/researcher/work/queue-84/r1/out/M09.log create mode 100644 agents/researcher/work/queue-84/r1/out/baseline-M09.log create mode 100644 agents/researcher/work/queue-84/r1/out/baseline.log create mode 100644 agents/researcher/work/queue-84/r1/out/step1-business.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-auth.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-conductor.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-config.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-discord.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-extension-package.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-foundation.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-queue.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-release.txt create mode 100644 agents/researcher/work/queue-84/r1/out/step2-test-task.txt create mode 100644 agents/researcher/work/queue-84/r1/out/summary.txt diff --git a/agents/researcher/work/queue-84/r1/BUILD.md b/agents/researcher/work/queue-84/r1/BUILD.md new file mode 100644 index 000000000..125ede21e --- /dev/null +++ b/agents/researcher/work/queue-84/r1/BUILD.md @@ -0,0 +1,158 @@ +# Row 84 (#1578) build, round 1: chief-of-staff role file, contract and tests + +Phase B is done. The gate passed, all nine mutants were killed, and the baseline for M09 was recorded. The packet is ready for the review request. Nothing is committed, the queue row is still in-progress (rev 874, op `researcher-h84-start-20261013`), and nothing was written to Gitea. + +## Identity + +- Row 84: M1, chief-of-staff role file, contract and tests. Brief: `docs/plans/2026-10-12_m1-chief-of-staff-role.md`, section "M1 chief-of-staff role file, contract and tests", blob `da2a7b7e791e750642383a3d99cc1ce51a2b25ae` (not edited). +- Owner: researcher (queue owner, authoritative per Sage's ruling 3). Reviewers: Darkwing and Filbert. Gate: Sage. Dispatch: Mos, 2026-10-13T00:06Z, via Sage (decisions 122 and 129). +- Brief owner line: the brief still says Owner: Rocko and names `agents/rocko/work/queue-/`. The brief is stale on both. This packet is under `agents/researcher/work/queue-84/r1/` per the queue owner. The brief is not edited. +- BASE: `ac6b56ee55278077d61f39e6ca42513725427154` (see `base.txt`). Re-pinned in Phase B from `58a3766d480a59de195bdd75f3f4ad4e41e21f5d`. +- Base drift check: `git diff --stat 58a3766d4 ac6b56ee5 -- roles/ packages/business/` is empty, and `packages/business/src/resolve.mjs` is unchanged between the two bases. No candidate path drifted, so the Phase A results carry over, and the gate was run again on the new base anyway. +- Worktree: `.claude/worktrees/agent-a1044a9b545fa3a36`, detached at BASE. Changes uncommitted. `node_modules` is a symlink to the canonical root `node_modules`; no `npm install` was run. +- Rebase steps: `git stash push -u -m r84-phaseB-20261013` (entry `0638f67ec05096f11dbebe73de196e4567e56766`), `git checkout --detach ac6b56ee5`, `git stash apply` by SHA (not pop), then the stash entry was dropped. The restored test file matches its stashed copy by sha256. Stamp about 14:50 UTC. + +## Changes + +| Path | New or modified | What | +| --- | --- | --- | +| `roles/chief-of-staff.json` | new | the role file, roleVersion 2 | +| `roles/chief-of-staff.md` | new | the duty contract, in the style of `roles/pm.md` | +| `packages/business/tests/chief-of-staff.test.mjs` | new | 7 tests, fixture business in a temp directory | +| `packages/business/README.md` | modified | new section "Chief-of-staff instance", placed before "Project file", with the instance entry | + +No other file is touched. `vocabulary.mjs`, `resolve.mjs`, `role.mjs`, `business.mjs` and `vars.mjs` are unchanged (ruling 1: no src change). Nothing writes Jason's business file. `resolve.mjs` was edited twice during the M09 baseline (apply, then restore) and was confirmed back at its pinned hash; see below. + +## Role file against decision 104, line by line + +Decision 104 (lead decisions, lines 2288 to 2313) and the role file: + +| Decision 104 says | `roles/chief-of-staff.json` | Match | +| --- | --- | --- | +| role named `chief-of-staff` | `"name": "chief-of-staff"` | yes | +| tools `read`, `grep`, `find`, `ls` | `"tools": ["read", "grep", "find", "ls"]` | yes, exact, in order | +| network `api-only` | `"network": "api-only"` | yes | +| authority.withinRole `message.send` only | `"withinRole": ["message.send"]` | yes | +| no crossRole authority | `"crossRole": []` | yes | +| no credentials | `"credentials": []` | yes | +| every other vocabulary action stays gated, including task.create and role.launch | no other action listed; test checks all of `ACTIONS` through `classify` | yes | +| contract `roles/chief-of-staff.md` | `"contract": "chief-of-staff.md"` | yes | + +Additional keys in the file, not in decision 104's list: `roleVersion` (2, the version the brief names), `title` ("Chief of staff"), which the brief asks for ("a title"). Neither grants anything. + +Contract (`roles/chief-of-staff.md`) against decision 104's four points: + +- Sends Jason's requests to the PM as messages and asks the PM for work: Duties, first two bullets. +- Relays Jason's rulings verbatim: Duties, third bullet ("verbatim"). +- Never resolves a decision for Jason: Authority, second paragraph. +- Never holds or imitates human authority, never routes around a refusal: Authority, first and second paragraphs. +- The contract names the role, not the persona (ruling 2). The word "Mos" does not occur in the file. The instance `mos` appears only in the README and in the test fixture. + +## Tests (`packages/business/tests/chief-of-staff.test.mjs`) + +Seven tests, each against a fixture business written to a temp directory (`businessDoc` from `helpers.mjs`, plus one `chief` instance): + +1. The role file loads and resolves through `loadBusiness`. +2. The shipped file equals decision 104's list exactly (every key except `title`, deep-equal). A widened file fails here. +3. The resolved record's `limits` and `credentials` equal the list. +4. `classify` returns within for `message.send` and gated for every other action in `ACTIONS`, including `task.create` and `role.launch`. +5. A business that grants the instance `task.create` and `role.launch` through `limits.authority` gets no effect: both stay gated and `launch` is null (ruling 1; `limits` only narrow, `resolve.mjs` lines 64 to 68). +6. A `launch` block whose `by` is the chief instance is refused, because the role holds no `role.launch`. +7. The instance cannot take credential references or a tracker block, and the loader refuses both. + +## README section + +The entry, as Jason would add it under `roles` in his business file (ruling 2 and decision 116): + +```json +"mos": { "definition": "chief-of-staff", "holder": "mos" } +``` + +No `launch` block, no `tracker`, no `credentials`. The section says the stack never writes the business file. + +## Gate (Phase B, on ac6b56ee5, worktree root, UTC stamps from `date -u`) + +Output files are in `out/`. Every step was gated on its exit status. + +- Step 1, business suite: `DOCKER_HOST=unix:///nonexistent.sock node --test` on the eight `packages/business/tests/*.test.mjs` files, listed explicitly. Stamp 14:51:04 to 14:51:06. rc 0. 67 tests, 67 pass, 0 fail. All seven chief-of-staff tests ran and passed. Output: `out/step1-business.txt`. (The glob form was not used; the explicit list is the same set of eight files, `helpers.mjs` is not a test.) +- Step 2, the nine `scripts/test-*.sh`, sequential. Only `test-task.sh` got `DOCKER_HOST=unix:///nonexistent.sock`. + +| Script | Stamp (UTC) | rc | +| --- | --- | --- | +| `test-config.sh` | 14:51:22 to 14:51:24 | 0 | +| `test-auth.sh` | 14:51:25 to 14:51:26 | 0 | +| `test-conductor.sh` | 14:51:27 to 14:51:33 | 0 | +| `test-discord.sh` | 14:51:34 to 14:51:40 | 0 | +| `test-extension-package.sh` | 14:51:42 to 14:51:43 | 0 | +| `test-foundation.sh` | 14:51:44 to 14:52:23 | 0 | +| `test-queue.sh` | 14:52:24 to 14:52:44 | 0 | +| `test-release.sh` | 14:52:46 to 14:52:57 | 0 | +| `test-task.sh` (dead DOCKER_HOST) | 14:52:58 to 14:53:00 | 0 | + +`test-task.sh` finished in about 2 seconds with the dead Docker host. I did not check whether it skipped Docker-dependent cases, so its rc 0 shows only that it exits clean. + +Output: `out/step2-test-*.txt`. + +## Mutants + +Runner: `mutants.py` (in this packet), run as `python3 -I mutants.py `, no Docker variable. Nine mutants: M01 to M08 on `roles/chief-of-staff.json`, M09 on `packages/business/src/resolve.mjs`. Each has one exact-once anchor (all nine were checked before the first run), runs `node --test packages/business/tests/chief-of-staff.test.mjs` in its own process group under a 180 s timeout, and is restored in a `finally`. The candidate manifest is checked before the baseline and after the last restore; `resolve.mjs` is checked against its pinned hash. + +Runner corrections made before the first run (Phase B): + +- The mutant suite no longer sets `DOCKER_HOST`. The business suite does not use Docker, and Sage's rule is that only `test-task.sh` gets the dead host. +- The leftover check skips the runner and its parent processes. The first version would have counted the runner itself, because it starts in the worktree. + +Run: stamp 14:53:36 to 14:53:37 UTC. Runner rc 0. Baseline green. Output: `out/baseline.log`, `out/M01.log` to `out/M09.log`, `out/summary.txt`. + +| Id | Change | Result | Failing tests (observed) | +| --- | --- | --- | --- | +| M01 | tools gains `bash` | killed | shipped-file equality; resolved-record match | +| M02 | tools gains `write` | killed | shipped-file equality; resolved-record match | +| M03 | network `api-only` becomes `open` | killed | shipped-file equality; resolved-record match | +| M04 | withinRole gains `role.launch` | killed | shipped-file equality; "a launch block whose by is the chief-of-staff instance is refused" | +| M05 | withinRole gains `task.create` | killed | shipped-file equality; resolved-record match; classify test ("message.send is within role and every other action is gated"); limits-grant test | +| M06 | crossRole gains `task.scope.change` | killed | shipped-file equality; resolved-record match; classify test | +| M07 | credentials gains a gitea entry | killed | seven tests fail. The fixture load refuses with `BusinessError` ("roles.chief.credentials must reference exactly the services its role definition needs"), and the equality and classify tests fail on their assertions. Not an assertion-only kill. | +| M08 | withinRole loses `message.send` | killed | shipped-file equality; resolved-record match; classify test; limits-grant test | +| M09 | `resolve.mjs`: the limits.authority line becomes a union, so a limits grant widens withinRole | killed | limits-grant test only ("a business grant of a gated action through limits does not widen the role") | + +Result: 9 of 9 killed. Each kill is a failing test. Eight are assertion failures; M07 also fails through the loader refusal shown above. None is a timeout or a runner crash. + +### M09 specifically + +- The new limits-grant test kills M09. The actual value is `withinRole: ['message.send', 'task.create']`, where the test expects `['message.send']`. +- The pinned hash of `resolve.mjs` (`c50340af650aaa2d0eb15224e823525ba942a91f475ed62bd7e7ad42055f182b`) matched before the baseline, after the M09 restore, and at the end. + +Baseline M09 (run after the mutants, to check whether the new test is the only catch): stamp 14:53:58 to 14:54:08 UTC. + +- The new test file was moved out of `packages/business/tests/`, M09 was applied to `resolve.mjs` by exact replacement, and the other eight business test files were run. Output: `out/baseline-M09.log`. +- Result: rc 1, 57 pass, 3 fail. +- `baseline M09: killed by existing tests in packages/business/tests/resolve.test.mjs`: + - "limits narrow the definition and never widen it" (withinRole gains `message.send`, `task.update.assigned`, `task.close`). + - "limits.authority without role.launch leaves the launcher with no launch block" (a gated action classifies as within). + - "limits.authority narrows cross-role actions too" (withinRole gains `task.create`, `task.assign`, `task.reassign`). +- So the new test is not the only thing that catches a widening through limits. The existing resolver tests already catch M09. The new test is a second, row-specific kill that pins the decision 104 outcome for the chief instance. +- After the baseline, `resolve.mjs` was restored by reverse replacement and checked at `c50340af...`. The test file was moved back and the manifest was checked again: all four entries OK. + +## Candidate manifest and patch + +- `candidate-manifest.sha256`: four repo-relative entries from `files.txt`, checked with `sha256sum -c` from the worktree root (all OK). Entries: `roles/chief-of-staff.json` 9a29c8ff..., `roles/chief-of-staff.md` d257bc27..., `packages/business/tests/chief-of-staff.test.mjs` c4198b67..., `packages/business/README.md` 875777f3.... +- Manifest sha256: `f72730debe864edc9cdd9631482de98da41d886df65b5653a51d835d43de0a08`. +- `build.patch`: `git add -N` on the three new files, then `git diff --binary ac6b56ee5 -- `. Intent-to-add entries were reset afterwards. 239 lines, four `diff --git` headers. +- Build patch sha256: `0989637709e8a5dcaba31c88768571837b2276a0f16de49f6340058bcbea94f2`. +- Patch check: `git apply --index --check` and `git apply --index` on a `git archive ac6b56ee5` copy in a scratch directory (removed afterwards). The four resulting files hash to the manifest values. + +## Packet files + +- `base.txt`: `ac6b56ee55278077d61f39e6ca42513725427154`. +- `files.txt`: unchanged, four paths. +- `mutants.py`: nine mutants, runner corrections above. +- `candidate-manifest.sha256`, `build.patch`: new in Phase B. +- `out/`: step 1, step 2, mutant, and baseline logs. Not in the manifest. + +## Open items + +- The brief's Owner line (Rocko) and its `agents/rocko/` path are stale. Recorded here; the brief is not edited. +- The brief's README section says "the exact instance entry for Jason's business file". The entry above is that entry, using `mos` (decision 116). +- Not done, by instruction: no commit, no queue move, no Gitea or Vikunja write, no push. +- Process check after the runs: no node, python or test process of this worktree is left. The cwd scan showed only the scan's own transient subshells, which had already exited. diff --git a/agents/researcher/work/queue-84/r1/base.txt b/agents/researcher/work/queue-84/r1/base.txt new file mode 100644 index 000000000..8d25787e1 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/base.txt @@ -0,0 +1 @@ +ac6b56ee55278077d61f39e6ca42513725427154 diff --git a/agents/researcher/work/queue-84/r1/build.patch b/agents/researcher/work/queue-84/r1/build.patch new file mode 100644 index 000000000..f9d201994 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/build.patch @@ -0,0 +1,239 @@ +diff --git a/packages/business/README.md b/packages/business/README.md +index ca372c5ed..8c7a43350 100644 +--- a/packages/business/README.md ++++ b/packages/business/README.md +@@ -94,6 +94,27 @@ instance whose definition holds `role.launch` within-role; otherwise the + file refuses. Only that instance keeps `role.launch`. Every other instance + loses it, so for them it's gated. + ++### Chief-of-staff instance ++ ++The `chief-of-staff` role (`roles/chief-of-staff.json`, contract ++`roles/chief-of-staff.md`, lead decision 104) is the main agent that ++carries Jason's requests to the PM. It holds `message.send` within role ++and nothing else: no write tool, no credentials, no crossRole action, and ++no `launch` block. Its instance is `mos` (decision 116). Jason adds this ++entry under `roles` in his business file, next to the other instances. The ++stack never writes that file. ++ ++```json ++"mos": { "definition": "chief-of-staff", "holder": "mos" } ++``` ++ ++The entry has no `tracker`, because the role uses no Vikunja, and no ++`credentials`, because it holds none. The file must not name `mos` as the ++`launch.by` instance: the loader refuses that, since the role has no ++`role.launch`. `packages/business/tests/chief-of-staff.test.mjs` checks the ++role against decision 104's list and loads it through `loadBusiness` from a ++fixture business under the name `chief`. ++ + ### Project file + + ```json +diff --git a/packages/business/tests/chief-of-staff.test.mjs b/packages/business/tests/chief-of-staff.test.mjs +new file mode 100644 +index 000000000..08374d122 +--- /dev/null ++++ b/packages/business/tests/chief-of-staff.test.mjs +@@ -0,0 +1,133 @@ ++// The chief-of-staff role (lead decision 104, M1, row 84). The role file is ++// policy: its tools, network, authority and credentials must equal the list ++// Jason approved, so a widened file fails here. Fixtures live in a temp ++// directory and nothing reads the real config. ++ ++import { test } from "node:test"; ++import assert from "node:assert/strict"; ++import { readFileSync } from "node:fs"; ++import { join } from "node:path"; ++import { ACTIONS, BusinessError, classify, loadBusiness, loadRole, resolveInstance } from "../src/index.mjs"; ++import { businessDoc, REPO_ROLES, systemFor, tmp, writeJson } from "./helpers.mjs"; ++ ++// Decision 104's list, as approved. A role file that differs from it fails. ++const APPROVED = Object.freeze({ ++ tools: ["read", "grep", "find", "ls"], ++ network: "api-only", ++ authority: Object.freeze({ withinRole: Object.freeze(["message.send"]), crossRole: Object.freeze([]) }), ++ credentials: Object.freeze([]), ++}); ++ ++// A fixture instance name. The shipped entry for Jason's business is `mos`. ++const INSTANCE = "chief"; ++ ++function refuses(fn, pattern) { ++ assert.throws(fn, (error) => { ++ assert.ok(error instanceof BusinessError, `expected BusinessError, got ${error}`); ++ assert.equal(error.exitCode, 2); ++ assert.match(error.message, pattern); ++ return true; ++ }); ++} ++ ++// Write a business file to a temp config directory and load it with ++// loadBusiness. The file is the shipped four roles from businessDoc(), plus ++// one chief-of-staff instance, with `mutate` applied after both. ++function loadFixture(mutate = () => {}) { ++ const root = tmp(); ++ const doc = businessDoc(root, "acme"); ++ doc.roles[INSTANCE] = { definition: "chief-of-staff", holder: "mos" }; ++ mutate(doc); ++ writeJson(join(root, "businesses", "acme.json"), doc); ++ return { root, business: loadBusiness("acme", { dir: root, rolesDir: REPO_ROLES }) }; ++} ++ ++function resolved({ root, business }) { ++ return resolveInstance({ system: systemFor(root), business, instance: INSTANCE }); ++} ++ ++test("the role file loads and resolves through loadBusiness", () => { ++ const fixture = loadFixture(); ++ const role = loadRole(REPO_ROLES, "chief-of-staff"); ++ assert.equal(role.roleVersion, 2); ++ assert.equal(role.name, "chief-of-staff"); ++ assert.equal(role.contract, "chief-of-staff.md"); ++ assert.equal(role.contractPath, join(REPO_ROLES, "chief-of-staff.md")); ++ ++ const record = resolved(fixture); ++ assert.equal(record.definition, "chief-of-staff"); ++ assert.equal(record.holder, "mos"); ++ assert.equal(record.contract, join(REPO_ROLES, "chief-of-staff.md")); ++ assert.equal(record.launch, null); ++ assert.equal(record.tracker, null); ++}); ++ ++test("the shipped file is exactly decision 104's list", () => { ++ const raw = JSON.parse(readFileSync(join(REPO_ROLES, "chief-of-staff.json"), "utf8")); ++ const { title, ...rest } = raw; ++ assert.equal(typeof title, "string"); ++ assert.deepEqual(rest, { ++ roleVersion: 2, ++ name: "chief-of-staff", ++ contract: "chief-of-staff.md", ++ ...APPROVED, ++ }); ++}); ++ ++test("the resolved record matches decision 104, so a widened file fails", () => { ++ const record = resolved(loadFixture()); ++ assert.deepEqual(record.limits, { ++ tools: APPROVED.tools, ++ network: APPROVED.network, ++ authority: APPROVED.authority, ++ }); ++ assert.deepEqual(record.credentials, {}); ++}); ++ ++test("message.send is within role and every other action is gated", () => { ++ const record = resolved(loadFixture()); ++ for (const action of ACTIONS) { ++ assert.equal(classify(record, action), action === "message.send" ? "within" : "gated", action); ++ } ++ assert.equal(classify(record, "message.send"), "within"); ++ assert.equal(classify(record, "task.create"), "gated"); ++ assert.equal(classify(record, "role.launch"), "gated"); ++}); ++ ++test("a business grant of a gated action through limits does not widen the role", () => { ++ // limits.authority only narrows, so the grant has no effect and both ++ // actions stay gated. limits only narrow (resolve.mjs), so the grant has ++ // no effect; this asserts that, rather than a refusal. ++ const fixture = loadFixture((doc) => { ++ doc.roles[INSTANCE].vars = { "limits.authority": ["message.send", "task.create", "role.launch"] }; ++ }); ++ const record = resolved(fixture); ++ assert.deepEqual(record.limits.authority, { withinRole: ["message.send"], crossRole: [] }); ++ assert.equal(classify(record, "task.create"), "gated"); ++ assert.equal(classify(record, "role.launch"), "gated"); ++ assert.equal(record.launch, null); ++}); ++ ++test("a launch block whose by is the chief-of-staff instance is refused", () => { ++ refuses( ++ () => loadFixture((doc) => { ++ doc.launch = { by: INSTANCE, instances: ["coder"], max: { sonnet: 1 } }; ++ }), ++ /launch\.by names chief, whose role chief-of-staff doesn't hold role\.launch within-role/, ++ ); ++}); ++ ++test("the instance takes no credential references and no tracker", () => { ++ refuses( ++ () => loadFixture((doc) => { ++ doc.roles[INSTANCE].credentials = { gitea: { file: "/x/token", rotateBy: "2099-01-01" } }; ++ }), ++ /must reference exactly the services its role definition needs \(none; got gitea\)/, ++ ); ++ refuses( ++ () => loadFixture((doc) => { ++ doc.roles[INSTANCE].tracker = { bot: "bot-acme-chief", botId: 7 }; ++ }), ++ /has "tracker" but chief-of-staff uses no vikunja credential/, ++ ); ++}); +diff --git a/roles/chief-of-staff.json b/roles/chief-of-staff.json +new file mode 100644 +index 000000000..031f4b7a6 +--- /dev/null ++++ b/roles/chief-of-staff.json +@@ -0,0 +1,13 @@ ++{ ++ "roleVersion": 2, ++ "name": "chief-of-staff", ++ "title": "Chief of staff", ++ "contract": "chief-of-staff.md", ++ "tools": ["read", "grep", "find", "ls"], ++ "network": "api-only", ++ "authority": { ++ "withinRole": ["message.send"], ++ "crossRole": [] ++ }, ++ "credentials": [] ++} +diff --git a/roles/chief-of-staff.md b/roles/chief-of-staff.md +new file mode 100644 +index 000000000..977f50354 +--- /dev/null ++++ b/roles/chief-of-staff.md +@@ -0,0 +1,43 @@ ++# Role contract: chief of staff (chief-of-staff) ++ ++You hold the `chief-of-staff` role for one business. You are the human's ++main agent in that business. You carry Jason's requests to the product ++manager, relay his rulings and ask the PM for work. You don't write the ++product's code, you don't review it and you don't create tasks yourself. ++ ++## Duties ++ ++- Send each request Jason makes to the PM as a message, addressed to the ++ `pm` role. Keep his words as he gave them, and name the request each ++ message carries. ++- Ask the PM for work by message. The PM decides which work becomes a ++ task, who holds it and when it runs. ++- Relay each ruling Jason gives verbatim to the role it concerns. Don't ++ summarize it, paraphrase it or add to it. Say whose ruling it is. ++- Report to Jason what the PM answers, and every refusal with its exact ++ text. ++ ++## Authority ++ ++Your role file lists what you do on your own: sending messages. Anything ++the list doesn't name is gated. That includes creating or assigning ++tasks, launching a role session, pushing to a repository, sending ++messages outside the business, minting credentials, and any change to ++policy or to a PRD. When an action is gated, raise it to Jason and wait ++for his answer. Never route around a refusal. ++ ++You hold no human authority. Never resolve a decision for Jason, never ++approve a request in his name, and never write words as if he had said ++them. When a question needs his decision, pass it on as his question and ++wait for his answer. ++ ++Only the human stops a run, with `mosaic stop`. If you think a coder or ++reviewer run should stop, say so to Jason. Don't stop it yourself. ++ ++## Protocol ++ ++- You act only through the typed tools the broker gives you. Your tools ++ are read, grep, find and ls. You have no write tool, and you never hold ++ a token or a credential. ++- Send messages through the bus, addressed to a role, not a person. ++- Report a blocker as soon as you find it, with what you tried. diff --git a/agents/researcher/work/queue-84/r1/candidate-manifest.sha256 b/agents/researcher/work/queue-84/r1/candidate-manifest.sha256 new file mode 100644 index 000000000..73a8215cd --- /dev/null +++ b/agents/researcher/work/queue-84/r1/candidate-manifest.sha256 @@ -0,0 +1,4 @@ +9a29c8ff962a7648d430cdd88c7181f9805133418872207de47e1a0325a49ca1 roles/chief-of-staff.json +d257bc276ad8a1b49cf217092d89d64caf72d1d2207449dc327d00ed8e9c76b7 roles/chief-of-staff.md +c4198b67ba9d634dbd9d4afb835f7812442924684b4349c91409c1e14010f04b packages/business/tests/chief-of-staff.test.mjs +875777f34e1c7a4f8a41b2866e3bfcc7dc291df33c4250eec68e5fc83b485336 packages/business/README.md diff --git a/agents/researcher/work/queue-84/r1/files.txt b/agents/researcher/work/queue-84/r1/files.txt new file mode 100644 index 000000000..fb52bd39f --- /dev/null +++ b/agents/researcher/work/queue-84/r1/files.txt @@ -0,0 +1,4 @@ +roles/chief-of-staff.json +roles/chief-of-staff.md +packages/business/tests/chief-of-staff.test.mjs +packages/business/README.md diff --git a/agents/researcher/work/queue-84/r1/mutants.py b/agents/researcher/work/queue-84/r1/mutants.py new file mode 100644 index 000000000..5062b46ce --- /dev/null +++ b/agents/researcher/work/queue-84/r1/mutants.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# Row 84 (#1578) mutation runner, chief-of-staff role. Written in Phase A; run once in Phase B (see BUILD.md). +# +# Phase B usage, from the worktree root, after Sage's "slot clear" and after the +# rebase onto the current refactor HEAD: +# python3 -I /mutants.py +# +# Nine mutants. M01 to M08 rewrite one exact-once anchor in roles/chief-of-staff.json. +# M09 rewrites one exact-once anchor in packages/business/src/resolve.mjs, which is +# NOT in the candidate manifest; its original sha256 is pinned below instead. Each +# mutant runs packages/business/tests/chief-of-staff.test.mjs in its own process group +# under a timeout, and restores the original bytes of its file in a finally block. +# The candidate manifest is checked before the baseline and after the last restore. +# resolve.mjs is checked against its pinned sha256 before the baseline, after each +# restore of M09, and at the end. A red baseline aborts before any mutant. A timeout +# counts as a failure. +# +# Exit codes: 0 every mutant killed; 1 a mutant survived or a run timed out; +# 2 baseline red, timed out, manifest or pinned-hash mismatch, or leftover processes; +# 3 an anchor was not found exactly once. + +import hashlib +import os +import signal +import subprocess +import sys + +TIMEOUT_S = 180 +ROLE = 'roles/chief-of-staff.json' +RESOLVE = 'packages/business/src/resolve.mjs' +# sha256 of resolve.mjs at BASE 58a3766d (checked: identical to base). Not in the candidate. +RESOLVE_SHA256 = 'c50340af650aaa2d0eb15224e823525ba942a91f475ed62bd7e7ad42055f182b' +SUITE = ['node', '--test', 'packages/business/tests/chief-of-staff.test.mjs'] + +# (id, file, old exact text, new exact text, what the test that must kill it checks) +MUTANTS = [ + ('M01', ROLE, '"tools": ["read", "grep", "find", "ls"],', + '"tools": ["read", "grep", "find", "ls", "bash"],', + 'widen tools: bash added; shipped-file equality test fails'), + ('M02', ROLE, '"tools": ["read", "grep", "find", "ls"],', + '"tools": ["read", "write", "grep", "find", "ls"],', + 'widen tools: write added (a write tool, which waits for I4); equality test fails'), + ('M03', ROLE, '"network": "api-only",', + '"network": "open",', + 'widen network: api-only to open; equality test fails'), + ('M04', ROLE, '"withinRole": ["message.send"],', + '"withinRole": ["message.send", "role.launch"],', + 'widen withinRole: role.launch within-role; equality test fails'), + ('M05', ROLE, '"withinRole": ["message.send"],', + '"withinRole": ["message.send", "task.create"],', + 'widen withinRole: task.create within-role; equality test and classify test fail'), + ('M06', ROLE, '"crossRole": []', + '"crossRole": ["task.scope.change"]', + 'widen crossRole: task.scope.change; equality test fails'), + ('M07', ROLE, '"credentials": []', + '"credentials": [{ "service": "gitea", "scopes": ["read:issue"] }]', + 'widen credentials: gitea scope; business fixture refuses the instance (no matching credential ref) and equality test fails'), + ('M08', ROLE, '"withinRole": ["message.send"],', + '"withinRole": [],', + 'narrow withinRole: message.send dropped; equality and classify (within for message.send) tests fail'), + ('M09', RESOLVE, 'withinRole = withinRole.filter((a) => vars["limits.authority"].includes(a));', + 'withinRole = [...new Set([...withinRole, ...vars["limits.authority"]])];', + 'widen through limits: a limits.authority grant adds gated actions to withinRole; the limits-grant test (task.create, role.launch stay gated) must fail'), +] + + +def sha256_of(path): + digest = hashlib.sha256() + with open(path, 'rb') as handle: + for block in iter(lambda: handle.read(65536), b''): + digest.update(block) + return digest.hexdigest() + + +def read_manifest(packet): + entries = [] + with open(os.path.join(packet, 'candidate-manifest.sha256'), encoding='utf-8') as handle: + for line in handle: + line = line.rstrip('\n') + if line: + digest, rel = line.split(' ', 1) + entries.append((digest, rel)) + return entries + + +def manifest_mismatches(wt, entries): + return [rel for digest, rel in entries if sha256_of(os.path.join(wt, rel)) != digest] + + +def resolve_mismatch(wt): + return sha256_of(os.path.join(wt, RESOLVE)) != RESOLVE_SHA256 + + +def kill_group(pgid): + try: + os.killpg(pgid, signal.SIGKILL) + except ProcessLookupError: + pass + + +def run_suite(wt, log_path): + with open(log_path, 'wb') as log: + proc = subprocess.Popen( + SUITE, + cwd=wt, + env=os.environ.copy(), + stdout=log, + stderr=subprocess.STDOUT, + start_new_session=True, + ) + try: + return proc.wait(timeout=TIMEOUT_S), 'exit' + except subprocess.TimeoutExpired: + kill_group(proc.pid) + proc.wait() + return None, 'timeout' + finally: + kill_group(proc.pid) + + +def ancestors(): + # This process and its parents: the runner and the shell that started it. + chain = set() + pid = os.getpid() + while pid > 1 and pid not in chain: + chain.add(pid) + try: + with open(os.path.join('/proc', str(pid), 'stat'), encoding='utf-8') as handle: + stat = handle.read() + except OSError: + break + pid = int(stat.rsplit(')', 1)[1].split()[1]) + return chain + + +def worktree_leftovers(wt): + # Reads /proc cwd links only. Reports; kills nothing. Skips this runner and its parents. + skip = ancestors() + found = [] + for pid in filter(str.isdigit, os.listdir('/proc')): + if int(pid) in skip: + continue + try: + cwd = os.readlink(os.path.join('/proc', pid, 'cwd')) + except OSError: + continue + if cwd == wt or cwd.startswith(wt + os.sep): + found.append((pid, cwd)) + return found + + +def main(): + if len(sys.argv) != 3: + sys.stderr.write('usage: mutants.py \n') + return 2 + wt = os.path.abspath(sys.argv[1]) + packet = os.path.abspath(sys.argv[2]) + out = os.path.join(packet, 'out') + os.makedirs(out, exist_ok=True) + entries = read_manifest(packet) + + broken = manifest_mismatches(wt, entries) + if broken: + print('manifest mismatch before baseline:', broken) + return 2 + if resolve_mismatch(wt): + print(f'{RESOLVE} differs from the pinned base sha256 before baseline') + return 2 + + rc, status = run_suite(wt, os.path.join(out, 'baseline.log')) + if status != 'exit' or rc != 0: + print('baseline red or timed out:', status, rc) + return 2 + + # Original bytes of every file a mutant touches, read once. + originals = {} + for _, rel, _, _, _ in MUTANTS: + if rel not in originals: + with open(os.path.join(wt, rel), 'rb') as handle: + originals[rel] = handle.read() + + # Check every anchor before the first mutant runs. + for mid, rel, old, _, _ in MUTANTS: + count = originals[rel].decode('utf-8').count(old) + if count != 1: + print(f'{mid} ANCHOR count={count} in {rel}; aborting before any mutant') + return 3 + + summary = ['baseline: green'] + failures = [] + for mid, rel, old, new, what in MUTANTS: + path = os.path.join(wt, rel) + original = originals[rel] + text = original.decode('utf-8') + log_path = os.path.join(out, f'{mid}.log') + try: + with open(path, 'wb') as handle: + handle.write(text.replace(old, new, 1).encode('utf-8')) + rc, status = run_suite(wt, log_path) + finally: + with open(path, 'wb') as handle: + handle.write(original) + if status == 'timeout': + verdict = 'TIMEOUT (counted as failure)' + failures.append(mid) + elif rc == 0: + verdict = 'SURVIVED' + failures.append(mid) + else: + verdict = 'killed' + summary.append(f'{mid} {verdict} {what}') + if rel == RESOLVE and resolve_mismatch(wt): + summary.append(f'{RESOLVE} differs from the pinned sha256 after restoring {mid}') + failures.append('restore') + + # The restore must leave the candidate byte-identical to the manifest. + after = manifest_mismatches(wt, entries) + if after: + summary.append(f'manifest mismatch after restore: {after}') + failures.append('restore') + if resolve_mismatch(wt): + summary.append(f'{RESOLVE} differs from the pinned sha256 after the last restore') + failures.append('restore') + + left = worktree_leftovers(wt) + if left: + summary.append(f'leftover processes with cwd in worktree: {left}') + failures.append('leftovers') + + with open(os.path.join(out, 'summary.txt'), 'w', encoding='utf-8') as handle: + handle.write('\n'.join(summary) + '\n') + print('\n'.join(summary)) + + if 'restore' in failures or 'leftovers' in failures: + return 2 + return 1 if failures else 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/agents/researcher/work/queue-84/r1/out/M01.log b/agents/researcher/work/queue-84/r1/out/M01.log new file mode 100644 index 000000000..92e45ffb5 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M01.log @@ -0,0 +1,81 @@ +✔ the role file loads and resolves through loadBusiness (3.994826ms) +✖ the shipped file is exactly decision 104's list (1.226275ms) +✖ the resolved record matches decision 104, so a widened file fails (1.193146ms) +✔ message.send is within role and every other action is gated (0.923285ms) +✔ a business grant of a gated action through limits does not widen the role (0.965033ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.281677ms) +✔ the instance takes no credential references and no tracker (1.622472ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 5 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 63.112702 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.226275ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + ... Skipped lines + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ... + 'ls', + + 'bash' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls', 'bash' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.193146ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + ... Skipped lines + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ... + 'ls', + + 'bash' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:79:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { tools: [ 'read', 'grep', 'find', 'ls', 'bash' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M02.log b/agents/researcher/work/queue-84/r1/out/M02.log new file mode 100644 index 000000000..216110f01 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M02.log @@ -0,0 +1,87 @@ +✔ the role file loads and resolves through loadBusiness (3.899432ms) +✖ the shipped file is exactly decision 104's list (1.304607ms) +✖ the resolved record matches decision 104, so a widened file fails (1.20563ms) +✔ message.send is within role and every other action is gated (0.943252ms) +✔ a business grant of a gated action through limits does not widen the role (0.924543ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.227585ms) +✔ the instance takes no credential references and no tracker (1.605778ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 5 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 62.820095 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.304607ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + ... Skipped lines + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ... + 'read', + + 'write', + 'grep', + 'find', + 'ls' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'write', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.20563ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + ... Skipped lines + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ... + 'read', + + 'write', + 'grep', + 'find', + 'ls' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:79:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { tools: [ 'read', 'write', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M03.log b/agents/researcher/work/queue-84/r1/out/M03.log new file mode 100644 index 000000000..a3c3385fe --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M03.log @@ -0,0 +1,88 @@ +✔ the role file loads and resolves through loadBusiness (4.072406ms) +✖ the shipped file is exactly decision 104's list (1.310913ms) +✖ the resolved record matches decision 104, so a widened file fails (1.324987ms) +✔ message.send is within role and every other action is gated (0.973603ms) +✔ a business grant of a gated action through limits does not widen the role (1.000633ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.252445ms) +✔ the instance takes no credential references and no tracker (1.612458ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 5 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 61.682328 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.310913ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + ... Skipped lines + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ... + name: 'chief-of-staff', + + network: 'open', + - network: 'api-only', + roleVersion: 2, + tools: [ + 'read', + 'grep', + 'find', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'open', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.324987ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ] + }, + + network: 'open', + - network: 'api-only', + tools: [ + 'read', + 'grep', + 'find', + 'ls' + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:79:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'open', authority: { withinRole: [Array], crossRole: [] } }, + expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M04.log b/agents/researcher/work/queue-84/r1/out/M04.log new file mode 100644 index 000000000..51063c2f6 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M04.log @@ -0,0 +1,67 @@ +✔ the role file loads and resolves through loadBusiness (4.048688ms) +✖ the shipped file is exactly decision 104's list (1.281228ms) +✔ the resolved record matches decision 104, so a widened file fails (1.092951ms) +✔ message.send is within role and every other action is gated (0.912028ms) +✔ a business grant of a gated action through limits does not widen the role (0.988276ms) +✖ a launch block whose by is the chief-of-staff instance is refused (1.104735ms) +✔ the instance takes no credential references and no tracker (1.85058ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 5 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 65.031515 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.281228ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send', + + 'role.launch' + ] + }, + contract: 'chief-of-staff.md', + credentials: [], + name: 'chief-of-staff', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:111:1 +✖ a launch block whose by is the chief-of-staff instance is refused (1.104735ms) + AssertionError [ERR_ASSERTION]: Missing expected exception. + at refuses (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:25:10) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:112:3) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'throws', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M05.log b/agents/researcher/work/queue-84/r1/out/M05.log new file mode 100644 index 000000000..21e56a764 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M05.log @@ -0,0 +1,130 @@ +✔ the role file loads and resolves through loadBusiness (4.24849ms) +✖ the shipped file is exactly decision 104's list (1.421829ms) +✖ the resolved record matches decision 104, so a widened file fails (1.408093ms) +✖ message.send is within role and every other action is gated (1.12563ms) +✖ a business grant of a gated action through limits does not widen the role (1.103811ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.329009ms) +✔ the instance takes no credential references and no tracker (1.89958ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 3 +ℹ fail 4 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 66.960529 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.421829ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send', + + 'task.create' + ] + }, + contract: 'chief-of-staff.md', + credentials: [], + name: 'chief-of-staff', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.408093ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send', + + 'task.create' + ] + }, + network: 'api-only', + tools: [ + 'read', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:79:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:87:1 +✖ message.send is within role and every other action is gated (1.12563ms) + AssertionError [ERR_ASSERTION]: task.create + + 'within' !== 'gated' + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:90:12) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 'within', + expected: 'gated', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:97:1 +✖ a business grant of a gated action through limits does not widen the role (1.103811ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + crossRole: [], + withinRole: [ + 'message.send', + + 'task.create' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:105:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { withinRole: [ 'message.send', 'task.create' ], crossRole: [] }, + expected: { withinRole: [ 'message.send' ], crossRole: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M06.log b/agents/researcher/work/queue-84/r1/out/M06.log new file mode 100644 index 000000000..98859baba --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M06.log @@ -0,0 +1,102 @@ +✔ the role file loads and resolves through loadBusiness (4.126964ms) +✖ the shipped file is exactly decision 104's list (1.380939ms) +✖ the resolved record matches decision 104, so a widened file fails (1.30879ms) +✖ message.send is within role and every other action is gated (1.100439ms) +✔ a business grant of a gated action through limits does not widen the role (1.02059ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.274746ms) +✔ the instance takes no credential references and no tracker (1.695635ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 4 +ℹ fail 3 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 64.901129 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.380939ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + + crossRole: [ + + 'task.scope.change' + + ], + - crossRole: [], + withinRole: [ + 'message.send' + ] + }, + contract: 'chief-of-staff.md', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [Array] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.30879ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + + crossRole: [ + + 'task.scope.change' + + ], + - crossRole: [], + withinRole: [ + 'message.send' + ] + }, + network: 'api-only', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:79:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [Array] } }, + expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:87:1 +✖ message.send is within role and every other action is gated (1.100439ms) + AssertionError [ERR_ASSERTION]: task.scope.change + + 'cross' !== 'gated' + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:90:12) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 'cross', + expected: 'gated', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M07.log b/agents/researcher/work/queue-84/r1/out/M07.log new file mode 100644 index 000000000..6950788d3 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M07.log @@ -0,0 +1,166 @@ +✖ the role file loads and resolves through loadBusiness (3.556457ms) +✖ the shipped file is exactly decision 104's list (1.466732ms) +✖ the resolved record matches decision 104, so a widened file fails (1.024723ms) +✖ message.send is within role and every other action is gated (0.899799ms) +✖ a business grant of a gated action through limits does not widen the role (0.834197ms) +✖ a launch block whose by is the chief-of-staff instance is refused (1.222533ms) +✖ the instance takes no credential references and no tracker (1.174486ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 0 +ℹ fail 7 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 64.501684 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:49:1 +✖ the role file loads and resolves through loadBusiness (3.556457ms) + Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-eGLdKg/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none) + at refuse (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/errors.mjs:12:9) + at checkCredentialMap (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:45:5) + at checkRoles (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:108:20) + at validateBusinessDocument (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:155:34) + at loadBusiness (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:203:10) + at loadFixture (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:42:28) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:50:19) + 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) { + exitCode: 2 + } + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.466732ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + ... Skipped lines + + { + authority: { + crossRole: [], + withinRole: [ + 'message.send' + ... + contract: 'chief-of-staff.md', + + credentials: [ + + { + + scopes: [ + + 'read:issue' + + ], + + service: 'gitea' + + } + + ], + - credentials: [], + name: 'chief-of-staff', + network: 'api-only', + roleVersion: 2, + tools: [ + 'read', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [ [Object] ] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.024723ms) + Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-o8Qu9s/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none) + at refuse (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/errors.mjs:12:9) + at checkCredentialMap (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:45:5) + at checkRoles (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:108:20) + at validateBusinessDocument (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:155:34) + at loadBusiness (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:203:10) + at loadFixture (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:42:28) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:78:27) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) { + exitCode: 2 + } + +test at packages/business/tests/chief-of-staff.test.mjs:87:1 +✖ message.send is within role and every other action is gated (0.899799ms) + Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-27UjrJ/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none) + at refuse (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/errors.mjs:12:9) + at checkCredentialMap (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:45:5) + at checkRoles (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:108:20) + at validateBusinessDocument (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:155:34) + at loadBusiness (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:203:10) + at loadFixture (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:42:28) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:88:27) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) { + exitCode: 2 + } + +test at packages/business/tests/chief-of-staff.test.mjs:97:1 +✖ a business grant of a gated action through limits does not widen the role (0.834197ms) + Error [BusinessError]: /mnt/storage/scratch/tmp/mosaic-business-IzUb4G/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none) + at refuse (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/errors.mjs:12:9) + at checkCredentialMap (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:45:5) + at checkRoles (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:108:20) + at validateBusinessDocument (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:155:34) + at loadBusiness (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/src/business.mjs:203:10) + at loadFixture (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:42:28) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:101:19) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) { + exitCode: 2 + } + +test at packages/business/tests/chief-of-staff.test.mjs:111:1 +✖ a launch block whose by is the chief-of-staff instance is refused (1.222533ms) + AssertionError [ERR_ASSERTION]: The input did not match the regular expression /launch\.by names chief, whose role chief-of-staff doesn't hold role\.launch within-role/. Input: + + '/mnt/storage/scratch/tmp/mosaic-business-k2CaoN/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)' + + at Object. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:28:12) + at expectedException (node:assert:547:17) + at expectsError (node:assert:672:21) + at strict.throws (node:assert:728:3) + at refuses (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:25:10) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:112:3) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '/mnt/storage/scratch/tmp/mosaic-business-k2CaoN/businesses/acme.json roles.chief.credentials must reference exactly the services its role definition needs (gitea; got none)', + expected: /launch\.by names chief, whose role chief-of-staff doesn't hold role\.launch within-role/, + operator: 'match', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:120:1 +✖ the instance takes no credential references and no tracker (1.174486ms) + AssertionError [ERR_ASSERTION]: Missing expected exception. + at refuses (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:25:10) + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:121:3) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: undefined, + operator: 'throws', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M08.log b/agents/researcher/work/queue-84/r1/out/M08.log new file mode 100644 index 000000000..2c4dd4fc5 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M08.log @@ -0,0 +1,132 @@ +✔ the role file loads and resolves through loadBusiness (4.401442ms) +✖ the shipped file is exactly decision 104's list (1.447679ms) +✖ the resolved record matches decision 104, so a widened file fails (1.399622ms) +✖ message.send is within role and every other action is gated (1.116527ms) +✖ a business grant of a gated action through limits does not widen the role (1.076498ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.326492ms) +✔ the instance takes no credential references and no tracker (1.916131ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 3 +ℹ fail 4 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 67.124477 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:65:1 +✖ the shipped file is exactly decision 104's list (1.447679ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + crossRole: [], + + withinRole: [] + - withinRole: [ + - 'message.send' + - ] + }, + contract: 'chief-of-staff.md', + credentials: [], + name: 'chief-of-staff', + network: 'api-only', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:69:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [], crossRole: [] }, credentials: [] }, + expected: { roleVersion: 2, name: 'chief-of-staff', contract: 'chief-of-staff.md', tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] }, credentials: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:77:1 +✖ the resolved record matches decision 104, so a widened file fails (1.399622ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + authority: { + crossRole: [], + + withinRole: [] + - withinRole: [ + - 'message.send' + - ] + }, + network: 'api-only', + tools: [ + 'read', + 'grep', + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:79:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [], crossRole: [] } }, + expected: { tools: [ 'read', 'grep', 'find', 'ls' ], network: 'api-only', authority: { withinRole: [Array], crossRole: [] } }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:87:1 +✖ message.send is within role and every other action is gated (1.116527ms) + AssertionError [ERR_ASSERTION]: message.send + + 'gated' !== 'within' + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:90:12) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 'gated', + expected: 'within', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/business/tests/chief-of-staff.test.mjs:97:1 +✖ a business grant of a gated action through limits does not widen the role (1.076498ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + crossRole: [], + + withinRole: [] + - withinRole: [ + - 'message.send' + - ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:105:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { withinRole: [], crossRole: [] }, + expected: { withinRole: [ 'message.send' ], crossRole: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/M09.log b/agents/researcher/work/queue-84/r1/out/M09.log new file mode 100644 index 000000000..de1f75ca2 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/M09.log @@ -0,0 +1,45 @@ +✔ the role file loads and resolves through loadBusiness (4.25175ms) +✔ the shipped file is exactly decision 104's list (0.541146ms) +✔ the resolved record matches decision 104, so a widened file fails (1.411464ms) +✔ message.send is within role and every other action is gated (1.024557ms) +✖ a business grant of a gated action through limits does not widen the role (1.819517ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.314522ms) +✔ the instance takes no credential references and no tracker (1.735865ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 6 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 67.477903 + +✖ failing tests: + +test at packages/business/tests/chief-of-staff.test.mjs:97:1 +✖ a business grant of a gated action through limits does not widen the role (1.819517ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + crossRole: [], + withinRole: [ + 'message.send', + + 'task.create' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/chief-of-staff.test.mjs:105:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { withinRole: [ 'message.send', 'task.create' ], crossRole: [] }, + expected: { withinRole: [ 'message.send' ], crossRole: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/baseline-M09.log b/agents/researcher/work/queue-84/r1/out/baseline-M09.log new file mode 100644 index 000000000..cd4295f15 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/baseline-M09.log @@ -0,0 +1,158 @@ +✔ config directory and file path follow MOSAIC_CONFIG (2.021224ms) +✔ the fixture business validates and comes back frozen (5.812521ms) +✔ two instances may share a definition (1.806194ms) +✔ top-level refusals (4.959277ms) +✔ arbiters and projects (7.209034ms) +✔ role instances (3.770139ms) +✔ Vikunja bots (6.692697ms) +✔ a role without Vikunja takes no tracker block (2.18964ms) +✔ credential references match the definition's services (2.468081ms) +✔ launch (7.28666ms) +✔ loadBusiness: file checks (1.80793ms) +✔ loadBusiness: not a regular file (32.902489ms) +✔ loading writes nothing (0.978599ms) +✔ names that are Object.prototype properties don't count as declared (2.581042ms) +✔ the shipped example refuses as written and validates once filled in (0.498041ms) +✔ usage errors exit 4 (252.367757ms) +✔ validate: a good business exits 0 and prints instance digests (65.232225ms) +✔ validate: project files (299.749384ms) +✔ validate: missing files and a broken system config (228.250255ms) +✔ validate: credential reference problems exit 2 and name each one (62.665718ms) +✔ validate: a token file inside the repository is refused (63.109055ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (180.114861ms) +✔ resolve: prints one instance's record (192.054508ms) +✔ resolve: refusals (353.946123ms) +✔ parse: exactly one of file or env, plus the service's date (3.089613ms) +✔ check: a good file has no problems (1.216735ms) +✔ check never opens the file: a write-only token passes (0.358227ms) +✔ check: file problems (1.073954ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.077818ms) +✔ check: dates and environment references (0.490084ms) +✔ path and load (2.635696ms) +✔ refusals (1.285323ms) +✔ systemVars flattens the validated config (2.080294ms) +✔ precedence: system, business, project, project role, agent (5.33056ms) +✖ limits narrow the definition and never widen it (3.16106ms) +✔ role.launch stays within-role only for the instance the launch block names (5.034758ms) +✖ limits.authority without role.launch leaves the launcher with no launch block (1.526233ms) +✖ limits.authority narrows cross-role actions too (1.199771ms) +✔ classify (1.545585ms) +✔ the record carries what the broker and launcher need (1.405872ms) +✔ digest: key order doesn't matter, any value change does (7.487623ms) +✔ refusals (2.19712ms) +✔ the four shipped version 2 roles load (3.319803ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.445222ms) +✔ shipped authority follows the note's table (0.778483ms) +✔ version 1 files keep loading with no authority (1.008424ms) +✔ the conductor policy isn't a role (0.215909ms) +✔ a missing role file is exit 4, a symbolic link too (0.369773ms) +✔ version 2 refusals (1.877697ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.192737ms) +✔ credentials: Gitea scopes (0.967394ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (4.765015ms) +✔ credentials: services (0.96753ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.938768ms) +✔ every key names known layers and a merge rule (1.616381ms) +✔ unknown keys and wrong layers refuse (0.628224ms) +✔ types (1.958411ms) +✔ merge: defaults, then the most specific layer wins (0.256552ms) +✔ merge: limits only narrow, and provenance lists each source (0.379627ms) +✔ merge doesn't change its inputs (0.211826ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 57 +ℹ fail 3 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1778.943009 + +✖ failing tests: + +test at packages/business/tests/resolve.test.mjs:69:1 +✖ limits narrow the definition and never widen it (3.16106ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + crossRole: [], + withinRole: [ + 'review.verdict', + + 'message.send', + + 'task.update.assigned', + + 'task.close' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/resolve.test.mjs:86:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { withinRole: [ 'review.verdict', 'message.send', 'task.update.assigned', 'task.close' ], crossRole: [] }, + expected: { withinRole: [ 'review.verdict' ], crossRole: [] }, + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/business/tests/resolve.test.mjs:112:1 +✖ limits.authority without role.launch leaves the launcher with no launch block (1.526233ms) + AssertionError [ERR_ASSERTION]: agent + + 'within' !== 'gated' + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/resolve.test.mjs:119:12) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 'within', + expected: 'gated', + operator: 'strictEqual', + diff: 'simple' + } + +test at packages/business/tests/resolve.test.mjs:124:1 +✖ limits.authority narrows cross-role actions too (1.199771ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + crossRole: [ + 'task.priority.change' + ], + withinRole: [ + 'task.create', + + 'task.assign', + + 'task.reassign', + + 'task.schedule', + + 'task.update.assigned', + 'task.close', + + 'role.launch', + + 'message.send', + + 'task.priority.change' + ] + } + + at TestContext. (file:///mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36/packages/business/tests/resolve.test.mjs:130:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: { withinRole: [ 'task.create', 'task.assign', 'task.reassign', 'task.schedule', 'task.update.assigned', 'task.close', 'role.launch', 'message.send', 'task.priority.change' ], crossRole: [ 'task.priority.change' ] }, + expected: { withinRole: [ 'task.create', 'task.close' ], crossRole: [ 'task.priority.change' ] }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/researcher/work/queue-84/r1/out/baseline.log b/agents/researcher/work/queue-84/r1/out/baseline.log new file mode 100644 index 000000000..0bf5804e1 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/baseline.log @@ -0,0 +1,15 @@ +✔ the role file loads and resolves through loadBusiness (4.336752ms) +✔ the shipped file is exactly decision 104's list (0.522107ms) +✔ the resolved record matches decision 104, so a widened file fails (1.232608ms) +✔ message.send is within role and every other action is gated (0.939823ms) +✔ a business grant of a gated action through limits does not widen the role (0.993219ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.213368ms) +✔ the instance takes no credential references and no tracker (1.669448ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 7 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 65.934698 diff --git a/agents/researcher/work/queue-84/r1/out/step1-business.txt b/agents/researcher/work/queue-84/r1/out/step1-business.txt new file mode 100644 index 000000000..9f7710c6a --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step1-business.txt @@ -0,0 +1,75 @@ +✔ config directory and file path follow MOSAIC_CONFIG (1.321193ms) +✔ the fixture business validates and comes back frozen (4.687127ms) +✔ two instances may share a definition (1.810628ms) +✔ top-level refusals (3.788242ms) +✔ arbiters and projects (5.242864ms) +✔ role instances (2.429966ms) +✔ Vikunja bots (5.470738ms) +✔ a role without Vikunja takes no tracker block (2.189017ms) +✔ credential references match the definition's services (2.398879ms) +✔ launch (6.863709ms) +✔ loadBusiness: file checks (1.564557ms) +✔ loadBusiness: not a regular file (35.048766ms) +✔ loading writes nothing (0.974609ms) +✔ names that are Object.prototype properties don't count as declared (2.575013ms) +✔ the shipped example refuses as written and validates once filled in (0.820943ms) +✔ the role file loads and resolves through loadBusiness (5.877368ms) +✔ the shipped file is exactly decision 104's list (0.584942ms) +✔ the resolved record matches decision 104, so a widened file fails (1.520389ms) +✔ message.send is within role and every other action is gated (1.581668ms) +✔ a business grant of a gated action through limits does not widen the role (1.116976ms) +✔ a launch block whose by is the chief-of-staff instance is refused (1.487857ms) +✔ the instance takes no credential references and no tracker (1.80515ms) +✔ usage errors exit 4 (257.995578ms) +✔ validate: a good business exits 0 and prints instance digests (67.096159ms) +✔ validate: project files (295.297719ms) +✔ validate: missing files and a broken system config (233.28047ms) +✔ validate: credential reference problems exit 2 and name each one (65.655802ms) +✔ validate: a token file inside the repository is refused (58.998863ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (178.568189ms) +✔ resolve: prints one instance's record (185.33035ms) +✔ resolve: refusals (351.447287ms) +✔ parse: exactly one of file or env, plus the service's date (1.961373ms) +✔ check: a good file has no problems (0.676509ms) +✔ check never opens the file: a write-only token passes (0.262342ms) +✔ check: file problems (0.637505ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.621696ms) +✔ check: dates and environment references (0.448876ms) +✔ path and load (2.620814ms) +✔ refusals (1.233241ms) +✔ systemVars flattens the validated config (3.451177ms) +✔ precedence: system, business, project, project role, agent (4.169078ms) +✔ limits narrow the definition and never widen it (2.334135ms) +✔ role.launch stays within-role only for the instance the launch block names (5.40089ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (1.549211ms) +✔ limits.authority narrows cross-role actions too (0.868372ms) +✔ classify (1.018188ms) +✔ the record carries what the broker and launcher need (0.911184ms) +✔ digest: key order doesn't matter, any value change does (5.172475ms) +✔ refusals (1.958311ms) +✔ the four shipped version 2 roles load (2.570572ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.150152ms) +✔ shipped authority follows the note's table (0.675188ms) +✔ version 1 files keep loading with no authority (0.937733ms) +✔ the conductor policy isn't a role (0.190986ms) +✔ a missing role file is exit 4, a symbolic link too (0.325947ms) +✔ version 2 refusals (1.428208ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (1.745317ms) +✔ credentials: Gitea scopes (0.697614ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.99846ms) +✔ credentials: services (0.479386ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.595185ms) +✔ every key names known layers and a merge rule (0.700528ms) +✔ unknown keys and wrong layers refuse (0.618688ms) +✔ types (1.4323ms) +✔ merge: defaults, then the most specific layer wins (0.300338ms) +✔ merge: limits only narrow, and provenance lists each source (0.42645ms) +✔ merge doesn't change its inputs (0.170421ms) +ℹ tests 67 +ℹ suites 0 +ℹ pass 67 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1758.099594 diff --git a/agents/researcher/work/queue-84/r1/out/step2-test-auth.txt b/agents/researcher/work/queue-84/r1/out/step2-test-auth.txt new file mode 100644 index 000000000..ac2287606 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-auth.txt @@ -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/researcher/work/queue-84/r1/out/step2-test-conductor.txt b/agents/researcher/work/queue-84/r1/out/step2-test-conductor.txt new file mode 100644 index 000000000..cf8c85a7a --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'ac6b56ee55278077d61f39e6ca42513725427154'. + +You are in 'detached HEAD' state. You can look around, make experimental +changes and commit them, and you can discard any commits you make in this +state without impacting any branches by switching back to a branch. + +If you want to create a new branch to retain commits you create, you may +do so (now or later) by using -c with the switch command. Example: + + git switch -c + +Or undo this operation with: + + git switch - + +Turn off this advice by setting config variable advice.detachedHead to false + +Not currently on any branch. +nothing to commit, working tree clean +Note: switching to 'ac6b56ee55278077d61f39e6ca42513725427154'. + +You are in 'detached HEAD' state. You can look around, make experimental +changes and commit them, and you can discard any commits you make in this +state without impacting any branches by switching back to a branch. + +If you want to create a new branch to retain commits you create, you may +do so (now or later) by using -c with the switch command. Example: + + git switch -c + +Or undo this operation with: + + git switch - + +Turn off this advice by setting config variable advice.detachedHead to false + +OK dry-run: allowed change, exit 0, nothing committed (exit 0) +OK dry-run committed nothing +OK apply: allowed change exits 0 (exit 0) +OK apply: attribution in commit subject +OK apply: target tree clean after commit +OK disallowed path refused (exit 1) +OK disallowed path: target untouched +OK syntax gate refused broken .mjs (exit 1) +OK syntax gate: target untouched +OK suite failure refused (exit 1) +OK suite failure: target reverted to clean +OK disabled policy refused (exit 2) +OK disabled policy: target untouched +OK failed run refused (exit 1) +OK failed run: target untouched +OK missing run exits 4 (exit 4) +OK invalid policy exits 2 (exit 2) + +selftest: 17 passed, 0 failed diff --git a/agents/researcher/work/queue-84/r1/out/step2-test-config.txt b/agents/researcher/work/queue-84/r1/out/step2-test-config.txt new file mode 100644 index 000000000..76c05ef74 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/researcher/work/queue-84/r1/out/step2-test-discord.txt b/agents/researcher/work/queue-84/r1/out/step2-test-discord.txt new file mode 100644 index 000000000..b013f9ffa --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-discord.txt @@ -0,0 +1,70 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/notify.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/notify.test.mjs +OK syntax: packages/discord/tests/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 178) +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 + +discord suite: 66 passed, 0 failed diff --git a/agents/researcher/work/queue-84/r1/out/step2-test-extension-package.txt b/agents/researcher/work/queue-84/r1/out/step2-test-extension-package.txt new file mode 100644 index 000000000..d5d83deee --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-extension-package.txt @@ -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: 1035415 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/researcher/work/queue-84/r1/out/step2-test-foundation.txt b/agents/researcher/work/queue-84/r1/out/step2-test-foundation.txt new file mode 100644 index 000000000..6338e1e6d --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-foundation.txt @@ -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/researcher/work/queue-84/r1/out/step2-test-queue.txt b/agents/researcher/work/queue-84/r1/out/step2-test-queue.txt new file mode 100644 index 000000000..ee78c7162 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-queue.txt @@ -0,0 +1,35 @@ +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/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 150 +ℹ pass 150 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/mnt/storage/src/mosaic-stack/.claude/worktrees/agent-a1044a9b545fa3a36) is not the queue's canonical root (/mnt/storage/src/mosaic-stack) + +queue suite: 27 passed, 0 failed diff --git a/agents/researcher/work/queue-84/r1/out/step2-test-release.txt b/agents/researcher/work/queue-84/r1/out/step2-test-release.txt new file mode 100644 index 000000000..7202b85d4 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-release.txt @@ -0,0 +1,16 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +OK status safe on empty state (exit 0) +OK status created no pointer +OK fault-injected activation refuses (exit 1) +OK refused activation wrote no pointer +OK refusal logged exactly once with valid fields +OK healthy activation succeeds (exit 0) +OK pointer written with valid fields +OK repeat activation succeeds (log grows) (exit 0) +OK log is append-only across activations +OK rollback without previous refuses (exit 1) + +selftest: 14 passed, 0 failed diff --git a/agents/researcher/work/queue-84/r1/out/step2-test-task.txt b/agents/researcher/work/queue-84/r1/out/step2-test-task.txt new file mode 100644 index 000000000..cd89fe9b2 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/step2-test-task.txt @@ -0,0 +1,32 @@ +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) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +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 recall pair: user recall run, recalled user name (docker daemon unavailable) +OK no agent identity on headless run + +selftest: 26 passed, 0 failed diff --git a/agents/researcher/work/queue-84/r1/out/summary.txt b/agents/researcher/work/queue-84/r1/out/summary.txt new file mode 100644 index 000000000..52496a711 --- /dev/null +++ b/agents/researcher/work/queue-84/r1/out/summary.txt @@ -0,0 +1,10 @@ +baseline: green +M01 killed widen tools: bash added; shipped-file equality test fails +M02 killed widen tools: write added (a write tool, which waits for I4); equality test fails +M03 killed widen network: api-only to open; equality test fails +M04 killed widen withinRole: role.launch within-role; equality test fails +M05 killed widen withinRole: task.create within-role; equality test and classify test fail +M06 killed widen crossRole: task.scope.change; equality test fails +M07 killed widen credentials: gitea scope; business fixture refuses the instance (no matching credential ref) and equality test fails +M08 killed narrow withinRole: message.send dropped; equality and classify (within for message.send) tests fail +M09 killed widen through limits: a limits.authority grant adds gated actions to withinRole; the limits-grant test (task.create, role.launch stay gated) must fail