From 9bf4683c65516410e8ca45e93f8ac11b96023b36 Mon Sep 17 00:00:00 2001 From: Rocko Date: Thu, 22 Oct 2026 00:06:35 -0500 Subject: [PATCH] docs: record Vikunja 502 focused verification and rebase evidence --- agents/rocko/work/vikunja-502/BUILD.md | 128 +++++++ agents/rocko/work/vikunja-502/RESULTS.md | 22 ++ .../rocko/work/vikunja-502/all-mutants.json | 52 +++ .../work/vikunja-502/base-drift-ce6a.json | 12 + agents/rocko/work/vikunja-502/base.txt | 1 + agents/rocko/work/vikunja-502/build.patch | 312 ++++++++++++++++++ .../vikunja-502/candidate-manifest.sha256 | 5 + agents/rocko/work/vikunja-502/candidate.txt | 1 + .../rocko/work/vikunja-502/changed-paths.txt | 5 + agents/rocko/work/vikunja-502/commit.txt | 1 + agents/rocko/work/vikunja-502/cwd-sweep.json | 1 + .../work/vikunja-502/focused-results.json | 87 +++++ agents/rocko/work/vikunja-502/gate-plan.json | 29 ++ .../work/vikunja-502/logs/bus-tasks-whole.log | 64 ++++ .../logs/processing-http-whole.log | 46 +++ .../vikunja-502/logs/python-regressions.log | 5 + .../rocko/work/vikunja-502/logs/replays.log | 11 + .../work/vikunja-502/logs/runner-green.log | 5 + .../work/vikunja-502/logs/runner-red.log | 13 + .../work/vikunja-502/logs/stop-run-whole.log | 82 +++++ .../work/vikunja-502/mutant-logs/KS43.tap | 33 ++ .../work/vikunja-502/mutant-logs/KS44.tap | 33 ++ .../work/vikunja-502/mutant-logs/KS54.tap | 94 ++++++ .../work/vikunja-502/mutant-logs/SR08.tap | 39 +++ .../vikunja-502/mutant-logs/V502-status.tap | 33 ++ .../work/vikunja-502/mutant-results.json | 112 +++++++ .../vikunja-502/mutant-resume-summary.json | 11 + .../work/vikunja-502/packet-manifest.sha256 | 39 +++ .../work/vikunja-502/pre-rebase-commit.txt | 1 + .../rocko/work/vikunja-502/rebase-proof.json | 12 + .../work/vikunja-502/rebase-range-diff.txt | 1 + .../work/vikunja-502/restored-candidate.json | 4 + .../work/vikunja-502/run-mutants.baseline.py | 178 ++++++++++ .../rocko/work/vikunja-502/runner-change.diff | 46 +++ agents/rocko/work/vikunja-502/runner.sha256 | 1 + .../rocko/work/vikunja-502/scan-summary.json | 5 + agents/rocko/work/vikunja-502/skips.json | 1 + agents/rocko/work/vikunja-502/tmpdir.txt | 1 + 38 files changed, 1526 insertions(+) create mode 100644 agents/rocko/work/vikunja-502/BUILD.md create mode 100644 agents/rocko/work/vikunja-502/RESULTS.md create mode 100644 agents/rocko/work/vikunja-502/all-mutants.json create mode 100644 agents/rocko/work/vikunja-502/base-drift-ce6a.json create mode 100644 agents/rocko/work/vikunja-502/base.txt create mode 100644 agents/rocko/work/vikunja-502/build.patch create mode 100644 agents/rocko/work/vikunja-502/candidate-manifest.sha256 create mode 100644 agents/rocko/work/vikunja-502/candidate.txt create mode 100644 agents/rocko/work/vikunja-502/changed-paths.txt create mode 100644 agents/rocko/work/vikunja-502/commit.txt create mode 100644 agents/rocko/work/vikunja-502/cwd-sweep.json create mode 100644 agents/rocko/work/vikunja-502/focused-results.json create mode 100644 agents/rocko/work/vikunja-502/gate-plan.json create mode 100644 agents/rocko/work/vikunja-502/logs/bus-tasks-whole.log create mode 100644 agents/rocko/work/vikunja-502/logs/processing-http-whole.log create mode 100644 agents/rocko/work/vikunja-502/logs/python-regressions.log create mode 100644 agents/rocko/work/vikunja-502/logs/replays.log create mode 100644 agents/rocko/work/vikunja-502/logs/runner-green.log create mode 100644 agents/rocko/work/vikunja-502/logs/runner-red.log create mode 100644 agents/rocko/work/vikunja-502/logs/stop-run-whole.log create mode 100644 agents/rocko/work/vikunja-502/mutant-logs/KS43.tap create mode 100644 agents/rocko/work/vikunja-502/mutant-logs/KS44.tap create mode 100644 agents/rocko/work/vikunja-502/mutant-logs/KS54.tap create mode 100644 agents/rocko/work/vikunja-502/mutant-logs/SR08.tap create mode 100644 agents/rocko/work/vikunja-502/mutant-logs/V502-status.tap create mode 100644 agents/rocko/work/vikunja-502/mutant-results.json create mode 100644 agents/rocko/work/vikunja-502/mutant-resume-summary.json create mode 100644 agents/rocko/work/vikunja-502/packet-manifest.sha256 create mode 100644 agents/rocko/work/vikunja-502/pre-rebase-commit.txt create mode 100644 agents/rocko/work/vikunja-502/rebase-proof.json create mode 100644 agents/rocko/work/vikunja-502/rebase-range-diff.txt create mode 100644 agents/rocko/work/vikunja-502/restored-candidate.json create mode 100644 agents/rocko/work/vikunja-502/run-mutants.baseline.py create mode 100644 agents/rocko/work/vikunja-502/runner-change.diff create mode 100644 agents/rocko/work/vikunja-502/runner.sha256 create mode 100644 agents/rocko/work/vikunja-502/scan-summary.json create mode 100644 agents/rocko/work/vikunja-502/skips.json create mode 100644 agents/rocko/work/vikunja-502/tmpdir.txt diff --git a/agents/rocko/work/vikunja-502/BUILD.md b/agents/rocko/work/vikunja-502/BUILD.md new file mode 100644 index 000000000..72e028fda --- /dev/null +++ b/agents/rocko/work/vikunja-502/BUILD.md @@ -0,0 +1,128 @@ +# Vikunja502: processing test and runner hardening + +Briefed by Rocko before implementation, from sage-2460 and mos-1267/1268. +Parent row191 is landed at0fa7bf421. This follow-up changes tests and the +owned runner copy; it does not change the live stack or processing policy. +Queue next currently offers211; Sage's explicit502 assignment governs this +work. No211claim or unrelated row work. + +## Outcome and bounds + +Keep row191's product behavior unchanged while making its failure evidence +precise and bounded. Four accepted fixes only: + +1. packages/processing-control/tests/stop-run.test.mjs:106-108: the second + host must pair its real sleep with a real advancing clock. Override now + with Date.now alongside its existing real sleep. Retain all identity, + signal, ownership and receipt assertions. Replaying SR08 must produce a + named assertion failure and exit rather than hang until the harness kills it. +2. packages/webui/tests/processing.test.mjs:52: replace coarse status>=400 + with a case table of exact status/error outcomes and no writer call per + refusal. Keep valid same-origin requests as the baseline; isolate the + unnamed-host case from the unrelated outer-origin refusal. The existing + direct-handler origin/site test stays intact. Replay KS43 and KS44 against + their appropriate existing tests to prove the right guard is detected. +3. packages/bus/tests/tasks.test.mjs:252-265: close the opened runtime in + finally around the dependency assertions. Keep each assertion and the + subsequent close-count check. Replaying KS54 must yield its named + dependency assertion and terminate without a90second hang. +4. Own-dir run-mutants.py: generic timeout causes must reflect the actual + same-invocation control record, including the no-control case. Start from + reviewed fe0d360f. Keep the old row191 runner and all historical outputs + immutable; deliver the corrected copy in this packet. Update the cause + call to receive its actual control. Add focused pure-function regression + checks for absent, failed and passing control wording. No policy change: + classification, controls,90second bound and cleanup remain as reviewed. + +The original row191 evidence stays frozen. No live files, bus store, units, +provider calls, Docker, browser, credentials or activation action. No push, +force-push, branch deletion or attribution lines. Sage sweeps the pending +canonical191 BUILD.md/SESSIONS/BUILD-LOG edits; Rocko will not commit those. + +## Build and acceptance evidence + +New isolated worktree /mnt/storage/scratch/rocko-v502, new branch +rocko/vikunja502-20261022, baseline0fa7bf421; link node_modules immediately. +Use scratch TMPDIR and a dead Docker socket for every permitted run. + +Write the runner wording regression checks before changing the helper. +Request Sage's slot before any test or mutant run. Proposed focused gate: +whole stop-run.test.mjs, processing.test.mjs and bus tasks.test.mjs singly; +runner wording regression file; replay SR08, KS43, KS44 and KS54 one at a +time with owned process groups and cleanup. The old SR08/KS54 red evidence +is retained, and the new replays must fail by named assertion and exit. +Run broader package suites only if Sage grants them. No carry from old tests +is claimed after these test changes. Grep readers/pins before final manifest. + +Packet will contain base, patch, changed paths, candidate hashes, exact run +logs, mutant detections and the corrected runner hash/diff. Source-only +candidate paths are the three test files; runner files are separately hashed +packet deliverables. Sage resolves reviewer/gate ownership and landing. + +## Current state + +Initial brief state: no tests or mutant runs yet; subsequent results below record the authorized run. + + +## Sage scope approval and slot: sage-2471 (2026-10-22 04:55:20Z) + +Scope above approved as written. Slot order is MVP205 gate,268r4,194,236; +Sage will fit502into a gap and sends explicit CLEAR. Nothing may run yet. + +Prepared5-site table:SR08(clock),KS43(direct handler origin),KS44(bodybound), +KS54(runtime cleanup),V502-status(cross-site403→400). The last site proves +the exact status assertion catches a response that the old>=400 accepted. +Each old site statically matches exactly once in this worktree. No mutation +applied and no syntax/test command executed during this preparation. + +A successful replay must be an assertion kill and exit, including SR08 and +KS54. Timeout kills do not satisfy this follow-up's fixture-hardening goal. +The new runner's root will point to rocko-v502 when its wording helper is +fixed after the first red. This is runner workspace configuration, not a +policy change. New packet files/evidence remain separate from landed191. + + +## Focused verification complete: sage-2475 + +Run04:59:34Z–05:00:52Z on0fa7bf421 in rocko-v502. Expected regression +red is a named assertion: old generic cause falsely claims a passed control. +After the minimal helper/context fix, that regression passes. Whole single +files:stop-run12/12,processingHTTP6/6,bustasks9/9;awk27pass/0fail/0skip. +All4Python regressions pass (missing,failed,passingcontrol and SR08cause). +No unexpected test failures. No ^skip or TAPSKIP lines. + +Five mutants are assertion kills,0timeouts:SR08(5.211s),KS43(.161s), +KS44(.156s),KS54(.883s),V502-status(.162s). SR08 now pairs the parent clock +with its real sleep, and KS54 closes its runtime even when the deps assertion +throws; neither requires timeout adjudication. The wrong-status mutant +returns400instead403 and is rejected by the exact fence assertions. + +All5candidate code/test hashes restored. Cwd node/chromium/Python sweep[]; +slot returned Sage05:01:29Z. No suites,Docker,browser,live actions or provider +calls. The expected red/green run is distinguished from verification failures. + +The candidate manifest binds3landed test files plus the corrected owned +runner and its Python regression source,5paths in total. This expands the +brief's initial source-only3path convention solely to bind the two already +in-scope runner deliverables to the same verified candidate. Other packet +files and historical baseline are inventory-hashed,not candidate paths. +RunnerSHA256:c5bdc764d1cfe11752db2df412c96baa4f366236777df1755bb337e9a943ddea. +Runner code changes:truthful control-aware cause, passing actual control to +the renderer, and workspace root set to this isolated worktree. Classification, +90second limit, group cleanup, restore and control policy unchanged. + +Earlier row191runner/evidence remain untouched. No code commit,push,live +step or queue move. Sage owns review assignment and integration of502. + + +## Rebase and commits: sage-2482 + +No candidate path changed from0fa7bf421 toce6a96ad6. On new branch +rocko/vikunja502-ce6a-20261022, source candidate was committed then rebased +without conflict ontoce6a96ad6. All5blobs remain byte-identical, range-diff +pairs equal and stable patch-id matches. Focused runs carry by Sage's ruling; +no rerun. rebase-proof.json and rebase-range-diff.txt contain exact hashes. +commit.txt names the source candidate commit; the subsequent packet commit +adds evidence only,not candidate source changes. No pending canonical logs +or row191packet files are included. No push; Sage publishes and requests +Dewey review after checking the two-commit range. diff --git a/agents/rocko/work/vikunja-502/RESULTS.md b/agents/rocko/work/vikunja-502/RESULTS.md new file mode 100644 index 000000000..20a494d1e --- /dev/null +++ b/agents/rocko/work/vikunja-502/RESULTS.md @@ -0,0 +1,22 @@ +# Vikunja502 focused results + +|Check|Pass|Fail|Skip| +|---|---:|---:|---:| +|stop-run whole|12|0|0| +|processing HTTP whole|6|0|0| +|bus tasks whole|9|0|0| +|Python regressions|4|0|0| + +Expected wording regression:1assertion red before fix, then green. No unexpected red. + +|Mutant|Result|Seconds|Named failing test| +|---|---|---:|---| +|SR08|assertion-killed|5.211|a separate host joins durable first ownership without duplicate signals| +|KS43|assertion-killed|0.161|processing handler independently refuses wrong origin and cross-site before its writer| +|KS44|assertion-killed|0.156|processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer| +|KS54|assertion-killed|0.883|the runtime refuses an invalid adapter and closes a valid one| +|V502-status|assertion-killed|0.162|processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer| + +5/5assertionkills,0timeoutkills. No skips. Candidate restored,process sweep[],slotreturned05:01:29Z. + +No package suites,Docker,browser or live action under focused grant. diff --git a/agents/rocko/work/vikunja-502/all-mutants.json b/agents/rocko/work/vikunja-502/all-mutants.json new file mode 100644 index 000000000..4db89a600 --- /dev/null +++ b/agents/rocko/work/vikunja-502/all-mutants.json @@ -0,0 +1,52 @@ +[ + { + "id": "SR08", + "file": "packages/processing-control/src/runtimes.mjs", + "old": "if (winner.id !== claim.id) {", + "new": "if (false) {", + "test": "packages/processing-control/tests/stop-run.test.mjs", + "pattern": "separate host joins durable first ownership", + "check": "cross-process exclusive first owner", + "status": "unrun" + }, + { + "id": "KS43", + "file": "packages/webui/src/processing-http.mjs", + "old": "if (one(req, 'origin') !== (preview.origin ?? `https://${preview.host}`)) return refuse(403, 'cross-origin');", + "new": "// skip origin fence", + "test": "packages/webui/tests/processing.test.mjs", + "pattern": "processing handler independently refuses wrong origin and cross-site before its writer", + "check": "same-origin browser write", + "status": "unrun" + }, + { + "id": "KS44", + "file": "packages/webui/src/processing-http.mjs", + "old": "if (size > 1024) throw Error();", + "new": "void 0;", + "test": "packages/webui/tests/processing.test.mjs", + "pattern": "processing write fences refuse", + "check": "bounded control payload", + "status": "unrun" + }, + { + "id": "KS54", + "file": "packages/bus/src/runtime.mjs", + "old": "adapter = await tasks({ broker, credentials, businesses, processing });", + "new": "adapter = await tasks({ broker, credentials, businesses });", + "test": "packages/bus/tests/tasks.test.mjs", + "pattern": "", + "check": "production tracker injection", + "status": "unrun" + }, + { + "id": "V502-status", + "file": "packages/webui/src/processing-http.mjs", + "old": "return refuse(403, 'cross-site')", + "new": "return refuse(400, 'cross-site')", + "test": "packages/webui/tests/processing.test.mjs", + "pattern": "processing write fences refuse", + "check": "exact refusal status rejects a wrong 400 that coarse >=400 accepted", + "status": "unrun" + } +] diff --git a/agents/rocko/work/vikunja-502/base-drift-ce6a.json b/agents/rocko/work/vikunja-502/base-drift-ce6a.json new file mode 100644 index 000000000..62458683b --- /dev/null +++ b/agents/rocko/work/vikunja-502/base-drift-ce6a.json @@ -0,0 +1,12 @@ +{ + "old_base": "0fa7bf421", + "new_base": "ce6a96ad6", + "candidate_paths": [ + "agents/rocko/work/vikunja-502/run-mutants.py", + "agents/rocko/work/vikunja-502/tests/test_timeout_cause.py", + "packages/bus/tests/tasks.test.mjs", + "packages/processing-control/tests/stop-run.test.mjs", + "packages/webui/tests/processing.test.mjs" + ], + "changed": [] +} diff --git a/agents/rocko/work/vikunja-502/base.txt b/agents/rocko/work/vikunja-502/base.txt new file mode 100644 index 000000000..807c1da28 --- /dev/null +++ b/agents/rocko/work/vikunja-502/base.txt @@ -0,0 +1 @@ +ce6a96ad6153a0a7a689e6d9d0f625b55eb86b51 diff --git a/agents/rocko/work/vikunja-502/build.patch b/agents/rocko/work/vikunja-502/build.patch new file mode 100644 index 000000000..707e96210 --- /dev/null +++ b/agents/rocko/work/vikunja-502/build.patch @@ -0,0 +1,312 @@ +diff --git a/agents/rocko/work/vikunja-502/run-mutants.py b/agents/rocko/work/vikunja-502/run-mutants.py +new file mode 100644 +index 000000000..8dce3064d +--- /dev/null ++++ b/agents/rocko/work/vikunja-502/run-mutants.py +@@ -0,0 +1,182 @@ ++#!/usr/bin/env python3 ++"""Run only with Sage's explicit slot grant. Mutations are exact-once and restored.""" ++from pathlib import Path ++import argparse, hashlib, json, os, re, signal, subprocess, sys, time ++packet = Path(__file__).resolve().parent ++root = Path('/mnt/storage/scratch/rocko-v502') ++all_sites = json.loads((packet/'all-mutants.json').read_text()) ++parser = argparse.ArgumentParser() ++parser.add_argument('ids', nargs='*') ++parser.add_argument('--resume-from') ++parser.add_argument('--control-file') ++parser.add_argument('--adjudicate-timeout') ++args = parser.parse_args() ++sites = all_sites ++if args.resume_from: ++ ids = [s['id'] for s in all_sites] ++ if args.ids or args.resume_from not in ids: raise SystemExit('invalid resume selection') ++ sites = all_sites[ids.index(args.resume_from):] ++elif args.ids: ++ wanted = set(args.ids); sites = [s for s in all_sites if s['id'] in wanted] ++ if {s['id'] for s in sites} != wanted: raise SystemExit('unknown mutant id') ++if args.adjudicate_timeout and not args.control_file: ++ raise SystemExit('timeout adjudication requires an unmutated whole-file control') ++originals = {e['file']: (root/e['file']).read_bytes() for s in sites for e in [s, *s.get('extra_edits', [])]} ++for s in sites: ++ for e in [s, *s.get('extra_edits', [])]: ++ if originals[e['file']].decode().count(e['old']) != 1: raise SystemExit('preflight '+s['id']) ++logs = packet/('resume-mutant-logs' if args.resume_from else 'mutant-logs'); logs.mkdir(exist_ok=True) ++result_file = packet/('resume-mutant-results.json' if args.resume_from else 'mutant-results.json') ++results = [] ++controls = {} ++run_id = str(time.time_ns()) ++ ++def verify_candidate(): ++ expected = (packet/'commit.txt').read_text().strip() ++ if subprocess.check_output(['git','rev-parse','HEAD'],cwd=root,text=True).strip()!=expected: ++ raise RuntimeError('candidate HEAD changed') ++ manifest = packet/'candidate-manifest.sha256' ++ digest = hashlib.sha256(manifest.read_bytes()).hexdigest() ++ if digest!=(packet/'candidate.txt').read_text().strip(): raise RuntimeError('manifest digest changed') ++ for line in manifest.read_text().splitlines(): ++ wanted,name = line.split(' ',1) ++ if hashlib.sha256((root/name).read_bytes()).hexdigest()!=wanted: raise RuntimeError('candidate drift '+name) ++ return {'head':expected,'manifest':digest} ++ ++def stat(pid): ++ try: ++ raw = Path('/proc',str(pid),'stat').read_text(); fields = raw[raw.rfind(')')+2:].split() ++ return dict(ppid=int(fields[1]), group=int(fields[2]), start=fields[19], state=fields[0]) ++ except (FileNotFoundError, PermissionError, ProcessLookupError): return None ++def capture(child, known): ++ current = {} ++ for p in Path('/proc').iterdir(): ++ if p.name.isdecimal(): ++ value=stat(int(p.name)) ++ if value: current[int(p.name)]=value ++ changed=True ++ while changed: ++ changed=False ++ for pid,value in current.items(): ++ if pid not in known and (pid==child or (value['ppid'] in known and current.get(value['ppid'],{}).get('start')==known[value['ppid']]['start'])): known[pid]=value;changed=True ++ ++def cleanup(known): ++ groups={} ++ for pid,old in known.items(): ++ cur=stat(pid) ++ if cur and cur['start']==old['start'] and cur['state']!='Z': groups.setdefault(cur['group'],[]).append(pid) ++ for group,anchors in groups.items(): ++ # Every member here descends from this test runner, identified by start ticks. ++ if not any((cur:=stat(pid)) and cur['start']==known[pid]['start'] and cur['group']==group for pid in anchors):continue ++ try: os.killpg(group, signal.SIGKILL) ++ except ProcessLookupError: pass ++def execute(test, pattern, log): ++ child = None; known = {}; timedout = False; started = time.monotonic() ++ command = ['node','--test','--test-reporter=tap'] ++ if pattern: command += ['--test-name-pattern='+re.escape(pattern)] ++ command += [test] ++ try: ++ with log.open('w') as out: ++ child = subprocess.Popen(command,cwd=root,stdout=out,stderr=subprocess.STDOUT,start_new_session=True, ++ env={**os.environ,'TMPDIR':os.environ['TMPDIR'],'DOCKER_HOST':'unix:///nonexistent.sock', ++ 'MOSAIC_CONFIG':str(Path(os.environ['TMPDIR'])/'no-live-config.json')}) ++ first = stat(child.pid) ++ if first: known[child.pid] = first ++ deadline = started+90 ++ while child.poll() is None: ++ capture(child.pid,known) ++ if time.monotonic()>=deadline: ++ timedout = True; cleanup(known) ++ try: os.killpg(child.pid,signal.SIGKILL) ++ except ProcessLookupError: pass ++ break ++ time.sleep(.05) ++ rc = child.wait() ++ finally: ++ if child and child.poll() is None: ++ cleanup(known) ++ try: os.killpg(child.pid,signal.SIGKILL) ++ except ProcessLookupError: pass ++ child.wait() ++ cleanup(known) ++ text = log.read_text() ++ return {'exit':rc,'timeout':timedout,'seconds':time.monotonic()-started,'log':str(log), ++ 'failing':re.findall(r'^\s*not ok \d+ - (.*)$',text,re.M), ++ 'counts':{k:int(v) for k,v in re.findall(r'^# (tests|pass|fail|skipped|cancelled) (\d+)\s*$',text,re.M)}} ++ ++def timeout_cause(site, control=None): ++ if site['id']=='SR08': ++ return ('runtimes.mjs:333 loops on now() <= deadline; stop-run.test.mjs:106-108 ' ++ 'gives the second host a fixed fake clock with a real sleep. Removing the join ' ++ 'branch enters the delivery loop without advancing that clock. Original log: TAP header only.') ++ if not control or control.get('passed') is not True: ++ return ('Mutated '+site['file']+' timed out after 90s; no passing same-file control ' ++ 'exists in this invocation. Detection is not established. ' ++ 'Detailed cause must be recorded before review.') ++ return ('Mutated '+site['file']+' timed out after 90s while the same whole test file ' ++ 'passed unmutated in this invocation. Detailed cause must be recorded before review.') ++ ++try: ++ binding = verify_candidate() ++ if args.control_file: ++ if args.control_file not in {s['test'] for s in all_sites}: raise SystemExit('unknown control file') ++ before = hashlib.sha256((root/args.control_file).read_bytes()).hexdigest() ++ control = execute(args.control_file,'',logs/'unmutated-control.tap') ++ after = hashlib.sha256((root/args.control_file).read_bytes()).hexdigest() ++ counts = control['counts'] ++ passed = (control['exit']==0 and not control['timeout'] and not control['failing'] and before==after ++ and counts.get('tests',0)>0 and counts.get('pass')==counts.get('tests') ++ and counts.get('fail')==0 and counts.get('cancelled')==0 and counts.get('skipped')==0) ++ control.update(run_id=run_id,test=args.control_file,test_sha256=before,passed=passed,**binding) ++ (packet/'unmutated-control.json').write_text(json.dumps(control,indent=2)+'\n') ++ print('CONTROL',json.dumps(control),flush=True) ++ if not passed: raise SystemExit('control failed; stop for Mos, no mutation applied') ++ verify_candidate() ++ controls[args.control_file] = control ++ if args.adjudicate_timeout: ++ # This is the already-observed SR08 timeout, not a rerun or an assertion kill. ++ if args.adjudicate_timeout!='SR08': raise SystemExit('only SR08 historical adjudication approved') ++ prior = json.loads((packet/'mutant-results.json').read_text()) ++ old = next((s for s in prior if s['id']=='SR08'),None) ++ site = next(s for s in all_sites if s['id']=='SR08') ++ if not old or old['status']!='timeout' or old['test']!=args.control_file or old['failing']: ++ raise SystemExit('historical timeout does not match approved control') ++ if Path(old['log']).read_text().strip()!='TAP version 13': raise SystemExit('historical timeout log changed') ++ adjudication = {**old,'status':'killed-by-timeout','cause':timeout_cause(site, controls[args.control_file]),'control':controls[args.control_file], ++ 'original_log_sha256':hashlib.sha256(Path(old['log']).read_bytes()).hexdigest()} ++ (packet/'SR08-timeout-adjudication.json').write_text(json.dumps(adjudication,indent=2)+'\n') ++ print('TIMEOUT_KILL SR08',adjudication['cause'],flush=True) ++ for i,s in enumerate(sites,1): ++ verify_candidate() ++ edits = [s, *s.get('extra_edits', [])] ++ for e in edits: ++ if (root/e['file']).read_bytes()!=originals[e['file']]: raise RuntimeError('source drift '+s['id']) ++ print(f'{i}/{len(sites)} {s["id"]}: {s["check"]}',flush=True) ++ try: ++ for e in edits: ++ (root/e['file']).write_bytes(originals[e['file']].decode().replace(e['old'],e['new'],1).encode()) ++ observed = execute(s['test'],s['pattern'],logs/(s['id']+'.tap')) ++ finally: ++ for e in edits: (root/e['file']).write_bytes(originals[e['file']]) ++ expected = not s['pattern'] or any(s['pattern'] in name for name in observed['failing']) ++ control = controls.get(s['test']) ++ if observed['timeout']: ++ status = 'killed-by-timeout' if control and control['passed'] and control['run_id']==run_id else 'timeout-without-control' ++ elif observed['exit']==0: status = 'survived' ++ elif observed['failing'] and expected: status = 'assertion-killed' ++ else: status = 'invalid-failure' ++ row = {**{k:s[k] for k in ['id','file','test','pattern','check']},**observed,'status':status} ++ if observed['timeout']: row.update(cause=timeout_cause(s, control),control=control) ++ results.append(row) ++ result_file.write_text(json.dumps(results,indent=2)+'\n') ++ print(status,flush=True) ++ if status not in ('assertion-killed','killed-by-timeout'): raise SystemExit(1) ++finally: ++ for name,data in originals.items(): (root/name).write_bytes(data) ++ if any((root/name).read_bytes()!=data for name,data in originals.items()): raise RuntimeError('restore failed') ++summary = {'assertion_kills':sum(s['status']=='assertion-killed' for s in results), ++ 'timeout_kills':sum(s['status']=='killed-by-timeout' for s in results), ++ 'historical_timeout_kills':int(bool(args.adjudicate_timeout)),'selected':len(sites), ++ 'run_id':run_id,'candidate':binding} ++(packet/'mutant-resume-summary.json').write_text(json.dumps(summary,indent=2)+'\n') ++print('MUTANT_SUMMARY',json.dumps(summary),flush=True) +diff --git a/agents/rocko/work/vikunja-502/tests/test_timeout_cause.py b/agents/rocko/work/vikunja-502/tests/test_timeout_cause.py +new file mode 100644 +index 000000000..444ec2c5e +--- /dev/null ++++ b/agents/rocko/work/vikunja-502/tests/test_timeout_cause.py +@@ -0,0 +1,44 @@ ++"""Exercise the actual pure cause renderer without executing the mutant driver.""" ++import ast ++from pathlib import Path ++import unittest ++ ++RUNNER = Path(__file__).resolve().parents[1] / 'run-mutants.py' ++ ++ ++def cause_renderer(): ++ tree = ast.parse(RUNNER.read_text()) ++ function = next(n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name == 'timeout_cause') ++ namespace = {} ++ exec(compile(ast.Module(body=[function], type_ignores=[]), str(RUNNER), 'exec'), namespace) ++ return namespace['timeout_cause'] ++ ++ ++class TimeoutCause(unittest.TestCase): ++ def setUp(self): ++ self.cause = cause_renderer() ++ self.site = {'id': 'KS54', 'file': 'packages/bus/src/runtime.mjs'} ++ ++ def test_missing_control_never_claims_an_unmutated_pass(self): ++ text = self.cause(self.site) ++ self.assertIn('no passing same-file control', text) ++ self.assertNotIn('passed unmutated', text) ++ ++ def test_failed_control_never_claims_an_unmutated_pass(self): ++ text = self.cause(self.site, {'passed': False}) ++ self.assertIn('no passing same-file control', text) ++ self.assertNotIn('passed unmutated', text) ++ ++ def test_passing_control_can_state_its_observed_pass(self): ++ text = self.cause(self.site, {'passed': True}) ++ self.assertIn('passed unmutated', text) ++ self.assertNotIn('no passing same-file control', text) ++ ++ def test_known_sr08_cause_remains_specific_without_claiming_a_control(self): ++ text = self.cause({'id': 'SR08', 'file': 'packages/processing-control/src/runtimes.mjs'}) ++ self.assertIn('fixed fake clock', text) ++ self.assertNotIn('passed unmutated', text) ++ ++ ++if __name__ == '__main__': ++ unittest.main() +diff --git a/packages/bus/tests/tasks.test.mjs b/packages/bus/tests/tasks.test.mjs +index 4c8aae9d6..081925299 100644 +--- a/packages/bus/tests/tasks.test.mjs ++++ b/packages/bus/tests/tasks.test.mjs +@@ -259,10 +259,13 @@ test('the runtime refuses an invalid adapter and closes a valid one', async (t) + return { handle: async () => ({ ok: 1 }), timeout: 1000, close: async () => closed++ }; + }, + }); +- // Row 191: runtime injects cancellation alongside the existing factory dependencies. +- assert.deepEqual(given, ['broker', 'businesses', 'credentials', 'processing']); +- assert.equal(typeof processing.work, 'function'); +- await runtime.close(); ++ try { ++ // Row 191: runtime injects cancellation alongside the existing factory dependencies. ++ assert.deepEqual(given, ['broker', 'businesses', 'credentials', 'processing']); ++ assert.equal(typeof processing.work, 'function'); ++ } finally { ++ await runtime.close(); ++ } + assert.equal(closed, 5); + await assert.rejects( + startBroker({ +diff --git a/packages/processing-control/tests/stop-run.test.mjs b/packages/processing-control/tests/stop-run.test.mjs +index ed69bc111..f54022251 100644 +--- a/packages/processing-control/tests/stop-run.test.mjs ++++ b/packages/processing-control/tests/stop-run.test.mjs +@@ -105,7 +105,7 @@ test('a separate host joins durable first ownership without duplicate signals', + const messages = []; child.on('message', value => messages.push(value)); + await once(child, 'message'); + const joined = stopRun({ ...f.args, owner: { decision: 'second-host' } }, +- { ...f.options, sleep: ms => new Promise(resolve => setTimeout(resolve, ms)) }); ++ { ...f.options, now: Date.now, sleep: ms => new Promise(resolve => setTimeout(resolve, ms)) }); + setTimeout(() => child.send('continue'), 30); + const result = await joined; + const exit = child.exitCode === null ? await once(child, 'exit') : [child.exitCode]; +diff --git a/packages/webui/tests/processing.test.mjs b/packages/webui/tests/processing.test.mjs +index e9874602c..14956867b 100644 +--- a/packages/webui/tests/processing.test.mjs ++++ b/packages/webui/tests/processing.test.mjs +@@ -52,15 +52,26 @@ test('Console halt and generation-bound resume use only the management writer an + test('processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer', async t => { + const f = await fixture(t); + const cases = [ +- { headers: { ...POST.headers, origin: 'https://elsewhere' } }, +- { headers: { ...POST.headers, 'sec-fetch-site': 'cross-site' } }, +- { headers: { ...POST.headers, host: `127.0.0.1:${f.server.address().port}`, 'x-user': undefined } }, +- { headers: { ...POST.headers, 'x-user': ['jason', 'jason'] } }, +- { headers: { ...POST.headers, 'content-type': 'text/plain' } }, +- { body: '{"human":"jason"}' }, { body: '[]' }, { body: 'null' }, { body: ' '.repeat(1025) + '{}' }, ++ ['wrong origin', { headers: { ...POST.headers, origin: 'https://elsewhere' } }, 403, 'cross-origin request refused'], ++ ['cross-site', { headers: { ...POST.headers, 'sec-fetch-site': 'cross-site' } }, 403, 'cross-site'], ++ ['unnamed host', { headers: { ...POST.headers, host: `127.0.0.1:${f.server.address().port}`, ++ origin: `http://127.0.0.1:${f.server.address().port}`, 'x-user': undefined } }, 403, 'read-only'], ++ ['duplicate identity', { headers: { ...POST.headers, 'x-user': ['jason', 'jason'] } }, 401, 'sign-in required'], ++ ['content type', { headers: { ...POST.headers, 'content-type': 'text/plain' } }, 415, 'invalid-request'], ++ ['extra key', { body: '{"human":"jason"}' }, 400, 'invalid-request'], ++ ['array', { body: '[]' }, 400, 'invalid-request'], ++ ['null', { body: 'null' }, 400, 'invalid-request'], ++ ['body bound', { body: ' '.repeat(1025) + '{}' }, 400, 'invalid-request'], + ]; +- for (const c of cases) assert.ok((await http(f.server, '/api/processing/halt', { ...POST, ...c })).status >= 400); +- assert.equal((await http(f.server, '/api/processing/resume', POST)).status, 400); ++ for (const [name, changed, status, error] of cases) { ++ const response = await http(f.server, '/api/processing/halt', { ...POST, ...changed }); ++ assert.equal(response.status, status, name); ++ assert.equal(response.value.error, error, name); ++ assert.deepEqual(f.calls, [], name + ' must refuse before the writer'); ++ } ++ const missingGeneration = await http(f.server, '/api/processing/resume', POST); ++ assert.equal(missingGeneration.status, 400); ++ assert.equal(missingGeneration.value.error, 'invalid-request'); + assert.deepEqual(f.calls, []); + }); + test('tester cannot invoke either control writer and failures never echo backend text', async t => { diff --git a/agents/rocko/work/vikunja-502/candidate-manifest.sha256 b/agents/rocko/work/vikunja-502/candidate-manifest.sha256 new file mode 100644 index 000000000..c3426addc --- /dev/null +++ b/agents/rocko/work/vikunja-502/candidate-manifest.sha256 @@ -0,0 +1,5 @@ +c5bdc764d1cfe11752db2df412c96baa4f366236777df1755bb337e9a943ddea agents/rocko/work/vikunja-502/run-mutants.py +475ab651a60bc4721ba8bb193b3f05ff67d25afc068e80382174b98828ad085d agents/rocko/work/vikunja-502/tests/test_timeout_cause.py +a17ef09d68a13181fb0d27fde9006bd14ecf340270e07d7c79f6b37025f773c8 packages/bus/tests/tasks.test.mjs +bbf88324b1adc9acbb91a49f08d3ae5edb1d65a233933bdb86bc96701b0a35d3 packages/processing-control/tests/stop-run.test.mjs +28caf153da5d16949c5a3e1cedf8c91613e7230840d9df65d5dc4bdc95e72bf5 packages/webui/tests/processing.test.mjs diff --git a/agents/rocko/work/vikunja-502/candidate.txt b/agents/rocko/work/vikunja-502/candidate.txt new file mode 100644 index 000000000..fc70b7367 --- /dev/null +++ b/agents/rocko/work/vikunja-502/candidate.txt @@ -0,0 +1 @@ +968d6ee345766da838df68f491b5a77c9a25157034dc49e9d4a9b603e4ce7812 diff --git a/agents/rocko/work/vikunja-502/changed-paths.txt b/agents/rocko/work/vikunja-502/changed-paths.txt new file mode 100644 index 000000000..0288379a5 --- /dev/null +++ b/agents/rocko/work/vikunja-502/changed-paths.txt @@ -0,0 +1,5 @@ +agents/rocko/work/vikunja-502/run-mutants.py +agents/rocko/work/vikunja-502/tests/test_timeout_cause.py +packages/bus/tests/tasks.test.mjs +packages/processing-control/tests/stop-run.test.mjs +packages/webui/tests/processing.test.mjs diff --git a/agents/rocko/work/vikunja-502/commit.txt b/agents/rocko/work/vikunja-502/commit.txt new file mode 100644 index 000000000..5e89bfa93 --- /dev/null +++ b/agents/rocko/work/vikunja-502/commit.txt @@ -0,0 +1 @@ +8e3c6a79bfa39e739bf728f949f51e9842ca0df9 diff --git a/agents/rocko/work/vikunja-502/cwd-sweep.json b/agents/rocko/work/vikunja-502/cwd-sweep.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/agents/rocko/work/vikunja-502/cwd-sweep.json @@ -0,0 +1 @@ +[] diff --git a/agents/rocko/work/vikunja-502/focused-results.json b/agents/rocko/work/vikunja-502/focused-results.json new file mode 100644 index 000000000..c2e456ee2 --- /dev/null +++ b/agents/rocko/work/vikunja-502/focused-results.json @@ -0,0 +1,87 @@ +[ + { + "step": "runner-red", + "command": [ + "python3", + "agents/rocko/work/vikunja-502/tests/test_timeout_cause.py", + "TimeoutCause.test_missing_control_never_claims_an_unmutated_pass" + ], + "start": "2026-10-22T04:59:34Z", + "end": "2026-10-22T04:59:34Z", + "exit": 1, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/runner-red.log" + }, + { + "step": "runner-green", + "command": [ + "python3", + "agents/rocko/work/vikunja-502/tests/test_timeout_cause.py", + "TimeoutCause.test_missing_control_never_claims_an_unmutated_pass" + ], + "start": "2026-10-22T05:00:01Z", + "end": "2026-10-22T05:00:01Z", + "exit": 0, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/runner-green.log" + }, + { + "step": "stop-run-whole", + "command": [ + "node", + "--test", + "--test-reporter=tap", + "packages/processing-control/tests/stop-run.test.mjs" + ], + "start": "2026-10-22T05:00:01Z", + "end": "2026-10-22T05:00:01Z", + "exit": 0, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/stop-run-whole.log" + }, + { + "step": "processing-http-whole", + "command": [ + "node", + "--test", + "--test-reporter=tap", + "packages/webui/tests/processing.test.mjs" + ], + "start": "2026-10-22T05:00:02Z", + "end": "2026-10-22T05:00:02Z", + "exit": 0, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/processing-http-whole.log" + }, + { + "step": "bus-tasks-whole", + "command": [ + "node", + "--test", + "--test-reporter=tap", + "packages/bus/tests/tasks.test.mjs" + ], + "start": "2026-10-22T05:00:02Z", + "end": "2026-10-22T05:00:03Z", + "exit": 0, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/bus-tasks-whole.log" + }, + { + "step": "python-regressions", + "command": [ + "python3", + "agents/rocko/work/vikunja-502/tests/test_timeout_cause.py" + ], + "start": "2026-10-22T05:00:03Z", + "end": "2026-10-22T05:00:03Z", + "exit": 0, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/python-regressions.log" + }, + { + "step": "replays", + "command": [ + "python3", + "agents/rocko/work/vikunja-502/run-mutants.py" + ], + "start": "2026-10-22T05:00:45Z", + "end": "2026-10-22T05:00:52Z", + "exit": 0, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/logs/replays.log" + } +] diff --git a/agents/rocko/work/vikunja-502/gate-plan.json b/agents/rocko/work/vikunja-502/gate-plan.json new file mode 100644 index 000000000..7bf4ebb18 --- /dev/null +++ b/agents/rocko/work/vikunja-502/gate-plan.json @@ -0,0 +1,29 @@ +{ + "status": "focused scope completed under sage-2475; no further run authorized", + "scope": "sage-2471", + "worktree": "/mnt/storage/scratch/rocko-v502", + "base": "0fa7bf421fc153a226b5f06ed484e089e48e23ba", + "red_command": [ + "python3", + "/mnt/storage/src/mosaic-stack/agents/rocko/work/vikunja-502/tests/test_timeout_cause.py", + "TimeoutCause.test_missing_control_never_claims_an_unmutated_pass" + ], + "green_command": [ + "python3", + "/mnt/storage/src/mosaic-stack/agents/rocko/work/vikunja-502/tests/test_timeout_cause.py" + ], + "single_files": [ + "packages/processing-control/tests/stop-run.test.mjs", + "packages/webui/tests/processing.test.mjs", + "packages/bus/tests/tasks.test.mjs" + ], + "replay_ids": [ + "SR08", + "KS43", + "KS44", + "KS54", + "V502-status" + ], + "replay_required_status": "assertion-killed for every site; no timeout kills accepted as fixture-hardening evidence", + "process_rules": "short scratch TMPDIR,deadDocker,one test file at a time,owned process groups,timeouts,restore and cwd sweep" +} diff --git a/agents/rocko/work/vikunja-502/logs/bus-tasks-whole.log b/agents/rocko/work/vikunja-502/logs/bus-tasks-whole.log new file mode 100644 index 000000000..7e64858e1 --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/bus-tasks-whole.log @@ -0,0 +1,64 @@ +TAP version 13 +# Subtest: recordTask keeps sync reads and a role write apart +ok 1 - recordTask keeps sync reads and a role write apart + --- + duration_ms: 22.077964 + type: 'test' + ... +# Subtest: read_at must be one canonical UTC format, so the projection compares strings safely +ok 2 - read_at must be one canonical UTC format, so the projection compares strings safely + --- + duration_ms: 9.65343 + type: 'test' + ... +# Subtest: a bad entry refuses the whole record +ok 3 - a bad entry refuses the whole record + --- + duration_ms: 10.008621 + type: 'test' + ... +# Subtest: taskView reads the projection for one business +ok 4 - taskView reads the projection for one business + --- + duration_ms: 11.605317 + type: 'test' + ... +# Subtest: requestTask hands only a holder and a task verb to the handler, and records refusals +ok 5 - requestTask hands only a holder and a task verb to the handler, and records refusals + --- + duration_ms: 21.879126 + type: 'test' + ... +# Subtest: the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous +ok 6 - the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous + --- + duration_ms: 294.372769 + type: 'test' + ... +# Subtest: without an adapter the server refuses every task verb +ok 7 - without an adapter the server refuses every task verb + --- + duration_ms: 39.665659 + type: 'test' + ... +# Subtest: the runtime refuses an invalid adapter and closes a valid one +ok 8 - the runtime refuses an invalid adapter and closes a valid one + --- + duration_ms: 51.674609 + type: 'test' + ... +# Subtest: the process loads the S3 adapter from plain-data trackers +ok 9 - the process loads the S3 adapter from plain-data trackers + --- + duration_ms: 248.929804 + type: 'test' + ... +1..9 +# tests 9 +# suites 0 +# pass 9 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 819.657876 diff --git a/agents/rocko/work/vikunja-502/logs/processing-http-whole.log b/agents/rocko/work/vikunja-502/logs/processing-http-whole.log new file mode 100644 index 000000000..a83855fc2 --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/processing-http-whole.log @@ -0,0 +1,46 @@ +TAP version 13 +# Subtest: read-only Console can read processing state but neither human control writes without --send +ok 1 - read-only Console can read processing state but neither human control writes without --send + --- + duration_ms: 17.100325 + type: 'test' + ... +# Subtest: Console halt and generation-bound resume use only the management writer and keep partial stops honest +ok 2 - Console halt and generation-bound resume use only the management writer and keep partial stops honest + --- + duration_ms: 3.741854 + type: 'test' + ... +# Subtest: processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer +ok 3 - processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer + --- + duration_ms: 5.750951 + type: 'test' + ... +# Subtest: tester cannot invoke either control writer and failures never echo backend text +ok 4 - tester cannot invoke either control writer and failures never echo backend text + --- + duration_ms: 5.062465 + type: 'test' + ... +# Subtest: processing writes use the existing SSO signer and still require --send and same-origin +ok 5 - processing writes use the existing SSO signer and still require --send and same-origin + --- + duration_ms: 4.114043 + type: 'test' + ... +# Subtest: processing handler independently refuses wrong origin and cross-site before its writer +ok 6 - processing handler independently refuses wrong origin and cross-site before its writer + --- + duration_ms: 0.618941 + type: 'test' + ... +1..6 +# tests 6 +# suites 0 +# pass 6 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 134.607131 diff --git a/agents/rocko/work/vikunja-502/logs/python-regressions.log b/agents/rocko/work/vikunja-502/logs/python-regressions.log new file mode 100644 index 000000000..68ac40f9f --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/python-regressions.log @@ -0,0 +1,5 @@ +.... +---------------------------------------------------------------------- +Ran 4 tests in 0.011s + +OK diff --git a/agents/rocko/work/vikunja-502/logs/replays.log b/agents/rocko/work/vikunja-502/logs/replays.log new file mode 100644 index 000000000..7750e09c4 --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/replays.log @@ -0,0 +1,11 @@ +1/5 SR08: cross-process exclusive first owner +assertion-killed +2/5 KS43: same-origin browser write +assertion-killed +3/5 KS44: bounded control payload +assertion-killed +4/5 KS54: production tracker injection +assertion-killed +5/5 V502-status: exact refusal status rejects a wrong 400 that coarse >=400 accepted +assertion-killed +MUTANT_SUMMARY {"assertion_kills": 5, "timeout_kills": 0, "historical_timeout_kills": 0, "selected": 5, "run_id": "1792645245774744257", "candidate": {"head": "0fa7bf421fc153a226b5f06ed484e089e48e23ba", "manifest": "968d6ee345766da838df68f491b5a77c9a25157034dc49e9d4a9b603e4ce7812"}} diff --git a/agents/rocko/work/vikunja-502/logs/runner-green.log b/agents/rocko/work/vikunja-502/logs/runner-green.log new file mode 100644 index 000000000..742846e6b --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/runner-green.log @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 test in 0.004s + +OK diff --git a/agents/rocko/work/vikunja-502/logs/runner-red.log b/agents/rocko/work/vikunja-502/logs/runner-red.log new file mode 100644 index 000000000..b2a5ceaa3 --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/runner-red.log @@ -0,0 +1,13 @@ +F +====================================================================== +FAIL: test_missing_control_never_claims_an_unmutated_pass (__main__.TimeoutCause.test_missing_control_never_claims_an_unmutated_pass) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/tests/test_timeout_cause.py", line 24, in test_missing_control_never_claims_an_unmutated_pass + self.assertIn('no passing same-file control', text) +AssertionError: 'no passing same-file control' not found in 'Mutated packages/bus/src/runtime.mjs timed out after 90s while the same whole test file passed unmutated in this invocation. Detailed cause must be recorded before review.' + +---------------------------------------------------------------------- +Ran 1 test in 0.003s + +FAILED (failures=1) diff --git a/agents/rocko/work/vikunja-502/logs/stop-run-whole.log b/agents/rocko/work/vikunja-502/logs/stop-run-whole.log new file mode 100644 index 000000000..448d3ff3e --- /dev/null +++ b/agents/rocko/work/vikunja-502/logs/stop-run-whole.log @@ -0,0 +1,82 @@ +TAP version 13 +# Subtest: unbound run stops by verified registry identity, fixed 5s grace and 2s observe policy +ok 1 - unbound run stops by verified registry identity, fixed 5s grace and 2s observe policy + --- + duration_ms: 41.095674 + type: 'test' + ... +# Subtest: same run joins the identical promise and completed repeats preserve owner and send no signals +ok 2 - same run joins the identical promise and completed repeats preserve owner and send no signals + --- + duration_ms: 32.241062 + type: 'test' + ... +# Subtest: identity changed before TERM or between TERM and KILL refuses the changed signal +ok 3 - identity changed before TERM or between TERM and KILL refuses the changed signal + --- + duration_ms: 13.366003 + type: 'test' + ... +# Subtest: failed signal delivery observes alive, records no invented signals and never restarts escalation +ok 4 - failed signal delivery observes alive, records no invented signals and never restarts escalation + --- + duration_ms: 29.310841 + type: 'test' + ... +# Subtest: missing run and another business never borrow a pid or signal +ok 5 - missing run and another business never borrow a pid or signal + --- + duration_ms: 8.032376 + type: 'test' + ... +# Subtest: halt joins an in-progress fire without another TERM or deadline and links its first receipt +ok 6 - halt joins an in-progress fire without another TERM or deadline and links its first receipt + --- + duration_ms: 26.551382 + type: 'test' + ... +# Subtest: a separate host joins durable first ownership without duplicate signals +ok 7 - a separate host joins durable first ownership without duplicate signals + --- + duration_ms: 99.31242 + type: 'test' + ... +# Subtest: corrupt and symlinked final receipts refuse as unknown rather than signalling again +ok 8 - corrupt and symlinked final receipts refuse as unknown rather than signalling again + --- + duration_ms: 24.064935 + type: 'test' + ... +# Subtest: a later exit gets separate halt evidence while the original alive result stays immutable +ok 9 - a later exit gets separate halt evidence while the original alive result stays immutable + --- + duration_ms: 58.520012 + type: 'test' + ... +# Subtest: root aliases join but a different root never shares the run stop +ok 10 - root aliases join but a different root never shares the run stop + --- + duration_ms: 44.780832 + type: 'test' + ... +# Subtest: a changed runtime record cannot replace the original stop identity after TERM +ok 11 - a changed runtime record cannot replace the original stop identity after TERM + --- + duration_ms: 7.48526 + type: 'test' + ... +# Subtest: identity is rechecked at actual signal delivery, not just at the loop observation +ok 12 - identity is rechecked at actual signal delivery, not just at the loop observation + --- + duration_ms: 6.177784 + type: 'test' + ... +1..12 +# tests 12 +# suites 0 +# pass 12 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 446.315604 diff --git a/agents/rocko/work/vikunja-502/mutant-logs/KS43.tap b/agents/rocko/work/vikunja-502/mutant-logs/KS43.tap new file mode 100644 index 000000000..c62e33fa8 --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-logs/KS43.tap @@ -0,0 +1,33 @@ +TAP version 13 +# Subtest: processing handler independently refuses wrong origin and cross-site before its writer +not ok 1 - processing handler independently refuses wrong origin and cross-site before its writer + --- + duration_ms: 4.161227 + type: 'test' + location: '/mnt/storage/scratch/rocko-v502/packages/webui/tests/processing.test.mjs:106:1' + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly equal: + + 503 !== 403 + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: 403 + actual: 503 + operator: 'strictEqual' + stack: |- + TestContext. (file:///mnt/storage/scratch/rocko-v502/packages/webui/tests/processing.test.mjs:118:12) + process.processTicksAndRejections (node:internal/process/task_queues:104:5) + async Test.run (node:internal/test_runner/test:1409:7) + async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) + ... +1..1 +# tests 1 +# suites 0 +# pass 0 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 113.430641 diff --git a/agents/rocko/work/vikunja-502/mutant-logs/KS44.tap b/agents/rocko/work/vikunja-502/mutant-logs/KS44.tap new file mode 100644 index 000000000..203810ea7 --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-logs/KS44.tap @@ -0,0 +1,33 @@ +TAP version 13 +# Subtest: processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer +not ok 1 - processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer + --- + duration_ms: 22.664684 + type: 'test' + location: '/mnt/storage/scratch/rocko-v502/packages/webui/tests/processing.test.mjs:52:1' + failureType: 'testCodeFailure' + error: |- + body bound + + 200 !== 400 + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: 400 + actual: 200 + operator: 'strictEqual' + stack: |- + TestContext. (file:///mnt/storage/scratch/rocko-v502/packages/webui/tests/processing.test.mjs:68:12) + process.processTicksAndRejections (node:internal/process/task_queues:104:5) + async Test.run (node:internal/test_runner/test:1409:7) + async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) + ... +1..1 +# tests 1 +# suites 0 +# pass 0 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 126.507861 diff --git a/agents/rocko/work/vikunja-502/mutant-logs/KS54.tap b/agents/rocko/work/vikunja-502/mutant-logs/KS54.tap new file mode 100644 index 000000000..6aed24767 --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-logs/KS54.tap @@ -0,0 +1,94 @@ +TAP version 13 +# Subtest: recordTask keeps sync reads and a role write apart +ok 1 - recordTask keeps sync reads and a role write apart + --- + duration_ms: 23.452757 + type: 'test' + ... +# Subtest: read_at must be one canonical UTC format, so the projection compares strings safely +ok 2 - read_at must be one canonical UTC format, so the projection compares strings safely + --- + duration_ms: 9.929636 + type: 'test' + ... +# Subtest: a bad entry refuses the whole record +ok 3 - a bad entry refuses the whole record + --- + duration_ms: 9.894173 + type: 'test' + ... +# Subtest: taskView reads the projection for one business +ok 4 - taskView reads the projection for one business + --- + duration_ms: 11.839691 + type: 'test' + ... +# Subtest: requestTask hands only a holder and a task verb to the handler, and records refusals +ok 5 - requestTask hands only a holder and a task verb to the handler, and records refusals + --- + duration_ms: 23.330397 + type: 'test' + ... +# Subtest: the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous +ok 6 - the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous + --- + duration_ms: 297.537138 + type: 'test' + ... +# Subtest: without an adapter the server refuses every task verb +ok 7 - without an adapter the server refuses every task verb + --- + duration_ms: 43.546083 + type: 'test' + ... +# Subtest: the runtime refuses an invalid adapter and closes a valid one +not ok 8 - the runtime refuses an invalid adapter and closes a valid one + --- + duration_ms: 50.654664 + type: 'test' + location: '/mnt/storage/scratch/rocko-v502/packages/bus/tests/tasks.test.mjs:239:1' + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly deep-equal: + + actual - expected + + [ + 'broker', + 'businesses', + 'credentials', + - 'processing' + ] + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: + 0: 'broker' + 1: 'businesses' + 2: 'credentials' + 3: 'processing' + actual: + 0: 'broker' + 1: 'businesses' + 2: 'credentials' + operator: 'deepStrictEqual' + stack: |- + TestContext. (file:///mnt/storage/scratch/rocko-v502/packages/bus/tests/tasks.test.mjs:264:12) + process.processTicksAndRejections (node:internal/process/task_queues:104:5) + async Test.run (node:internal/test_runner/test:1409:7) + async Test.processPendingSubtests (node:internal/test_runner/test:974:7) + ... +# Subtest: the process loads the S3 adapter from plain-data trackers +ok 9 - the process loads the S3 adapter from plain-data trackers + --- + duration_ms: 249.07671 + type: 'test' + ... +1..9 +# tests 9 +# suites 0 +# pass 8 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 829.345079 diff --git a/agents/rocko/work/vikunja-502/mutant-logs/SR08.tap b/agents/rocko/work/vikunja-502/mutant-logs/SR08.tap new file mode 100644 index 000000000..f7a8b18f1 --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-logs/SR08.tap @@ -0,0 +1,39 @@ +TAP version 13 +# Subtest: a separate host joins durable first ownership without duplicate signals +not ok 1 - a separate host joins durable first ownership without duplicate signals + --- + duration_ms: 5083.453611 + type: 'test' + location: '/mnt/storage/scratch/rocko-v502/packages/processing-control/tests/stop-run.test.mjs:90:1' + failureType: 'testCodeFailure' + error: |- + the second host sends no signal + + actual - expected + + + [ + + 'SIGTERM', + + 'SIGKILL' + + ] + - [] + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: + actual: + 0: 'SIGTERM' + 1: 'SIGKILL' + operator: 'deepStrictEqual' + stack: |- + TestContext. (file:///mnt/storage/scratch/rocko-v502/packages/processing-control/tests/stop-run.test.mjs:113:10) + async Test.run (node:internal/test_runner/test:1409:7) + async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) + ... +1..1 +# tests 1 +# suites 0 +# pass 0 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 5132.608875 diff --git a/agents/rocko/work/vikunja-502/mutant-logs/V502-status.tap b/agents/rocko/work/vikunja-502/mutant-logs/V502-status.tap new file mode 100644 index 000000000..68c678703 --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-logs/V502-status.tap @@ -0,0 +1,33 @@ +TAP version 13 +# Subtest: processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer +not ok 1 - processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer + --- + duration_ms: 19.005372 + type: 'test' + location: '/mnt/storage/scratch/rocko-v502/packages/webui/tests/processing.test.mjs:52:1' + failureType: 'testCodeFailure' + error: |- + cross-site + + 400 !== 403 + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: 403 + actual: 400 + operator: 'strictEqual' + stack: |- + TestContext. (file:///mnt/storage/scratch/rocko-v502/packages/webui/tests/processing.test.mjs:68:12) + process.processTicksAndRejections (node:internal/process/task_queues:104:5) + async Test.run (node:internal/test_runner/test:1409:7) + async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) + ... +1..1 +# tests 1 +# suites 0 +# pass 0 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 130.795212 diff --git a/agents/rocko/work/vikunja-502/mutant-results.json b/agents/rocko/work/vikunja-502/mutant-results.json new file mode 100644 index 000000000..896a77da6 --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-results.json @@ -0,0 +1,112 @@ +[ + { + "id": "SR08", + "file": "packages/processing-control/src/runtimes.mjs", + "test": "packages/processing-control/tests/stop-run.test.mjs", + "pattern": "separate host joins durable first ownership", + "check": "cross-process exclusive first owner", + "exit": 1, + "timeout": false, + "seconds": 5.2106276580598205, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/mutant-logs/SR08.tap", + "failing": [ + "a separate host joins durable first ownership without duplicate signals" + ], + "counts": { + "tests": 1, + "pass": 0, + "fail": 1, + "cancelled": 0, + "skipped": 0 + }, + "status": "assertion-killed" + }, + { + "id": "KS43", + "file": "packages/webui/src/processing-http.mjs", + "test": "packages/webui/tests/processing.test.mjs", + "pattern": "processing handler independently refuses wrong origin and cross-site before its writer", + "check": "same-origin browser write", + "exit": 1, + "timeout": false, + "seconds": 0.16104768705554307, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/mutant-logs/KS43.tap", + "failing": [ + "processing handler independently refuses wrong origin and cross-site before its writer" + ], + "counts": { + "tests": 1, + "pass": 0, + "fail": 1, + "cancelled": 0, + "skipped": 0 + }, + "status": "assertion-killed" + }, + { + "id": "KS44", + "file": "packages/webui/src/processing-http.mjs", + "test": "packages/webui/tests/processing.test.mjs", + "pattern": "processing write fences refuse", + "check": "bounded control payload", + "exit": 1, + "timeout": false, + "seconds": 0.15614235494285822, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/mutant-logs/KS44.tap", + "failing": [ + "processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer" + ], + "counts": { + "tests": 1, + "pass": 0, + "fail": 1, + "cancelled": 0, + "skipped": 0 + }, + "status": "assertion-killed" + }, + { + "id": "KS54", + "file": "packages/bus/src/runtime.mjs", + "test": "packages/bus/tests/tasks.test.mjs", + "pattern": "", + "check": "production tracker injection", + "exit": 1, + "timeout": false, + "seconds": 0.8833191560115665, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/mutant-logs/KS54.tap", + "failing": [ + "the runtime refuses an invalid adapter and closes a valid one" + ], + "counts": { + "tests": 9, + "pass": 8, + "fail": 1, + "cancelled": 0, + "skipped": 0 + }, + "status": "assertion-killed" + }, + { + "id": "V502-status", + "file": "packages/webui/src/processing-http.mjs", + "test": "packages/webui/tests/processing.test.mjs", + "pattern": "processing write fences refuse", + "check": "exact refusal status rejects a wrong 400 that coarse >=400 accepted", + "exit": 1, + "timeout": false, + "seconds": 0.16231847403105348, + "log": "/mnt/storage/scratch/rocko-v502/agents/rocko/work/vikunja-502/mutant-logs/V502-status.tap", + "failing": [ + "processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer" + ], + "counts": { + "tests": 1, + "pass": 0, + "fail": 1, + "cancelled": 0, + "skipped": 0 + }, + "status": "assertion-killed" + } +] diff --git a/agents/rocko/work/vikunja-502/mutant-resume-summary.json b/agents/rocko/work/vikunja-502/mutant-resume-summary.json new file mode 100644 index 000000000..e973ff75f --- /dev/null +++ b/agents/rocko/work/vikunja-502/mutant-resume-summary.json @@ -0,0 +1,11 @@ +{ + "assertion_kills": 5, + "timeout_kills": 0, + "historical_timeout_kills": 0, + "selected": 5, + "run_id": "1792645245774744257", + "candidate": { + "head": "0fa7bf421fc153a226b5f06ed484e089e48e23ba", + "manifest": "968d6ee345766da838df68f491b5a77c9a25157034dc49e9d4a9b603e4ce7812" + } +} diff --git a/agents/rocko/work/vikunja-502/packet-manifest.sha256 b/agents/rocko/work/vikunja-502/packet-manifest.sha256 new file mode 100644 index 000000000..8348473aa --- /dev/null +++ b/agents/rocko/work/vikunja-502/packet-manifest.sha256 @@ -0,0 +1,39 @@ +9dbbf20dc65be5306b1a4fb1259bfcf179bc748390f5b206ca42012f9c8ca078 agents/rocko/work/vikunja-502/BUILD.md +8a5e6ea781f413f838aa9f0f3b49364202600aac4d095c9a22b52e959d68de66 agents/rocko/work/vikunja-502/RESULTS.md +501bbda0c8c33f37857856345322a020af0f77e91d86860f1ac0d0244f708cd5 agents/rocko/work/vikunja-502/all-mutants.json +011d0877029723282896967d6fcd0c6f6c1eff532e5a841b43eda16189395e8f agents/rocko/work/vikunja-502/base-drift-ce6a.json +d0c009ec9f9cad210038db986fc135c7aa358ab96a1c61c78dce4543333bc737 agents/rocko/work/vikunja-502/base.txt +d66e23404156a941f0d4f8d97a83c3de8ba2a243bedccf511462053254542ec9 agents/rocko/work/vikunja-502/build.patch +968d6ee345766da838df68f491b5a77c9a25157034dc49e9d4a9b603e4ce7812 agents/rocko/work/vikunja-502/candidate-manifest.sha256 +62ebb87cb798e5c96fed4354399a5252861bf57621fd8bc21d552adfe3a28ab4 agents/rocko/work/vikunja-502/candidate.txt +ebaa1dd2a828fd591a625f1e9bfa7b853c3a1e886bd538fe2de4c26d4beab044 agents/rocko/work/vikunja-502/changed-paths.txt +edd003aa45d2b541012de68a8e7869c47c388e8270bc06adca804bf645c3ff86 agents/rocko/work/vikunja-502/commit.txt +37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570 agents/rocko/work/vikunja-502/cwd-sweep.json +0e5426a7de4dc633a11c15ca9a22812c5ccb7300e1bf02a806c0556b866c7f79 agents/rocko/work/vikunja-502/focused-results.json +7a0d496a5b7babf07e04e31991b804759510b40d58f9573b0b78c5e91e4b3b18 agents/rocko/work/vikunja-502/gate-plan.json +0b91b9016d0da6dc94b496447c4624bac9eefa486d3eba9ea4e733987d13f753 agents/rocko/work/vikunja-502/logs/bus-tasks-whole.log +4245c5f91bfa421c5f697841a93a679afc1d901762c72409830460efa4d6116b agents/rocko/work/vikunja-502/logs/processing-http-whole.log +d52cbb1058abf83e3acb402d1c2b8ee089749613d6075f5d973dd66c8ef09200 agents/rocko/work/vikunja-502/logs/python-regressions.log +bbc1528eeeb64ce52b2532db1e0e798eded967fd66c805a030d35ef34d605ca9 agents/rocko/work/vikunja-502/logs/replays.log +a626407facf1102b512cc38f00f6e7d889aa6280a5c03d1722ac6d1c4eb08dc5 agents/rocko/work/vikunja-502/logs/runner-green.log +23ffbda3d00898ea85570996abad994c521b9500698b36bd8481622f5943e161 agents/rocko/work/vikunja-502/logs/runner-red.log +7c52ba2561dd20f8bdc272647ac6a6cb6358a3005d0cf66ef38914e7d98a173d agents/rocko/work/vikunja-502/logs/stop-run-whole.log +5e0bde2f8340fa3ae0660f08ac1eab29aae0f84856d72df6784b76e0c61110c4 agents/rocko/work/vikunja-502/mutant-logs/KS43.tap +4d57c485599655d5f5c4b3e6d42f24d5dfd32553af3d4993a0b287d939b4be90 agents/rocko/work/vikunja-502/mutant-logs/KS44.tap +5d91c5a8380f7d4f8a9bb8677561bc6edf0c9444eb780e502d5b1dc1fa9f1bcc agents/rocko/work/vikunja-502/mutant-logs/KS54.tap +f195652fa91e8b07c46128ac809b8ec458632f00bced4f64bf8846761ea91ba1 agents/rocko/work/vikunja-502/mutant-logs/SR08.tap +ac40be01b48349b3b3545a92a16576951e1481513bea4aeb5526e66ca6ecbe6e agents/rocko/work/vikunja-502/mutant-logs/V502-status.tap +46306ee1d3a681e6fb1e61d2887b3b62a4832f3617f51209ee8ec8e4c07bc0b7 agents/rocko/work/vikunja-502/mutant-results.json +286e53b9cd0849db12da5fb4113aab2c9c89a49341d1661fe70b23c757ae3b51 agents/rocko/work/vikunja-502/mutant-resume-summary.json +730813393354bb940f766f4894fde1a4dffe6fe4afbcc9d2df31fa4a829da2cd agents/rocko/work/vikunja-502/pre-rebase-commit.txt +d44d16850224227a86842652fd0f9071592e330f580852db5cfce5c60455c727 agents/rocko/work/vikunja-502/rebase-proof.json +a4dd7e62f35597edef0fc970e8fe9e5be3f4ff15feac61863b02735bc3d8c6a4 agents/rocko/work/vikunja-502/rebase-range-diff.txt +bdae7b9f187f9945828e6b164f70e4f7a73ecbcafbfe780727e91ea3dbdc9c83 agents/rocko/work/vikunja-502/restored-candidate.json +fe0d360f86d86ab2df967da5338479c593c27c88d06eb38a1389d99a08489369 agents/rocko/work/vikunja-502/run-mutants.baseline.py +c5bdc764d1cfe11752db2df412c96baa4f366236777df1755bb337e9a943ddea agents/rocko/work/vikunja-502/run-mutants.py +708397d67087d30bbfacdd27d0dfd81055aa47d841cbcbc8281bc4bb98632f21 agents/rocko/work/vikunja-502/runner-change.diff +c68bc5fb03bfec8ef2cb1559e8d7bfc67e2460c41828e55c9abd6e21a355f3f1 agents/rocko/work/vikunja-502/runner.sha256 +041a6eed7c60eb2f6900974718ed8db85efb0178afe71ca5ce828f0e89588b96 agents/rocko/work/vikunja-502/scan-summary.json +37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570 agents/rocko/work/vikunja-502/skips.json +475ab651a60bc4721ba8bb193b3f05ff67d25afc068e80382174b98828ad085d agents/rocko/work/vikunja-502/tests/test_timeout_cause.py +9532b4498e2aa1ad91b37aa032a8305c338b6b9953728e2d88b5ae0071647097 agents/rocko/work/vikunja-502/tmpdir.txt diff --git a/agents/rocko/work/vikunja-502/pre-rebase-commit.txt b/agents/rocko/work/vikunja-502/pre-rebase-commit.txt new file mode 100644 index 000000000..791109089 --- /dev/null +++ b/agents/rocko/work/vikunja-502/pre-rebase-commit.txt @@ -0,0 +1 @@ +3b04790e28a2390a9b3c9cfad71ab63acc35e0ce diff --git a/agents/rocko/work/vikunja-502/rebase-proof.json b/agents/rocko/work/vikunja-502/rebase-proof.json new file mode 100644 index 000000000..8cfb4dc38 --- /dev/null +++ b/agents/rocko/work/vikunja-502/rebase-proof.json @@ -0,0 +1,12 @@ +{ + "old_base": "0fa7bf421", + "old_commit": "3b04790e28a2390a9b3c9cfad71ab63acc35e0ce", + "new_base": "ce6a96ad6153a0a7a689e6d9d0f625b55eb86b51", + "new_commit": "8e3c6a79bfa39e739bf728f949f51e9842ca0df9", + "paths": 5, + "blob_drift": [], + "old_patch_id": "37135ebe64f1e9b40f952f4b7047fbd31db58362", + "new_patch_id": "37135ebe64f1e9b40f952f4b7047fbd31db58362", + "range_diff": "equal", + "verification_carries": "sage-2482" +} diff --git a/agents/rocko/work/vikunja-502/rebase-range-diff.txt b/agents/rocko/work/vikunja-502/rebase-range-diff.txt new file mode 100644 index 000000000..810e0d746 --- /dev/null +++ b/agents/rocko/work/vikunja-502/rebase-range-diff.txt @@ -0,0 +1 @@ +1: 3b04790e2 = 1: 8e3c6a79b test: harden processing failure diagnostics and cleanup diff --git a/agents/rocko/work/vikunja-502/restored-candidate.json b/agents/rocko/work/vikunja-502/restored-candidate.json new file mode 100644 index 000000000..3c22ff71f --- /dev/null +++ b/agents/rocko/work/vikunja-502/restored-candidate.json @@ -0,0 +1,4 @@ +{ + "paths": 5, + "drift": [] +} diff --git a/agents/rocko/work/vikunja-502/run-mutants.baseline.py b/agents/rocko/work/vikunja-502/run-mutants.baseline.py new file mode 100644 index 000000000..28f4c266f --- /dev/null +++ b/agents/rocko/work/vikunja-502/run-mutants.baseline.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""Run only with Sage's explicit slot grant. Mutations are exact-once and restored.""" +from pathlib import Path +import argparse, hashlib, json, os, re, signal, subprocess, sys, time +packet = Path(__file__).resolve().parent +root = Path('/mnt/storage/scratch/rocko-r191-r3') +all_sites = json.loads((packet/'all-mutants.json').read_text()) +parser = argparse.ArgumentParser() +parser.add_argument('ids', nargs='*') +parser.add_argument('--resume-from') +parser.add_argument('--control-file') +parser.add_argument('--adjudicate-timeout') +args = parser.parse_args() +sites = all_sites +if args.resume_from: + ids = [s['id'] for s in all_sites] + if args.ids or args.resume_from not in ids: raise SystemExit('invalid resume selection') + sites = all_sites[ids.index(args.resume_from):] +elif args.ids: + wanted = set(args.ids); sites = [s for s in all_sites if s['id'] in wanted] + if {s['id'] for s in sites} != wanted: raise SystemExit('unknown mutant id') +if args.adjudicate_timeout and not args.control_file: + raise SystemExit('timeout adjudication requires an unmutated whole-file control') +originals = {e['file']: (root/e['file']).read_bytes() for s in sites for e in [s, *s.get('extra_edits', [])]} +for s in sites: + for e in [s, *s.get('extra_edits', [])]: + if originals[e['file']].decode().count(e['old']) != 1: raise SystemExit('preflight '+s['id']) +logs = packet/('resume-mutant-logs' if args.resume_from else 'mutant-logs'); logs.mkdir(exist_ok=True) +result_file = packet/('resume-mutant-results.json' if args.resume_from else 'mutant-results.json') +results = [] +controls = {} +run_id = str(time.time_ns()) + +def verify_candidate(): + expected = (packet/'commit.txt').read_text().strip() + if subprocess.check_output(['git','rev-parse','HEAD'],cwd=root,text=True).strip()!=expected: + raise RuntimeError('candidate HEAD changed') + manifest = packet/'candidate-manifest.sha256' + digest = hashlib.sha256(manifest.read_bytes()).hexdigest() + if digest!=(packet/'candidate.txt').read_text().strip(): raise RuntimeError('manifest digest changed') + for line in manifest.read_text().splitlines(): + wanted,name = line.split(' ',1) + if hashlib.sha256((root/name).read_bytes()).hexdigest()!=wanted: raise RuntimeError('candidate drift '+name) + return {'head':expected,'manifest':digest} + +def stat(pid): + try: + raw = Path('/proc',str(pid),'stat').read_text(); fields = raw[raw.rfind(')')+2:].split() + return dict(ppid=int(fields[1]), group=int(fields[2]), start=fields[19], state=fields[0]) + except (FileNotFoundError, PermissionError, ProcessLookupError): return None +def capture(child, known): + current = {} + for p in Path('/proc').iterdir(): + if p.name.isdecimal(): + value=stat(int(p.name)) + if value: current[int(p.name)]=value + changed=True + while changed: + changed=False + for pid,value in current.items(): + if pid not in known and (pid==child or (value['ppid'] in known and current.get(value['ppid'],{}).get('start')==known[value['ppid']]['start'])): known[pid]=value;changed=True + +def cleanup(known): + groups={} + for pid,old in known.items(): + cur=stat(pid) + if cur and cur['start']==old['start'] and cur['state']!='Z': groups.setdefault(cur['group'],[]).append(pid) + for group,anchors in groups.items(): + # Every member here descends from this test runner, identified by start ticks. + if not any((cur:=stat(pid)) and cur['start']==known[pid]['start'] and cur['group']==group for pid in anchors):continue + try: os.killpg(group, signal.SIGKILL) + except ProcessLookupError: pass +def execute(test, pattern, log): + child = None; known = {}; timedout = False; started = time.monotonic() + command = ['node','--test','--test-reporter=tap'] + if pattern: command += ['--test-name-pattern='+re.escape(pattern)] + command += [test] + try: + with log.open('w') as out: + child = subprocess.Popen(command,cwd=root,stdout=out,stderr=subprocess.STDOUT,start_new_session=True, + env={**os.environ,'TMPDIR':os.environ['TMPDIR'],'DOCKER_HOST':'unix:///nonexistent.sock', + 'MOSAIC_CONFIG':str(Path(os.environ['TMPDIR'])/'no-live-config.json')}) + first = stat(child.pid) + if first: known[child.pid] = first + deadline = started+90 + while child.poll() is None: + capture(child.pid,known) + if time.monotonic()>=deadline: + timedout = True; cleanup(known) + try: os.killpg(child.pid,signal.SIGKILL) + except ProcessLookupError: pass + break + time.sleep(.05) + rc = child.wait() + finally: + if child and child.poll() is None: + cleanup(known) + try: os.killpg(child.pid,signal.SIGKILL) + except ProcessLookupError: pass + child.wait() + cleanup(known) + text = log.read_text() + return {'exit':rc,'timeout':timedout,'seconds':time.monotonic()-started,'log':str(log), + 'failing':re.findall(r'^\s*not ok \d+ - (.*)$',text,re.M), + 'counts':{k:int(v) for k,v in re.findall(r'^# (tests|pass|fail|skipped|cancelled) (\d+)\s*$',text,re.M)}} + +def timeout_cause(site): + if site['id']=='SR08': + return ('runtimes.mjs:333 loops on now() <= deadline; stop-run.test.mjs:106-108 ' + 'gives the second host a fixed fake clock with a real sleep. Removing the join ' + 'branch enters the delivery loop without advancing that clock. Original log: TAP header only.') + return ('Mutated '+site['file']+' timed out after 90s while the same whole test file ' + 'passed unmutated in this invocation. Detailed cause must be recorded before review.') + +try: + binding = verify_candidate() + if args.control_file: + if args.control_file not in {s['test'] for s in all_sites}: raise SystemExit('unknown control file') + before = hashlib.sha256((root/args.control_file).read_bytes()).hexdigest() + control = execute(args.control_file,'',logs/'unmutated-control.tap') + after = hashlib.sha256((root/args.control_file).read_bytes()).hexdigest() + counts = control['counts'] + passed = (control['exit']==0 and not control['timeout'] and not control['failing'] and before==after + and counts.get('tests',0)>0 and counts.get('pass')==counts.get('tests') + and counts.get('fail')==0 and counts.get('cancelled')==0 and counts.get('skipped')==0) + control.update(run_id=run_id,test=args.control_file,test_sha256=before,passed=passed,**binding) + (packet/'unmutated-control.json').write_text(json.dumps(control,indent=2)+'\n') + print('CONTROL',json.dumps(control),flush=True) + if not passed: raise SystemExit('control failed; stop for Mos, no mutation applied') + verify_candidate() + controls[args.control_file] = control + if args.adjudicate_timeout: + # This is the already-observed SR08 timeout, not a rerun or an assertion kill. + if args.adjudicate_timeout!='SR08': raise SystemExit('only SR08 historical adjudication approved') + prior = json.loads((packet/'mutant-results.json').read_text()) + old = next((s for s in prior if s['id']=='SR08'),None) + site = next(s for s in all_sites if s['id']=='SR08') + if not old or old['status']!='timeout' or old['test']!=args.control_file or old['failing']: + raise SystemExit('historical timeout does not match approved control') + if Path(old['log']).read_text().strip()!='TAP version 13': raise SystemExit('historical timeout log changed') + adjudication = {**old,'status':'killed-by-timeout','cause':timeout_cause(site),'control':controls[args.control_file], + 'original_log_sha256':hashlib.sha256(Path(old['log']).read_bytes()).hexdigest()} + (packet/'SR08-timeout-adjudication.json').write_text(json.dumps(adjudication,indent=2)+'\n') + print('TIMEOUT_KILL SR08',adjudication['cause'],flush=True) + for i,s in enumerate(sites,1): + verify_candidate() + edits = [s, *s.get('extra_edits', [])] + for e in edits: + if (root/e['file']).read_bytes()!=originals[e['file']]: raise RuntimeError('source drift '+s['id']) + print(f'{i}/{len(sites)} {s["id"]}: {s["check"]}',flush=True) + try: + for e in edits: + (root/e['file']).write_bytes(originals[e['file']].decode().replace(e['old'],e['new'],1).encode()) + observed = execute(s['test'],s['pattern'],logs/(s['id']+'.tap')) + finally: + for e in edits: (root/e['file']).write_bytes(originals[e['file']]) + expected = not s['pattern'] or any(s['pattern'] in name for name in observed['failing']) + control = controls.get(s['test']) + if observed['timeout']: + status = 'killed-by-timeout' if control and control['passed'] and control['run_id']==run_id else 'timeout-without-control' + elif observed['exit']==0: status = 'survived' + elif observed['failing'] and expected: status = 'assertion-killed' + else: status = 'invalid-failure' + row = {**{k:s[k] for k in ['id','file','test','pattern','check']},**observed,'status':status} + if observed['timeout']: row.update(cause=timeout_cause(s),control=control) + results.append(row) + result_file.write_text(json.dumps(results,indent=2)+'\n') + print(status,flush=True) + if status not in ('assertion-killed','killed-by-timeout'): raise SystemExit(1) +finally: + for name,data in originals.items(): (root/name).write_bytes(data) + if any((root/name).read_bytes()!=data for name,data in originals.items()): raise RuntimeError('restore failed') +summary = {'assertion_kills':sum(s['status']=='assertion-killed' for s in results), + 'timeout_kills':sum(s['status']=='killed-by-timeout' for s in results), + 'historical_timeout_kills':int(bool(args.adjudicate_timeout)),'selected':len(sites), + 'run_id':run_id,'candidate':binding} +(packet/'mutant-resume-summary.json').write_text(json.dumps(summary,indent=2)+'\n') +print('MUTANT_SUMMARY',json.dumps(summary),flush=True) diff --git a/agents/rocko/work/vikunja-502/runner-change.diff b/agents/rocko/work/vikunja-502/runner-change.diff new file mode 100644 index 000000000..3de04d60c --- /dev/null +++ b/agents/rocko/work/vikunja-502/runner-change.diff @@ -0,0 +1,46 @@ +--- agents/rocko/work/vikunja-502/run-mutants.baseline.py 2026-10-21 23:53:18.260554870 -0500 ++++ agents/rocko/work/vikunja-502/run-mutants.py 2026-10-22 00:00:01.232405637 -0500 +@@ -3,7 +3,7 @@ + from pathlib import Path + import argparse, hashlib, json, os, re, signal, subprocess, sys, time + packet = Path(__file__).resolve().parent +-root = Path('/mnt/storage/scratch/rocko-r191-r3') ++root = Path('/mnt/storage/scratch/rocko-v502') + all_sites = json.loads((packet/'all-mutants.json').read_text()) + parser = argparse.ArgumentParser() + parser.add_argument('ids', nargs='*') +@@ -104,11 +104,15 @@ + 'failing':re.findall(r'^\s*not ok \d+ - (.*)$',text,re.M), + 'counts':{k:int(v) for k,v in re.findall(r'^# (tests|pass|fail|skipped|cancelled) (\d+)\s*$',text,re.M)}} + +-def timeout_cause(site): ++def timeout_cause(site, control=None): + if site['id']=='SR08': + return ('runtimes.mjs:333 loops on now() <= deadline; stop-run.test.mjs:106-108 ' + 'gives the second host a fixed fake clock with a real sleep. Removing the join ' + 'branch enters the delivery loop without advancing that clock. Original log: TAP header only.') ++ if not control or control.get('passed') is not True: ++ return ('Mutated '+site['file']+' timed out after 90s; no passing same-file control ' ++ 'exists in this invocation. Detection is not established. ' ++ 'Detailed cause must be recorded before review.') + return ('Mutated '+site['file']+' timed out after 90s while the same whole test file ' + 'passed unmutated in this invocation. Detailed cause must be recorded before review.') + +@@ -138,7 +142,7 @@ + if not old or old['status']!='timeout' or old['test']!=args.control_file or old['failing']: + raise SystemExit('historical timeout does not match approved control') + if Path(old['log']).read_text().strip()!='TAP version 13': raise SystemExit('historical timeout log changed') +- adjudication = {**old,'status':'killed-by-timeout','cause':timeout_cause(site),'control':controls[args.control_file], ++ adjudication = {**old,'status':'killed-by-timeout','cause':timeout_cause(site, controls[args.control_file]),'control':controls[args.control_file], + 'original_log_sha256':hashlib.sha256(Path(old['log']).read_bytes()).hexdigest()} + (packet/'SR08-timeout-adjudication.json').write_text(json.dumps(adjudication,indent=2)+'\n') + print('TIMEOUT_KILL SR08',adjudication['cause'],flush=True) +@@ -162,7 +166,7 @@ + elif observed['failing'] and expected: status = 'assertion-killed' + else: status = 'invalid-failure' + row = {**{k:s[k] for k in ['id','file','test','pattern','check']},**observed,'status':status} +- if observed['timeout']: row.update(cause=timeout_cause(s),control=control) ++ if observed['timeout']: row.update(cause=timeout_cause(s, control),control=control) + results.append(row) + result_file.write_text(json.dumps(results,indent=2)+'\n') + print(status,flush=True) diff --git a/agents/rocko/work/vikunja-502/runner.sha256 b/agents/rocko/work/vikunja-502/runner.sha256 new file mode 100644 index 000000000..97f28a32d --- /dev/null +++ b/agents/rocko/work/vikunja-502/runner.sha256 @@ -0,0 +1 @@ +c5bdc764d1cfe11752db2df412c96baa4f366236777df1755bb337e9a943ddea agents/rocko/work/vikunja-502/run-mutants.py diff --git a/agents/rocko/work/vikunja-502/scan-summary.json b/agents/rocko/work/vikunja-502/scan-summary.json new file mode 100644 index 000000000..76ced999f --- /dev/null +++ b/agents/rocko/work/vikunja-502/scan-summary.json @@ -0,0 +1,5 @@ +{ + "scope": "owned502packet", + "binary_files": [], + "secret_pattern_matches": [] +} diff --git a/agents/rocko/work/vikunja-502/skips.json b/agents/rocko/work/vikunja-502/skips.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/agents/rocko/work/vikunja-502/skips.json @@ -0,0 +1 @@ +[] diff --git a/agents/rocko/work/vikunja-502/tmpdir.txt b/agents/rocko/work/vikunja-502/tmpdir.txt new file mode 100644 index 000000000..1ed1b13a9 --- /dev/null +++ b/agents/rocko/work/vikunja-502/tmpdir.txt @@ -0,0 +1 @@ +/mnt/storage/scratch/tmp/gU6o