docs: record Vikunja 502 focused verification and rebase evidence
This commit is contained in:
1 parent
8e3c6a79bf
commit
9bf4683c65
38 files changed
+1526
No files matched your search
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -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": []
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ce6a96ad6153a0a7a689e6d9d0f625b55eb86b51
|
||||||
@@ -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 => {
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
968d6ee345766da838df68f491b5a77c9a25157034dc49e9d4a9b603e4ce7812
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
8e3c6a79bfa39e739bf728f949f51e9842ca0df9
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
....
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 4 tests in 0.011s
|
||||||
|
|
||||||
|
OK
|
||||||
@@ -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"}}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 1 test in 0.004s
|
||||||
|
|
||||||
|
OK
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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.<anonymous> (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
|
||||||
@@ -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.<anonymous> (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
|
||||||
@@ -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.<anonymous> (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
|
||||||
@@ -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.<anonymous> (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
|
||||||
@@ -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.<anonymous> (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
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"assertion_kills": 5,
|
||||||
|
"timeout_kills": 0,
|
||||||
|
"historical_timeout_kills": 0,
|
||||||
|
"selected": 5,
|
||||||
|
"run_id": "1792645245774744257",
|
||||||
|
"candidate": {
|
||||||
|
"head": "0fa7bf421fc153a226b5f06ed484e089e48e23ba",
|
||||||
|
"manifest": "968d6ee345766da838df68f491b5a77c9a25157034dc49e9d4a9b603e4ce7812"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3b04790e28a2390a9b3c9cfad71ab63acc35e0ce
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1: 3b04790e2 = 1: 8e3c6a79b test: harden processing failure diagnostics and cleanup
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"paths": 5,
|
||||||
|
"drift": []
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
c5bdc764d1cfe11752db2df412c96baa4f366236777df1755bb337e9a943ddea agents/rocko/work/vikunja-502/run-mutants.py
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"scope": "owned502packet",
|
||||||
|
"binary_files": [],
|
||||||
|
"secret_pattern_matches": []
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/mnt/storage/scratch/tmp/gU6o
|
||||||
Reference in new issue
Block a user