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