From 2d274a5b6c35c3e74b07206ffd724a6579bb149d Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sat, 24 Oct 2026 05:06:00 -0500 Subject: [PATCH] row 217: correct new runtime fixture and record first-run evidence --- agents/beakley/work/queue-217/BUILD.md | 31 +++++++++++++ .../beakley/work/queue-217/manifest.r6.sha256 | 23 ++++++++++ agents/beakley/work/queue-217/mutants.py | 46 ++++++++++++++----- packages/bus/tests/citation-runtime.test.mjs | 11 +++-- 4 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 agents/beakley/work/queue-217/manifest.r6.sha256 diff --git a/agents/beakley/work/queue-217/BUILD.md b/agents/beakley/work/queue-217/BUILD.md index 530bcf645..02fcfe9ca 100644 --- a/agents/beakley/work/queue-217/BUILD.md +++ b/agents/beakley/work/queue-217/BUILD.md @@ -225,3 +225,34 @@ an edit. Full green gate, mutants and Webby verdict remain outstanding. Range-diff receipt rendering trims trailing whitespace on diff separator lines; no substantive range-diff content changed. This removes whitespace warnings caused by embedding raw diff output in a tracked text file. Final base-to-candidate whitespace check found a trailing blank line inherited from Launchpad in limits.mjs; removed it without behavior changes. manifest.r5.sha256 supersedes r4 for the exact candidate file hashes. + + +## CLEAR sage-4042/4043 — execution started 2026-10-24 + +Queue beakley-217-start initially refused Lena's review-outcome lock twice; +same-op retry succeeded at rev3894 (briefed→in-progress). No queue bypass. +Short TMPDIR /mnt/storage/scratch/tmp/tNoueZ. Worktree-local npm ci at root +(128 packages) and packages/webui (2) completed; neither path was a symlink. +No npm/rm command ran in canonical. All test commands use timeout -k 10. + +E4 current file: 3 pass/0 fail/0 skip. M5 isolated pre-fix stand-in: 1 pass, +2 expected assertion failures (citation-invalid instead of citation-unavailable), +exit1. Restored product bytes and checkout product hashes, then E4 restored +control 3/3. Receipts under run-r2/m5-red. M1 six-space anchors both match +exactly once on the eight-space lines. + +Touched test files first run: 56 pass/1 fail, zero skip/cancel. The sole first-red +was NEW citation-runtime.test.mjs invalid-authority at Broker.checkBusiness: +validator output has unresolved roles and is not the host's broker projection. +Adjusted that new fixture only, following limits-runtime.test.mjs: minimal +empty-authority coder, validated real project mappings, real organization adapter +and systemFor(root). Single rerun 1/1, then all touched files 57/57. No base test +failure or edit. First log retained as run-r2/touched-first.log and reported to +Sage in beakley-0014. No production change was required. + +The isolated mutant driver now wraps each node run in timeout -k 10, refuses a +symlinked root node_modules, restores mutated files in finally, verifies original +checkout product hashes after every mutant and reruns the restored control. +Optional name filters allow the E4 M5 RED before the full mutant pass. No +canonical dependency link is used; isolated copies link only this worktree's +own dependency install. diff --git a/agents/beakley/work/queue-217/manifest.r6.sha256 b/agents/beakley/work/queue-217/manifest.r6.sha256 new file mode 100644 index 000000000..2aefac75f --- /dev/null +++ b/agents/beakley/work/queue-217/manifest.r6.sha256 @@ -0,0 +1,23 @@ +eeca81ab965f9e94f930ee5f51d16f019af6368bc6c3994895e204f7a54ec8d2 packages/bus/src/broker.mjs +ad3c686a089f67f3c89610937c6fc9b32367cea5d9735125d89bef042082d4e8 packages/bus/src/client.mjs +36b05dd3119e5929211aa1cc5d45c5043f5a1c34567ddc7cac8f002835d44abe packages/bus/src/runtime.mjs +66b26d131a19f4f411686427274936850c1ff0aed11e9b281ae4aadcf4b289fc packages/bus/src/server.mjs +cdd0304ec25ef1ad3e31c52502fa46f2c80bd77c8d3c2e13f8ed4b5328f342c9 packages/bus/tests/citation-availability.test.mjs +2e05074cd9d15d810103658af3858161cee2af78b0a2e1ba910222fab3e84401 packages/bus/tests/citation-guard.test.mjs +2c7a2102e2e0244876e41b664f4f706baefe3375100b956655551db1230f5da3 packages/bus/tests/citation-helpers.mjs +9768b6dc5ae078c5cc4f76473097287b74c5bc5853d39e1f57f153e646f861af packages/bus/tests/citation-runtime.test.mjs +abaeac64f2c3bf8ba83c9a4488edd9105c4707a6564560c5333d5421445653af packages/business/src/business.mjs +3a6044de5d1ca2cc35c5285b460c3e402b4274837e5d88b97f489797f6004903 packages/business/src/organization.mjs +4d2eb11beaa80175e524af1b95c81e7164990fba1d3e1754f5ba06eee390a14f packages/business/tests/reply-limits.test.mjs +a2bb98734bcc2556f8b2168f0faf8a89f7064c3f0aa49feb7d1975d596192f09 packages/cli/README.md +2f291a71f490f2741edada7a3e3788500f8976a08eb8ab1411f6c5982f778260 packages/cli/src/limits.mjs +f38af2d46d5f5f6388f599912507b94b87a0bf9dcc2104bb2187c38ee35118d5 packages/cli/tests/reply-limits.test.mjs +6ad72b4245e0a66e32725941900c1ece64c2e5de2fb73d65c8e042b4f3a691a6 packages/harness/src/runner.mjs +a5ef1b65230239a7baf6d0e8f2b8a51c0516348c7c4339a55b3f0a1a6ff531ba packages/harness/tests/citation-runner.test.mjs +330bc71bf6c7e64d3ddb70440903d2d9acf7878d44a49d7e30c2352ee4c807ec packages/harness/tests/fixtures/citation-adapter.mjs +f710d9600a17f6e2989a289cfe84e58f161d86c7306605946ce05fa71b364eb2 packages/library/src/guard.mjs +c0d630798914e300e461c247340533328e97119e54909c6a9406340e61fed8b7 packages/library/src/index.mjs +98ef45ed61144aaea44d8aea1ac43082b6d340297e46846d01772da38728e530 packages/library/src/limits.mjs +7b46f47b5e9d6b554b5d39bce2cd95c4610f5f35b8ab8fdc5a2864b3214be809 packages/library/src/reply-protocol.mjs +406146ebbce3710f308243b684a1c17e7626c1b65a4468489364317e31537a48 packages/library/tests/guard.test.mjs +39c8e6c8250a13fa43015fb2c407195e30ee642f729a9f6a644c38b4ea758a72 packages/seat/src/sandbox.mjs diff --git a/agents/beakley/work/queue-217/mutants.py b/agents/beakley/work/queue-217/mutants.py index cff7f03e2..0283e06e9 100644 --- a/agents/beakley/work/queue-217/mutants.py +++ b/agents/beakley/work/queue-217/mutants.py @@ -6,6 +6,7 @@ Usage: python3 agents/beakley/work/queue-217/mutants.py OUTPUT_DIRECTORY """ from pathlib import Path import io +import hashlib import os import subprocess import sys @@ -40,29 +41,50 @@ MUTANTS = [ archive = subprocess.check_output(["git", "archive", "HEAD", "packages", "roles", "scripts", "tools", "docs", "package.json", "RELEASE"], cwd=ROOT) summary = [] +selected = set(sys.argv[2:]) +product_files = [line.split(' ', 1)[1] for line in (ROOT / 'agents/beakley/work/queue-217/manifest.r5.sha256').read_text().splitlines()] +def hashes(base, files): + return {file: hashlib.sha256((base / file).read_bytes()).hexdigest() for file in files} +original_products = hashes(ROOT, product_files) for name, changes, tests in MUTANTS: + if selected and name not in selected: + continue with tempfile.TemporaryDirectory(prefix="row217-mutant-") as directory: work = Path(directory) with tarfile.open(fileobj=io.BytesIO(archive)) as source: source.extractall(work, filter="data") modules = ROOT / "node_modules" + if modules.is_symlink(): + raise SystemExit('root node_modules must be a local install, never a canonical link') if modules.exists(): os.symlink(modules.resolve(), work / "node_modules", target_is_directory=True) - cmd = ["node", "--test", *tests] + cmd = ["timeout", "-k", "10", "120", "node", "--test", *tests] with (OUT / f"{name}-control.log").open("w") as log: control = subprocess.run(cmd, cwd=work, stdout=log, stderr=subprocess.STDOUT, timeout=120) if control.returncode: raise SystemExit(f"{name}: control RED, no mutant result claimed") - for file, before, after in changes: - path = work / file - text = path.read_text() - if text.count(before) != 1: - raise SystemExit(f"{name}: expected exactly one mutation anchor in {file}") - path.write_text(text.replace(before, after)) - with (OUT / f"{name}.log").open("w") as log: - mutant = subprocess.run(cmd, cwd=work, stdout=log, stderr=subprocess.STDOUT, timeout=120) - if mutant.returncode == 0: - raise SystemExit(f"{name}: SURVIVED") - summary.append(f"{name}: killed (control green, mutant exit {mutant.returncode})") + originals = {file: (work / file).read_bytes() for file, _, _ in changes} + try: + for file, before, after in changes: + path = work / file + text = path.read_text() + if text.count(before) != 1: + raise SystemExit(f"{name}: expected exactly one mutation anchor in {file}") + path.write_text(text.replace(before, after)) + with (OUT / f"{name}.log").open("w") as log: + mutant = subprocess.run(cmd, cwd=work, stdout=log, stderr=subprocess.STDOUT, timeout=140) + if mutant.returncode != 1: + raise SystemExit(f"{name}: expected assertion failure exit 1, got {mutant.returncode}") + finally: + for file, content in originals.items(): + (work / file).write_bytes(content) + assert all((work / file).read_bytes() == content for file, content in originals.items()) + assert hashes(ROOT, product_files) == original_products, 'checkout product hashes changed' + (OUT / f"{name}-restored.sha256").write_text(''.join(f'{digest} {file}\n' for file, digest in sorted(original_products.items()))) + with (OUT / f"{name}-restored-green.log").open("w") as log: + restored = subprocess.run(cmd, cwd=work, stdout=log, stderr=subprocess.STDOUT, timeout=140) + if restored.returncode: + raise SystemExit(f"{name}: restored control RED") + summary.append(f"{name}: killed (control green, mutant exit {mutant.returncode}, hashes restored, restored green)") (OUT / "summary.txt").write_text("\n".join(summary) + "\n") print(summary[-1], flush=True) diff --git a/packages/bus/tests/citation-runtime.test.mjs b/packages/bus/tests/citation-runtime.test.mjs index 70d821e06..fdcfdadfc 100644 --- a/packages/bus/tests/citation-runtime.test.mjs +++ b/packages/bus/tests/citation-runtime.test.mjs @@ -6,7 +6,7 @@ import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { startBroker } from '../src/runtime.mjs'; import { validateBusinessDocument } from '../../business/src/business.mjs'; -import { businessDoc, rolesCopy, writeJson } from '../../business/tests/helpers.mjs'; +import { businessDoc, rolesCopy, systemFor, writeJson } from '../../business/tests/helpers.mjs'; import { mappingEntry } from '../../library/tests/helpers.mjs'; test('C2-06 runtime reads changed and unreadable reply limits without restarting', async t => { @@ -14,8 +14,13 @@ test('C2-06 runtime reads changed and unreadable reply limits without restarting const doc = businessDoc(root), rolesDir = rolesCopy(root), dir = join(root, 'config'), dataRoot = join(root, 'data'); doc.projects.books = mappingEntry(); doc.replyLimits = { requireCitation: false, citationsPerReply: 3 }; const file = writeJson(join(dir, 'businesses/acme.json'), doc); - const business = validateBusinessDocument(doc, file, { rolesDir }); mkdirSync(dataRoot, { mode: 0o700 }); - const runtime = await startBroker({ dataRoot, businesses: { acme: business }, organization: { dir, rolesDir } }); + const checked = validateBusinessDocument(doc, file, { rolesDir }); mkdirSync(dataRoot, { mode: 0o700 }); + // Runtime takes the host's broker projection, not the business validator's unresolved role entries. + // No agent action is needed here: use the same empty-authority shape as limits-runtime.test.mjs. + const business = { id: 'acme', human: 'jason', projects: checked.projects, + arbiters: { technical: 'coder', delivery: 'coder' }, + roles: { coder: { definition: 'coder', authority: { withinRole: [], crossRole: [] } } } }; + const runtime = await startBroker({ dataRoot, businesses: { acme: business }, organization: { dir, rolesDir, system: systemFor(root) } }); t.after(() => runtime.close()); const human = runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }); const status = () => runtime.broker.request(human, { verb: 'library.status', args: { project: 'books' } });