Compare commits
94
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6af85df4be | ||
|
|
04a86cf338 | ||
|
|
407c8d7be2 | ||
|
|
980cec88c2 | ||
|
|
c33cba810c | ||
|
|
01ea1fb11b | ||
|
|
b831733053 | ||
|
|
62f53b1e07 | ||
|
|
bda4ee6138 | ||
|
|
3a5275a9ad | ||
|
|
eccd6e276e | ||
|
|
571ca3d912 | ||
|
|
2f7fc6832f | ||
|
|
1d5d00939a | ||
|
|
46dedf9259 | ||
|
|
cac129e23a | ||
|
|
f12441d4fa | ||
|
|
e617543b8d | ||
|
|
d4dfc79b18 | ||
|
|
b5f1e09d6c | ||
|
|
0165be2661 | ||
|
|
6d6cc0f608 | ||
|
|
4c485c63b9 | ||
|
|
f58cfc3617 | ||
|
|
cee3f25a37 | ||
|
|
1313bfb718 | ||
|
|
618fe0f0b3 | ||
|
|
d20c92bf65 | ||
|
|
47a35bf436 | ||
|
|
6b32424adb | ||
|
|
bd5f11dedd | ||
|
|
e40b74bebc | ||
|
|
ce74477e14 | ||
|
|
4d9d62ae89 | ||
|
|
5e5ba94b19 | ||
|
|
97bbe7fd30 | ||
|
|
91f7029923 | ||
|
|
be11a1f7e6 | ||
|
|
d87483d683 | ||
|
|
be2b008cc0 | ||
|
|
ab7377bcf1 | ||
|
|
fd6dd327c3 | ||
|
|
5fd178e44d | ||
|
|
345f3ace67 | ||
|
|
54ec93d06f | ||
|
|
4f68485272 | ||
|
|
c48239ec3d | ||
|
|
5d2c274420 | ||
|
|
9e5ad7c599 | ||
|
|
939356a1c2 | ||
|
|
3521c23c3e | ||
|
|
2311a50e1c | ||
|
|
29daebf548 | ||
|
|
2f08fd8f23 | ||
|
|
b8c3d00362 | ||
|
|
14124d96d4 | ||
|
|
e7fb59f225 | ||
|
|
e122aad8e2 | ||
|
|
4c4b8559cf | ||
|
|
b0fd28b6f3 | ||
|
|
b765f5cb81 | ||
|
|
da5d3651d7 | ||
|
|
82c8281166 | ||
|
|
ceadad993c | ||
|
|
f5611c937e | ||
|
|
343b6dd766 | ||
|
|
f40f2e770e | ||
|
|
23c3d62183 | ||
|
|
70e289ba73 | ||
|
|
ab50de9fe3 | ||
|
|
b3665f5ab1 | ||
|
|
c27207f6e6 | ||
|
|
ce6a96ad61 | ||
|
|
59f708ca21 | ||
|
|
f751505ada | ||
|
|
0fa7bf421f | ||
|
|
93d211c295 | ||
|
|
08c896e3c5 | ||
|
|
03ed2bccce | ||
|
|
ad6020754d | ||
|
|
5c8f983dde | ||
|
|
f1267e49af | ||
|
|
d2be97c5fc | ||
|
|
c13c68c813 | ||
|
|
4bdc7e0e86 | ||
|
|
86f95bb27f | ||
|
|
5365adaef6 | ||
|
|
0bbf0f6d18 | ||
|
|
95f4bd8fdc | ||
|
|
d8034c51bc | ||
|
|
b28baf7ee8 | ||
|
|
3673463182 | ||
|
|
7991662dc0 | ||
|
|
378a76b854 |
@@ -5220,4 +5220,78 @@ Before: managed project/thread verbs had no Console conversation area. After: fi
|
||||
|
||||
2026-10-21T22:30:20Z | Huey | T1 sessions design r3, scrooge-0007 R1, Vikunja 463 | Before: r2 e19e30df2 nested new session checkouts under the retained legacy instance workspace, allowing project-name collision or historical parent inspection to reach live siblings. After: exact path is dataRoot/session-workspaces/business/instance/session, outside the legacy root; canonicalized ancestor/descendant overlap refuses; T2 nesting mutant covers historical mount/inspection and a legacy project named sessions. All other r2 content retained as approved, revision label updated. Verification: old nested path absent, manual R1-only delta, whitespace and installed queue guard checked; no code/builds/suites/live changes/provider calls/credential reads.
|
||||
|
||||
2026-10-20 18:51:10 UTC | Row 191 #1689 design phase | Before: per-business launch revoke and per-binding Discord STOP leave task containers, active sessions and background work without one switch. Proposed per-data-root durable halt with human-only resume, generation-fenced starts, bounded concurrent termination, write-once stop receipts and existing event mirror; no schema migration. DESIGN.md inventories starter/continuation paths and container control-mount restriction. Sent to Dewey with Sage copied; implementation waits independent design approval. No tests, commits or live actions.
|
||||
|
||||
2026-10-20 18:57:51 UTC | Row 191 design revision | Dewey r1 changes: interim Console --send absence, reserved processing events and missing-state semantics required correction. DESIGN-r2.md now names CLI interim path, unavailable UI controls, trusted initialization and fail-closed missing leaf, container delete/replace mount tests, reservations and id-bound reconciliation. Small notes addressed including diagnostic seats and aliases. Recommended smaller Docker scope to Sage; design and scope approval pending, no code or test runs.
|
||||
|
||||
2026-10-20T19:50:56Z | Rocko | row 191 #1689 implementation stage: from independent brakes to shared root state, generation-fenced launcher and tracked stops, Discord/task guards and control HTTP/UI. Evidence is agents/rocko/work/queue-191/logs/ and PROGRESS.md. Fixture single files only so far; heavy verification and candidate not complete. Scope additions sage-1367 (one read-only CODE file) and sage-1392 (processing-only client socket timeout); no schema, proof baseline, live file/unit, commit or push change.
|
||||
|
||||
2026-10-20 22:58:45 UTC | Rocko | row 191 #1689 rebase inspection candidate | Rebuilt uncommitted kill-switch tree from da230ca48 onto landed row 190 base 00d4cc1f6 under new branch rocko/row191-r2-00d4. Hand merges retained instance flags and SSO/sign-out changes; fixture tests exposed and fixed SSO signer compatibility and two unknown-runtime cleanup defects. Final touched singles 249/249 across 26 files; 15 overlapping Client reader files 102/102. Manifest a4f780c2 has 78 paths; packet 185 files verified with scan clear. Mutants and owner gate remain pending; no review-ready or activation claim, no commit/push/live action.
|
||||
|
||||
2026-10-21 23:39:21 UTC | Rocko | row 191 #1689 inspection snapshot | Sage-2242 authorized a local inspection commit for Honker's row 194 S6. Created d52b4db1c on new rocko/row191-inspection-20261021, parent 00d4cc1f6, preserving the reviewed-design build and candidate a4f780c2 (78 paths). Static manifest/index/source scan and diff check verified; no tests run, no push credential or push attempt. Existing single-file evidence is historical, 249/249 and overlapping Client readers 102/102; suites/mutants/gate remain pending. Current code has verified runtime signalling and instance halt, but no dedicated per-run stop API yet; Honker and Sage notified of that seam directly. Snapshot is not review-ready or approved for live use.
|
||||
|
||||
2026-10-22 00:04:05 UTC | Rocko | row 191 #1689 per-run stop inspection | Implemented agreed Honker-0079/0081 stopRun contract with verified per-signal identity, fixed 5s+2s, durable first-owner join and immutable result plus later-exit observations. Focused CLEAR sage-2250: final 9 files 62/62, no skipped, after correcting host guard placement and peer-discovery fixture timeout isolation; initial failures and failed teardown logs retained, only own failed group stopped. Slot returned with cwd sweep clear. Sage published f7bf7dfec; sage-2265 clean rebase creates 7aa18b0f6 on dcd8d4dda under new branch, explicit Rocko identity, all 79 manifest product blobs identical so 62/62 evidence carried. Direct S6 handoff sent to Honker and Sage. Wider suites, mutants, review and activation remain pending.
|
||||
|
||||
2026-10-22T00:18:12Z | Sage (Opus 5.5) | row 254 landing (#1759), library.sync human timeout | Before: the human transport gave library.sync the 5-second default, so a sync longer than that reported outcome-unknown, and six library-* codes reached the Console as 502. After: 331597709 lands Megavolt's candidate (7 paths, tree d49e4626f, manifest digest be63cfbe recomputed and matched). human-cli.mjs waits 130 s for library.sync, past the server's 120 s hold, and 5 s for other verbs. webui bus.mjs maps library-sync-busy, library-diverged and library-accept-stale to 409, library-accept-invalid to 400, and library-credential-missing and library-fetch-failed to 503. channel-proof re-pinned. Merged-tree gate on the landing commit: library 77/0, bus 628/0, webui 305/0 (1 skip), cli 749/0 (23:44:33-23:55:11Z). eb9672c91 adds the README request-timeout sentence (Dewey N1) and a DEFERRED entry for the four library codes still at 502 (Dewey N2, binds the Console library routes row). Overlap sweep before push: 268 rebased to r2 ecd9b35eb (README only, approval carried webby-0096), 194 rebased to b7. Done rev 3072, #1759 closed.
|
||||
|
||||
2026-10-22 00:24:34 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 full gate on c78f48946 | stopped on second integration failure per sage-2275; singles 269/269, processing-control 61/61, bus 646/646; CLI interrupted with 26 observed failures; no further edits; cwd sweep empty and slot returned to Sage.
|
||||
|
||||
2026-10-22 00:48:20 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 eb3abba12 full gate | stopped at Discord 190/192; 32 singles 304/304, processing-control 61/61, bus 646/646, cli 761/761; two uninitialized Discord fixtures reported to Sage; no later run; cwd sweep empty, slot clear.
|
||||
|
||||
2026-10-22 00:58:27 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2294 fixture fix | e6800380d on eb3abba12, two test files/four additions, assertions preserved; 338-entry static all-package sweep recorded; no tests; sent Sage for publication and Honker for dependent gate.
|
||||
|
||||
2026-10-22 01:30:04 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 e6800380d resume | binding15/15, recover5/5, Discord192/192; stopped WebUI314 passed/4 failed/1 esbuild skip; processing polling violates sign-out/manual redirect and bar changes layout; no fixes/retries, cwd sweep empty, slot returned.
|
||||
|
||||
2026-10-22 01:38:08 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2317 Console integration fix | 8e1f55c1d on e6800380d; shared sign-out/manual redirect/interval and desktop frame fixes, assertions retained, six browser cases, 73 mutant sites preflight; syntax only, no tests; sent Sage/Honker.
|
||||
|
||||
2026-10-22 02:06:38 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 rebase sage-2329 | eafcbd3e7 on ddfb29af7, new branch, dispatcher retains processing and access; CLI/readme auto-merges checked; 87-path fd0f2740 manifest and 73-site preflight; Sage2333 published, awaiting CLEAR; no tests.
|
||||
|
||||
2026-10-22 02:20:51 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 eafcbd3e7 full gate | stopped singles313/6: six new browser cases count app freshness timer as processing timer; diagnosis sent Sage, no fix/retry; no suites/shells/mutants; cwd sweep empty, slot clear.
|
||||
|
||||
2026-10-22 02:23:13 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2345 timer fixture | 4db8b5778 on eafcbd3e7, one line attributes timer to processing.js; all assertions retained, no product change or tests; sent Sage and Honker; awaiting publication/CLEAR.
|
||||
|
||||
2026-10-22 02:25:29 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 sage-2349 record commit | 0f8235277 on 4db8b5778 adds packet BUILD.md cause record; product manifest remains ffb46fbd/87 paths; no amend, product change or tests; Sage/Honker notified.
|
||||
|
||||
2026-10-22 02:48:36 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 0f8235277 full gate second red | singles330/330, processing61/61, bus646/646, cli783/783, discord192/192; WebUI323/1/1 host esbuild skip; shell geometry1062vs1000; stopped/no retry, no later suites/shells/mutants; cwd sweep empty, slot returned, Sage escalates Mos.
|
||||
|
||||
2026-10-22 02:57:36 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 #1689 mos-1248 desktop layout restart | FF 5cd32f4c1 on 0f8235277: desktop grid allocates processing bar, existing shell assertions kept, visibility/no-scroll assertions added; 74 static mutant sites; candidate 2f93ef19; sent Webby/Sage for static review; no runs, awaiting review and CLEAR.
|
||||
|
||||
2026-10-22 03:11:09 UTC | Rocko (Codex gpt-6.1-sol, d99326cc) | row 191 static nav layout, mos-1249/1250 | FF da4334806 on 5cd32f4c1; controls first in nav, non-sticky per sage-2386/webby-0108; candidate-only placement assertion changed, landed shell assertions preserved; 77 mutants statically preflighted; manifest 245dceb1; Webby/Sage handoff sent, no runs, awaiting static look/publish/debug CLEAR.
|
||||
|
||||
2026-10-22 03:15:11 UTC | Rocko (d99326cc) | row 191 sage-2394 debug | da4334806 shell5/5 and processing-browser9/9, awk14/0/0; geometry heights reported with read-only distinction; cwd sweep[] and slot returned; full window awaits separate CLEAR.
|
||||
|
||||
2026-10-22 02:01:30 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 202 #1700 FED R5 landing | ddfb29af7 on 9c9732983: 6 manifest paths from a74cd9f87 (digests match CANDIDATE.sha256, patch-id 9d89077b equal); Duckworth APPROVE r5 rev 3026; C1 gate by Quackerjack on merge 3b86180b9: cli 771/771 skip 0 (run 2, run 1 red on gate TMPDIR length), 10 test-*.sh rc 0, M1-M13 killed; done rev 3108; landing note refused for length before the done move, so the record is the commit body and #1700 c30265; 191 and 194 rebased onto it (one scripts/mosaic case line)
|
||||
|
||||
2026-10-22 03:35:55 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 273 #1778 activate --send/--no-sso landing | 0bbf0f6d1 landed on 378a76b85, a rebase of cac7d0f67 (0042f713d) with all 6 range-diff pairs = and equal patch-ids; 4-path manifest e1031023 unchanged; Filbert APPROVE rev 3111 (c30308) carried merge-scoped under mos-1160, carry comment c30319 names 0bbf0f6d1. Gate on 0bbf0f6d1 by Darkwing: stack-release.test.mjs 218/218 skip 0, stack-release-external.test.mjs 25/25 skip 0, test-release.sh rc 0 (selftest 14/0, no skips), T58 controls c0 11/11 pass and c1 11/11 fail, M1-M7 killed. Rerun because row 202 changed scripts/mosaic, which stack-release.test.mjs copies. Red 1 was 978143bf4 (T53 test 6 usage pin). This doc commit adds the TOOLS.md usage line and F39 rules (Filbert N1).
|
||||
|
||||
2026-10-22 04:02:12 UTC | Rocko (d99326cc) | row191 full window0fa7bf421 | touched35files336pass, packages2403pass/1esbuildskip, shells425selftests+156queue node pass; SR01–07killed, SR08timeout,69unrun; stopped/no retry;88blobs restored,cwd sweep[],owned network removed,slot returned Sage; await Mos ruling.
|
||||
|
||||
2026-10-22 04:14:56 UTC | Rocko (d99326cc) | row191 control/resume sage-2427 | control12/12;SR08separate timeoutkill;49resumed assertionkills thenKS43survived;total56assertionkills+1timeout,1survivor,19unrun;88blobs restored,cwd sweep[],slot returned,Sage/Mos informed;no retry/edit.
|
||||
|
||||
2026-10-22 04:21:30 UTC | Rocko (d99326cc) | row191 KS43+19 retarget run | KS43assertionkill thenKS44invalid mutation syntax;57assertionkills+1timeout,1invalid,18unrun;stopped,no edit/retry;88hashes restored,cwd sweep[],slot returned,Sage/Mos notified.
|
||||
|
||||
2026-10-22 04:30:06 UTC | Rocko (d99326cc) | row191 KS44+18 resume | 10assertionkills thenKS54timeout-without-control despite dependency assertion;total67assertionkills+1timeoutkill,1unqualifiedtimeout,8unrun;88hashes restored,cwd sweep[],slot returned,Sage/Mos notified,no retry/edit.
|
||||
|
||||
2026-10-22 04:41:46 UTC | Rocko (Codex gpt-6.1-sol,d99326cc) | row191 #1689 builder complete/in-review | published0fa7bf421,manifest0bcbf337;35singles336pass,9packages2403pass/1knownskip,10shells425selftests+156queue nodepass;75assertionkills+2controlledtimeoutkills=77;88hashrestore,cwd sweep[],slotreturned;in-review rev3114,expected no-token failure3115(exit1);Sage/Dewey handoff,no live action.
|
||||
|
||||
2026-10-22 05:03:18 UTC | Rocko (Codex gpt-6.1-sol,d99326cc) | Vikunja502 processing test/runner hardening | brief+isolatedrocko-v502;expected wordingred→green,Node27/27,Python4/4,5/5assertionkills(includingSR08andKS54nohang),0skips/timeouts;sweep[],candidate5hashrestored,slotreturned05:01:29Z;candidate968d6ee3,runnerc5bdc764;packetready,no commit/push/liveaction.
|
||||
|
||||
2026-10-22 05:07:34 UTC | Rocko (d99326cc) | Vikunja502 committed/review handoff sage-2482 | newbranchrocko/vikunja502-ce6a-20261022,source8e3c6a79b+packet9bf4683c6 on ce6a96ad6;5paths no base drift,range-diff=,patch-id37135ebe unchanged;manifest968d6ee3,packet93889519 verifies39/39;focused runs carry;Sage publish/Dewey review handoff sent,no push/liveaction.
|
||||
|
||||
2026-10-22 05:10:00 UTC | Sage (1ef1e4f8) | serial landing 205 then 268 | 205: 3 commits, 4 paths, digest 858463b1, every scripts/test-*.sh green on ce6a96ad6, Duckworth N1-N4 carried (N1/N3 to 205b or R3, N2 to the 193b2 brief, N4 #1782 before any user thread goes live). 268 r4: c27207f6e on 205, 9 paths, digest caa0fa4e, patch-id 2942acc8 equal to r3. Overlap checks clean before both pushes. No live action; LIVE stays 86f95bb27.
|
||||
|
||||
2026-10-22 05:58:59 UTC | Rocko | row 279 #1784: before, deadline overshoot skipped final observation and returned unknown; after, one-line for (;;) retains final mapping and unchanged unreachable fallback. Regression red as specified; whole stop-run 13/13 and processing-control 62/62; M1 assertion kill, no skips, restored and swept. Source 1016b8584, packet cad927137; manifest 312ed511d8f1cff826ee2fd547de8368923130c7bfe06236d8e29d8655b80aaa. Pending Sage publication and Dewey review; no live action.
|
||||
|
||||
2026-10-22 06:15:54 UTC | Sage (1ef1e4f8) | row 279 #1784 landed (mos-1269) | Before: stopRun returned unknown for a live run when a sleep overshot the deadline. After: LANDED ceadad993 as a carry of cad927137 onto 343b6dd76 (range-diff 1:= 2:=, patch-ids 6d8ae7f4/e484c297 equal, Dewey carry c30410). Sage gate on cad927137: business 209/0/0, cli 794/0/0, discord processing + processing-stop 9/0/0. Correction: the first discord run wrote to a wrong out path and ran nothing; the rerun is the result. Overlap sweep clean (13, 257). Note rev 3144, done rev 3145. No live action; LIVE stays 86f95bb27.
|
||||
|
||||
2026-10-22 06:37:43 UTC | Sage (1ef1e4f8) | row 194 b16 third red (mos-1269/mos-1270) | Gate on ceadad993 + 9cdc47d04: business 228/0, cli 826/0 (T9a passes), control-board 124/0, bus 705/1 at grant-executor.test.mjs G13 (timed out 10044 ms); conversation, webui and scripts/test-*.sh never ran. runb8 green 436/0/0, 17/17 mutants. Correction to sage-2524 (mos-1270): the stuck wait is the first until at :733, the holding file after decision.resolve, so the executor never started the install and hosts.revoke at :734 never ran; it is not a revoke-path hang. Ruling: a diagnosis run, 30 base + 30 candidate alternating under cli load, no gate retry.
|
||||
|
||||
2026-10-22 07:51:04 UTC | Rocko | row 280 #1785: before, any TCP connect could accept a port our sshd failed to bind; after, attempt-local exact log proof plus liveness and connect, non-ephemeral picker, bounded scratch remote sleep cleanup. Both occupied regressions red on old acceptance; CRLF build failure captured and fixed under sage-2535. Committed source b765f5cb8, packet d6c924764, manifest27806c0579c76d0999d2e6be4180c3867570df0268af9d7e0d7b971af799e6f5. Repeats07:29:03Z–07:48:38Z grant1230/1230,host780/780,CLI1588/1588; final M1/M2 four assertion kills,0skips/timeouts,sweepclear. Sage publishes/gates; Duckworth reviews.
|
||||
2026-10-22 08:00:02 UTC | Sage (1ef1e4f8) | row 280 #1785 landed | before: the bus tests' scratch sshd helper returned a port on a connect alone, and 194 b16's gate hung at G13 (base hung too, honker-0127). After: b0fd28b6f on refactor, a ff from da5d3651d (b765f5cb8 + Rocko slim packet; full evidence d6c924764 on rocko/row280-da5d-20261022). Duckworth approve rev 3153 (manifest 27806c05). Sage gate bus 656/0/0, ge 41/0/0, hr 26/0/0 (c30431). Rocko repeat 2010/2010 under cli 1588/1588, no timeout; M1/M2 killed 4/4. Two build-time slips recorded in Rocko's packet: a runner root path (no test ran) and the CRLF sshd log line. Overlap 13/257 clean. Note rev 3154, done 3155, #1785 closed c30432, P19 511 comment 623. 194 b17 rebases on b0fd28b6f (sage-2542).
|
||||
|
||||
2026-10-22 08:38:11 UTC | Sage (1ef1e4f8) | row 194 #1692 landed | before: 194 b16 9cdc47d04 went red three times (stopRun deadline, then the bus test sshd port race), fixed by rows 279 and 280. After: fd6dd327c on refactor, a ff on e122aad8e. b18 is b17 3f451772d rebased after my doc and queue push blocked a ff (16 =, series patch-id 64303253f8e6, packages equal b17). Webby approve rev 3160 on manifest 29f131ab. Honker gate on fd6dd327c: runb8 503/0/0, mutants 17/17; 18 packages 3668/0, ten scripts rc=0, Docker up, env skips named. The secret scan hit three fake_*_token fixtures in bus tests (false positive). Note 3161, done 3162; #1692 closed c30435; P19 414 comment 625. LIVE still 86f95bb27.
|
||||
2026-10-22 08:42:04 UTC | Honker (Claude Opus 5.5, ecd17786) | row 194 #1692 agent lifecycle build | before: no Console or CLI lifecycle for agents (create, launch, terminate, regenerate, delete) and no per-agent hire/fire veto. After: 16 commits landed as fd6dd327c (b18, a rebase of b17 3f451772d on e122aad8e: 16 =, series patch-id 64303253f8e6, packages equal b17). Manifest 53 paths, digest 29f131ab. Final run on fd6dd327c: runb8 39 files whole 503/0/0, mutants 17/17 killed, restores matched; gate.sh merge onto e122aad8e, 18 packages 3668/0, ten scripts rc=0, Docker up, four env skips named (webui esbuild pin, queue render outside canonical root, test-task live provider x2); sweep clean. Corrections: b16 went red at G13; my diagnosis (60 runs, base hang on port 38927 sshd bind EADDRINUSE) placed it outside 194, fixed by row 280; the g13diag load kill missed its process group and overran about 8 minutes; one runb8 launch failed rc=126 on a lost exec bit before any test ran; the b17 runb8 was stopped unscored when refactor moved. In-review revs 3156/3157, Webby approve rev 3160.
|
||||
2026-10-22 09:43:34 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 236 #1742 Gitea grant landing, and row 281 filing | 236 landed bd5f11ded at 09:37:31Z, a fast-forward on be2b008cc: 08840f3c1 rebased past 194 (commit 1 unions only, 2-7 =), e40b74beb for two stale out-of-manifest pins, bd5f11ded for a gitea-not-configured raise test. Dewey approve rev 3168 on manifest 0b00269b (34 paths). Megavolt gate under my slot: 2c bus 131, cli 224, route 28, docker 1; whole bus 765, business 228, cli 832, ten scripts rc=0; mutants 40/40; skips named, env only. Overlap with 13 and 257 clean. Note 3169, done 3170; #1742 closed c30451; P19 436 comment 639. Correction: the first whole run on ce74477e1 went red 762/764 on the two stale pins; that run is void. Follow-up per mos-1271: row 281 (#1786), a fire or delete revokes host and Gitea grants, Megavolt owns, Dewey reviews; brief docs/plans/2026-10-22_fire-revokes-grants.md; DEFERRED entry points at it.
|
||||
2026-10-22 10:30:51 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 274 #1779 FED R3 broker user principal landing | 274 landed 4c485c63b at 10:28:26Z, a fast-forward on 47a35bf43: six Morgana commits d20c92bf6..4c485c63b. r5 f58cfc361 moved covers, LEVELS and the all marker into business/src/vocabulary.mjs so the broker keeps its sandbox import closure (M16/M16b/M16c). r6 4c485c63b pins USER_VERBS as the ruled seven and widens the R3 off-list set. Dewey approve rev 3178 on manifest 00047b52 (13 paths), comment 30487. Morgana gate under my slot, 10:09-10:22Z: 23 touched files 0 fail, bus 787, harness 151, business 228, cli 832, seat 167, ten scripts rc=0, mutants 22/22; skips named, env only (test-queue 1, test-task 2). Overlap with 13 and 257 clean. Note 3179, done 3180; #1779 closed c30491; P19 414 comment 646. Corrections: the r5 gate on f58cfc361 went red (M3x/M6x survived the R3 off-list set), void; my gate comment 30490 said 24 touched files, the count is 23; the kit comment guessed human-required for the M3x wall, the run code is not-holder.
|
||||
2026-10-22 10:57:34 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 256 #1761 the human deletes a creative instance, landing | before: row 194's deleteRefusal refused every END_PROTECTED definition for every actor, so nobody could delete a creative instance; the human could only delist one with launch: false. After: 256 landed f12441d4f, a fast-forward on 0165be266 (the 274 landing plus its docs and queue commits): four Honker commits b5f1e09d6..f12441d4f, patch-id b28e63746f8c. One protectedRefusal predicate serves delete and the listing's per-viewer endProtected; any non-human actor gets organization-creative-human-only, fire unchanged. Rebased twice (past 236, then past 274), each range-diff 4/4 =, carries checked by Webby (webby-0126, webby-0128). Webby APPROVE on manifest-r3 01eb75aa (10 paths), comment 30519, posted by Sage as jarvis because Webby has no token; review request c30518 (Sage, revs 3183/3184) after Honker's request failed (no token, revs 3181/3182). Gate by Honker under Sage's slot 10:33Z-10:52Z on merge tree ced6bf5f8: touched files 31/0/0, 18 packages 3757/0 (bus 788, business 231, cli 832, webui 335), ten scripts/test-*.sh rc=0, mutants M1, M2, M3, M4a, M4b killed with blob-checked restores. Skips, named and environment-only: webui editor rebuild byte check (esbuild 0.28.1 installed, pin 0.28.2), test-queue non-canonical root, test-task live provider x2. Overlap with in-review rows 13 and 257 clean. Order change sage-2594: 256 landed before 277 because 277's gate had not run. LIVE is still 86f95bb27. Queue note rev 3186, done rev 3187; #1761 closed c30520; P19 416 comment 649.
|
||||
2026-10-22 11:19:28 UTC | Sage (Claude Opus 5.5, 1ef1e4f8) | row 277 #1782 the owner sees a thread agent's claims, landing | before: a thread run's role.claim wrote thread_claims only, and the agents verb read role_claims only, so the owner had no way to see a thread agent's claim (PRD :1083-1084). After: 277 landed 62f53b1e0, a fast-forward on 46dedf925 (the 256 landing plus its docs and queue commits): seven Morgana commits 1d5d00939..62f53b1e0, range patch-id 7f0533520bd2. A bus verb thread.claims serves human and reader sessions; mosaic agents adds thread lines without --tester or --json, --json keeps the agents array, the tester path makes one call, a refused thread.claims prints the role lines and the refusal code, and an error with no bus code still throws. Rebased onto 0165be266, then onto 46dedf925 after 256 landed first (sage-2594); both range-diffs all =, manifest e281aa2c unchanged (duckworth-0126). Duckworth APPROVE comment 30539, recorded rev 3192, posted by Sage as jarvis; review request c30538 (revs 3190/3191) after Morgana's request failed (no token, revs 3188/3189). Gate by Morgana under Sage's slot 11:01:23Z-11:13:56Z: 7 touched files 0 fail (thread-claims 5, message-filter 6, user-principal 22, format 4, tester 11, other-tree 4, host 43), bus 793, cli 836, harness 151, business 231, seat 167, ten scripts/test-*.sh rc=0, M1-M9 killed on committed bytes. Skips named, environment only: test-queue non-canonical root, test-task live provider x2. Overlap with in-review 13 and 257 clean. Duckworth N1 (M8 dies on a rethrown error) to DEFERRED; 0123 N3 per-session claims binds 264a r7. Note rev 3193, done rev 3194; #1782 closed c30540; P19 506 comment 652. Also landed as doc-only picks: Honker's 256 BUILD.md gate results (80620a3a0 as b83173305) and Megavolt's 236 BUILD.md runs (9c6e1a165 as 01ea1fb11). LIVE is still 86f95bb27.
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# Row 273 (#1778): activate --send and --no-sso
|
||||
|
||||
Darkwing, 2026-10-22. Brief: `docs/plans/2026-10-22_activate-send-nosso.md` (blob in f0d0b31e9), approved by
|
||||
Mos in mos-1246 (F39). Base: origin/refactor 0042f713d. Reviewer: Filbert. Gate owner: Sage.
|
||||
|
||||
## What changed
|
||||
|
||||
Only `scripts/stack-release.sh` and its two node test files.
|
||||
|
||||
- `activate --send` puts `--send` into the run's Console flags after `--sso` and before the tester
|
||||
flags, where webui-service.sh renders it. It refuses `send-needs-sso` unless those flags hold `--sso`,
|
||||
given or carried. The plan line is `send: on; --send was given`, or `send: carried` when the pointer
|
||||
already holds it. `check_target` lets it name the pointer's copy only when the pointer holds no `--send`,
|
||||
so a second `--send` on the same copy refuses `already-active`.
|
||||
- `activate --no-sso` refuses `sso-not-on` when the pointer holds no `--sso`. Otherwise it takes the
|
||||
Console flags from a rollback set (below) and keeps the pointer's tester flags. `send_for_target`
|
||||
drops `--send`, whether it came from the set or the pointer. It is the only place that does, so one
|
||||
mutant there (M3) shows the drop is tested. The plan prints `sso: off; Console flags from <set>/<file>; restores <flags>`
|
||||
and a send line, `send: dropped; --no-sso drops --send (F39)` or `send: off; --no-sso was given and the
|
||||
pointer holds no --send`. The steps log gets `drop --send; --no-sso was given` and
|
||||
`turn off --sso; the Console flags come from <set>/<file>`.
|
||||
- Usage errors: `--send` with `--no-send`; `--no-sso` with `--sso`, `--send`, a proof pair or
|
||||
`--no-proof`; either flag on any verb but activate; either flag under `--units external`.
|
||||
`--no-sso --no-send` is allowed, since the brief doesn't forbid it and both drop the same flag.
|
||||
- The set read that `sso_base` did inline for a rollback to a Console without `--sso` moved into
|
||||
`set_flags`, which both paths call. Its three messages are unchanged, and T57 still pins them.
|
||||
- `from_log` has a new mode, `sets`: the activation of each ok activate and rollback line, newest first,
|
||||
stopping at the adopt line, which took no rollback set.
|
||||
|
||||
## The consequential choice: which set --no-sso reads
|
||||
|
||||
The brief says the flags come from "the rollback set of the activation that turned `--sso` on", and that
|
||||
"a set whose Console already ran with `--sso`" refuses. Each set holds the Console unit from before its
|
||||
run. So the activation that turned `--sso` on is the newest one whose set holds a Console without
|
||||
`--sso`: every activation after it ran with `--sso`, and so did every set it left.
|
||||
|
||||
`no_sso_base` walks the log's sets newest first and stops at the first set whose Console has no `--sso`.
|
||||
It refuses `sso-off-flags` at the first set it can't read (missing, failing its SHA256SUMS, or with no
|
||||
argv the script can parse), since it can't tell whether that set was the one. If every set ran with
|
||||
`--sso`, it refuses with `the Console in <set>/<file> already ran with --sso, and the log names no
|
||||
earlier activation`. A log with no set at all refuses with `the log names no activation with a rollback
|
||||
set`.
|
||||
|
||||
The tradeoff: a later same-copy re-pin that carried `--sso` (a `--no-send`, or a re-pin onto a newer
|
||||
copy) doesn't break `--no-sso`, which the phase 2 window needs if a re-pin lands between step 5 and a
|
||||
rollback. The cost is that "a set that ran with `--sso` refuses" now applies only when no older set
|
||||
without `--sso` exists. A tampered set on the walk still refuses, even one newer than the set the walk
|
||||
would have used. Filbert, please rule on this reading.
|
||||
|
||||
## Filbert's early look: B1 and N1 (sage-2315)
|
||||
|
||||
Filbert accepted the walk. Sage ruled both findings into this row.
|
||||
|
||||
- B1: row 200's rollback to a copy whose Console has no `--sso` took the flags from the set and kept
|
||||
the pointer's `--send`. From an `--sso --send` pointer, which this row's `--send` can now make, that
|
||||
gave a Console with `--send` and no `--sso`, which F39 b forbids. `send_for_target` now drops `--send`
|
||||
whenever the run drops the pointer's `--sso`, so it is still the one place that drops it. The plan
|
||||
line reads `send: dropped; the run drops --sso, and --send needs it (F39)`, and the steps log gets
|
||||
`drop --send; the run drops --sso`. Row 200's sso line now names only the tester as following the
|
||||
pointer.
|
||||
- Changed existing assertion, for the review to approve by name: T57 item 3 (sage-1389), "the
|
||||
plain case", ends with `assert.deepEqual(pointer(g).console,[...noSend(gbase),...pair(f)])`. It
|
||||
used to expect `'--send'` at the end. The case seeds a legacy `--send` pointer, turns `--sso` on
|
||||
(which carries `--send`), then rolls back to B, whose Console has no `--sso`. A carried `--send`
|
||||
that ends without `--sso` breaks F39 b whatever its source, so the old pin asserted the wrong
|
||||
outcome (Sage accepted this in sage-2321). Row 200's two sso plan strings also lose "--send and"
|
||||
(`ROLLBACK_PLAN` and the round 3 named-instance case), since `--send` no longer follows the pointer
|
||||
on that path.
|
||||
- N1: the walk skipped an ok line with no activation. Skipping it could land on an older set that
|
||||
isn't the right one, so it now refuses `sso-off-flags` with `an ok line in the log names no
|
||||
activation`. The walk reads the log through `mapfile`. That gives no entry for an empty log, so a
|
||||
log with no set still gets its own message.
|
||||
|
||||
## test-release.sh
|
||||
|
||||
The window plan says test-release.sh "gets cases for both". The brief lists it only as a file to run
|
||||
whole. test-release.sh is release.sh's Docker selftest and doesn't call stack-release.sh, so the cases
|
||||
live in the node tests and test-release.sh runs unchanged.
|
||||
|
||||
## Tests (T58 in stack-release.test.mjs)
|
||||
|
||||
- T1: `--sso --send` from B onto C1 with a tester port: plan, pointer, unit and steps. `--send` beside a
|
||||
carried `--sso` re-pins C1 once; a second `--send` refuses `already-active`; C carries it.
|
||||
- T2: `--send` without `--sso` refuses on another copy and on the pointer's copy, also when the pointer
|
||||
carries `--send` alone; `--plan` lists the refusal.
|
||||
- T2 (sage-2315 B1): `--sso --send` onto C1, then a rollback to B, whose Console has no `--sso`. The
|
||||
pointer and the unit end with neither flag, and the plan and steps name the drop.
|
||||
- T2 and T4 usage: every forbidden pairing and verb, checked against the full usage text.
|
||||
- T3: from an `--sso --send` pointer, `--no-sso` on the same copy gives back the pre-`--sso` ExecStart
|
||||
line byte for byte, without `--send`. A second case walks past a `--no-send` re-pin that ran with
|
||||
`--sso` and keeps the pointer's tester port (7334), which differs from the set's (7333). A third
|
||||
(sage-2305 a) turns `--sso` on, then re-pins twice with `--send` and `--no-send`, so the two newest
|
||||
sets both hold `--sso`. `--no-sso` then gives back the arrival's ExecStart line byte for byte.
|
||||
- T4 (sage-2305 b): two sets, both edited or written to hold `--sso` with sums intact, refuse
|
||||
`sso-off-flags` through `refused()`.
|
||||
- T4: `sso-not-on`; an ok line with no activation; a set that fails its sums, ran with `--sso`, has no
|
||||
argv or is missing; a log with no set. Each runs through `refused()`, so nothing stops and nothing else changes.
|
||||
- T5: stack-release-external.test.mjs adds `--send` and `--no-sso` to the row 232 usage list.
|
||||
- T55 test 5 and the T57 usage test match the new usage line.
|
||||
|
||||
## Mutants
|
||||
|
||||
| # | Change | Expected killer |
|
||||
|---|---|---|
|
||||
| M1 | `set_flags` skips the SHA256SUMS comparison when `--no-sso` is given | T4 tampered set |
|
||||
| M2 | `no_sso_base` skips the `sso-not-on` refusal | T4 `sso-not-on` |
|
||||
| M3 | the `--no-sso` branch of `send_for_target` keeps `--send` | T3 |
|
||||
| M4 | `--send` skips the `send-needs-sso` refusal | T2 |
|
||||
| M5 | parse_opts drops the `--send`/`--no-send` rule | T2 and T4 usage |
|
||||
| M6 | a rollback to a Console without `--sso` keeps `--send` (B1) | T2 (sage-2315 B1) |
|
||||
| M7 | the walk skips an ok line with no activation (N1) | T4 no-activation line |
|
||||
|
||||
Runs and results go in the round's notes once Sage clears the slot.
|
||||
@@ -0,0 +1,80 @@
|
||||
# Row 268 build notes (Gizmoduck)
|
||||
|
||||
Case 2 C2-01c, playground levels. Gitea #1773, Vikunja 464. Brief: docs/plans/2026-10-20_case2-rows.md,
|
||||
"C2-01c playground levels: universe-level prefixes and draft universes" (blob a6ac627c8). The ruling is
|
||||
agents/gosalyn/work/case2/DESIGN.md "Ruling after r11: playgrounds at three levels" (:49-88 at
|
||||
db1e7732a), and it wins where the two differ. Reviewer Webby, gate owner Sage. Branch gizmoduck/row268
|
||||
from refactor db1e7732a. Line numbers are on this branch unless a commit is named.
|
||||
|
||||
## mapping.mjs
|
||||
|
||||
- `kind` (:71). An entry with a `kind` key must say `"hybrid"` or `"draft"`. Before, a plain universe
|
||||
with `kind` refused as an unknown key, and `kind: "draft"` did the same.
|
||||
- A draft (:73-86) refuses `books` with its own message before the key check, so the cause reads
|
||||
plainly. It takes `kind`, `name` and `playground`, needs one prefix or more, and comes back as
|
||||
`{ kind: 'draft', name, books: [], playground }`, frozen with the rest.
|
||||
- The prefix level check moved out of the per-entry loop to :127-140, after every entry is read,
|
||||
because it needs every entry's books. It runs after the book-holds-book, prefix-holds-prefix and
|
||||
listed-twice checks, so an overlap still reports as before. For each prefix and each book folder:
|
||||
- equal to any book folder: `library-mapping-invalid` (brief, What ships, "or equal to a book folder");
|
||||
- inside or holding another entry's book: `library-playground-other-universe`, and the message says
|
||||
which, with the book and its entry;
|
||||
- holding its own entry's book: `library-mapping-invalid`.
|
||||
A prefix inside its own book is book level, and one that touches no book is universe level. A draft
|
||||
has no books, so its prefixes are either outside every book or refused as another entry's.
|
||||
- `sources` (:117-120) now refuses any entry with a `kind`, so a draft can't be a source either. The
|
||||
message changed from "not a non-hybrid universe" to "not a plain universe". mapping.test.mjs:61 on
|
||||
db1e7732a pinned the old words, and that line moves with it.
|
||||
- `library-playground-outside-book` is gone from packages and docs/case2.
|
||||
|
||||
## scope.mjs
|
||||
|
||||
`classify` (:12-24) now runs two passes. The first finds a prefix in any entry, and the file is
|
||||
`playground` with that entry's id and `book` set to the entry's book folder that holds the prefix, or
|
||||
`null`. The second finds a book folder, and the file is `canon`. `place` (:26-32) works out `type` and
|
||||
`folder` from the first folder under the prefix or the book, as before. The validator keeps prefixes
|
||||
from overlapping, so the first pass finds at most one.
|
||||
|
||||
## Readers on this base
|
||||
|
||||
- reader.mjs:182 and :194 spread `classify`'s answer into each file record, so a universe-level or
|
||||
draft file carries `book: null`. Nothing in the reader reads `book`.
|
||||
- reader.mjs:206-209. R5 fires for a canon file linking into any playground. R6 fires when the target's
|
||||
universe differs and isn't a source. A draft can't be a source, so a link into one gives R5 and R6.
|
||||
A link into the file's own universe-level prefix gives R5 only. No code change was needed. The new
|
||||
test covers both.
|
||||
- survey.mjs:66 and mirror.mjs:246 read only `scope`. mirror.mjs:251 calls `readLibrary` and checks
|
||||
only `path`.
|
||||
- No other package calls this `classify` or reads `universes` (git grep over packages/*/src).
|
||||
- C2-05a (row 216) and C2-06 (row 217) have no candidate branch on origin at the time of writing. I
|
||||
check again before in-review.
|
||||
|
||||
## Tests
|
||||
|
||||
- mapping.test.mjs: the old outside-book test splits into the other-universe refusals (inside another
|
||||
entry's book, holding one, a draft prefix inside a book), the mapping-invalid ones (equal to a book,
|
||||
own or another's, and holding its own), universe level in a plain universe and in a hybrid with
|
||||
`books: []`, and the draft shape (frozen with `books: []`, with `books`, with none or empty
|
||||
prefixes, an extra key, and five bad `kind` values). The overlap test gains a cross-entry hold and a
|
||||
cross-entry duplicate. The hybrid test gains a draft source. The caller-supplied `fail` test now
|
||||
uses `Second World/Playground/` and sees the new code, since `Elsewhere/` is valid now.
|
||||
- scope.test.mjs, new: one case per level (book, universe in a plain universe, universe in a hybrid,
|
||||
draft), `.canvas` under each new prefix staying unmapped, prefixes first inside a book that holds a
|
||||
prefix, and the reader case for R5 and R6, with `book: null` on the read records.
|
||||
- business.test.mjs: `Elsewhere/Playground/` now loads, and `Second World/Playground/` refuses with
|
||||
`library-playground-other-universe` through `validateBusinessDocument`.
|
||||
|
||||
## Docs
|
||||
|
||||
- packages/library/README.md: the universe keys, a line on the three levels, the refusal table and
|
||||
the Scope section.
|
||||
- docs/case2/SETUP-SHEET.md "Your playground": one line per level and the draft entry.
|
||||
- docs/case2/SURVEY-RUN.md:114: the new code and message.
|
||||
|
||||
## Hash pins
|
||||
|
||||
`git grep -E '[0-9a-f]{64}'` over packages/*/tests finds no pin on any changed file.
|
||||
|
||||
## Runs
|
||||
|
||||
None yet. Every run waits for Sage's CLEAR.
|
||||
@@ -1052,3 +1052,34 @@ Doc-only. `git diff 899e30a80 <this commit> -- packages` is empty, so the runs c
|
||||
- **Correction to N4 above.** "Until a human verb or a new end clears it" is wrong. `#aliveAfterFire`
|
||||
checks only `session.ended`, and today nothing else clears the flag. It stays until the follow-up
|
||||
row adds a clear.
|
||||
|
||||
### b15 (a), product change: the constructor's argument checks move ahead of the store reads (sage-2501)
|
||||
|
||||
- **Why.** The b14 runb8 (`run-20261022T052118Z`) went red on chief-of-staff T8b (`:864/868`). It
|
||||
builds `new Broker({ store: null, roleFile: 'roles' })` and expects `roleFile must be a function`.
|
||||
Row 191 (4bdc7e0e8) reads `dirname(store.directory)` in the constructor before the roleFile check,
|
||||
so a null store throws its own TypeError first. The order was already like this at b8, but the last
|
||||
194 run before 191 landed was b7, and runb8 had never run on 191's base until today.
|
||||
- **Change.** The harnessOn (row 65), testerOn and roleFile type checks move up, unchanged, to just after
|
||||
the invalid-business check and before `this.#businesses`, `this.#store` and every store read. Each
|
||||
descriptive comment stays with its assignment, and one new comment line goes above the moved checks.
|
||||
Nothing else moves. 191's lines are byte-identical. invalid-business stays first because it was
|
||||
already ahead of every store read. Diff: broker.mjs +4/-3.
|
||||
- **Pins.** chief-of-staff.test `:868` (roleFile, null store) now gets its message.
|
||||
routes-verb.test `:89` (harnessOn 'yes', a real store) keeps its message.
|
||||
|
||||
### b15 (b'), test fixture: T15 drops the reviewer stand-in's registry entry (sage-2503, sage-2507)
|
||||
|
||||
- **Why.** The other b14 red was cli lifecycle T15 (`:744`). The debug run printed the launch log tail:
|
||||
after the resume the coder launch closes `launch-failed` with why `running session has no exact
|
||||
provider/model; reconcile before launching` (business/src/launch.mjs usageCounts, row 122). The
|
||||
reviewer stand-in has exited, but its registry entry is still there, and that entry carries no
|
||||
provider/model. The launcher's exit handler only removes the runs it started itself.
|
||||
- **Not (b).** I didn't give standIn's entry provider/model (honker-0118). prepare()'s limits are
|
||||
`{ sonnet: 1 }`, and the reviewer and coder both run claude-sonnet-5-5, so the stale entry would count
|
||||
1 against a limit of 1 and the same launch would refuse on the limit instead. Sage ruled (b')
|
||||
in place of (b) (sage-2507). standIn is unchanged.
|
||||
- **Change.** T15, after `assert.equal(await x.exited, 143)`, adds
|
||||
`s.launcher.registry.remove("acme", "reviewer-run")` with N6's comment. That's the convention at
|
||||
N6 `:763-764`, and it's test-only. standIn's `t.after` removal still runs, and a second remove of
|
||||
a missing run is the same no-op it already is in N6.
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# Row 256 (#1761) build notes (append only)
|
||||
|
||||
## 2026-10-22 b1 (Honker)
|
||||
|
||||
Design d2 8c50481d2 was accepted by Webby (webby-0123). The code is on honker/row256 from be2b008cc.
|
||||
|
||||
### Correction to the design: the non-creative END_PROTECTED definition is `assistant`
|
||||
|
||||
- DESIGN.md (d1 and d2) says "the chief of staff" for the END_PROTECTED definition that isn't creative,
|
||||
following the brief's wording. In the code that definition is `assistant`:
|
||||
- `THREAD_DEFINITION = "assistant"` (vocabulary.mjs:203).
|
||||
- `END_PROTECTED = THREAD_DEFINITIONS = [assistant, ...CREATIVE_DEFINITIONS]`.
|
||||
- The `chief-of-staff` definition (the `chief` instance) isn't END_PROTECTED. Its delete refuses
|
||||
`organization-instance-referenced`, because it is launch.by, and that is unchanged.
|
||||
- So T3 and M2 use an `assistant` instance. The code is the same either way: protectedRefusal refuses
|
||||
every END_PROTECTED definition that isn't creative.
|
||||
- DESIGN.md isn't edited after approval; this note is the record.
|
||||
|
||||
### Persona file on delete (brief item 4)
|
||||
|
||||
- `<configDir>/personas/<business>/<instance>.md` is left in place. Nothing removes it, so there is no
|
||||
new open and no O_NOFOLLOW test.
|
||||
- Nothing reads it once the entry is gone. bundle.mjs reads it only for a launched creative instance,
|
||||
and the name is retired, so `add` refuses it (instance-settings.mjs, organization-instance-retired)
|
||||
and no later instance picks up the old persona.
|
||||
- The human removes the file by hand if they want it gone.
|
||||
|
||||
### What changed
|
||||
|
||||
- packages/business/src/instance-settings.mjs:
|
||||
- New `protectedRefusal(document, instance, actor)`.
|
||||
- `deleteRefusal(..., actor)`, `planDelete(..., actor)` and `checkDelete(..., actor)` take the
|
||||
actor, and set op delete passes its actor.
|
||||
- `read(business, actor)`. Its `endProtected` is `protectedRefusal(...) !== null`.
|
||||
- commit's and fire's returned listings read with the writer's actor.
|
||||
- The comment at :28-31 is rewritten.
|
||||
- packages/bus/src/runtime.mjs: `instances` and `instances.check` pass the actor. There is no broker
|
||||
change.
|
||||
- packages/webui/src/public/agents.js: comment only.
|
||||
- docs/plans/DEFERRED.md: a closing line on the creative-delete entry.
|
||||
- Tests:
|
||||
- business instance-settings.test: T1-T3 and T5 in one test, T4 in its own test, and the row 213
|
||||
test's `endProtected` for the human's add is now `false`.
|
||||
- business lifecycle-settings.test: checkDelete with the actor; fire unchanged.
|
||||
- bus creative.test: T6 on the real runtime.
|
||||
- webui agents-browser.test: T7/T8. The comment on the existing assertion is reworded.
|
||||
|
||||
### Mutants for the gate (exact-once replacements, runb8 shape)
|
||||
|
||||
- M1 (instance-settings.mjs): ` if (actor === document.human) return null;` becomes ` return null;`.
|
||||
The kill is instance-settings.test "row 256 T1-T3".
|
||||
- M2 (instance-settings.mjs): move the human check ahead of the creative one. Replace
|
||||
` if (!CREATIVE_DEFINITIONS.includes(definition)) return { code: "organization-definition-refused" };\n if (actor === document.human) return null;`
|
||||
with
|
||||
` if (actor === document.human) return null;\n if (!CREATIVE_DEFINITIONS.includes(definition)) return { code: "organization-definition-refused" };`.
|
||||
The kill is "row 256 T1-T3" (T3).
|
||||
- M3 (instance-settings.mjs): ` endProtected: protectedRefusal(document, name, actor) !== null,` becomes
|
||||
` endProtected: END_PROTECTED.includes(entry.definition),`. The kills are "row 256 T1-T3" (T5) and
|
||||
bus creative.test T6.
|
||||
- M4a (runtime.mjs): `instances.checkDelete(id,args.instance,actor)` becomes
|
||||
`instances.checkDelete(id,args.instance)`. The kill is creative.test "row 256 T6".
|
||||
- M4b (instance-settings.mjs): `planDelete(old, instance, actor), { revision` becomes
|
||||
`planDelete(old, instance), { revision`. The kills are "row 256 T1-T3" (T1) and creative.test T6.
|
||||
|
||||
### Tests not run
|
||||
|
||||
None of these have run. They wait for Sage's CLEAR, after the rebase onto 236.
|
||||
|
||||
## 2026-10-22 b2 (Honker): Webby's N1 (webby-0124)
|
||||
|
||||
- Two comments said the chief of staff where they mean the assistant. Both now name the assistant:
|
||||
- instance-settings.mjs:30, "The assistant is refused for every actor, the human included."
|
||||
- agents.js:46, "true for the assistant and on the tester's."
|
||||
- Comment-only. The mutant strings above are unchanged and still occur once.
|
||||
|
||||
## 2026-10-22 gate results (Honker, doc-only after landing)
|
||||
|
||||
This entry was added after landing. It records the gate on f12441d4f, which was the candidate, with manifest-r3
|
||||
01eb75aa9d51367337f4eda855ad5271c5e22b14650aa5a2130333d275a7ab90.
|
||||
|
||||
### Rebases
|
||||
|
||||
- r2 1e761962a onto 236's landing bd5f11ded, and r3 f12441d4f onto 274's landing 0165be266. In both
|
||||
range-diffs, every commit is `=` and nothing conflicted.
|
||||
- The landing patch-id, from `git diff BASE TIP | git patch-id --stable`, is
|
||||
b28e63746f8c1bda1c49c021447c61f2df6ea0d0 on both bases.
|
||||
- Before that, Sage granted an early slot on 1e761962a (sage-2567), limited to steps 1-2. It was an early
|
||||
signal, not gate evidence: 31/0/0, and M1-M4b were all killed.
|
||||
|
||||
### Full gate (Sage's slot, sage-2586, 10:33Z to 10:50Z)
|
||||
|
||||
- The gate tested a merge of the candidate onto origin/refactor 0165be266, tree ced6bf5f8, which matches the
|
||||
candidate's own tree. The DEFERRED.md fallback did not fire.
|
||||
- Touched test files, each run whole: instance-settings, lifecycle-settings, bus creative, and webui
|
||||
agents-browser. 31 pass, 0 fail, 0 skip.
|
||||
- Mutants: M1, M2, M3 (on instance-settings and on creative), M4a, and M4b (on instance-settings and on
|
||||
creative). All were killed by the assertions listed above. Every restore matched the original blob.
|
||||
- Package suites, all 18 at rc=0, 3757 pass, 0 fail:
|
||||
|
||||
| Package | Pass |
|
||||
| --- | --- |
|
||||
| business | 231 |
|
||||
| bus | 788 |
|
||||
| cli | 832 |
|
||||
| control-board | 124 |
|
||||
| conversation | 183 |
|
||||
| discord | 192 |
|
||||
| harness | 151 |
|
||||
| ledger | 89 |
|
||||
| library | 83 |
|
||||
| mosaic | 69 |
|
||||
| plugins | 111 |
|
||||
| processing-control | 62 |
|
||||
| project | 41 |
|
||||
| queue | 156 |
|
||||
| runs | 43 |
|
||||
| seat | 167 |
|
||||
| tasks | 100 |
|
||||
| webui | 335 |
|
||||
|
||||
- scripts/test-*.sh, all 10 at rc=0:
|
||||
|
||||
| Script | Pass |
|
||||
| --- | --- |
|
||||
| auth | 15 |
|
||||
| conductor | 17 |
|
||||
| config | 31 |
|
||||
| discord | 81 |
|
||||
| extension-package | 18 |
|
||||
| foundation | 44 |
|
||||
| land-work | 86 |
|
||||
| queue | 28 |
|
||||
| release | 14 |
|
||||
| task | 91 |
|
||||
|
||||
- Skips: 4, none on a touched path.
|
||||
- The webui editor rebuild byte check, because esbuild 0.28.1 is installed and the check needs 0.28.2.
|
||||
- test-queue verify and render --check, because the gate worktree is not the canonical root.
|
||||
- test-task live provider cases, counted twice. Row 244 makes no provider call in the gate.
|
||||
- Leftovers: none.
|
||||
- Evidence is under /mnt/storage/scratch/honker-t/r256/: run-20261022T103355Z, gate-20261022T103414Z,
|
||||
and console-r3.txt.
|
||||
|
||||
### Review and landing
|
||||
|
||||
- Webby approved manifest round 1 in webby-0129 (REVIEW-r3.md, 00502a740). The approval was recorded at
|
||||
rev 3185, comment c30519.
|
||||
- Sage landed f12441d4f as a fast-forward at 10:57Z. #1761 is closed with comment c30520.
|
||||
@@ -0,0 +1,186 @@
|
||||
# Row 256 (#1761) design: the human deletes a creative instance
|
||||
|
||||
Brief: docs/plans/2026-10-21_creative-delete-human.md (REQ-COS-3). Base be2b008cc (row 194 landed as
|
||||
fd6dd327c). Builder Honker, reviewer Webby. Draft 2, 2026-10-22: Webby's d1 review (webby-0122, notes
|
||||
171480e6e) R1, Q1, Q2 and N1-N2 applied; the changes are listed at the end.
|
||||
|
||||
## What changes
|
||||
|
||||
### 1. One predicate in instance-settings.mjs
|
||||
|
||||
Today `deleteRefusal(document, rolesDir, instance)` refuses every END_PROTECTED definition with
|
||||
`organization-definition-refused`, and read()'s `endProtected` field is the same check
|
||||
(`END_PROTECTED.includes(definition)`).
|
||||
|
||||
New, in `packages/business/src/instance-settings.mjs`:
|
||||
|
||||
```js
|
||||
// Row 256 (#1761, REQ-COS-3): the protected-definition part of a delete. A creative instance is
|
||||
// the human's to delete; any other actor is refused. A non-creative END_PROTECTED definition (the
|
||||
// chief of staff) is refused for every actor, the human included.
|
||||
function protectedRefusal(document, definition, actor) {
|
||||
if (!END_PROTECTED.includes(definition)) return null;
|
||||
if (!CREATIVE_DEFINITIONS.includes(definition)) return { code: "organization-definition-refused" };
|
||||
return actor === document.human ? null : { code: "organization-creative-human-only" };
|
||||
}
|
||||
export function deleteRefusal(document, rolesDir, instance, actor) {
|
||||
...unknown-instance unchanged...
|
||||
const refusal = protectedRefusal(document, entry.definition, actor);
|
||||
if (refusal) return refusal;
|
||||
...references, kept, credentials unchanged...
|
||||
}
|
||||
```
|
||||
|
||||
- A creative definition with `actor === document.human` goes on to the remaining checks:
|
||||
references, then kept, then credentials.
|
||||
- The code and message match `creativeHumanOnly` in launch-settings.mjs:13.
|
||||
- The code is `organization-creative-human-only`.
|
||||
- It is raised as a plain refusal code here, as deleteRefusal's other codes are.
|
||||
- I don't call `creativeHumanOnly` itself, because it throws and deleteRefusal returns.
|
||||
- `actor` undefined, null, "tester", or a role actor (`<definition>.<instance>`) never equals
|
||||
`document.human`, so those actors are refused.
|
||||
- validateBusinessDocument runs `requireId(document.human)` (business.mjs:239), so an undefined
|
||||
actor can't match an undefined human.
|
||||
|
||||
### 2. The actor reaches every delete path
|
||||
|
||||
| Path | Today | Change |
|
||||
| --- | --- | --- |
|
||||
| `planDelete(document, instance)` | no actor | `planDelete(document, instance, actor)` |
|
||||
| `set(business, args, actor)` op delete (:207) | has the actor, doesn't pass it | passes it to planDelete |
|
||||
| `checkDelete(business, instance)` | no actor | `checkDelete(business, instance, actor)` |
|
||||
| runtime.mjs:283 `instances.check` | `checkDelete(id, args.instance)` | `checkDelete(id, args.instance, actor)` |
|
||||
|
||||
- The broker's verb check `#deleteRequest` already passes `s.human` to `instances.check` (broker.mjs:2843).
|
||||
- The launcher pass `lifecycleDelete` already passes the request's `r.by`, which is
|
||||
`#owner(s)` at record time, to `instances.set op delete`.
|
||||
- So the broker gets no logic change. Only the deferred comment at :2825 is touched, if it names
|
||||
the old rule.
|
||||
- The CLI (`mosaic instances delete`) and the Console both reach the same bus verb with the human's
|
||||
session.
|
||||
- The tester is refused `tester-refused` at the verb, before any of this (broker.mjs:2595,
|
||||
serve.mjs:394).
|
||||
- That is unchanged.
|
||||
|
||||
### 3. The listing: `endProtected` becomes the per-viewer delete check
|
||||
|
||||
- `read(business)` becomes `read(business, actor)`, and runtime.mjs passes the actor.
|
||||
- The broker already passes `"tester"` for a tester session and `s.human` otherwise (broker.mjs:2627).
|
||||
- Each instance's `endProtected` becomes `protectedRefusal(document, def, actor) !== null`.
|
||||
- This is the same server predicate the delete runs, per the brief.
|
||||
- The human's read shows `endProtected: false` for a creative instance.
|
||||
- The tester's read, and any role read, show `true`.
|
||||
- The chief of staff shows `true` for everyone.
|
||||
- The listing that a write returns is read with the writer's actor.
|
||||
- This covers `commit`'s `{ ...read(business), ... }` at :405 and fire's at :338.
|
||||
- So the human's add of bean returns `endProtected: false`, and the existing :288 assertion
|
||||
changes from `true` to `false` for "jason".
|
||||
- I keep the field name, so the listing gains no key and the deepEqual shapes in
|
||||
instance-settings.test, lifecycle-settings.test and agents-browser.test keep their keys.
|
||||
- Only the value for a creative entry read by the human changes.
|
||||
- The cost is a name that now means "delete refused for its protected definition, for this
|
||||
viewer".
|
||||
- The read() comment says so.
|
||||
- A rename (for example `deletable`) touches every listing fixture; I'd rather not, but I'll take
|
||||
Webby's call.
|
||||
- agents.js:46 is unchanged in logic: `if (!e.endProtected)` offers Delete.
|
||||
- Its comment changes to name row 256.
|
||||
- Delete stays inside the existing `!c.isTester` branch, so the tester page offers none whatever
|
||||
the field says.
|
||||
- The tester has two guards: the server's `true`, and the page's tester branch.
|
||||
|
||||
### 4. Fire is unchanged
|
||||
|
||||
- `fireChecks` / `checkFire` / `instance.fire` / `role.fire` keep refusing END_PROTECTED for
|
||||
every actor.
|
||||
- protectedRefusal is not called from fire.
|
||||
- A test pins that the human's fire of a creative instance still refuses.
|
||||
|
||||
### 5. Persona file
|
||||
|
||||
- `<configDir>/personas/<business>/<instance>.md` is left in place on delete.
|
||||
- That is the brief's smallest choice.
|
||||
- The delete commits the business file and renames the workspace under `.retired`, as today.
|
||||
- The persona file isn't under either path, and nothing reads it once the entry is gone.
|
||||
- bundle.mjs reads it only for a launched creative instance.
|
||||
- Retired names are never reused (row 194 D1), so a later hire can't pick up the old persona.
|
||||
- This goes in BUILD.md, plus a DEFERRED line if Sage wants a cleanup later.
|
||||
- No new file open, so no O_NOFOLLOW test.
|
||||
|
||||
### 6. DEFERRED.md
|
||||
|
||||
The "Creative instances: the human can't delete one" entry (DEFERRED.md:387) gets a closing line:
|
||||
|
||||
> Closed by row 256 (#1761) for delete. Fire still refuses, by REQ-COS-3.
|
||||
|
||||
The entry says "the row covers fire and delete together". The brief keeps fire unchanged, so the
|
||||
closing line says the fire half is closed by ruling, not by code.
|
||||
|
||||
## Tests
|
||||
|
||||
| File | Test |
|
||||
| --- | --- |
|
||||
| business/tests/instance-settings.test.mjs | T1: the human deletes a creative instance (bean); the file loses the entry. |
|
||||
| | T2: the same delete by a role actor (`chief-of-staff.chief`), by "tester" and by no actor refuses `organization-creative-human-only`, and the file is unchanged. |
|
||||
| | T3: the human's delete of the chief of staff refuses `organization-definition-refused`. |
|
||||
| | T4 (R1): with bean the only name in launch.instances, the human's delete of bean gets the last-listed BusinessError from deletedDocument (:64-66), not the creative code, so it passes protectedRefusal and reaches the later checks. A creative instance can't be in launch.keep, launch.by or the arbiters (business.mjs:208, :181-183, :251-255), so those aren't used. |
|
||||
| | T5: read(business, human) shows `endProtected: false` for bean; read(business, "tester") and read(business) with no actor show `true`; the chief of staff shows `true` for every actor. |
|
||||
| | The existing :283-292 test is updated where it pinned the old refusal. |
|
||||
| business/tests/lifecycle-settings.test.mjs | checkDelete with the human passes for a creative instance and with another actor refuses. |
|
||||
| | The fire of a creative instance by the human still refuses (fire unchanged). |
|
||||
| bus/tests/creative.test.mjs (Q2: its runtime() is a real startBroker with the business writer) | T6: the human's `instances.set op delete` on bean records a lifecycle.delete request. |
|
||||
| | Taking it and running the launcher pass commits the delete (the r.by path). |
|
||||
| | The chief is refused `human-required` (broker.mjs:2585) and the tester `tester-refused` (:2595), both at the verb. |
|
||||
| | The human's `instances` read has `endProtected: false` for bean; the tester's has `true`. |
|
||||
| webui/tests/agents-browser.test.mjs | T7: the human page with bean at `endProtected: false` offers Delete for bean. |
|
||||
| | T8: the tester page with the same listing offers no Delete. |
|
||||
| | The existing sage-2164 assertion moves to the tester-shaped listing (bean `endProtected: true`). |
|
||||
|
||||
- The browser tests stub `/api/instances`, so they pin the page's use of the field.
|
||||
- The server side of "never on the tester listener" is T5/T6.
|
||||
- No test touches live paths: fixtures under mktemp in /mnt/storage/scratch, as today.
|
||||
|
||||
## Mutants (all must be killed)
|
||||
|
||||
- M1: protectedRefusal's creative branch returns `null` for every actor, so a non-human deletes a
|
||||
creative instance. Killed by T2 only (N1): through the bus no non-human actor reaches the delete,
|
||||
since the chief and the tester are refused at the verb.
|
||||
- M2: protectedRefusal checks the human before the creative check, so
|
||||
`actor === document.human ? null : ...` for every END_PROTECTED definition. The human then
|
||||
deletes the chief of staff. Killed by T3.
|
||||
- M3: read() keeps the old `END_PROTECTED.includes`, so the listing isn't the per-viewer predicate.
|
||||
Killed by T5.
|
||||
- M4 (N2): the actor isn't threaded: runtime.mjs:283 calls `checkDelete(id, args.instance)`
|
||||
without it. Killed by T6, the human's real-writer delete. A second variant, set op delete not
|
||||
passing the actor to planDelete, is killed by T1 and by T6's launcher pass.
|
||||
|
||||
## Boundaries kept
|
||||
|
||||
- No change to M10 add/change, role.hire, fire, veto or launch.keep.
|
||||
- No schema change and no migration.
|
||||
- No new verb.
|
||||
- No change to the tester gate.
|
||||
|
||||
## Gate (on Sage's CLEAR)
|
||||
|
||||
- Touched test files, each whole.
|
||||
- The business, bus, cli and webui suites.
|
||||
- Every scripts/test-*.sh.
|
||||
- The four mutants above (M4 in both variants), each on a green base, restored and blob-checked (the runb8 shape).
|
||||
|
||||
## Comments rewritten (Q1)
|
||||
|
||||
- `endProtected` keeps its name: agents.js:46 is its only reader, and it means "don't offer Delete".
|
||||
- instance-settings.mjs:28-31 loses "no one deletes one yet". It says the human deletes a creative
|
||||
instance and any other actor is refused, and that the listing's endProtected is protectedRefusal
|
||||
for the reader.
|
||||
- agents.js:45 says the same, naming row 256.
|
||||
|
||||
## Changes from draft 1 (webby-0122)
|
||||
|
||||
- R1: T4 uses the last-listed refusal instead of launch.keep, which the validator refuses for a
|
||||
creative instance.
|
||||
- Q1: keep the name; comments rewritten; T5 pins read(business) with no actor as true.
|
||||
- Q2: the bus test goes in creative.test.mjs.
|
||||
- N1: M1 is killed by T2 only; T6 pins the chief's `human-required`.
|
||||
- N2: M4 added for the threading.
|
||||
@@ -0,0 +1,170 @@
|
||||
# Row 236 build notes: #436b Gitea grant path (#1742, schema 3j-3k)
|
||||
|
||||
Builder Megavolt, reviewer Dewey, gate owner Sage. Draft; the results sections are filled in after the
|
||||
runs on Sage's gate.
|
||||
|
||||
## Sources
|
||||
|
||||
- Brief: `docs/plans/2026-10-20_agent-credentials.md`, "Brief: #436b Gitea grant path, build (schema 3j-3k)",
|
||||
blob 9259ba2a1.
|
||||
- Design: `agents/megavolt/work/436b/DESIGN.md` r5 at 1be535aca (sha256 68df02cd…f4), unedited. Scope:
|
||||
sections 2 to 7, and section 9's X-G1 to X-G15 and mutants. Section 10 (GitHub half) is out of scope.
|
||||
- Build carry: `agents/megavolt/work/436b/BUILD-CARRY.md` at 3330c901b (N9, N10).
|
||||
- Schema label 3j-3k per sage-2237. If 264a or 206 lands first, this rebases onto a new branch name and reruns
|
||||
the whole bus suite, the schema.test counts and the T46 lists.
|
||||
- Rebases: onto 86f95bb27 after row 273 (sage-2403, no conflicts), then onto 0fa7bf421 after row 191
|
||||
(sage-2452, sage-2457). The 191 rebase took three unions in commit 1, 191's `processing` first and then the
|
||||
grant options: the Broker constructor (broker.mjs:317), the runtime's Broker options (runtime.mjs:173) and
|
||||
the cli case line in `scripts/mosaic` (191's `halt|resume|processing|` plus `grants`).
|
||||
|
||||
## What changed
|
||||
|
||||
- `packages/bus/src/gitea-grant.mjs` (new): the grant body check, the admin slot (set, rotate, remove), the
|
||||
admin and seat API calls, the start-time scan (`giteaStart`, `openGiteaGrants`, `tokenUnknown`).
|
||||
- `packages/bus/src/broker.mjs`: the gitea executor (steps 0 to 8), settle, restart reconcile, revoke and fire,
|
||||
sweep, status, and the `grants.*` verbs.
|
||||
- `packages/bus/src/credentials.mjs`: `held` pairs at start; the register and claim paths the executor uses.
|
||||
- `packages/bus/src/migrations/3j-3k.sql`, `schema.sql`, `store.mjs`: `gitea_users` and `gitea_grants`, new
|
||||
tables only.
|
||||
- `packages/bus/src/runtime.mjs`, `server.mjs`, `human-cli.mjs`: wiring of the start scan and the verbs.
|
||||
- `packages/cli/src/grants.mjs` (new), `cli.mjs`, `scripts/mosaic`: `mosaic grants`.
|
||||
- Tests: `gitea-grant.test.mjs`, `gitea-migration.test.mjs`, `gitea-grant-docker.test.mjs` (X-G3),
|
||||
`fake-gitea-api.mjs`, `fixtures/schema-3j.sql` (02da6a2ae's schema.sql), `cli/tests/grants.test.mjs`, and the
|
||||
pin updates in the migration tests, `schema.test.mjs`, `channel-proof.test.mjs` and `stack-release.test.mjs`
|
||||
(T46).
|
||||
- READMEs of bus and cli.
|
||||
|
||||
## Choices and assumptions
|
||||
|
||||
Each is a reading of the design where it left room. Dewey may rule otherwise.
|
||||
|
||||
1. Admin rotation is due once today is past `rotateBy` (the date itself is still good).
|
||||
2. A seat-token rotation moves the record at step 8, after the old token's delete (step 7).
|
||||
3. A step 7 failure leaves the grant applied, with the old id in `serviceLeft` and the notice "The previous
|
||||
token X is still live".
|
||||
4. A failure after the commit step's rename returns `settle-pending`; the next start settles it.
|
||||
5. tester-cli keeps its 5 s timeout; the grant verbs answer at once and the executor runs after.
|
||||
6. A revoke of a token-unknown agent takes both ids from that decision's last commit step.
|
||||
7. A user adopted at restart is upserted in the same transaction as its `grant.failed interrupted`.
|
||||
8. `#giteaUnlink` (first-grant commit undo) uses a temporary reference dated `9999-12-31`, so it is never due
|
||||
while it lives.
|
||||
9. X-G12's org-team case: the fake models team membership as collaborator reach on the repository, which is
|
||||
what `/user/repos` shows (F11, F12).
|
||||
10. X-G15's look-alikes are stored under `action.refused` and under a non-grant kind, since the 3j trigger
|
||||
refuses the human's NULL pair on `action.allowed`. The trigger cases of X-G13 (session, NULL pair,
|
||||
`grant.host.enrolled`) are in `grant-migration.test.mjs`.
|
||||
11. Step 6 faults: a pre-rename failure is injected by making `gitea.tmp` a directory; a step 8 store fault by
|
||||
a TEMP trigger on `gitea_grants`.
|
||||
12. The credentials-verb revoke close is tracked, so `giteaIdle()` waits for it.
|
||||
13. `openGiteaGrants` gives `agent: null` for a spend with no step yet, so `status.running` can show a null
|
||||
agent; the CLI prints it empty.
|
||||
14. N9: a restart ignores a settle-pending commit with a `grant.revoked` after it and ends it `revoked` with
|
||||
no message (notify false). A 404 on a delete by id counts as done.
|
||||
15. N10: `serviceLeft` is a list in every body and answer.
|
||||
16. A restart claims the agent's key for each open grant it undoes. One executor runs per agent
|
||||
(grant-busy), so a killed child leaves at most one open grant per agent. If a store ever held two, the
|
||||
second's claim fails and the undo runs with no admin: its token is left and named in `serviceLeft`,
|
||||
never deleted unclaimed.
|
||||
17. X-G7 changes the binding with a fixture decision whose hash differs (row 234's G9 pattern), since the
|
||||
broker keeps its own copy of the business. The admin url check takes row 199's `parseGiteaUrl`, which
|
||||
accepts the origin with or without a trailing slash.
|
||||
18. r2, Dewey B1 (dewey-0781): step 8 and both settle branches (forward, back) check for a `grant.revoked`
|
||||
of the agent after the decision's raise, in the same transaction as their write. A revoke that came
|
||||
while step 7's or the settle's delete awaited took the record and both tokens, so the grant ends
|
||||
`revoked` with the human's notice, as a revoke of a running executor ends it (X-G11), and no record is
|
||||
written back. N9's restart case stays silent: a restart never settles a revoked rotation.
|
||||
19. r2, Dewey B2: a sweep retry of `tokens` or `login` checks the user's id first, as the revoke close does,
|
||||
against the `gitea_users` row (it survives a fire). A 404 skips with `gitea-user-gone`, a 200 with
|
||||
another id with `gitea-user-foreign`, any other status leaves the entry. Skips go in `grant.swept` and
|
||||
the sweep answer as `skipped: [{agent, user, id, reason}]`; a skipped entry is not left, so the next
|
||||
sweep does not retry it.
|
||||
20. r3, Dewey N5 (dewey-0783): the settle's 401 branch and its grant-token-unknown branch take the same
|
||||
revoke check. A revoke during the whoami ends the grant `revoked`, not `grant-lost`, and the
|
||||
token-unknown branch neither holds the agent again nor writes `grant-token-unknown`, so no start
|
||||
names a file the revoke unlinked.
|
||||
21. sage-2453, ruling (a) on megavolt-0123: 191's `#processingGate` refuses every agent verb off its read list
|
||||
while processing is halted. `grants.status` is a read like `hosts.status`, and the kill-switch plan
|
||||
(`docs/plans/2026-10-20_kill-switch.md`:16) says "Reads and the human's own verbs, including the clear,
|
||||
still work", so it joins the list. This touches row 191's list (one entry). An agent's `grants.revoke`
|
||||
stays refused while halted, as `hosts.revoke` does.
|
||||
|
||||
## Named for DEFERRED (r2, Dewey N1-N4)
|
||||
|
||||
None is a small local change, so none is in r2. sage-2388: all four DEFERRED; N3 and N4 get a bus README
|
||||
sentence each in r3 (a left admin token is deleted by hand at Gitea; a running executor keeps the admin
|
||||
api it started with).
|
||||
|
||||
- N1: an admin rotate cut between the mint and the slot write leaves a token at Gitea that no event names.
|
||||
A `grant.admin rotate-begin {oldName}` before the mint would close it; that is a new event shape.
|
||||
- N2: `adminTokenLeft` is `unknown` when `/api/v1/token` gives no name. Which 1.27 F-fact shows the name
|
||||
needs a check against the container, not a code change.
|
||||
- N3: admin set or remove records `adminTokenLeft: null`, so a left token drops out of status. Carrying it
|
||||
forward lets `rotate --finish` delete by name under a slot of another account or url; refusing remove
|
||||
while one is left blocks the human's remove when Gitea is down. Either needs a ruling.
|
||||
- N4: rotate or remove while a gitea claim is held. Refusing it blocks the human's remove during a run;
|
||||
the running executor keeps the api it began with. Needs a ruling or a line in the README.
|
||||
|
||||
## Q188
|
||||
|
||||
No test creates a grant except through fixtures or the human's yes on a scratch store. No test reaches
|
||||
git.mosaicstack.dev, tasks.woltje.com or any provider. Unit tests use the fake Gitea on 127.0.0.1; X-G3 uses a
|
||||
throwaway gitea/gitea:1.27.1 container from the local image (never pulled), removed after. Every token is a
|
||||
fake or a scratch-container token; values are compared with `assert.ok`, never printed.
|
||||
|
||||
## Runs
|
||||
|
||||
Sage's CLEAR (sage-2554), 10-22 09:00-09:28Z, one slot script that stops at the first red. Logs are in
|
||||
`scratch/megavolt-r236/logs/` (`slot-2c.txt`, `slot-gate.txt`, `slot-mutants.txt`, `slot-all.txt`).
|
||||
|
||||
- Section 2c files whole, in the seat worktree, TMPDIR under `/mnt/storage/scratch/tmp`:
|
||||
- bus (gitea-grant, the migration tests, schema, channel-proof, chief-of-staff, grant-host,
|
||||
project-runtime): 131 pass, 0 fail, 0 skipped;
|
||||
- cli (grants, stack-release): 224 pass;
|
||||
- route (grants, access): 28 pass;
|
||||
- docker (gitea-grant-docker): 1 pass.
|
||||
- The whole gate, in a detached worktree at bd5f11ded:
|
||||
- `packages/bus/tests/*.test.mjs` 765 pass, `packages/business/tests/` 228 pass,
|
||||
`packages/cli/tests/*.test.mjs` 832 pass, each 0 fail and 0 skipped;
|
||||
- every `scripts/test-*.sh` rc=0: auth, conductor, config, discord, extension-package, foundation,
|
||||
land-work, queue (156 pass), release, task.
|
||||
- Named skips: test-queue line 34 (queue verify and render --check, because the gate worktree isn't the
|
||||
queue's canonical root), and test-task lines 87 and 93 (live provider cases, row 244: no provider call in
|
||||
the gate).
|
||||
- The gate worktree was clean after the run. GATE_PASSED at 09:23:11Z.
|
||||
|
||||
## Mutants
|
||||
|
||||
(section 9's list, each with the test that kills it; filled in after the runs)
|
||||
|
||||
Result, at bd5f11ded after the gate (`r236-mutants.sh`): the base (gitea-grant, gitea-migration,
|
||||
grant-migration, schema; cli grants) was green, then all 40 killed and none survived: M1-M37, N9a, N9b and M38.
|
||||
Each mutant's restore left the worktree clean. SLOT_GREEN at 09:28:50Z.
|
||||
|
||||
A static pass over the 39 mutants (all apply, problems=0) found five with no test to kill them. Four tests
|
||||
were added before any mutant run:
|
||||
|
||||
- M8, M9 (user or mint step written after its call) and M16 (grant.result written before its call): "X-G6
|
||||
and X-G8: the user and mint steps are written before their calls, and each result after its call". It
|
||||
reads the store from the fake while user-create and token-create are at Gitea, and checks every result's
|
||||
status.
|
||||
- M29 (fork parent not checked): "X-G4: a fork by the seat user of another repository than the project
|
||||
refuses fork-foreign".
|
||||
- M28 (memory and file revoked after the Gitea delete): "X-G11: a revoke unloads memory and removes the file
|
||||
before its Gitea delete", which holds the DELETE open and reads the file and state.
|
||||
- M37 (evidence without its kind filter): "X-G15: a look-alike spend under another kind with the grant writer
|
||||
pair is not an open grant".
|
||||
|
||||
r2 tests, each red on 8bf442be9 by reading:
|
||||
|
||||
- B1: "X-G11: a revoke during a rotation step 7 ends it revoked, and step 8 writes no record back" and
|
||||
"X-G11: a revoke while a sweep settles a rotation, forward or back, ends it revoked with no record".
|
||||
On 8bf442be9 step 8 and the settle write the record and `grant.applied` (forward) or `grant.failed
|
||||
interrupted settled:back` after the revoke deleted it.
|
||||
- B2: "X-G14: a sweep retries a fire only for the user it fired: a remade or gone user is skipped and
|
||||
named". On 8bf442be9 the retry fires the remade user (token list, delete, PATCH) and has no `skipped`.
|
||||
- N5 (r3, sage-2388): "X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not
|
||||
held" and "X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost". On 77dd43c9e
|
||||
they end `grant-token-unknown` (the agent back in tokenUnknown) and `grant-lost`.
|
||||
- Choice 21 (sage-2453): "kill switch (plan :16): while halted the chief still reads grants.status, and an
|
||||
agent grants.revoke refuses". M38 drops `'grants.status'` from the read list; the chief's status then
|
||||
refuses `processing-halted` and the test fails.
|
||||
@@ -0,0 +1,229 @@
|
||||
# FED R3 (#414): the broker's user principal
|
||||
|
||||
Brief: docs/plans/2026-10-22_federation-r3.md (f0d0b31e9). Design: FED r4.11 (b93f098f0), sections 3.5, 3.6 and
|
||||
the R3 row of section 7. Base: row 205a's candidate 67640f892. If 205a changes in review, R3 rebases onto a new
|
||||
branch name. No schema change. Nothing has run yet: every test run waits for Sage's CLEAR.
|
||||
|
||||
## Changes
|
||||
|
||||
`packages/bus/src/broker.mjs`:
|
||||
- `USER_VERBS` is the collaborator list: `project.list`, `thread.create`, `thread.list`, `thread.open`,
|
||||
`thread.send`, `thread.messages`, `names`. The grant level is `collaborator`, through R1's `covers`.
|
||||
- The Broker takes `access`, a function that returns the checked access document (the runtime passes R1's
|
||||
`loadAccess`). `#userGrants` calls it once per user request. A throw, null or no document gives no grants.
|
||||
- `bindUser({business, user})` (3.6 item 1). The keys must be exactly those two. A user id that fails
|
||||
`ID_PATTERN` or is `human`, `tester`, `user` or the business human fails `access-refused`, whatever the file
|
||||
says. No live collaborator grant fails `access-revoked`. The session is
|
||||
`{business, user, role: null, run: null, human: false}`.
|
||||
- `request()`: for a user, a verb off `USER_VERBS` fails `access-refused` before any argument check. Then the
|
||||
grants are read; none fails `access-revoked`. The view the verb sees carries them as `grants`.
|
||||
`requestTask`, `requestWeb` and `requestHost` fail a user `access-refused` first.
|
||||
- Filters: `project.list` keeps granted projects. `thread.list` keeps the user's own threads its grants still
|
||||
cover. `names` keeps granted instances. `thread.create` checks the project and the thread instance against
|
||||
the grants before the project lookup, so an ungranted id and an unknown id both refuse `access-refused`.
|
||||
`thread.open`, `thread.send` and `thread.messages` refuse a thread the grants no longer cover.
|
||||
- `#owner` gives `user:<id>` (item 2). `#threadFor` refuses a user as it refuses the human (item 3).
|
||||
`#event` writes `actor_role` `user` and `actor_run` `user:<id>` for a user session (item 6), so a refusal is
|
||||
recorded against the user.
|
||||
- `thread.send` from a user: `from_role` `user`, `from_run` `user:<id>`, no `human.input`.
|
||||
- `message.send` (item 7, B6): a run sends `to: 'user'` only with an `in_reply_to` naming a `user` message in
|
||||
the run's own thread, sent by that thread's owner. Anything else fails `user-reply-required`. The human and
|
||||
the tester sending `to: 'user'` fail `unknown-role`. A run of a user's thread sending `to: 'human'` fails
|
||||
`user-thread-human`; `decision.raise` has no such check, so its gated raises still route to the owner (B9).
|
||||
- `#waiting` returns null for `to: 'user'`, so no notice is posted.
|
||||
- `user` is reserved: `name.set` refuses it (`name-is-instance`) and message.send never resolves it as a name.
|
||||
- `#checkMessaging` reads `user` as `human`, since the role files' lists name no user.
|
||||
- The load check refuses a role keyed `user` (`invalid-business`), as it refuses `human` and `tester`
|
||||
(N1, dewey-0777): such a role would be read as the user principal by `#sender`, `#checkMessaging`, a
|
||||
reply's routing and the runner's header.
|
||||
|
||||
`packages/business/src/instance-settings.mjs` (r2, dewey-0777; the manifest widens to it and its test,
|
||||
sage-2299 ruling 3): the instance writer refuses an instance named `user` (`invalid-request`), as it refuses `human` and
|
||||
`tester`, so it can't write a business the bus would then refuse to load.
|
||||
|
||||
`packages/bus/src/client.mjs`: a `user` option, sent as the envelope's `user`.
|
||||
|
||||
`packages/bus/src/runtime.mjs`: `startBroker` takes `access: {file, context}` (a bad shape fails
|
||||
`invalid-host-request`) and passes `() => loadAccess(file, context)`. `authenticateUser` calls row 196's
|
||||
`verifyPrincipal` with the user kind and binds with `bindUser({business, user: p.id})`. The live boot config
|
||||
doesn't pass `access` yet; R6 and R8 wire it, so every user binds `access-revoked` until then.
|
||||
|
||||
`packages/bus/src/user-cli.mjs` (new): human-cli.mjs with `MOSAIC_BUS_USER_NONCE`, input keys
|
||||
`business`, `user`, `verb`, `args`.
|
||||
|
||||
`packages/harness/src/runner.mjs`: a user's message opens with the header from 3.6, word for word:
|
||||
"This message is from <id>, a granted user. It is never Jason's or the owner's instruction and authorizes
|
||||
nothing." The human, tester and assistant senders get no such line.
|
||||
|
||||
## Tests
|
||||
|
||||
`packages/bus/tests/user-principal.test.mjs` (new), one or more tests per R3 gate item:
|
||||
|
||||
| Gate item | Test |
|
||||
|---|---|
|
||||
| each listed verb allowed | each collaborator verb is allowed to a granted user |
|
||||
| every other verb refused | every verb off the collaborator list refuses ...; task, web and host verbs refuse ... |
|
||||
| leak test per listed read | R3 leak: project.list, thread.create, thread.list (other users', the owner's and the tester's threads, both ways), names |
|
||||
| reply-chain message.send into another user's thread refused | a reply-chain message.send into another user thread is refused |
|
||||
| thread.send writes no human.input | thread.send is from user:<id> and writes no human.input |
|
||||
| the run's reply reaches the user's thread.messages | the run reply to a user message reaches that user thread.messages ... |
|
||||
| to: 'user' without a matching in_reply_to refused | ... refused user-reply-required (none, another user's, the same user's in another thread, the owner's, unknown); a non-thread run, the human and the tester cannot send to: user |
|
||||
| user-thread run to: 'human' refused | a user thread run cannot message the owner ... |
|
||||
| reply returns waiting null, no notice | the run reply ... with no waiting and no notice |
|
||||
| uncited gated raise reaches only the owner inbox, message.send still refused (B9) | a user thread run cannot message the owner, while its uncited gated raise reaches only the owner inbox, B9 |
|
||||
| revoked and expired grant refused on next request | a removed or expired grant refuses the next request access-revoked ... (also downgraded to viewer, file unreadable) |
|
||||
| refusals recorded | a user refusal is recorded as action.refused by user:<id> |
|
||||
| a user named like the human refused | a user named like the business human, the tester or a principal word is refused ... |
|
||||
| N1: role key `user` refused (r2) | a business with a role keyed user refuses invalid-business |
|
||||
|
||||
`packages/harness/tests/runner.test.mjs`: the user header, exact string, and its absence for the other senders.
|
||||
|
||||
`packages/business/tests/instance-settings.test.mjs` (r2): row 165's validation test also refuses instance
|
||||
`user`.
|
||||
|
||||
Row 196's `principal.test.mjs` already covers the user kind of `verifyPrincipal` (agent parent, non-Console
|
||||
parent, kind confusion are its mutants). No runtime test of `authenticateUser` or user-cli yet: the stub
|
||||
`authenticateUser` path needs a real Console parent, which is R6's harness.
|
||||
|
||||
## Audit of `s.human` and `s.tester` branches
|
||||
|
||||
A user reaches only the seven `USER_VERBS` through `request()`. Task, web and host requests refuse it first.
|
||||
The user cap is transient: the server binds it per request and releases it, and never returns it to a client,
|
||||
so the cap-taking launcher and git ops (`authorize`, `checkPush`, `recordPush`, `checkLand`, `reviewFetch`,
|
||||
`recordEvent`, `recordTask`) can't receive one.
|
||||
|
||||
| Branch | For a user |
|
||||
|---|---|
|
||||
| `#agent`, `#human` | `#human` refuses a user (`human-required`); every listed verb skips it with `!s.user`. `#agent` is reached only from `names` for a non-human, non-tester, non-user session. |
|
||||
| `#threadFor` | refuses a user (item 3). Not reachable: message.send and the review verbs are off the list. An equivalent mutant; the line stays. |
|
||||
| `#testerOwns`, `#testerThread` | `s.tester` false and `s.user` sessions have no thread: not taken. |
|
||||
| `#owner`, `#sender`, `#event` | changed: `user:<id>`, `user`, actor columns. |
|
||||
| `#userThread` | new; false for a user session itself. |
|
||||
| `endLaunch` session sweep | matches `s.run === record.run`; a user's run is null and a record's run is an identifier: never matches. |
|
||||
| `identity` | returns `{business, role: null, run: null, human: null}`. server.mjs reads only `business`, for the web service, and requestWeb then refuses. |
|
||||
| `recordTask` | its cap check would pass a user, but only `requestTask`'s handler and the git ops call it with a cap: not reachable. |
|
||||
| `request()` creative raise, usage, limitStop, organization verbs, hosts, project, library, credentials, reader and tester gates | after the user gate: not reachable. |
|
||||
| `#hostsCaller`, `#requestCredential`, `#limitStop`, `#dispatch` role checks, `#claimEnd`, `#checkBlock` | reached only from verbs off the list: not reachable. |
|
||||
| `message.send`, `message.receive`, `message.read`, `messages`, `replies`, `trail`, `decisions`, `credentials`, `state`, `usage`, `decision.withdraw`, `assistant.stop`, `name.set`, `project.create`, `role.claim` | off the list: not reachable. |
|
||||
| `project.list`, `thread.create`, `thread.list`, `thread.open`, `thread.send`, `thread.messages`, `names` | changed as above. |
|
||||
|
||||
## Mutants (each must fail its named test, in user-principal.test.mjs unless named)
|
||||
|
||||
| Mutant | Edit | Named test |
|
||||
|---|---|---|
|
||||
| M1 | the grant read skipped: no `access-revoked` after the per-request read | a removed or expired grant refuses the next request access-revoked ... |
|
||||
| M2a | project.list filter dropped | R3 leak: project.list shows granted projects only |
|
||||
| M2b | thread.list filter dropped | R3 leak: thread.list shows ... never others |
|
||||
| M2c | names filter dropped | R3 leak: names shows granted instances only |
|
||||
| M2d | `#grantedThread` check dropped | R3 leak: thread.list shows ... never others |
|
||||
| M2e | thread.create grant check dropped | R3 leak: thread.create needs the project and the thread instance granted ... |
|
||||
| M3x | `message.send` added to `USER_VERBS` (list mutant) | every verb off the collaborator list refuses ... |
|
||||
| M4 | the reply-to check dropped | a reply to: user without a matching in_reply_to is refused user-reply-required |
|
||||
| M5 | the business human dropped from bindUser's reserved list | a user named like the business human ... |
|
||||
| M6x | `trail` and `replies` added to `USER_VERBS` (second list mutant) | every verb off the collaborator list refuses ... |
|
||||
| M7 | user-thread-human check dropped | a user thread run cannot message the owner ..., B9 |
|
||||
| M8 | `to === 'user'` dropped from `#waiting` | the run reply to a user message reaches ... with no waiting and no notice |
|
||||
| M9 | thread.send writes `human.input` for a user | thread.send is from user:<id> and writes no human.input |
|
||||
| M10 | the load check accepts a role keyed `user` | a business with a role keyed user refuses invalid-business |
|
||||
| M11 | instance-settings accepts instance `user` | instance-settings.test.mjs: row 165 refuses each validation failure ... |
|
||||
|
||||
Rulings from Dewey's early look (dewey-0777), confirmed by Sage (sage-2299 rulings 1 and 2), on the two
|
||||
mutants the design and brief name:
|
||||
1. "#threadFor's user refusal removed" is an equivalent mutant. No user request reaches `#threadFor`: its
|
||||
only callers are message.send and `#reviewMessage` (review.request and review.verdict), all off the list.
|
||||
The line stays as defence in depth. M3x stays as a list mutant. The kit also runs the reply-chain test
|
||||
under M3x as the two-walls evidence: with the access-refused assertion turned into a log line, it records
|
||||
the code alice's send gets (expected `human-required`, from `#human`, since a user has no run) and still
|
||||
asserts bob's thread holds one message.
|
||||
2. The brief's "drops the 193b1a filter for user sessions" line is void (sage-2299 ruling 2): a user can't
|
||||
call trail or replies, so no filtered read reaches a user. M6x is a second list mutant, not a stand-in.
|
||||
|
||||
## Notes
|
||||
|
||||
- N1: fixed in r2 (dewey-0777). The broker's load check and the instance writer refuse `user`, one test each
|
||||
(M10, M11). The instance-settings change stays in R3 and the manifest widens to it and its test (sage-2299
|
||||
ruling 3). Sage checked the live business config: no role or instance is named `user`, so a re-pin
|
||||
carrying R3 loads (ruling 4; Sage checks again before that re-pin).
|
||||
- N2: server.mjs answers a `user` envelope with no `authenticateUser` as `human-required`, the existing
|
||||
default for a non-tester kind. The runtime always passes `authenticateUser`, so only a bare test server sees it.
|
||||
|
||||
## r4 (sage-2486, sage-2557): on row 194's landing
|
||||
|
||||
Row 194 landed as fd6dd327c. r4 rebases R3 onto it and adds four changes. Sage ruled on each.
|
||||
|
||||
- Halt (sage-2486): processing halt is the kill switch, so it refuses a granted user's writes
|
||||
(thread.create, thread.open, thread.send) with `processing-halted`. The reads on #processingGate's list,
|
||||
thread.messages among them, still answer. The kill-switch plan (:14-16) needs this, and Dewey agreed. Test:
|
||||
"R3 r4: while halted, a granted user's thread.send refuses processing-halted and thread.messages still
|
||||
answers". setup() takes a `processing` stand-in for it, as lifecycle-verbs T15 does.
|
||||
- S1 (sage-2557): row 194's hire takes an instance name. Its two checks refused only `human` and `tester`, so
|
||||
a chief could hire `user` and get past N1's load-check refusal. Both now refuse `user` with the code they
|
||||
already give (`invalid-request`): broker #hireFireArgs, and the writer's hire() in instance-settings.mjs. One
|
||||
test each: user-principal "R3 r4: the chief's hire refuses an instance named user ..." (with `helper` as a
|
||||
control that passes the name check), and a hire loop in instance-settings' "row 165 refuses each
|
||||
validation failure ..." that checks the file's bytes stay unchanged.
|
||||
- S2 (sage-2557, sage-2501): the `access` TypeError check moves up beside harnessOn, testerOn and roleFile,
|
||||
before the store is read or initializeControl runs. Test: "R3 r4: a non-function access throws before
|
||||
the store is read" (null store, T8b style).
|
||||
- Merge: the three 194 conflicts in broker.mjs are unions. R3's `user` role refusal moves into 194's
|
||||
checkBusiness(). instance-settings takes 194's add/change/delete op list with R3's `user` refusal.
|
||||
|
||||
Mutants M12-M15 kill each of the changes above: M12 the broker hire, M13 the writer hire, M14 the access
|
||||
check moved below the store read, M15 thread.send added to the halt-exempt list. The kit has 19 mutants plus
|
||||
the M3x two-walls run. All 19 anchors match exactly once on the r4 tree.
|
||||
|
||||
## r5 (sage-2566): covers moves into the vocabulary
|
||||
|
||||
r4 was rebased onto row 236's landing, bd5f11ded, as 1920b926f. Sage ruled (b) in sage-2566, with Dewey's support
|
||||
(dewey-0820), and r5 goes on refactor 47a35bf43. Checking that rebase found a fault in R3 itself.
|
||||
The broker imported `covers` from business/src/access.mjs. That broke the pin in project-runtime.test.mjs
|
||||
("Broker keeps the sandbox client import closure ..."), and it would have broken the runner: the session
|
||||
sandbox mounts only seat/src/sandbox.mjs RUNNER_FILES, and access.mjs, which imports errors, business and
|
||||
util, isn't one of them. R3 had never been run, so nobody had seen this.
|
||||
|
||||
- `covers`, `LEVELS` and the all marker (now `GRANT_ALL`) move into business/src/vocabulary.mjs. That file
|
||||
is already in RUNNER_FILES and still imports nothing, the same reason row 194 put `unhireableAction`
|
||||
there. access.mjs imports them back and re-exports `LEVELS` and `covers`, so index.mjs, the cli and the
|
||||
access tests don't change.
|
||||
- The broker takes `covers` from its vocabulary import. The pin test adds it to that line, with a comment.
|
||||
- RUNNER_FILES is unchanged.
|
||||
- M16 puts the access.mjs import back, and three tests must fail on it: the project-runtime pin; seat
|
||||
sandbox.test.mjs "RUNNER_FILES is exactly what the runner and the harness sources it starts import"
|
||||
(M16b), which walks the real import graph from the runner; and the confined-session test there (M16c),
|
||||
which starts the runner under bwrap with only the RUNNER_FILES mounts, so it proves the closure by loading
|
||||
it. M16c is skipped where bwrap can't run, and the gate log names any skip. The kit has 22 mutant runs plus
|
||||
the M3x two-walls run, and every anchor matches exactly once.
|
||||
- The manifest widens to vocabulary.mjs, access.mjs and project-runtime.test.mjs. Tests that import
|
||||
either module use named exports only; none deepEquals an export list or pins a hash (sage-2566 item 4).
|
||||
The kit runs project-runtime, access, access-levels and seat sandbox whole, plus the seat suite, and adds the cli suite and every `scripts/test-*.sh`
|
||||
(mos-1200).
|
||||
|
||||
## r6 (sage-2576): the list test pins the collaborator list
|
||||
|
||||
The r5 gate on f58cfc361 stopped red at 10:05:09Z: M3x and M6x survived (morgana-0108). The test "every
|
||||
verb off the collaborator list ..." deleted the broker's own USER_VERBS from its off-list set, so a verb a
|
||||
mutant added to the list left the checked set as well. The broker's allowlist at the user gate is right.
|
||||
|
||||
- The test now deepEquals USER_VERBS against the ruled seven verbs, written out, before the delete loop.
|
||||
M3x and M6x fail on that line.
|
||||
- The off-list set adds GRANTS_VERBS, LIFECYCLE_HUMAN_VERBS, WEB_VERBS and HOST_VERBS (sage-2576 item 2).
|
||||
The user gate refuses each of them `access-refused` before any other check. If one doesn't, that is a
|
||||
finding for Sage, not a test fix.
|
||||
- Test only. Only user-principal.test.mjs's blob changes in the manifest. The gate reruns whole on the r6 tip.
|
||||
|
||||
## Gate
|
||||
|
||||
The kit is /mnt/storage/scratch/morgana-r3 (`slot-r3.sh`, `mutate.py`, `busy.sh`), run as
|
||||
`bash busy.sh && CAND=<sha> bash slot-r3.sh` on Sage's CLEAR. It runs these files whole: user-principal,
|
||||
principal, human, tester, tester-threads, tester-writes, threads, names, messaging, messages, decisions,
|
||||
decision-route, broker, transport, peer-transport, process, message-filter, harness runner and business
|
||||
instance-settings, project-runtime, business access and access-levels, and seat sandbox. Then the bus, harness, business, cli and seat suites, then the 22 mutant runs (r5), each on its named
|
||||
test and restored after each, then the M3x two-walls run. It uses
|
||||
umask 022 and TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/gr3XXXX`, refuses unless HEAD is the
|
||||
candidate and the tree is clean, and stops after two unexpected failures (mos-0823).
|
||||
|
||||
The kit runs every `scripts/test-*.sh` after the suites and counts each script's `^skip` lines (r5). Every run is slot work (sage-2280,
|
||||
sage-2299 ruling 5); the brief's "single test files run freely" line is stale.
|
||||
|
||||
Static check: `node --check` on every changed source and test file, run from `/`. All parse. All fifteen
|
||||
mutant anchors match exactly once.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Row 205a (193b1a, #1703): the trail and replies message filter
|
||||
|
||||
Design: DESIGN.md d2 in this directory, with rulings in section 7 (sage-2288). Base: refactor 02da6a2ae. No schema
|
||||
change. Nothing has run yet: every test run waits for Sage's CLEAR.
|
||||
|
||||
## Changes
|
||||
|
||||
`packages/bus/src/broker.mjs`:
|
||||
- `#hiddenMessage(s, id)`: the message's thread (`message_threads` joined to `threads`) is owned by a `user:`
|
||||
other than `#owner(s)`, and `#readable(s, t)` is false. A message with no thread, or in the business human's
|
||||
or the tester's thread, is never hidden. A reader session owns nothing, so every user thread is hidden from it.
|
||||
The tester reads through its reader view, and `#owner` still gives `tester`.
|
||||
- `#readable(s, t)` returns false. Rule 1 (a share in force) can't hold before 193b2's schema 3j, so nobody
|
||||
reads another's thread. 193b2 adds the share rule and rules 2-4 here.
|
||||
- `#trail`: a hidden subject returns `[]` before anything is read, exactly as an unknown id does (ruling 3).
|
||||
After the rows are collected, it drops hidden `messages` rows, `deliveries` rows of a hidden message, and
|
||||
`events` rows whose subject is a hidden message. Decisions, decision events, task snapshots, run events and
|
||||
claims stay.
|
||||
- `replies`: a hidden id fails `message-not-found`, as an unknown id does. Hidden replies are left out in SQL
|
||||
(`NOT EXISTS` a thread whose owner starts with `user:` and isn't the caller), so the `REPLIES` limit counts
|
||||
only visible rows.
|
||||
- The SQL clause doesn't call `#readable`. That is exact while `#readable` is false. 193b2 must add the share
|
||||
exception to the clause and to `#readable` together.
|
||||
|
||||
`packages/bus/tests/message-filter.test.mjs` (new). It writes a `user:alice` thread and its messages
|
||||
straight to the store, as R3's `thread.send` and the run's `to: 'user'` reply will write them, because nothing
|
||||
makes a user session before R3.
|
||||
- `trail` on a hidden id equals `trail` on an unknown id, byte for byte, for the owner, a reader and the tester.
|
||||
- `replies` on a hidden id equals `replies` on an unknown id (code and message), for all three.
|
||||
- `trail` on a user-thread run's decision shows the decision and the owner's message, and drops the user
|
||||
thread message, its delivery and its event.
|
||||
- `replies` with `REPLIES` hidden replies ahead of a visible one returns the visible one.
|
||||
- Messages in the business human's and the tester's threads stay visible on both verbs (row 177 control).
|
||||
- The governance pin, one assertion per item in the user sentence: inbox entry, claim, task, project file and
|
||||
the closed decision in `decisions {list: 'recent'}` show to the owner. `trail` and `replies` hide the user
|
||||
message, and the decision's trail carries no user body.
|
||||
|
||||
## Mutants (each must fail its named test in message-filter.test.mjs)
|
||||
|
||||
| Mutant | Edit | Named test |
|
||||
|---|---|---|
|
||||
| M1 | `#readable` returns true | governance pin: the owner sees decisions, inbox entries, claims, tasks and project files, but not the user messages |
|
||||
| M11 | the trail drop loop iterates nothing | trail on a decision keeps the decision and drops the user thread messages, their deliveries and their events |
|
||||
| M12 | the replies `NOT EXISTS` clause removed | replies leaves out other users thread messages and still fills a page |
|
||||
| M13 | trail fails `message-not-found` for a hidden subject | trail on a hidden message id answers exactly as on an unknown id, for the owner, a reader and the tester |
|
||||
| M14 | replies returns `[]` for a hidden id | replies on a hidden message id answers exactly as on an unknown id, for the owner, a reader and the tester |
|
||||
|
||||
## Gate
|
||||
|
||||
The kit is /mnt/storage/scratch/morgana-r205 (`slot205.sh`, `mutate.py`, `busy.sh`), run as
|
||||
`bash busy.sh && CAND=<sha> bash slot205.sh` on Sage's CLEAR. It runs these files whole:
|
||||
- message-filter;
|
||||
- the bus files that call trail or replies: replies, tester, tester-threads, tester-writes, broker, decisions,
|
||||
decision-close, end-launch and task-read.
|
||||
|
||||
Then it runs the bus suite, then M1 and M11-M14, each on its named test and restored after each. It uses umask
|
||||
022 and TMPDIR from `mktemp -d /mnt/storage/scratch/tmp/g205XXXX`. It refuses unless HEAD is the candidate
|
||||
and the tree is clean, and it stops at the first unexpected failure (sage-2371).
|
||||
|
||||
Sage's gate adds every `scripts/test-*.sh`. The webui tests that read trail or replies run there.
|
||||
|
||||
Static check: `node --check` on broker.mjs and the new test, run from `/`. Both parse. All five mutant anchors
|
||||
match exactly once.
|
||||
|
||||
## r2 (after the 742cba805 slot, failure 1 of the window, sage-2428)
|
||||
|
||||
slot205 on 742cba805 stopped red in message-filter, 4 pass and 2 fail. Both fixes are in the test only.
|
||||
broker.mjs is unchanged (sage-2430).
|
||||
|
||||
- "trail on a decision ..." (:124, its event shows). `#trail` reaches events on a decision trail by
|
||||
`subject` = the decision or `body.$.decision` = the decision. `put()` wrote every event body as `{}`, so the
|
||||
shown message's event was out of reach. That also made the "no hidden event" check (:127) vacuous: the hidden
|
||||
event was never collected, so dropping it proved nothing. Fix: `put()` writes `{decision}` as the event body
|
||||
when the message has a decision. Both events are now collected; the filter keeps one and drops the other.
|
||||
- Governance pin, claims item (:175). A thread run's `role.claim` writes `thread_claims` only and returns
|
||||
(broker.mjs:2682). `agents` (:3309) reads `role_claims` only. No owner verb shows a thread claim. The gap
|
||||
predates 205. Ruling sage-2430, option (a): the pin checks through `threadsState` (the launcher view) that
|
||||
the claim is recorded with holder `assistant-run` and isn't filtered. `agents` is unchanged in 205.
|
||||
Follow-up: Sage adds a row for an owner-visible thread-claim read. Like the filter, it lands before any
|
||||
user thread goes live, since PRD :1084 and the FED R6 sentence promise that the owner sees claims.
|
||||
|
||||
M1 still fails the pin: with `#readable` true, `trail` on the user message returns rows instead of `[]`.
|
||||
@@ -0,0 +1,173 @@
|
||||
# Row 205 (193b1, #1703): viewer verbs, board refusals and the message filter
|
||||
|
||||
Owner Morgana, reviewer Duckworth, gate owner Sage (sage-2280). Brief: `docs/plans/2026-10-20_human-users-build.md`,
|
||||
"Brief: 193b1, viewer verbs, board refusals and the message filter" (blob a73e1bba0, rev 3088). Design:
|
||||
row 193 DESIGN.md r5 (e2d161e2e, Duckworth approved), sections 4.3, 5.4, 5.5, 5.7, 8 and 9. Base: refactor
|
||||
02da6a2ae. Draft 2: Sage's rulings (sage-2288) are in section 7. Sections 1-6 are d1 as sent (morgana-0063).
|
||||
|
||||
## 1. The dependency gap
|
||||
|
||||
The brief chains 193b1 on FED R3, FED R4 and row 196's verifier. Only row 196 exists (7247d80c7). On 02da6a2ae:
|
||||
|
||||
- R3 and R4 aren't filed. The FED brief (`docs/plans/2026-10-20_federation-r1-r2-r5.md`:12-13) files R3 "after
|
||||
R1 and row 196" and R4 after R3, R5 and 193a. No `*/fed-r3` branch is on origin.
|
||||
- Row 196's `verifyPrincipal` returns the `user` kind, and server.mjs accepts the `user` envelope, but
|
||||
runtime.mjs wires only `authenticateHuman` and `authenticateTester`. The broker has no `bindUser`, no
|
||||
`s.user`, no per-request grant read and no collaborator verb list. FED 3.6 and row R3 own all of these.
|
||||
- So nothing makes a user session today, and no thread is owned by `user:<id>` except through a direct
|
||||
store write.
|
||||
|
||||
193b1 has two halves that depend on R3 in different ways.
|
||||
|
||||
| Item | Needs a user session (R3) | Testable on 02da6a2ae |
|
||||
|---|---|---|
|
||||
| Viewer verb list (4.3) | yes: it extends R3's collaborator list | no |
|
||||
| Per-grant level checks via `covers(..., {level})` | yes: R3's grant read | no |
|
||||
| Board reads refused to users (5.5) | yes | no |
|
||||
| `trail` and `replies` filter (5.7) | no: it filters owner, reader and tester sessions | yes, with seeded `user:<id>` threads |
|
||||
| `#readable` owner-only | no | yes |
|
||||
| Governance pin test (owner side) | no: the owner reads decisions, inbox, agents, tasks and `project.file` | yes, with a seeded user-thread run |
|
||||
| Mutants 6 (viewer `thread.send`), 10 (user allowed `trail`) | yes | no |
|
||||
| Mutants 11, 12 (trail, replies filter removed) | no | yes |
|
||||
| Mutants 1, 3 (`#readable` rules 1 and 3 dropped) | see 4 | see 4 |
|
||||
|
||||
## 2. Recommendation: split 205 into 205a now and 205b after R3
|
||||
|
||||
- **205a, the message filter.** `trail` and `replies` hide messages in other users' threads from the owner,
|
||||
reader sessions and the tester, with `#readable` owner-only. Plus the owner-side governance pin test and
|
||||
mutants 11 and 12. No R3 dependency, no schema change.
|
||||
- **205b, viewer verbs and board refusals.** Built on R3's candidate, as Q177 chains a row on its parent's
|
||||
candidate. The viewer list, the level checks, users refused on board reads, the user-session half of the
|
||||
pin test, mutants 6 and 10.
|
||||
|
||||
Why split. The filter is the item the boundary orders first: "No user thread goes live before 193b1's
|
||||
message filter has landed" (mos-1021 B2, FED R8). Landing it before R3 means R3 and every later row can't
|
||||
create a user thread that the owner can read on `trail`. Waiting for R3 holds the filter behind a row that
|
||||
isn't filed. The other option, building all of 193b1 against R3's named interface as 193a did with R1,
|
||||
doesn't work here: 193a only called R1's exported `covers`, while 193b1 would have to write `bindUser`, the
|
||||
grant read and the collaborator list itself, which is R3's scope and owner.
|
||||
|
||||
Tradeoff: two rows and two reviews in place of one, and 205b rebases onto R3. The split is a brief change,
|
||||
so it's Sage's to rule.
|
||||
|
||||
## 3. 205a design
|
||||
|
||||
### 3.1 The hidden-message check
|
||||
|
||||
One broker method, `#hiddenMessage(s, id)`:
|
||||
|
||||
```
|
||||
SELECT t.owner FROM message_threads mt JOIN threads t ON t.id = mt.thread
|
||||
WHERE mt.message = ? AND t.business = ?
|
||||
```
|
||||
|
||||
A message is hidden when that owner starts with `user:`, isn't `#owner(s)`, and `#readable(s, t)` is false.
|
||||
A message with no thread row, or in a thread owned by the business human or `tester`, is never hidden, so
|
||||
row 177's behaviour is unchanged. A reader session has no `#owner`, so every user thread's messages are
|
||||
hidden from it.
|
||||
|
||||
`#readable(s, t)` in 205a returns false. Before 193b2's schema 3j there is no `thread_shares` table, so rule 1
|
||||
(a share in force) can't hold, and the function is false for anyone but the thread's owner (5.7: "b1 hides
|
||||
every message in another user's thread"). It is written as its own method so 193b2 adds the share rule and
|
||||
rules 2-4 in one place.
|
||||
|
||||
### 3.2 `trail`
|
||||
|
||||
- If `subject` is a hidden message id, return `[]`. Design 5.7 says `message-not-found`, "as an unknown id
|
||||
does", but `trail` on an unknown id returns `[]` today (it has no not-found check). A different code for a
|
||||
hidden id would confirm the id exists, which is what 5.7 wants to prevent, so I follow its reason over its
|
||||
code. Question 3.
|
||||
- After `#trail` collects its rows, drop: `messages` rows that are hidden, `deliveries` rows whose `message`
|
||||
is hidden, and `events` rows whose `subject` is a hidden message id. This covers each way `#trail` reaches
|
||||
a message: by id, by `decision`, and through `task.created` → `human.input` → `body.message`.
|
||||
- Decision rows, decision events, task snapshots, run events and role claims stay. 5.7 puts them on the
|
||||
governance side.
|
||||
|
||||
### 3.3 `replies`
|
||||
|
||||
A hidden `id` fails `message-not-found`, and hidden messages are dropped from the result. The `LIMIT`
|
||||
applies before the filter today; the filter moves into the SQL (`AND NOT EXISTS` a thread owned by another
|
||||
`user:`) so a page of hidden replies doesn't return short. For 205a that SQL form is exact because
|
||||
`#readable` is false; 193b2 adds the share exception to the same clause.
|
||||
|
||||
### 3.4 Tests (packages/bus/tests/message-filter.test.mjs, new)
|
||||
|
||||
Fixtures seed a project, a thread owned by `user:alice` with messages in `message_threads`, a delivery and an
|
||||
event on one of them, and a decision raised by that thread's run that cites the message. Sessions: the
|
||||
business human, a reader session and the tester.
|
||||
|
||||
- `trail` on a hidden message id: `[]` for all three sessions, the same as an unknown id.
|
||||
- `trail` on the decision: the decision row and its events show; the user's message, its delivery and its
|
||||
events don't.
|
||||
- `trail` on a human-owned thread's message: unchanged (row 177 control).
|
||||
- `replies` on a hidden id: `message-not-found`. `replies` on a visible id with hidden and visible replies:
|
||||
only the visible ones, and a full page when hidden replies come first.
|
||||
- Governance pin, owner side: for the user-thread run, `decisions {list: 'recent'}`, `inbox`, `agents`,
|
||||
`tasks` and `project.file` each show its item, and `trail` and `replies` hide the user's message body.
|
||||
Each item in the user sentence is one named assertion.
|
||||
|
||||
### 3.5 Mutants
|
||||
|
||||
| Mutant | Edit | Named test |
|
||||
|---|---|---|
|
||||
| 11 | the `trail` filter removed | trail on the decision hides the user's message, delivery and events |
|
||||
| 12 | the `replies` filter removed | replies hides other users' thread messages |
|
||||
| 11b | the hidden-subject early return removed | trail on a hidden message id returns [] |
|
||||
| 1 | `#readable` returns true | governance pin: trail and replies hide the user body |
|
||||
|
||||
## 4. Mutants 1 and 3
|
||||
|
||||
Design section 8 lists mutants 1 (rule 1 dropped) and 3 (rule 3 dropped) for b1. With no shares table,
|
||||
rule 1 is always false, so `#readable` is false whatever rules 2-4 say. Dropping rule 3 changes nothing a
|
||||
test can see until 193b2 adds shares, and rules 2 and 3 need R3's grant read. So:
|
||||
|
||||
- Mutant 1 in 205a is "`#readable` returns true", killed by the pin test above.
|
||||
- Mutant 3 moves to 193b2, where rule 1 can hold and rule 3 decides.
|
||||
|
||||
This is a second brief change for Sage to rule on.
|
||||
|
||||
## 5. Gate (Sage's slot, on CLEAR only)
|
||||
|
||||
The brief's gate: Duckworth approves the manifest; the bus suite and every `scripts/test-*.sh` green on
|
||||
Sage's gate; the mutants killed. The new test file whole, the touched existing files whole, then the bus
|
||||
suite, then each mutant on its named test with a restore check. umask 022, TMPDIR from
|
||||
`mktemp -d /mnt/storage/scratch/tmp/gXXXXXX`. No test touches the live data root, config, units, slot
|
||||
files, any live access.json or `~/.mosaic`.
|
||||
|
||||
## 6. Questions for Sage
|
||||
|
||||
1. Split 205 into 205a (filter, now) and 205b (viewer verbs and board refusals, after R3's candidate)?
|
||||
2. Mutant 1 as "`#readable` returns true" in 205a, and mutant 3 moved to 193b2?
|
||||
3. A hidden `trail` subject returns `[]`, the same as an unknown id, in place of 5.7's `message-not-found`
|
||||
(3.2)? `replies` already fails `message-not-found` on an unknown id, so there the code stays.
|
||||
|
||||
## 7. Rulings (sage-2288, re morgana-0063)
|
||||
|
||||
1. **Split approved.** 205a is built now: the 5.7 `trail` and `replies` filter over owner, reader and tester
|
||||
sessions, `#readable` owner-only, the owner-side governance pin test, and mutants 11 and 12. 205b comes
|
||||
after R3's candidate and R4: viewer verbs, `covers` level checks, board reads refused to users, the
|
||||
user-side pin, and mutants 6 and 10. 205a's base is origin/refactor 02da6a2ae or later. R3 builds on
|
||||
205a's candidate and extends the filter to user sessions, so the filter isn't written twice.
|
||||
2. **Mutant 1** in 205a is "`#readable` returns true", killed by the pin test. **Mutant 3** moves to 193b2
|
||||
(row 206). Sage changes the brief to match.
|
||||
3. **Hidden equals unknown.** A hidden `trail` subject returns `[]`, exactly as an unknown id does. `replies`
|
||||
keeps `message-not-found` for both. The point of 5.7 is that a caller can't tell hidden from unknown.
|
||||
So 205a adds:
|
||||
- one test per verb comparing the hidden response with the unknown-id response byte for byte
|
||||
(`JSON.stringify` of the result, or of the error's code and message), for each of the three sessions;
|
||||
- a mutant M13 that makes the hidden path distinguishable: `trail` fails `message-not-found` for a
|
||||
hidden subject. The `trail` equality test must kill it. M14 does the same for `replies`: a hidden id
|
||||
returns `[]` instead of failing. The `replies` equality test must kill it.
|
||||
|
||||
### 7.1 205a mutant table (replaces 3.5)
|
||||
|
||||
| Mutant | Edit | Named test |
|
||||
|---|---|---|
|
||||
| M1 (design 1) | `#readable` returns true | governance pin: the owner sees decisions, inbox entries, claims, tasks and project files, but not the user's messages |
|
||||
| M11 (design 11) | the `trail` filter of hidden rows removed | trail on the decision hides the user's message, its delivery and its events |
|
||||
| M12 (design 12) | the `replies` filter removed | replies drops other users' thread messages and still fills a page |
|
||||
| M13 | `trail` fails `message-not-found` for a hidden subject | trail on a hidden id answers exactly as on an unknown id |
|
||||
| M14 | `replies` returns `[]` for a hidden id | replies on a hidden id answers exactly as on an unknown id |
|
||||
|
||||
Queue: Sage re-pins 205 to the split brief and adds 205b and the R3 row at the next gap. I claim after
|
||||
that. No run before Sage's CLEAR.
|
||||
@@ -0,0 +1,267 @@
|
||||
# #1782: the owner sees a thread agent's claims (design r1)
|
||||
|
||||
Morgana, 2026-10-22, for Duckworth. Brief: docs/plans/2026-10-22_owner-thread-claims.md (Sage, for
|
||||
mos-1262). The brief wasn't committed and had no queue row when this was written, so the folder is named
|
||||
for the brief. It moves to queue-<row> once the row exists. Base: refactor c27207f6e (205 landed at
|
||||
ce6a96ad6, then 268). No code yet.
|
||||
|
||||
## What the code does now
|
||||
|
||||
- A thread run's `role.claim` writes `thread_claims` only and returns (broker.mjs:2682 at ce6a96ad6).
|
||||
- `agents` (broker.mjs:3409) reads `role_claims` only:
|
||||
- human and reader sessions get `#agentState(c)` per held role;
|
||||
- role and tester sessions get the raw rows.
|
||||
- A held thread claim is the thread's latest `thread_claims` row with op `claim`. `#threadHolders`
|
||||
(:483) and `#threadRows` (:624) already compute it that way, with `c.seq = max(seq)` per thread.
|
||||
- `threads.name` is the name a user wrote. `threads.instance` is the role instance from the business
|
||||
file, which the operator writes, not the user.
|
||||
|
||||
## The problem with the brief's shape
|
||||
|
||||
The brief adds thread rows to the human and reader `agents` result, after the role rows. Every consumer
|
||||
of that result assumes one row per role:
|
||||
|
||||
| Consumer | What it reads | What a thread row would do |
|
||||
|---|---|---|
|
||||
| Console Agents view, webui bus.js:474 | `c.role` in a "Held roles" table, `claims.length` as its count | blank role cell, wrong count |
|
||||
| Console board held table, bus.js:447 and :462 `held-count` | same | same |
|
||||
| Console composer, bus.js:298 | `c.role`, filtered with `typeof r === 'string'` | dropped safely |
|
||||
| CLI TUI agents view, screens.mjs:280 and :284, app.mjs:288 and :433 | `c.role` as title and column | blank title |
|
||||
| CLI `mosaic agents`, format.mjs:54 | `c.role` | `undefined` in the line (brief item 4 covers this one) |
|
||||
|
||||
So the shape as briefed also needs the Console (webui) and the TUI changed in this row, or the live
|
||||
Agents view breaks on the next re-pin. The brief's manifest names only format.mjs.
|
||||
|
||||
## Proposed shape: a separate owner verb
|
||||
|
||||
The brief allows this ("for example a separate owner verb, it goes in BUILD.md to Duckworth before
|
||||
code").
|
||||
|
||||
**D1. New verb `thread.claims`**, human and reader sessions only. Every other session refuses with
|
||||
`human-required`, the code `decisions` uses at :3426. No arguments (`keys(a, [], [])`). The result, in
|
||||
thread creation order:
|
||||
|
||||
```js
|
||||
[{ thread, instance, holder_run, harness, at }]
|
||||
```
|
||||
|
||||
- One row per thread whose latest `thread_claims` op is `claim`.
|
||||
- `at` is that claim row's time.
|
||||
- It doesn't return `name`, message content or `address`. `address` matches the brief's four fields;
|
||||
it can be added if Duckworth wants it next to the role rows.
|
||||
- `instance` is beyond the brief's four. It names which assistant holds the thread, and the operator
|
||||
writes it, not the user.
|
||||
|
||||
SQL, the `#threadHolders` join without the instance and owner filter:
|
||||
|
||||
```sql
|
||||
SELECT t.id AS thread, t.instance, c.holder_run, c.harness, c.at FROM threads t
|
||||
JOIN thread_claims c ON c.seq=(SELECT max(seq) FROM thread_claims WHERE thread=t.id)
|
||||
WHERE t.business=? AND c.op='claim' ORDER BY t.seq
|
||||
```
|
||||
|
||||
**D2. `agents` doesn't change** for any session. That is stronger than brief item 2 (role sessions
|
||||
only), and it means the Console, TUI and composer need no change in this row.
|
||||
|
||||
**D3. Reader allow-list.** `thread.claims` goes in the reader list at :2370. The processing gate (:402)
|
||||
already exempts human and reader sessions, so a halt doesn't hide claims. No TESTER_VERBS or USER_VERBS
|
||||
entry is added (see D6).
|
||||
|
||||
**D4. CLI `mosaic agents`** (cli.mjs:314) calls `agents` and then `thread.claims`. `formatAgents(list,
|
||||
threads = [])` prints the role lines unchanged, then for each held thread claim:
|
||||
|
||||
```
|
||||
thread <thread> <instance> <holder_run> <harness> since <at>
|
||||
```
|
||||
|
||||
Each line starts with `thread `, so a reader can tell the two kinds apart (brief item 1). Role output
|
||||
with no thread claims is byte-equal to today, including "agents: no role is claimed". When there are no
|
||||
roles but there are thread claims, the empty-roles line is still printed, then the thread lines.
|
||||
|
||||
**D5. The governance pin.** message-filter.test.mjs, the "claims" item: `threadsState` becomes
|
||||
`call(owner, 'thread.claims')`, which must include `{ thread: U, holder_run: 'assistant-run' }`. The
|
||||
comment that names this row goes away (brief item 5).
|
||||
|
||||
**D6. What this row leaves out**, so as not to widen beyond owner and reader sessions (mos-1202):
|
||||
|
||||
- tester sessions;
|
||||
- FED R3 user principals, who own threads under R3, so their own claims are an R6 question;
|
||||
- a Console section;
|
||||
- the TUI.
|
||||
|
||||
## Questions for Duckworth and Sage
|
||||
|
||||
- **Q1.** Do you accept D1 over the briefed mixed list? If you'd rather have the mixed list, the
|
||||
manifest grows to webui bus.js, screens.mjs and app.mjs, plus their tests (bus-browser,
|
||||
webui bus.test and the TUI tests).
|
||||
- **Q2.** Should the Console show thread claims before a user thread goes live? PRD :1083-1084 promises
|
||||
visibility to the owner, and a Console user won't run `mosaic agents`. My recommendation: a follow-up
|
||||
row bound like this one (it lands before any user thread goes live), so this row stays small for
|
||||
10-27. The alternative is a "Thread claims" section in the Console Agents view in this row.
|
||||
|
||||
## Tests (under D1)
|
||||
|
||||
All new tests go in packages/bus/tests/agents.test.mjs unless named otherwise. Each one runs on a store
|
||||
with one held role claim and one thread run.
|
||||
|
||||
- **T1.** Human `thread.claims` lists the held thread claim (thread, instance, holder_run, harness,
|
||||
at). Reader gets the same result.
|
||||
- **T2.** After release, the thread is gone. After a re-claim followed by a revoke, it's gone again.
|
||||
The thread has an earlier `claim` row, so a first-op reading would still list it.
|
||||
- **T3.** A role session's `thread.claims` refuses `human-required`.
|
||||
- **T4.** `agents` is deepEqual to a copy taken before the thread run claimed, for the role, human
|
||||
and reader sessions on the same store.
|
||||
- **T5.** `JSON.stringify` of the `thread.claims` result has neither the thread's name nor the body of
|
||||
a message sent in that thread.
|
||||
- **T6.** packages/cli/tests/format.test.mjs: a mixed list renders the role lines exactly as today,
|
||||
then `thread …` lines. The empty-roles case with threads is covered too.
|
||||
- **T7.** packages/bus/tests/message-filter.test.mjs: the moved pin (D5).
|
||||
|
||||
## Mutants (each killed by a named test)
|
||||
|
||||
| Mutant | Change | Killed by |
|
||||
|---|---|---|
|
||||
| M1 | `thread.claims` returns `[]` | T1, T7 |
|
||||
| M2 | the held check uses `min(seq)` (first op) | T2 |
|
||||
| M3 | the human/reader check is dropped, so a role session gets thread rows | T3 |
|
||||
| M4 | `agents` for human appends the thread rows | T4 |
|
||||
| M5 | the SELECT adds `t.name` | T5 |
|
||||
| M6 | formatAgents drops the thread lines | T6 |
|
||||
|
||||
## Manifest (expected)
|
||||
|
||||
- packages/bus/src/broker.mjs
|
||||
- packages/bus/README.md (a paragraph for `thread.claims`, next to `agents`)
|
||||
- packages/bus/tests/agents.test.mjs
|
||||
- packages/bus/tests/message-filter.test.mjs
|
||||
- packages/cli/src/cli.mjs
|
||||
- packages/cli/src/format.mjs
|
||||
- packages/cli/tests/format.test.mjs
|
||||
- this BUILD.md
|
||||
|
||||
Verb-list pins checked at c27207f6e:
|
||||
|
||||
- tester-writes.test.mjs:19 pins `TESTER_VERBS` exactly. D6 adds nothing to it, so the pin holds.
|
||||
- No test pins the reader list at :2370.
|
||||
- `thread.messages` appears in tester-threads, threads and webui chat as calls, not as a verb set.
|
||||
|
||||
Before the in-review move, I'll rerun the same grep for `thread.claims` and for any new verb-set pin.
|
||||
|
||||
Gate (brief): Duckworth approves the manifest. On Sage's gate, the touched test files run whole, the
|
||||
bus and cli suites are green, every scripts/test-*.sh is green, and M1-M6 are killed.
|
||||
|
||||
## r2 (duckworth-0118: D1 approved with C1-C3)
|
||||
|
||||
Q1: Duckworth accepts D1. Q2: Duckworth is fine with a follow-up row under the same bind; Sage rules on
|
||||
it. Duckworth's note is agents/duckworth/work/row1782/DESIGN-r1.md on duckworth/reviews 13fb9abc5.
|
||||
|
||||
**C1. `--tester` makes one call.** callFor binds the tester when `--tester` is given (cli.mjs:192).
|
||||
D6 keeps `thread.claims` off TESTER_VERBS, so a second call there would refuse tester-refused and exit
|
||||
3. tester.test.mjs:128 pins exactly one spawn, `agents`. So cli.mjs calls `thread.claims` only when
|
||||
`--tester` is absent. A tester's `mosaic agents` is unchanged.
|
||||
- T8 (packages/cli/tests/format.test.mjs or the CLI test that drives main() with a stand-in transport;
|
||||
the exact file is picked at build): `agents --tester` makes one call, `agents`; human `agents` makes
|
||||
two, `agents` then `thread.claims`.
|
||||
- Gate adds tester.test.mjs and other-tree.test.mjs, each run whole.
|
||||
- M7: thread.claims is called on the tester path too. T8 and tester.test.mjs:128 kill it.
|
||||
|
||||
**C2. `--json` shape.** `mosaic agents --json` prints the `agents` array unchanged, as `inbox --json`
|
||||
prints decisions only (cli.mjs:308). Only the text output adds the `thread …` lines. The machine read of
|
||||
thread claims is the bus verb, and the bus README paragraph documents `thread.claims` as such. A grep at
|
||||
c27207f6e found no consumer of `mosaic agents --json` in packages/*/tests or scripts. T6 pins both: the
|
||||
text output is mixed, and the `--json` output deepEquals the `agents` result alone.
|
||||
|
||||
**C3. Where the pin moves.** Under D1 the governance pin moves to the owner's `thread.claims` call, not
|
||||
to `agents`. That is brief item 5 read under D1: the brief named `agents` because it put thread rows
|
||||
there.
|
||||
|
||||
Notes taken:
|
||||
- N1: T4's before/after equality covers the tester session too.
|
||||
- N2: T3 adds a tester: `thread.claims` refuses tester-refused.
|
||||
- N3: FED R3 user principals must not get other users' thread claims. This row gives them none
|
||||
(no USER_VERBS entry). The rule carries with 205 N1/N3 to 205b/R3.
|
||||
- N4: the :403 processing-gate list is in the verb-set grep. Human and reader sessions return before
|
||||
that list (:402), so `thread.claims` needs no entry there, and no test pins the list (grep at
|
||||
c27207f6e). The :2370 reader list is the one list that gains the verb.
|
||||
|
||||
Mutants are now M1-M7. Building starts once Sage creates the row.
|
||||
|
||||
## r3: build (sage-2486, on 23c3d6218)
|
||||
|
||||
Changes from the design, each a placement choice:
|
||||
|
||||
- **Bus tests file.** T1-T5 go in a new file, packages/bus/tests/thread-claims.test.mjs, not
|
||||
agents.test.mjs. agents.test.mjs's businesses have no `assistant` instance, so a thread run would
|
||||
need a new fixture there. The new file copies message-filter.test.mjs's setup (chief plus an
|
||||
`assistant` thread instance, human, reader and tester sessions). Its threads are owned by the
|
||||
business human (`jason`), so the human's `assistant.stop` reaches the run for T2's revoke.
|
||||
- **T3** covers the chief's role session and the thread run itself (both `human-required`) and the
|
||||
tester (`tester-refused`, N2).
|
||||
- **T4** covers human, reader, tester and chief sessions (N1).
|
||||
- **T5** writes the thread's message to the store, as message-filter.test.mjs's put() does. The run's
|
||||
own message.send would also pass through the messaging graph, which T5 doesn't test.
|
||||
- **T6/T8 split.** format.test.mjs holds T6's text cases: mixed, empty roles with threads, and no
|
||||
threads argument. The `--json` pin needs main(), so it sits with T8 in tester.test.mjs, whose
|
||||
setup has both stand-in transports. That test also pins the call order: tester `["agents"]`; human
|
||||
`["agents", "thread.claims"]`; human `--json` adds only `agents`. Its JSON deepEquals the broker's
|
||||
own `agents` result.
|
||||
- **T7.** The message-filter.test.mjs pin now reads `holder_run` from the owner's `thread.claims`.
|
||||
The old pin read `holder` from `threadsState`.
|
||||
|
||||
Manifest (actual):
|
||||
|
||||
- packages/bus/src/broker.mjs
|
||||
- packages/bus/README.md (a "Thread claims" paragraph at the end of "Agent state")
|
||||
- packages/bus/tests/thread-claims.test.mjs (new)
|
||||
- packages/bus/tests/message-filter.test.mjs
|
||||
- packages/cli/src/cli.mjs
|
||||
- packages/cli/src/format.mjs
|
||||
- packages/cli/tests/format.test.mjs
|
||||
- packages/cli/tests/tester.test.mjs
|
||||
- agents/morgana/work/queue-277/BUILD.md
|
||||
|
||||
Mutants: M1 (T1, T7), M2 (T2), M3 (T3), M4 (T1, T4), M5 (T5), M6 (T6), M7 (T8). Gate: the touched test
|
||||
files run whole, plus other-tree.test.mjs. Then the bus and cli suites and every scripts/test-*.sh.
|
||||
Nothing runs before Sage's CLEAR.
|
||||
|
||||
Early look (sage-2504, 05:27Z), on c050bb90b: thread-claims.test.mjs failed 5/5. The test's `call`
|
||||
helper took any string as a session name, but a run's capability is a string too, so the run's calls
|
||||
went out unauthenticated. T1 also compared null-prototype store rows strictly. Both are test bugs, and
|
||||
the fix is r2 of the build. On r2, thread-claims 5/5, cli tester 9/9, other-tree 4/4 pass, 0 skipped.
|
||||
|
||||
## r3: Duckworth notes N1/N2 (duckworth-0119, ruled sage-2505), for the post-194 rebase round
|
||||
|
||||
- **N1.** A human `mosaic agents` no longer fails as a whole when `thread.claims` is refused.
|
||||
cli.mjs prints the role lines, then `thread claims: refused <code>`, and exits 0. The choice of
|
||||
exit 0 follows D1: `agents` stays as it was, and it exited 0 before this row. Only an error with a
|
||||
bus code is caught (the same `^[a-z-]{1,64}$` shape `markRead` checks); any other error still
|
||||
throws. Pinned by a new test in cli tester.test.mjs: a stand-in transport refuses thread.claims
|
||||
with `processing-halted`, the role row prints, then the refusal line. This matches row 278 item 4
|
||||
for the Console. M8 (the catch drops the refusal line, or rethrows) is killed by that test.
|
||||
- **N2.** packages/cli/README.md: the usage line gains `[--tester]`, and a bullet describes the
|
||||
`thread` lines, the refusal line, `--json` and `--tester`. host.test.mjs:855 reads this README
|
||||
for a unit file name, which is still there. host.test.mjs joins the gate's whole-file runs.
|
||||
|
||||
Manifest adds packages/cli/README.md (10 paths). Nothing here has run yet. It runs whole after the
|
||||
rebase onto 194, on Sage's CLEAR.
|
||||
|
||||
## r4: rebase onto 0165be266 and Duckworth's delta notes (sage-2587, duckworth-0123)
|
||||
|
||||
- **Rebase.** Commits 1-6 are cherry-picked onto refactor 0165be266, which has 194, 236 and 274
|
||||
landed, on morgana/row277-0165. The range-diff is `=` for all six and the patch-ids are unchanged.
|
||||
Four files auto-merged around upstream lines: bus README, broker.mjs, cli README and cli.mjs.
|
||||
- **N1 (duckworth-0123).** The N1 catch's rethrow branch had no test, so a mutant that takes every
|
||||
error as a refusal (it would print `refused undefined`) survived. A new cli tester.test.mjs test
|
||||
throws from thread.claims with no code, then with `ECONNRESET`. Each rejects main with the same
|
||||
error and prints nothing. M9 (the code check dropped) is killed by that test.
|
||||
- **N2 (duckworth-0123), accepted.** Since 274, the owner's and a reader's thread.claims can include
|
||||
user threads (id, instance, holder run, harness, time; no name, no messages). That is the design
|
||||
join approved in duckworth-0118, with no owner filter, and owner-visible run governance under
|
||||
sage-1368 B1.
|
||||
- **N3 (duckworth-0123), for later.** When 264a and 277 meet, thread.claims's latest row per thread
|
||||
goes per session, and thread-claims.test.mjs needs the default session. It is checked in whichever
|
||||
round lands second.
|
||||
|
||||
Gate (sage-2590): the touched test files plus user-principal, other-tree and host whole; the bus,
|
||||
cli, harness, business and seat suites; every scripts/test-*.sh; M1-M9. The kit prints HEAD and the
|
||||
manifest check first. Nothing runs before Sage's CLEAR.
|
||||
@@ -0,0 +1,814 @@
|
||||
# Row 191 stopRun inspection round
|
||||
|
||||
Design: ../DESIGN-r2.md, approved by Dewey in 29473; scope sage-2244;
|
||||
per-run contract ../STOP-RUN-CONTRACT.md, honker-0079/0081. Not review-ready:
|
||||
full package/shell/Docker gate and acceptance/guard mutants remain pending.
|
||||
|
||||
Current commit 7aa18b0f6e1a09188fb602853ebb02815b642ac9, new branch
|
||||
rocko/row191-stoprun-dcd8-20261022, parent dcd8d4dda. Worktree
|
||||
/mnt/storage/scratch/rocko-r191-r3. Earlier inspection f7bf7dfec is preserved.
|
||||
All 79 manifest paths have identical blobs at f7bf7dfec and 7aa18b0f6;
|
||||
rebase-product-diff.stat is empty, rebase-product-check.txt names the check.
|
||||
The manifest SHA-256 stays
|
||||
9971ac5fe56d0d36eb914ecaf2c282e14ab3cd29860fd27b268ec0baadaf0e52.
|
||||
Rocko author and committer identity were selected explicitly. No attribution
|
||||
lines, force-push or branch deletion. Sage publishes through jarvis.
|
||||
|
||||
## Changes Dewey must review: two focused-run fixes
|
||||
|
||||
1. packages/bus/src/broker.mjs, requestHost: the new processingWork guard was
|
||||
initially inserted into the inner refusal logger's try block. That let the
|
||||
original request reach validation, then prevented its refusal being logged
|
||||
while halted, returning storage-unavailable. Move it to the main admission
|
||||
try, before keys/credentials/authority/effects. The refusal logger retains
|
||||
its existing unconditional audit behavior. The new host-admission test now
|
||||
expects processing-halted and proves neither executor method was called.
|
||||
Initial evidence: ../logs/stoprun-focused-packages_bus_tests_processing.test.mjs.log.
|
||||
|
||||
2. packages/bus/tests/processing.test.mjs, real socket await test: row 196's
|
||||
asynchronous kernel-peer subprocess now precedes dispatch. The old 5 ms
|
||||
server deadline could fire during that subprocess, before the processing
|
||||
await branch extends it. This test already stubs authenticateHuman; give it
|
||||
a deterministic peerReader too, with read and close, and assert exactly two
|
||||
proof reads for halt/resume. Keep the 5 ms ordinary timeout and 30 ms halt
|
||||
delay. Production peer discovery, proof/transport and timeout behavior are
|
||||
unchanged. The test still fails if the async branch timeout is removed.
|
||||
The first fixture retry omitted close(); its teardown failed and left its
|
||||
own server running. Only that verified test group 2831504 was killed. Both
|
||||
unsuccessful logs remain. Corrected evidence:
|
||||
../logs/stoprun-focused-bus-processing-fixed-2.log (14/14).
|
||||
|
||||
These are diagnosed code/fixture mistakes, not declared flakes. No failed log
|
||||
was removed and no existing security assertion was weakened.
|
||||
|
||||
## Focused evidence, sage-2250
|
||||
|
||||
Ran singly on 41b0cf755 because none of row 254's seven paths overlaps ours.
|
||||
Sage-2265 carries this evidence to dcd8d4dda after the empty 79-path diff.
|
||||
Scripted busy check exited 0 before the first file. Every test runner used its
|
||||
own process group, 180 s timeout, scratch TMPDIR
|
||||
/mnt/storage/scratch/rocko-r191-tmp and DOCKER_HOST=unix:///nonexistent.sock.
|
||||
There was no Docker, browser, package suite, shell suite or mutant run.
|
||||
|
||||
| File | Final passed | Failed | Skipped |
|
||||
|---|---:|---:|---:|
|
||||
| processing-control/tests/stop-run.test.mjs | 12 | 0 | 0 |
|
||||
| processing-control/tests/runtimes.test.mjs | 10 | 0 | 0 |
|
||||
| processing-control/tests/coordinator.test.mjs | 3 | 0 | 0 |
|
||||
| processing-control/tests/containers.test.mjs | 6 | 0 | 0 |
|
||||
| processing-control/tests/effects.test.mjs | 5 | 0 | 0 |
|
||||
| processing-control/tests/task.test.mjs | 7 | 0 | 0 |
|
||||
| bus/tests/processing.test.mjs | 14 | 0 | 0 |
|
||||
| bus/tests/processing-runtime.test.mjs | 2 | 0 | 0 |
|
||||
| bus/tests/project-runtime.test.mjs | 3 | 0 | 0 |
|
||||
| Script sum | 62 | 0 | 0 |
|
||||
|
||||
All final runners exited 0. The batch stopped at the initial bus failure,
|
||||
then that file was diagnosed/rerun before the final two files. Counts are from
|
||||
focused-run-table-final.json and include only each final successful file log.
|
||||
Earlier bus failures are reported above, not silently included as green.
|
||||
Approximate run span 23:59Z–00:00:41Z; start second was not recorded.
|
||||
Cwd-scoped node/chromium sweep was empty at 00:00:41Z; slot returned to Sage.
|
||||
|
||||
## stopRun implementation
|
||||
|
||||
The export resolves host-owned run identity even without a bound bus claim.
|
||||
It checks boot/start/group identity immediately before each signal, preserves
|
||||
verified member anchors, waits 5 s before KILL and observes for 2 s. Signals
|
||||
are acknowledgements only, never invented. Root/business/run cache joins the
|
||||
same Promise. Atomic hard-link publication plus durable exclusive claim
|
||||
coordinates other host processes. First owner/reason/deadline/result persists;
|
||||
completed retries never re-escalate or overwrite that result. Halt links its
|
||||
generation to the first fire receipt without relabelling the runtime's stop
|
||||
cause. A later verified exit is separate immutable observation evidence.
|
||||
The seam writes no bus events. Honker maps killed/alive to lifecycle events.
|
||||
|
||||
No global unit, live config, binding, token, slot file or live store touched.
|
||||
No schema migration. First activation still requires stopping old processing
|
||||
before trusted initialization and aligning the copy/image/read-only mount in
|
||||
Mos's window. Existing result.json evidence remains write-once.
|
||||
|
||||
## Integration and outstanding work
|
||||
|
||||
The 12 latest-base conflict resolutions are listed in STATUS.md. New landed
|
||||
host execution, publish and library sync paths are now wrapped in processing
|
||||
scopes; host credential cancellation composes with its existing revocation
|
||||
signal. This changes only files already owned by 191. Those wider integrations
|
||||
still need the full owning-file/package gate and mutant evidence.
|
||||
|
||||
Fifteen proposed per-run mutants preflight exact once in stoprun-mutants.json;
|
||||
none has been applied or run. The broader kill-switch acceptance/guard table
|
||||
and executable mutant runner are still being completed. No kill count or
|
||||
review-ready claim is made. Ask Sage for the later CLEAR when the packet is
|
||||
complete, as sage-2259/2262 requires.
|
||||
|
||||
### Gate preparation after the focused slot
|
||||
|
||||
The published product implementation is unchanged. Three uncommitted test-file
|
||||
strengthenings are saved in post-inspection-tests.patch: effects.test.mjs adds a
|
||||
4-second bounded abort expectation for the watcher mutant; Discord's early
|
||||
start fence is also checked before a malformed journal can be read; WebUI's
|
||||
body limit now uses oversized valid JSON, and a direct-handler test isolates
|
||||
its own origin/site guards from serve's outer fence. These edits are unrun and
|
||||
are not included in the published 9971ac5f manifest. They must pass before a
|
||||
new candidate is cut. Thus 62/62 is prior focused evidence, not a claim for
|
||||
these changed test bytes.
|
||||
|
||||
The complete proposed table has 70 exact-once sites in all-mutants.json,
|
||||
including expected killing test names. Static preflight is clean; no mutant
|
||||
has been applied. run-mutants.py validates preflight, uses new process groups,
|
||||
tracks only its own verified descendant identities, kills those groups on
|
||||
timeout, checks named TAP failures, and restores all source bytes in finally.
|
||||
A timeout or wrong-test failure is not counted as a valid kill. The table and
|
||||
runner are prepared evidence, not a kill result. The requested nine-package,
|
||||
ten-shell, seventy-mutant gate is in gate-plan.json; Sage CLEAR is pending.
|
||||
|
||||
## Published test-strengthening commit and full gate (sage-2269/2271)
|
||||
|
||||
81889b58d57889b12a584aa225e1ac26e90e5700 is one fast-forward commit on
|
||||
7aa18b0f6, same branch rocko/row191-stoprun-dcd8-20261022. It commits the
|
||||
three test changes saved in post-inspection-tests.patch; that patch remains
|
||||
historical evidence, not an uncommitted candidate. Product implementation is
|
||||
unchanged. Earlier manifest 9971ac5f belongs to 7aa18b0f6; candidate.txt now
|
||||
names the manifest for 81889b58d. Sage published this tip before granting CLEAR.
|
||||
|
||||
The full gate checks HEAD equality and clean product bytes before its first
|
||||
file. Changed test files run singly, then nine packages, all ten shell suites,
|
||||
then seventy mutants. Each step gates on its exit status and retains its full
|
||||
log. run-gate.sh uses pipefail and an EXIT trap for the label-verified Compose
|
||||
network rocko-r191-gate_default, project rocko-r191-gate. TMPDIR is
|
||||
/mnt/storage/scratch/tmp/rocko-r191-gate. Docker is available only for test-task
|
||||
and test-release; other steps use unix:///nonexistent.sock. Live provider calls
|
||||
are disabled. No daemon, socket, volume or live configuration changes.
|
||||
|
||||
Gate start: 2026-10-22T00:18:00Z. Results remain pending until gate-results.json
|
||||
and mutant-results.json provide actual exit statuses and counts.
|
||||
|
||||
### First full-gate stop and fixture correction (sage-2274)
|
||||
|
||||
The 81889b58d run stopped at its first failure, 00:18:00Z–00:19:21Z.
|
||||
27 files completed: 268 passed, 1 failed, 0 skipped. Packages, shells and
|
||||
mutants did not start. Full logs/results are in gate-attempt-81889/.
|
||||
The tester refusal test's fixture requested tester:true over TCP, which row
|
||||
196 now refuses before route handling. This is a stale fixture, not a flake.
|
||||
The correction uses the existing startTesterSocket helper, a private fixture
|
||||
root, socketPath requests, TESTER_ORIGIN and no human identity header. Every
|
||||
assertion remains; the request now reaches the intended processing refusal
|
||||
rather than failing listener setup or the outer identity fence. A grep of all
|
||||
changed test files found no other tester:true TCP setup. The cwd node/chromium
|
||||
sweep returned []; no Docker test workload ran. Commit c78f48946 is a single
|
||||
fast-forward test-only fix on 81889b58d, authorized by sage-2274. The complete
|
||||
gate restarts from its first single file only after Sage publishes this tip.
|
||||
|
||||
Sage-2275 published c78f48946 and granted full restart. Second attempt begins
|
||||
2026-10-22T00:21:03Z; any further failure stops before additional edits.
|
||||
Candidate manifest b49fd493dc75ef2484ffc255213dc8eeaaaa348dee416c8d26d1c2bab8e20067.
|
||||
|
||||
### Second attempt stopped; no further changes (sage-2275)
|
||||
|
||||
Run 00:21:03Z–00:23:50Z on published c78f48946: all 27 changed singles
|
||||
269/269, processing-control 61/61, bus 646/646. The CLI package emitted 26
|
||||
failed subtests before its verified own runner group 3119310 was stopped.
|
||||
Its result is interrupted, not a complete package count. Six gate-lane image
|
||||
checks refuse because scripts/testing/lane-image.mjs pins the old four COPY
|
||||
lines and 191 adds processing-control to Containerfile. Twenty launch-account,
|
||||
launch-limit-checks, launch-limits and launch-routes cases hit
|
||||
processing-uninitialized on fixture roots with no explicit control init.
|
||||
No edits, retries, later packages, shells, Docker test workloads or mutants
|
||||
followed this second failure. Parent runner exited; cwd node/chromium sweep
|
||||
returned [] at 00:23:50Z. Slot returned to Sage at 00:24:05Z with the exact
|
||||
failures and a request for scope on image-manifest and fixture initialization.
|
||||
This is an integration failure, not a declared flake or a passing gate.
|
||||
|
||||
## Image-input manifest and launcher fixture scope (sage-2278, sage-2282)
|
||||
|
||||
Commit eb3abba12 is one fast-forward commit on c78f48946. This is an image-input
|
||||
manifest change: scripts/testing/lane-image.mjs now pins the exact Containerfile
|
||||
COPY of packages/processing-control/src/state.mjs and entrypoint.mjs and hashes
|
||||
both named source paths, without a wildcard. Its only two changed lines add
|
||||
those inputs. Without them, a lane could reuse a stale image after a reader or
|
||||
entrypoint change. The image test fixtures copy both files and each has its
|
||||
own receipt-invalidating test. Mutant KS56 drops state.mjs from the hash list;
|
||||
its named killer is the state.mjs receipt-invalidating test. Static preflight
|
||||
is 71 sites / 0 errors. No new run result is claimed before CLEAR.
|
||||
|
||||
The launch-account, launch-limit-checks, launch-limits and launch-routes fixtures
|
||||
now explicitly call initializeControl(f.dataRoot) before constructing their
|
||||
launchers. Existing assertion lines are byte-identical. A new launch-limit-checks
|
||||
test leaves initialization off and asserts processing-uninitialized, no
|
||||
mayLaunch/authorizeLaunch, and no registered session. A live root gets control
|
||||
initialized by the bus broker. The re-pin order is bus start before any seat
|
||||
launch; a launcher started before that refuses. This supplements the existing
|
||||
window precondition to stop old processing before the first trusted init.
|
||||
|
||||
Sage-2282 supersedes the test-only image ruling: lane-image.mjs is a product
|
||||
blob, so mos-1200 carries no earlier results. The complete plan restarts from
|
||||
the first single file, all nine packages, all ten shells and all 71 mutants
|
||||
only after publication and Sage CLEAR. Earlier attempts remain in their
|
||||
separate gate-attempt directories; neither is a passing full gate.
|
||||
|
||||
Sage-2283 published eb3abba12. Current 85-path candidate manifest is
|
||||
01a2b33651414c9b0cb094d2108f9e39c48a41b79974dcc16bdec374f8371091.
|
||||
The gate prints HEAD and this digest, verifies every manifest blob before its
|
||||
first test, and carries no old results. Awaiting CLEAR after Gander 253.
|
||||
|
||||
## Full restart on eb3abba12 stopped at Discord (sage-2286)
|
||||
|
||||
Verified HEAD and all 85 manifest blobs before tests. Run span:
|
||||
2026-10-22T00:34:50Z–00:47:33Z. No old result carried.
|
||||
|
||||
| Step | Passed | Failed | Skipped | Exit |
|
||||
|---|---:|---:|---:|---:|
|
||||
| 32 changed test files singly | 304 | 0 | 0 | 0 |
|
||||
| processing-control | 61 | 0 | 0 | 0 |
|
||||
| bus | 646 | 0 | 0 | 0 |
|
||||
| cli | 761 | 0 | 0 | 0 |
|
||||
| discord | 190 | 2 | 0 | 1 |
|
||||
|
||||
Singles sum independently confirmed by awk over all 32 TAP logs. Discord's
|
||||
binding.test.mjs:201 STOP check and recover.test.mjs:145 ready recovery check
|
||||
both get processing-uninitialized before the expected behavior. Those older
|
||||
fixture roots do not explicitly initialize control. The gate stopped at this
|
||||
step; no subsequent package, shell suite, Docker test workload or mutant ran.
|
||||
Docker availability was checked read-only (29.7.2); no daemon/volume changes.
|
||||
The EXIT trap completed. Cwd node/chromium sweep returned [] at 00:47:49Z,
|
||||
source tree remains clean, and the slot was handed back at 00:47:58Z.
|
||||
No test or product edits or retries followed this failure; Sage has the table
|
||||
and the fixture-initialization recommendation. This is not a green full gate.
|
||||
|
||||
## Discord fixture init and all-package static sweep (sage-2294)
|
||||
|
||||
Commit e6800380dc0765ce3f1477044e2ce4f773ebb43c is test-only and fast-forwards eb3abba12.
|
||||
Only binding.test.mjs and recover.test.mjs change: one initializeControl import
|
||||
and one initializeControl(dep.dataRoot) call in each affected CLI fixture.
|
||||
Every original assertion line is retained. Product blobs are unchanged.
|
||||
FIXTURE-SWEEP.md and fixture-sweep.json enumerate 338 source call-site hits,
|
||||
with the two fixes and the already-initialized chains, plus the screens for
|
||||
rootless optional contexts and read-only/usage CLI calls. The generator reads
|
||||
repository source only and invokes no fixture. Deliberate missing/deleted/
|
||||
corrupt-state refusal tests remain negative; no shared helper masks them.
|
||||
|
||||
Current manifest be36af8f0855f57336e6a33283aa0a8e94297f338b1f8180936cbccf1119c60a, 87 paths. Sage-2294 carries only unchanged
|
||||
processing-control 61/61, bus 646/646, CLI 761/761 and unchanged singles from
|
||||
gate-attempt-eb3/. The resume must run both touched files wholly, whole Discord,
|
||||
then the never-run WebUI/tasks/seat/project/library packages, every shell suite
|
||||
and all 71 mutants. No result is claimed for the new test bytes before CLEAR.
|
||||
The live initialization order remains bus start before any seat or Discord
|
||||
start; no fixture change initializes or repairs a live root.
|
||||
|
||||
## Resume on e6800380d stopped at WebUI (sage-2311)
|
||||
|
||||
Start 2026-10-22T01:27:40Z; failed-step exit 01:28:45Z. Startup verified HEAD,
|
||||
manifest be36af8f and all 87 blobs, and read-only Docker availability (29.7.2).
|
||||
|
||||
| New step | Passed | Failed | Skipped | Exit |
|
||||
|---|---:|---:|---:|---:|
|
||||
| binding.test.mjs whole | 15 | 0 | 0 | 0 |
|
||||
| recover.test.mjs whole | 5 | 0 | 0 | 0 |
|
||||
| Discord whole | 192 | 0 | 0 | 0 |
|
||||
| WebUI whole | 314 | 4 | 1 | 1 |
|
||||
|
||||
Carried unchanged under sage-2294: 32 singles 304/304, processing-control 61/61,
|
||||
bus 646/646 and CLI 761/761, from gate-attempt-eb3/. These are explicitly carried
|
||||
results, not fresh runs on e6800380d.
|
||||
|
||||
Four WebUI failures are retained in gate-logs/package-webui.log:
|
||||
- board-held.test.mjs:91: processing's config/state GETs omit redirect:manual.
|
||||
- board-held.test.mjs:194 and send-browser.test.mjs:58: processing's independent
|
||||
hash/focus/interval refresh continues to read after the shell signs out.
|
||||
- shell.test.mjs:104: the added control bar makes the short desktop page 1095px
|
||||
high at a 1000px viewport. Its new height is not accounted for by the existing
|
||||
frame/sidebar/footer layout.
|
||||
|
||||
Static diagnosis: processing.js ignores the existing body.signed-out state and
|
||||
mosaic-signed-out event that app.js and bus.js already share. Its GETs also lack
|
||||
manual redirect handling. processing.css adds an external bar while shell.css's
|
||||
frame minimum and sidebar heights still subtract only bar-h and foot-h. These
|
||||
are product integration defects; no assertions were weakened and no fix was
|
||||
made after the failed step. Sage received the table and recommendations.
|
||||
|
||||
One TAP skip: the editor rebuild requires exactly esbuild 0.28.2; installed
|
||||
shared node_modules has 0.28.1. Full skip line is in tap-skips.json. No shell
|
||||
suite ran, so there are no ^skip shell lines (skip-lines.txt remains empty).
|
||||
No tasks/seat/project/library suite, shell Docker workload or mutant ran after
|
||||
WebUI failed. Parent runner exited, EXIT cleanup completed, cwd node/chromium
|
||||
sweep returned [] at 01:29:00Z, source tree is clean, and slot returned to Sage.
|
||||
No retries or source edits were made. This is not a passing gate.
|
||||
|
||||
## Console shared sign-out and bounded desktop layout (sage-2317)
|
||||
|
||||
Commit 8e1f55c1db6a096e227b5114cd13a7142493b112, fast-forward on e6800380d, changes only the
|
||||
processing assets and their owning two tests. Current 87-path manifest:
|
||||
d3b014e6a0e9e547a29ae780eb27b1c0e57cc7b0e73dc49954abbff623929f41.
|
||||
|
||||
All three processing fetch sites use redirect:manual. responseBody dispatches
|
||||
the existing mosaic-signed-out event for an opaque redirect, or its signIn
|
||||
variant for exactly 401/sign-in required. No independent sign-out state is
|
||||
added: refresh/action guards read body.signed-out. The existing event cancels
|
||||
the interval, invalidates pending reads, clears stale state and disables
|
||||
writes. In-flight answers cannot restore state after sign-out. Initial work
|
||||
waits for DOMContentLoaded because this deferred asset precedes app.js in the
|
||||
HTML and needs its shared sign-out listener registered first. app.js/bus.js
|
||||
and their proof baselines remain untouched.
|
||||
|
||||
The CSS keeps the control bar in normal document flow. On desktop, the frame
|
||||
flexes into the remaining viewport rather than imposing another full-height
|
||||
minimum below the bar. Fixed navigation retains the shell's command/footer
|
||||
bounds and existing responsive column widths. Mobile retains natural flow.
|
||||
No short-page, sidebar or footer assertion is changed; rendering remains to
|
||||
be verified on the next authorized gate, not claimed from static checks.
|
||||
|
||||
The VM fixture models the existing body class, document events and DOM ready;
|
||||
its original assertion lines are unchanged. Six new browser cases cover config,
|
||||
state and POST opaque redirects and sign-in-required replies, manual redirect
|
||||
requests, interval cancellation and no subsequent fetch from forced refreshes
|
||||
or clicks. KS57 removes manual redirect from the config GET; KS58 removes the
|
||||
refresh signed-out guard. Both name the config-redirect browser case as their
|
||||
killer. Static preflight: 73 exact-once sites / 0 errors. Syntax checks pass;
|
||||
no owning test or mutant has run on this commit.
|
||||
|
||||
A product blob changes, so sage-2317/mos-1200 carries nothing. The next CLEAR
|
||||
runs all changed files singly, all nine package suites, all ten shell suites
|
||||
with Docker available and all 73 mutants. The editor rebuild's esbuild0.28.1
|
||||
vs pinned0.28.2 skip remains host drift and is reported separately. Earlier
|
||||
failed attempts remain immutable in gate-attempt-* directories.
|
||||
|
||||
## Rebase onto row 202 (sage-2329)
|
||||
|
||||
New branch rocko/row191-stoprun-ddfb-20261022, tip eafcbd3e7d6ef6e259fb59d87a7b0857b42337aa,
|
||||
base ddfb29af7. The prior published branch is preserved; no force-push, delete
|
||||
or old-name reuse. Six Rocko commits rebased, with one manual conflict in
|
||||
scripts/mosaic's human CLI case: prepend halt|resume|processing and retain
|
||||
row 202's appended access. packages/cli/src/cli.mjs auto-merges retain the
|
||||
access import/branch and all three processing commands. CLI README retains
|
||||
both the Access and Instance processing switch sections.
|
||||
|
||||
The full old/new tip diff has 15 paths (base plans/queue/logs, row 202's new
|
||||
access files and its three shared CLI paths), 2327 insertions/32 deletions.
|
||||
Within row 191's owned paths only CLI README, cli.mjs and scripts/mosaic differ,
|
||||
with the row 202 additions. rebase-ddfb-log.txt, rebase-ddfb-tip-diff.stat,
|
||||
rebase-ddfb-owned-diff.stat and rebase-ddfb-range-diff.txt retain exact evidence.
|
||||
|
||||
Manifest fd0f2740b1cccadaaad8c613640fdddb0497cdab33ec2c0fd11debf3cae6c4ee, 87 candidate paths. The package command lists were
|
||||
regenerated from this base; CLI includes access.test.mjs. All 73 mutant sites
|
||||
must preflight again. No tests ran, no result carries, and the complete gate
|
||||
waits for publication and Sage CLEAR after Megavolt's debug run.
|
||||
|
||||
Sage-2333 published eafcbd3e7. Their per-file patch-id comparison matches
|
||||
the prior six-commit range except the exactly ruled scripts/mosaic dispatch
|
||||
line; binary/secrets/attribution checks pass. Gate remains unrun pending CLEAR.
|
||||
|
||||
## eafcbd3e7 full gate stopped on timer fixture attribution (sage-2339)
|
||||
|
||||
Run 2026-10-22T02:18:04Z–02:19:25Z. HEAD, 87 blobs and fd0f2740 manifest
|
||||
verified. Short mktemp TMPDIR /mnt/storage/scratch/tmp/gxbC; named Compose
|
||||
project rocko-r191-gate. Nothing carried. 32 changed files completed:
|
||||
313 passed, 6 failed, 0 skipped. All six new processing-browser sign-out
|
||||
cases stop at their common precondition, processingTimers length2 vs1.
|
||||
The original two browser cases pass. Their recorder attributes every 5000ms
|
||||
interval to processing, but app.js:307 already arms its own freshness timer.
|
||||
The intended processing interval is at processing.js:87. These new cases have
|
||||
not yet exercised sign-out or killed KS57/KS58, so no behavior proof is claimed.
|
||||
|
||||
The gate stopped at this first red step. No package, shell, Docker workload
|
||||
or mutant ran; the esbuild rebuild was not reached. Runner exited, EXIT cleanup
|
||||
completed and cwd node/chromium sweep returned [] at 02:19:34Z. No source edit
|
||||
or retry followed the failure. Sage received the exact failure and a proposed
|
||||
test-only caller-attribution filter with the assertions unchanged. This is a
|
||||
fixture error, not a passing gate or a declared flake.
|
||||
|
||||
## Timer recorder attribution correction (sage-2345)
|
||||
|
||||
Commit 4db8b57785895bc62f5f015468a69515da7fc77f, fast-forward on eafcbd3e7, is a one-line
|
||||
test-only correction in processing-browser.test.mjs. Cause: app.js:307's
|
||||
freshness interval is also 5000ms, and the recorder incorrectly counted it as
|
||||
a processing interval. The recorder now requires the processing.js caller
|
||||
stack. Every assertion remains byte-identical, including exactly one timer
|
||||
before sign-out and zero after it. A second 5000ms interval from processing.js
|
||||
would still be counted and fail the exactly-one assertion. No product byte
|
||||
changes, and no test or mutant ran for this correction.
|
||||
|
||||
Manifest ffb46fbdb875f97ff2eb7a7bed8bf6d5c4df53b8a63c738ffcde5fc5f6b7f6ad, 87 paths. Full restart after publication and CLEAR,
|
||||
with nothing carried. Sage-2345 counts the timer-fixture red as the first in
|
||||
this 191 window; any second red stops and is escalated to Mos. Previous logs
|
||||
are retained in gate-attempt-eaf/.
|
||||
|
||||
## Full gate on 0f8235277: second red (sage-2351)
|
||||
|
||||
Candidate: 0f8235277e1fc21588edb6f2931d66c8e154a400 on ddfb29af7.
|
||||
Manifest: ffb46fbdb875f97ff2eb7a7bed8bf6d5c4df53b8a63c738ffcde5fc5f6b7f6ad.
|
||||
No prior result carried. Start 2026-10-22T02:33:41Z; failed-step exit
|
||||
2026-10-22T02:46:17Z. Fresh TMPDIR /mnt/storage/scratch/tmp/giNF.
|
||||
Compose project rocko-r191-gate, trapped ownership-checked network cleanup.
|
||||
|
||||
| Step | Passed | Failed | Skipped | Exit |
|
||||
|---|---:|---:|---:|---:|
|
||||
| 34 changed test files singly | 330 | 0 | 0 | 0 |
|
||||
| processing-control | 61 | 0 | 0 | 0 |
|
||||
| bus | 646 | 0 | 0 | 0 |
|
||||
| cli (including access) | 783 | 0 | 0 | 0 |
|
||||
| discord | 192 | 0 | 0 | 0 |
|
||||
| webui | 323 | 1 | 1 | 1 |
|
||||
| tasks, seat, project, library | not run | — | — | — |
|
||||
| all ten scripts/test-*.sh | not run | — | — | — |
|
||||
| all 73 mutants | not run | — | — | — |
|
||||
|
||||
Singles sum independently confirmed with awk over all 34 TAP logs.
|
||||
The sole failure is shell.test.mjs:380 in the full shell-browser test:
|
||||
scrollHeight 1062 vs innerHeight 1000. Full evidence is
|
||||
gate-logs/package-webui.log (not ok 271, line 1626).
|
||||
All prior manual-redirect/sign-out failures and eight processing-browser cases
|
||||
pass, but the desktop short-page geometry is still wrong. No passing layout
|
||||
or full-gate claim is made.
|
||||
|
||||
Known host-drift TAP skip: editor rebuild requires exactly esbuild 0.28.2,
|
||||
while shared node_modules has 0.28.1. Full line is in tap-skips.json. This skip
|
||||
is not counted as a red. No shell suite ran, so skip-lines.txt has no ^skip
|
||||
shell line. No Docker test workload or mutant started.
|
||||
|
||||
Stopped at the first failed step without retry or product edits, per sage-2351.
|
||||
This is the second red in the 191 window and is with Sage for Mos escalation
|
||||
under mos-0823. Parent runner exited; EXIT cleanup completed. Cwd-scoped node
|
||||
and chromium sweep returned [] at 02:47:14Z, product worktree is clean, and the
|
||||
slot was returned. No in-review move was made.
|
||||
|
||||
|
||||
### mos-1248 / sage-2363: desktop viewport allocation (unrun static candidate)
|
||||
|
||||
The second red on 0f8235277 was shell.test.mjs:380 (1062 versus 1000).
|
||||
The shell gives .frame a viewport-based minimum at packages/webui/src/public/shell.css:135;
|
||||
processing.css:12-14 replaced that minimum but used a column flex body with only
|
||||
min-height:100vh, so the new bar was not allocated a bounded share of the viewport.
|
||||
The desktop body now uses explicit grid rows for the command bar, controls, optional
|
||||
banner, frame and footer. The frame row is minmax(min-content,1fr): it takes the
|
||||
remaining short-page space while long content may grow the document. No clipping,
|
||||
overflow suppression, fixed body height, test viewport change or assertion removal.
|
||||
The existing shell.test.mjs:380 equality stays byte-identical. Added assertions
|
||||
check actual processing-bar visibility, positive height, viewport placement and
|
||||
no document scroll at 1440x1000; the existing 1440x420 long-page checks remain.
|
||||
KS59 reverts processing.css to 0f8235277's bytes and must fail that same shell
|
||||
browser test. Total: 74 planned mutants. No test or mutant has run on this fix.
|
||||
Webby must statically review this delta before Sage opens the full no-carry gate.
|
||||
|
||||
|
||||
### Correction after Webby B1 (webby-0104, 2026-10-22 03:00Z)
|
||||
|
||||
The cause stated in the mos-1248/sage-2363 note above is withdrawn.
|
||||
processing.css loads after shell.css and already overrides the frame minimum.
|
||||
With the tree fixed and inspector hidden, the remaining floor is the Board's
|
||||
main content. The grid min-content row preserves that floor, so 5cd32f4c1
|
||||
is not an accepted layout fix. Its static review is CHANGES at Webby's
|
||||
REVIEW-layout.md, commit e2e7c60e5. No run established a new layout measurement.
|
||||
Next step requires a granted fixture probe for main/control heights and an
|
||||
owner layout ruling; no further product change or gate is authorized yet.
|
||||
Existing shell assertions remain unchanged.
|
||||
|
||||
|
||||
### Static nav placement: mos-1249/mos-1250, sage-2383/2385/2386, webby-0106/0108
|
||||
|
||||
Fast-forward on 5cd32f4c1; the rejected grid is removed. The unchanged labeled
|
||||
region is now the first child of nav.tree, before its Console h2. The banner
|
||||
stays at its original body location with every id, role, label and shipped
|
||||
string unchanged. No processing.js change and no runtime reparenting.
|
||||
At >=760 the tree is the same fixed box, with the same width breakpoints and
|
||||
ordinary overflow:auto. The controls are non-shrinking and non-sticky; they
|
||||
scroll with overflowing nav links (sage-2386 withdraws persistent visibility).
|
||||
Below760 the region takes a full flex line before Console/Projects. Its mobile
|
||||
location changes from before .frame to first in the static nav; the banner
|
||||
therefore precedes it in a halted mobile view. Banner behavior itself is unchanged.
|
||||
Specificity (0,3,0) resets .btn margin/order and .small margin/basis, leaving
|
||||
the shell rules unchanged. No content is shortened and no overflow is hidden.
|
||||
|
||||
Correct height arithmetic (Webby B1): page=max(innerHeight,86+C), where C is
|
||||
main content height. Controls consume space within the existing nav box, not
|
||||
another body row. The old failing layout was max(innerHeight,86+barHeight+C).
|
||||
The halted banner remains a body row; Sage tracks that separately.
|
||||
|
||||
Candidate-test design change at processing-ui.test.mjs:67 under mos-1250.
|
||||
Old assertion:
|
||||
```js
|
||||
assert.ok(html.indexOf('id="processing-controls"') < html.indexOf('<div class="frame">'));
|
||||
```
|
||||
New assertion text:
|
||||
```js
|
||||
const controlsAt = html.indexOf('id="processing-controls"');
|
||||
const mainAt = html.indexOf('<main id="main"'), inspectorAt = html.indexOf('<aside class="inspector"');
|
||||
assert.ok(controlsAt >= 0 && controlsAt < mainAt, 'controls are outside and before #main');
|
||||
assert.ok(!(inspectorAt < controlsAt && controlsAt < html.indexOf('</aside>', inspectorAt)),
|
||||
'controls are outside aside.inspector');
|
||||
```
|
||||
Reason: the bar moves into the frame's first column under mos-1249 while
|
||||
remaining outside route-specific #main and aside.inspector. This is a change
|
||||
to our unlanded candidate assertion, not a landed assertion.
|
||||
|
||||
All existing shell assertions stay unchanged. Added bar-inside-tree assertion
|
||||
and diagnostic height output. New processing browser coverage checks 400px
|
||||
Stop order=0, visibility, focus and position above Projects; also on-load
|
||||
visibility/focus at1440x1000,1440x420,800x420. Debug logs report controls/tree
|
||||
heights at those desktop sizes and main height; Sage judges controls over half
|
||||
the 334px nav at1440x420 before the window. No run yet.
|
||||
|
||||
Mutant delta: KS59 now reverts the complete layout (HTML and CSS together);
|
||||
KS60 hides the controls; KS61 moves them into #main; KS62 drops the (0,3,0)
|
||||
button reset. Total77. Every new site has its named killing test.
|
||||
|
||||
|
||||
### sage-2394 debug on published da4334806 (2026-10-22)
|
||||
|
||||
03:14:37Z–03:14:53Z: shell.test.mjs whole 5/5, processing-browser.test.mjs
|
||||
whole 9/9, 0 failures/skips. awk sum14/0/0. Original shell short-page and
|
||||
long-page assertions pass unchanged. Logs/results and empty cwd sweep are in
|
||||
debug-da4334806/. Slot clear sent Sage/Webby03:15:11Z after sweep.
|
||||
Controls/tree/main heights: read-only shell1440x1000 212.5/914/914; writable
|
||||
processing fixture1440x1000 127/914/914;1440x420 127/334/907.734375;800x420
|
||||
127/334/1131.65625. Stop visible/order0/focusable/aboveProjects at400x1000.
|
||||
Writable127 is below half the334 nav. The read-only212.5 height at1000 was
|
||||
flagged for Sage's usability call; that state was not measured at420, so no
|
||||
claim is made that all states use less than half the short nav.
|
||||
No mutants or full gate run under this debug grant. Await separate CLEAR.
|
||||
|
||||
|
||||
### R6 accepted: sage-2399 / webby-0111 (2026-10-22 03:16Z)
|
||||
|
||||
Sage accepts the short-nav usability tradeoff. Writable controls occupy38%
|
||||
of the334px nav at1440x420. The read-only recovery note makes that about64%,
|
||||
inferred from the measured212.5px height at the same width. The region is
|
||||
non-sticky and scrolls away; Projects was already below the fold at that size
|
||||
before191. Webby confirmed the debug TAP and has no window blocker. Sage
|
||||
tracks the read-only note length with the halt-banner follow-up; no change
|
||||
to this row.
|
||||
|
||||
Do not rebase or run now. Row273 lands first. On Sage's landing message,
|
||||
rebase the candidate including the two latest layout commits onto the new
|
||||
refactor under a new branch name (mos-1086), then provide range-diff,
|
||||
patch-ids and the refreshed88-product-path manifest. The full no-carry
|
||||
window requires a separate Sage CLEAR on that published tip.
|
||||
|
||||
|
||||
### Rebase after row273: sage-2402 (2026-10-22 03:42Z)
|
||||
|
||||
New branch rocko/row191-stoprun-86f9-20261022, tip
|
||||
0fa7bf421fc153a226b5f06ed484e089e48e23ba, on86f95bb27. All10 commits
|
||||
rebase cleanly and range-diff pairs each with '='; no conflict resolution.
|
||||
All88 per-file patch-ids match the old candidate. Whole product diff stable
|
||||
patch-id before/after:0846823aefe165363601a6c68134ad46cc701c73.
|
||||
87 product blobs are identical; only docs/TOOLS.md changes because the new
|
||||
base carries row273's --send/--no-sso usage and three explanatory F39 lines.
|
||||
191's TOOLS hunks stay unchanged. New88-path manifestSHA256:
|
||||
0bcbf337433f6d46d809ee1dcaf8a5a8503160403d273cf73d6dac64adef3c02.
|
||||
Range-diff, per-path patch-ids and TOOLS comparison are rebase-86f9-* files.
|
||||
Static77-site exact-once preflight has0errors. No tests run, nothing carried
|
||||
into the required full window; await Sage publication and separate CLEAR.
|
||||
|
||||
|
||||
### Full no-carry window on0fa7bf421: stopped at SR08 (sage-2407/2418)
|
||||
|
||||
03:44:56Z–04:01:29Z. All35touched files passed336/0/0; nine packages
|
||||
passed2403/0 with1known editor/esbuild skip. Every shell exited0:425selftests
|
||||
plus156queue node tests passed; the canonical-root queue check and two
|
||||
explicit live-provider exclusions are saved in skip-lines.txt. See
|
||||
GATE-RESULTS.md for per-suite counts and mutant table.
|
||||
SR01–SR07 killed. SR08 timed out at90seconds without named assertion failure
|
||||
(TAP header only); runner restored all88candidate blobs and stopped. The69
|
||||
later mutants did not run. SR08 is not counted as killed. Cwd sweep[] and
|
||||
owned network cleanup completed; slot clear sent Sage04:02:12Z.
|
||||
Saved mutation, timeout policy lines64–89 and shell/skip table sent Sage in
|
||||
response to sage-2418. No source edit or retry; no in-review move. Await
|
||||
Mos ruling and explicit Sage CLEAR before any control/replay/resume.
|
||||
|
||||
|
||||
### SR08 timeout policy: mos-1259 / sage-2423 (2026-10-22 04:07Z)
|
||||
|
||||
Cause confirmed by Mos: runtimes.mjs:333 loops on now() <= deadline, while
|
||||
stop-run.test.mjs:106-108 gives the second host a fixed fake clock with real
|
||||
sleep. Removing the join branch enters the stop loop without advancing that
|
||||
clock. Original SR08 log contains TAP header only; original timeout record
|
||||
stays immutable. Mos permits separate classification as killed-by-timeout
|
||||
only after the unmutated same whole test file passes the same90second
|
||||
harness in this resumed run. **Control result: not run; awaiting CLEAR.**
|
||||
No assertion kill is claimed for SR08.
|
||||
|
||||
Runner review requested from Dewey, Sage copied. SHA256 of run-mutants.py:
|
||||
fe0d360f86d86ab2df967da5338479c593c27c88d06eb38a1389d99a08489369. Diff:runner-mos1259.diff; baseline:
|
||||
run-mutants.before-mos1259.py. Shared execution harness uses90seconds, own
|
||||
process groups, verified descendant cleanup and restored source bytes. A
|
||||
control must pass every test with exit0, no not-ok/skip/cancel/fail. Its record
|
||||
binds invocation id, candidate HEAD/manifest and test hash; all88candidate
|
||||
blobs verified before and after control, and before each mutation. The
|
||||
approved historical SR08 classification and69-site SR09+resume use separate
|
||||
files so the original seven kills and timeout evidence remain intact.
|
||||
Assertion kills and timeout kills have separate counters. No control for the
|
||||
same file in this invocation means a timeout stops; survivors and invalid
|
||||
failures stop too. Every timeout gets a cause/control record before review.
|
||||
|
||||
No product/test change, no control/mutant execution. Wait for independent
|
||||
runner approval and Sage CLEAR. Green suites carry only on unchanged0fa7bf421
|
||||
and manifest0bcbf337. Test clock fix is deferred to Vikunja502 as Mos ruled.
|
||||
|
||||
|
||||
### Independent runner approval: dewey-0789 (2026-10-22 04:11:20Z)
|
||||
|
||||
Dewey APPROVES the frozen fe0d360f86d86ab2df967da5338479c593c27c88d06eb38a1389d99a08489369
|
||||
runner, static only. Use only the proposed --control-file stop-run.test.mjs
|
||||
--adjudicate-timeout SR08 --resume-from SR09 invocation. A positional-id or
|
||||
full run would overwrite the original evidence and is not this resume.
|
||||
|
||||
Report the complete tally as7 prior assertion kills (old status killed),
|
||||
plus SR08 separately adjudicated by timeout after control passes, plus69
|
||||
resumed sites. The summary file counts only this invocation. No control or
|
||||
resume result exists yet; Sage CLEAR remains required.
|
||||
|
||||
Classification change: the reviewed runner recognizes indented not-ok lines
|
||||
with ^\s* rather than only top-level lines. Controls therefore reject nested
|
||||
failures too, and mutant assertion kills still require the named pattern in
|
||||
a failing test name. SR01–SR07 evidence was classified by the original runner.
|
||||
|
||||
Historical SR08 has no HEAD field. Its binding to0fa7bf421 relies on the saved
|
||||
commit.txt/log timestamps and the new candidate HEAD+88blob check, not a
|
||||
retroactively invented HEAD field in the old result. Preserve this provenance
|
||||
in the adjudication report. Any newly detected timeout needs a concrete cause
|
||||
before review; the generic placeholder is not accepted review evidence.
|
||||
|
||||
|
||||
### Control and resume: sage-2427, stopped on KS43
|
||||
|
||||
Control/resume began04:13:44Z; last saved mutant result2026-10-22T04:14:08Z; process
|
||||
absence confirmed04:14:56Z and slot returned to Sage. Whole unmutated
|
||||
stop-run.test.mjs control passed12/12, exit0, no not-ok, no skips/cancels,
|
||||
in0.459seconds. The reviewed runner adjudicated original SR08 separately
|
||||
as killed-by-timeout under mos-1259. The immutable original SR08 row/log
|
||||
stay unchanged; adjudication binds the control, exact cause, current HEAD
|
||||
and manifest. Historical HEAD provenance remains as Dewey N4 described.
|
||||
|
||||
Resume stopped at KS43, not retried. It removes the inner processingHTTP
|
||||
origin check and its selected outer-route test passed1/1(exit0). Saved
|
||||
evidence:resume-mutant-logs/KS43.tap and resume-mutant-results.json.
|
||||
Tally:7prior assertion kills +49resumed assertion kills =56assertion kills;
|
||||
1historical timeout kill;1survivor(KS43);19unrun. No new timeouts or placeholder
|
||||
causes. All88candidate blobs restored and resume cwd sweep[]; no product or
|
||||
test change. No in-review move. Sage and Mos notified directly.
|
||||
|
||||
Static diagnosis only: KS43 selects processing.test.mjs:52's outer-server
|
||||
fence test. The unchanged direct-handler test at:95 already isolates this
|
||||
origin guard from serve's outer refusal. Its pattern was not selected. Sent
|
||||
recommendation to Sage/Mos to correct only the mutant's named killer after
|
||||
their ruling, then run KS43+19remaining. No metadata edit or rerun authorized
|
||||
yet; no claim the direct-handler test kills it until the run proves it.
|
||||
|
||||
|
||||
### KS43 metadata retarget: mos-1261 (2026-10-22 04:16:41Z)
|
||||
|
||||
Original pattern `processing write fences refuse` selected processing.test:52.
|
||||
KS43 survived with1passing test and no writer call, preserved in
|
||||
resume-mutant-results.json and resume-mutant-logs/KS43.tap. The serve-level
|
||||
test cannot isolate the handler guard: outer fences and later refusal can
|
||||
mask its removal. No product defect or new kill is claimed from static read.
|
||||
New pattern: `processing handler independently refuses wrong origin and cross-site before its writer`,
|
||||
the existing direct processingHTTP test at:95-107 with valid user/site/content
|
||||
headers, asserted403 and no writer call. Only KS43's pattern changed in the
|
||||
77-site table; product/test/mutation bytes unchanged. Generator mirrors it.
|
||||
|
||||
TableSHA256:5dbc8308c995e1974d6da63b3daa967896d822154eab38a8bb96154be667edf5. Diff:mutant-table-mos1261.diff,
|
||||
baseline:all-mutants.before-mos1261.json. Dewey must independently review
|
||||
this metadata diff before the next run. Static audit of all19unrun sites:
|
||||
mutant-pattern-audit-mos1261.md; no other serve-level/direct-handler mismatch
|
||||
with an existing direct test warrants retargeting. KS44 body-bound case has
|
||||
no direct-handler counterpart; its oversized whitespace+{} case is valid
|
||||
JSON without the bound, so it retains the existing pattern.
|
||||
|
||||
Carry56assertion kills+1timeout kill and green suites only on unchanged
|
||||
0fa7bf421. Next approved scope is KS43+19sites after review and Sage CLEAR.
|
||||
No run authorized by this metadata change.
|
||||
|
||||
|
||||
### Retarget review APPROVE and C1 archive: dewey-0791 (2026-10-22 04:19:55Z)
|
||||
|
||||
Dewey approves table5dbc8308 and the19-site audit, conditional on preserving
|
||||
the first resume evidence before any next run. Archive:mos-1261-archive/,
|
||||
full per-file inventory:mos-1261-archive/sha256-manifest.txt.
|
||||
|
||||
- `resume-mutant-results.json` SHA256 `f5725ef26cdb488441285081fadaa9ce4a3e7d390ab2a1c6d04acfd27f105fc6`
|
||||
|
||||
- `resume-mutant-logs/KS43.tap` SHA256 `c778ee33edb667f795ab560a328fd5c1c04ef6acf99d7b79c364f6302cf29864`
|
||||
|
||||
- `unmutated-control.json` SHA256 `ee9ddcd69d24276165abe1185f159a2737157fcae8fd37f731768e9324b765e5`
|
||||
|
||||
- `SR08-timeout-adjudication.json` SHA256 `214a04696d3329ef67bf10578daf4d251599816fd246bc31abecae1a6230e1e0`
|
||||
|
||||
The50-row result, all first-resume logs and control/adjudication records
|
||||
were copied byte-for-byte. Original paths remain untouched too. A separate
|
||||
retarget-mos1261/ directory holds byte-identical approved runnerfe0d360f,
|
||||
reviewed table5dbc8308 and identical candidate/commit files. The proposed
|
||||
--resume-from KS43 invocation there selects20sites and writes its own logs
|
||||
and results without overwriting either original or archive. No control flag
|
||||
is needed for these tests; any timeout without a same-file control stops.
|
||||
Final tally will combine7old assertionkills +49first-resume assertionkills +
|
||||
SR08separate timeout adjudication +20retarget results. Still no run; awaiting
|
||||
Sage CLEAR. Product/test bytes remain unchanged.
|
||||
|
||||
|
||||
### KS43 retarget run: sage-2436, stopped on KS44 invalid mutation
|
||||
|
||||
Run began04:21:06Z; KS43 is killed by the named direct-handler assertion.
|
||||
KS44 then fails import with SyntaxError Unexpected token catch at
|
||||
processing-http.mjs:40. The failing TAP name is the test file, not the
|
||||
selected test, so runner records invalid-failure and stops without a kill.
|
||||
Evidence remains in retarget-mos1261/console.log,resume-mutant-results.json
|
||||
and resume-mutant-logs/KS43.tap,KS44.tap. No original evidence overwritten.
|
||||
|
||||
Cause: our KS44 mutation uses `// no body bound` to replace an inline
|
||||
`if (size > 1024) throw Error();` in a one-line for-await body. The comment
|
||||
swallows the remaining chunks.push and closing brace. This is malformed
|
||||
mutation metadata, not an observed product failure. No metadata correction
|
||||
or retry yet. Proposed to Sage/Mos: replace only the new text with `void 0;`,
|
||||
then independent review and a new CLEAR for KS44+18 on unchanged candidate.
|
||||
|
||||
Current tally57assertionkills (7+49+1),1SR08timeoutkill,1invalid(KS44),18unrun.
|
||||
Cwd sweep[] and all88candidate hashes restored04:21:30Z; slot returned and
|
||||
Sage/Mos notified. No in-review move. Wait for ruling before any edit or run.
|
||||
|
||||
|
||||
### KS44 syntax metadata correction: mos-1264 (2026-10-22 04:22:16Z)
|
||||
|
||||
Only KS44.new changes from `// no body bound` to `void 0;`. Old site,
|
||||
test and pattern stay unchanged. The invalid import result remains in
|
||||
retarget-mos1261/resume-mutant-results.json and resume-mutant-logs/KS44.tap.
|
||||
TableSHA256:85d9a7b13ad510e840f258d892afbfce393cefbc4006e1f808605b83adc84570. Diff:mutant-table-mos1264.diff;
|
||||
baseline:all-mutants.before-mos1264.json. Generator mirrors the one field.
|
||||
|
||||
Authorized syntax-class preflight applied KS44–KS62 individually in scratch,
|
||||
ran node --check on each JavaScript file, and restored before advancing.
|
||||
All15JS checks passed; no other replacement fix was needed. KS59–KS62
|
||||
change only HTML/CSS and are explicitly asset-only/not-applicable for the
|
||||
Node parser; Mos/Sage informed, no false JS syntax pass claimed. All19
|
||||
mutations restored and88candidate hashes match. Per-site logs/results and
|
||||
restoration record:syntax-mos1264/. No behavioral test or assertion-mutant
|
||||
run occurred. Dewey must review table diff and syntax results before next
|
||||
Sage CLEAR for KS44+18. Carry57assertionkills+1timeoutkill only on unchanged
|
||||
0fa7bf421. No product or test change.
|
||||
|
||||
|
||||
### dewey-0795 approval: syntaxfixed-mos1264 staged (unrun)
|
||||
|
||||
Dewey APPROVES table85d9a7b1 and syntax preflight, static only. Conditions
|
||||
met:syntaxfixed-mos1264/ holds byte copies of runnerfe0d360f, table85d9a7b1,
|
||||
commit.txt,candidate.txt,candidate-manifest.sha256. Full SHA256 and byte-cmp
|
||||
list is syntaxfixed-mos1264/byte-copy-check.txt. Invocation after Sage CLEAR:
|
||||
python3 syntaxfixed-mos1264/run-mutants.py --resume-from KS44 (19sites).
|
||||
All previous invalid/survivor evidence stays untouched in its original dirs.
|
||||
No behavioral run occurred during preparation.
|
||||
|
||||
|
||||
### Final19 resume stopped at KS54: sage-2444 (2026-10-22)
|
||||
|
||||
Run began04:27:45Z; KS44–KS53 are10named assertion kills. KS54 then timed out
|
||||
at90.073seconds. Its log contains the expected dependency-list assertion:
|
||||
"the runtime refuses an invalid adapter and closes a valid one" at
|
||||
bus/tests/tasks.test.mjs:263. The mutation drops processing from runtime.mjs
|
||||
tasks factory deps. The test opens runtime then asserts before close at:265;
|
||||
a failing assertion skips the close and leaves Node alive. No same-file
|
||||
control ran in this invocation, so this is timeout-without-control, not a
|
||||
kill. No assertion or timeout kill is claimed for KS54.
|
||||
|
||||
Raw resume result's generic cause sentence says a control passed, which is
|
||||
incorrect: its control:null and timeout-without-control status establish the
|
||||
opposite. The raw evidence remains unchanged; the concrete cause and
|
||||
correction are in syntaxfixed-mos1264/KS54-timeout-report.json. This explicit
|
||||
report replaces the placeholder as explanation without altering the log.
|
||||
|
||||
Current tally67assertionkills(7+49+1+10),1adjudicatedSR08timeoutkill,
|
||||
1unqualifiedKS54timeout,8unrun. All88candidate blobs restored; cwd sweep[]
|
||||
and slot returned04:30:06Z to Sage, Mos directly copied as instructed. No
|
||||
retry, product/test edit or in-review move. Sage's disclosed syntaxcheck
|
||||
caused no busy refusal or foreign-node report. Await owner ruling/CLEAR.
|
||||
|
||||
|
||||
### Final controls/mutants GREEN: mos-1267, sage-2448, dewey-0798
|
||||
|
||||
On unchanged0fa7bf421, whole-file tasks control9/9 passed in0.815s; KS54
|
||||
rerun killed-by-timeout with the exact dependency not-ok at tasks.test:263.
|
||||
KS55–KS62 are8assertionkills. Final run04:35:32Z–04:37:17Z, cwd sweep[] and
|
||||
88hashrestore04:37:48Z; slot returned to Sage and direct handoff to Dewey/Mos.
|
||||
Final77-site tally:75assertionkills(7+49+1+10+8),2separate timeoutkills
|
||||
(SR08adjudication,KS54control+rerun). final-mutant-results.json consolidates
|
||||
latest accepted evidence by id; no prior record or log overwritten.
|
||||
GATE-RESULTS.md is the final per-suite/mutant table; prior narrative saved
|
||||
as GATE-RESULTS-before-final.md.
|
||||
|
||||
KS54 raw placeholder result saved as resume-mutant-results.raw.json before
|
||||
filling concrete cause in its review result. Same-invocation control id
|
||||
1792643732624962173; prior syntaxfixed eleven-row result file remains exact
|
||||
0ede036954ff77c21f29e1a744b21d1d9b18eb75736f78ecd1cf4ae559358911.
|
||||
Dewey independently read logs/control/restoration and confirmed tally in
|
||||
dewey-0798, explicitly not yet a manifest verdict. N6test cleanup stays
|
||||
deferred under mos-1267/Vikunja502; no candidate byte changes permitted.
|
||||
|
||||
|
||||
### Claimant review handoff
|
||||
|
||||
Queue move rocko-191-review-r1 entered in-review at revision3114, round1
|
||||
on#1689, manifest0bcbf337. Revision3115 records the expected missing-token
|
||||
request failure; command exit1. Receipt:in-review-receipt.txt. Sage posts
|
||||
the request as jarvis; Dewey owns the manifest verdict. Candidate source
|
||||
remains published0fa7bf421 with88product paths, base86f95bb27. No live
|
||||
action, push, source change or further gate authorized by this handoff.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Row279: stopRun deadline final observation
|
||||
|
||||
Scope: sage-2512, mos-1269; brief docs/plans/2026-10-22_stoprun-deadline.md.
|
||||
Base f40f2e770. Reviewer Dewey; gate owner Sage. V502 stays separate.
|
||||
|
||||
## Cause and smallest change
|
||||
|
||||
The loop's precondition now()<=deadline can skip the existing final status
|
||||
read when a sleep wakes beyond deadline. Use for(;;) so the existing
|
||||
now()>=deadline branch is the only time exit. Change that loop header only;
|
||||
all record/identity/ended exits and result mapping remain byte-identical.
|
||||
The old trailing fallback remains unreachable in the fixed loop. M1 restores
|
||||
only the old header and thereby restores the complete old fall-through path.
|
||||
|
||||
## Regression and evidence plan
|
||||
|
||||
Add one test to stop-run.test.mjs, preserving existing tests. Its fake sleep
|
||||
advances requested duration plus1ms, so the last sleep crosses the deadline.
|
||||
Its signal stub records TERM/KILL and returns success without killing the
|
||||
always-live fixture process. Expect both signals, time>7000ms and alive.
|
||||
The old loop should fail only the alive assertion with unknown.
|
||||
|
||||
Ask Sage CLEAR before any run, including the expected regression red. Then
|
||||
apply the one-line fix, run the touched whole file and processing-control
|
||||
package as granted, and kill M1 with that same named regression. Busy check,
|
||||
short scratch TMPDIR,deadDocker,owned process groups,timeouts,restore,sweep.
|
||||
No live state,units,store,provider,browser,Docker or V502 changes.
|
||||
|
||||
Packet includes base,patch,candidate hashes,regression/gate/mutant logs and
|
||||
exact source-diff check. Commit candidate and packet on the new branch only
|
||||
after authorized checks. No token is available; Sage publishes as jarvis.
|
||||
The business/CLI suites remain Sage's landing gate unless separately granted.
|
||||
|
||||
## Authorized run and result
|
||||
|
||||
Claim rev 3138, op rocko-279-start-r1. Sage granted this exact sequence in
|
||||
sage-2516 (2026-10-22T05:55:21Z). Runs: 05:56:03Z to 05:58:03Z.
|
||||
|
||||
| Step | Pass | Fail | Skip | Result |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| Regression on unfixed base | 0 | 1 | 0 | Expected assertion: unknown versus alive |
|
||||
| stop-run.test.mjs whole | 13 | 0 | 0 | Green |
|
||||
| processing-control package whole | 62 | 0 | 0 | Green |
|
||||
| M1 old loop | 0 | 1 | 0 | Killed by the named regression, unknown versus alive |
|
||||
|
||||
M1's killer is `a sleep past the stop deadline still observes an alive run
|
||||
after both signals`. It exits 1 with that assertion, never a syntax error or
|
||||
timeout. Logs and results.json retain the intentional failures separately.
|
||||
Green runs total 75 passes (the whole-file tests are also part of the package).
|
||||
No skips. All commands used a short scratch TMPDIR, a dead Docker socket and
|
||||
fixture-only configuration. Each node command ran in its own process group
|
||||
with a 120-second timeout. No timeout occurred. Final cwd sweep: empty.
|
||||
The source restoration and exact one-line product scope are checked in
|
||||
source-scope-check.json. The trailing unknown return stays unchanged and is
|
||||
unreachable after the fix. Business and CLI suites remain Sage's owner gate.
|
||||
Slot returned to Sage at 05:58:03Z. No live action or V502 change.
|
||||
@@ -0,0 +1 @@
|
||||
f40f2e770e0d8be394f1ef53b404045fd6a49c0e
|
||||
@@ -0,0 +1,35 @@
|
||||
diff --git a/packages/processing-control/src/runtimes.mjs b/packages/processing-control/src/runtimes.mjs
|
||||
index 05e439c1a..a11cb8e52 100644
|
||||
--- a/packages/processing-control/src/runtimes.mjs
|
||||
+++ b/packages/processing-control/src/runtimes.mjs
|
||||
@@ -330,7 +330,7 @@ async function ownStop(args, key, options) {
|
||||
};
|
||||
const termEnd = started + grace, deadline = termEnd + observe;
|
||||
let termTried = false, killTried = false;
|
||||
- while (now() <= deadline) {
|
||||
+ for (;;) {
|
||||
try { record = runtime(root, record.id); }
|
||||
catch {
|
||||
const present = readdirSync(directory(root, 'runtimes')).includes(record.id + '.json');
|
||||
diff --git a/packages/processing-control/tests/stop-run.test.mjs b/packages/processing-control/tests/stop-run.test.mjs
|
||||
index ed69bc111..5277ea7fe 100644
|
||||
--- a/packages/processing-control/tests/stop-run.test.mjs
|
||||
+++ b/packages/processing-control/tests/stop-run.test.mjs
|
||||
@@ -61,6 +61,17 @@ test('failed signal delivery observes alive, records no invented signals and nev
|
||||
assert.equal(f.sleeps.reduce((a,b)=>a+b,0), 7000);
|
||||
assert.deepEqual(await stopRun(f.args, f.options), result);
|
||||
});
|
||||
+test('a sleep past the stop deadline still observes an alive run after both signals', async t => {
|
||||
+ const f = setup(t); let time = 0;
|
||||
+ f.options.now = () => time;
|
||||
+ f.options.sleep = async ms => { time += ms + 1; };
|
||||
+ f.options.signal = (_r, sig) => { f.signals.push(sig); return true; };
|
||||
+ const result = await stopRun(f.args, f.options);
|
||||
+ assert.ok(time > 7000, 'the last sleep wakes past the complete stop deadline');
|
||||
+ assert.deepEqual(f.signals, ['SIGTERM', 'SIGKILL']);
|
||||
+ assert.equal(result.outcome, 'alive');
|
||||
+ assert.deepEqual(result.signals, ['SIGTERM', 'SIGKILL']);
|
||||
+});
|
||||
test('missing run and another business never borrow a pid or signal', async t => {
|
||||
const f = setup(t);
|
||||
for (const args of [{ ...f.args, run: 'missing' }, { ...f.args, business: 'other' }]) {
|
||||
@@ -0,0 +1,2 @@
|
||||
7145420aa354180689a5c84423928fc60c8123476dcfed3364c9cf4542f8c7ea packages/processing-control/src/runtimes.mjs
|
||||
7ce7a99363b987f8525f8992fd57940f4305597e870c6162fc158962f2101ff6 packages/processing-control/tests/stop-run.test.mjs
|
||||
@@ -0,0 +1 @@
|
||||
1016b85846c60dc159d49373f99ca36794a51fd9
|
||||
@@ -0,0 +1,2 @@
|
||||
packages/processing-control/src/runtimes.mjs
|
||||
packages/processing-control/tests/stop-run.test.mjs
|
||||
@@ -0,0 +1 @@
|
||||
1016b85846c60dc159d49373f99ca36794a51fd9
|
||||
@@ -0,0 +1,32 @@
|
||||
TAP version 13
|
||||
# Subtest: a sleep past the stop deadline still observes an alive run after both signals
|
||||
not ok 1 - a sleep past the stop deadline still observes an alive run after both signals
|
||||
---
|
||||
duration_ms: 40.278473
|
||||
type: 'test'
|
||||
location: '/mnt/storage/scratch/rocko-r279/packages/processing-control/tests/stop-run.test.mjs:64:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly equal:
|
||||
|
||||
'unknown' !== 'alive'
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: 'alive'
|
||||
actual: 'unknown'
|
||||
operator: 'strictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r279/packages/processing-control/tests/stop-run.test.mjs:72: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 89.021437
|
||||
@@ -0,0 +1,382 @@
|
||||
TAP version 13
|
||||
# Subtest: container admission publishes its generated name and root/runtime labels before dispatch
|
||||
ok 1 - container admission publishes its generated name and root/runtime labels before dispatch
|
||||
---
|
||||
duration_ms: 9.883733
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: container sweep sends TERM then KILL only after verifying exact id and both labels
|
||||
ok 2 - container sweep sends TERM then KILL only after verifying exact id and both labels
|
||||
---
|
||||
duration_ms: 66.167157
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: foreign labels and a replaced container id stay unknown and are never signalled
|
||||
ok 3 - foreign labels and a replaced container id stay unknown and are never signalled
|
||||
---
|
||||
duration_ms: 5.105671
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: an unverified missing container remains unknown rather than claiming a pending start ended
|
||||
ok 4 - an unverified missing container remains unknown rather than claiming a pending start ended
|
||||
---
|
||||
duration_ms: 15.538468
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a removed --rm container is settled only by its trusted owner completion receipt
|
||||
ok 5 - a removed --rm container is settled only by its trusted owner completion receipt
|
||||
---
|
||||
duration_ms: 28.897791
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: after signalling a verified container, successful empty Docker listing proves --rm removed that id
|
||||
ok 6 - after signalling a verified container, successful empty Docker listing proves --rm removed that id
|
||||
---
|
||||
duration_ms: 24.215826
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt publishes before awaiting stops, resume refuses pending, and only clear permits new work
|
||||
ok 7 - halt publishes before awaiting stops, resume refuses pending, and only clear permits new work
|
||||
---
|
||||
duration_ms: 161.569582
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a new coordinator replays a persisted halt and an unresolved start keeps clear refused
|
||||
ok 8 - a new coordinator replays a persisted halt and an unresolved start keeps clear refused
|
||||
---
|
||||
duration_ms: 1102.680874
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a new halt generation never inherits a previous quiescent status before its sweep
|
||||
ok 9 - a new halt generation never inherits a previous quiescent status before its sweep
|
||||
---
|
||||
duration_ms: 4.450121
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt aborts an active local request and prevents a subsequent effect after halt/resume
|
||||
ok 10 - halt aborts an active local request and prevents a subsequent effect after halt/resume
|
||||
---
|
||||
duration_ms: 35.831508
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: processing scopes isolate roots and cancel only the registered child group
|
||||
ok 11 - processing scopes isolate roots and cancel only the registered child group
|
||||
---
|
||||
duration_ms: 59.946603
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halted scopes never invoke their body and unscoped human work has no processing context
|
||||
ok 12 - halted scopes never invoke their body and unscoped human work has no processing context
|
||||
---
|
||||
duration_ms: 3.067699
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: host git is stopped promptly by the active scope rather than waiting for its long deadline
|
||||
ok 13 - host git is stopped promptly by the active scope rather than waiting for its long deadline
|
||||
---
|
||||
duration_ms: 151.711241
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a child leader exiting does not erase an unverified surviving group receipt
|
||||
ok 14 - a child leader exiting does not erase an unverified surviving group receipt
|
||||
---
|
||||
duration_ms: 117.914283
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: the actual nested read-only control mount refuses unlink, replacement and parent removal
|
||||
ok 15 - the actual nested read-only control mount refuses unlink, replacement and parent removal
|
||||
---
|
||||
duration_ms: 14.941666
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: the image entrypoint refuses halted and stale generations using the shared reader
|
||||
ok 16 - the image entrypoint refuses halted and stale generations using the shared reader
|
||||
---
|
||||
duration_ms: 89.020485
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt terminates a tracked stand-in and leaves honest write-once stop evidence
|
||||
ok 17 - halt terminates a tracked stand-in and leaves honest write-once stop evidence
|
||||
---
|
||||
duration_ms: 95.55672
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a TERM-resistant workload is killed after the bounded wait
|
||||
ok 18 - a TERM-resistant workload is killed after the bounded wait
|
||||
---
|
||||
duration_ms: 146.00258
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: wrong start ticks or boot identity cannot signal a live replacement
|
||||
ok 19 - wrong start ticks or boot identity cannot signal a live replacement
|
||||
---
|
||||
duration_ms: 65.844444
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a pending start cannot be reported quiescent and a second root is never signalled
|
||||
ok 20 - a pending start cannot be reported quiescent and a second root is never signalled
|
||||
---
|
||||
duration_ms: 93.749051
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: escalation still kills the tracked group after its leader exits
|
||||
ok 21 - escalation still kills the tracked group after its leader exits
|
||||
---
|
||||
duration_ms: 192.463864
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: session TERM targets its verified runner before forcing the outer namespace
|
||||
ok 22 - session TERM targets its verified runner before forcing the outer namespace
|
||||
---
|
||||
duration_ms: 100.923976
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: new runtime and stop directories sync their containing directories
|
||||
ok 23 - new runtime and stop directories sync their containing directories
|
||||
---
|
||||
duration_ms: 42.560646
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a vanished group leader with unverified surviving members stays unknown, never quiescent
|
||||
ok 24 - a vanished group leader with unverified surviving members stays unknown, never quiescent
|
||||
---
|
||||
duration_ms: 130.848284
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: persisted member identity anchors a group after the leader exited before the sweep
|
||||
ok 25 - persisted member identity anchors a group after the leader exited before the sweep
|
||||
---
|
||||
duration_ms: 124.574935
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: an unconfirmed secondary brake does not leave a verified workload running or claim success
|
||||
ok 26 - an unconfirmed secondary brake does not leave a verified workload running or claim success
|
||||
---
|
||||
duration_ms: 75.057063
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt persists across readers and resume needs the current generation
|
||||
ok 27 - halt persists across readers and resume needs the current generation
|
||||
---
|
||||
duration_ms: 9.437871
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: first initialization is explicit; missing state after control exists fails closed and halt repairs it
|
||||
ok 28 - first initialization is explicit; missing state after control exists fails closed and halt repairs it
|
||||
---
|
||||
duration_ms: 5.039685
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses invalid JSON instead of enabling work
|
||||
ok 29 - refuses invalid JSON instead of enabling work
|
||||
---
|
||||
duration_ms: 1.867281
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses unknown keys instead of enabling work
|
||||
ok 30 - refuses unknown keys instead of enabling work
|
||||
---
|
||||
duration_ms: 2.65956
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses bad timestamp instead of enabling work
|
||||
ok 31 - refuses bad timestamp instead of enabling work
|
||||
---
|
||||
duration_ms: 2.144084
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses nonboolean state instead of enabling work
|
||||
ok 32 - refuses nonboolean state instead of enabling work
|
||||
---
|
||||
duration_ms: 2.039112
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses bad generation instead of enabling work
|
||||
ok 33 - refuses bad generation instead of enabling work
|
||||
---
|
||||
duration_ms: 2.085806
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses loose mode instead of enabling work
|
||||
ok 34 - refuses loose mode instead of enabling work
|
||||
---
|
||||
duration_ms: 2.34307
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: refuses oversized file instead of enabling work
|
||||
ok 35 - refuses oversized file instead of enabling work
|
||||
---
|
||||
duration_ms: 2.916273
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a symlink leaf never follows its target; human halt replaces only the leaf
|
||||
ok 36 - a symlink leaf never follows its target; human halt replaces only the leaf
|
||||
---
|
||||
duration_ms: 4.530858
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: unsafe parent and interrupted initialization never get repaired as off
|
||||
ok 37 - unsafe parent and interrupted initialization never get repaired as off
|
||||
---
|
||||
duration_ms: 0.385291
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: repeated halt is a no-op and a different root keeps running
|
||||
ok 38 - repeated halt is a no-op and a different root keeps running
|
||||
---
|
||||
duration_ms: 7.00566
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: transition rejects nonstring identity before publishing state
|
||||
ok 39 - transition rejects nonstring identity before publishing state
|
||||
---
|
||||
duration_ms: 3.121939
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: bootstrap syncs the containing root as well as the state and control directory
|
||||
ok 40 - bootstrap syncs the containing root as well as the state and control directory
|
||||
---
|
||||
duration_ms: 36.144072
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: state ownership and bounded read guards refuse before trusting a document
|
||||
ok 41 - state ownership and bounded read guards refuse before trusting a document
|
||||
---
|
||||
duration_ms: 101.892703
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: canonical root aliases address one switch and never create independent control state
|
||||
ok 42 - canonical root aliases address one switch and never create independent control state
|
||||
---
|
||||
duration_ms: 4.179236
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: unbound run stops by verified registry identity, fixed 5s grace and 2s observe policy
|
||||
ok 43 - unbound run stops by verified registry identity, fixed 5s grace and 2s observe policy
|
||||
---
|
||||
duration_ms: 38.851257
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: same run joins the identical promise and completed repeats preserve owner and send no signals
|
||||
ok 44 - same run joins the identical promise and completed repeats preserve owner and send no signals
|
||||
---
|
||||
duration_ms: 27.976805
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: identity changed before TERM or between TERM and KILL refuses the changed signal
|
||||
ok 45 - identity changed before TERM or between TERM and KILL refuses the changed signal
|
||||
---
|
||||
duration_ms: 15.642865
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: failed signal delivery observes alive, records no invented signals and never restarts escalation
|
||||
ok 46 - failed signal delivery observes alive, records no invented signals and never restarts escalation
|
||||
---
|
||||
duration_ms: 32.602087
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a sleep past the stop deadline still observes an alive run after both signals
|
||||
ok 47 - a sleep past the stop deadline still observes an alive run after both signals
|
||||
---
|
||||
duration_ms: 31.045314
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: missing run and another business never borrow a pid or signal
|
||||
ok 48 - missing run and another business never borrow a pid or signal
|
||||
---
|
||||
duration_ms: 10.113655
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt joins an in-progress fire without another TERM or deadline and links its first receipt
|
||||
ok 49 - halt joins an in-progress fire without another TERM or deadline and links its first receipt
|
||||
---
|
||||
duration_ms: 30.224351
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a separate host joins durable first ownership without duplicate signals
|
||||
ok 50 - a separate host joins durable first ownership without duplicate signals
|
||||
---
|
||||
duration_ms: 99.128156
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: corrupt and symlinked final receipts refuse as unknown rather than signalling again
|
||||
ok 51 - corrupt and symlinked final receipts refuse as unknown rather than signalling again
|
||||
---
|
||||
duration_ms: 22.649789
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a later exit gets separate halt evidence while the original alive result stays immutable
|
||||
ok 52 - a later exit gets separate halt evidence while the original alive result stays immutable
|
||||
---
|
||||
duration_ms: 55.85559
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: root aliases join but a different root never shares the run stop
|
||||
ok 53 - root aliases join but a different root never shares the run stop
|
||||
---
|
||||
duration_ms: 45.6493
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a changed runtime record cannot replace the original stop identity after TERM
|
||||
ok 54 - a changed runtime record cannot replace the original stop identity after TERM
|
||||
---
|
||||
duration_ms: 8.507771
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: identity is rechecked at actual signal delivery, not just at the loop observation
|
||||
ok 55 - identity is rechecked at actual signal delivery, not just at the loop observation
|
||||
---
|
||||
duration_ms: 6.500324
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: task halt keeps the true Docker exit status, writes kill-switch once and leaves snapshots unchanged
|
||||
ok 56 - task halt keeps the true Docker exit status, writes kill-switch once and leaves snapshots unchanged
|
||||
---
|
||||
duration_ms: 201.141886
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt refuses direct task launch before Docker or a run record; clear allows a fresh normal result
|
||||
ok 57 - halt refuses direct task launch before Docker or a run record; clear allows a fresh normal result
|
||||
---
|
||||
duration_ms: 167.652567
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt refuses hello.sh before Docker, credentials or release alignment
|
||||
ok 58 - halt refuses hello.sh before Docker, credentials or release alignment
|
||||
---
|
||||
duration_ms: 122.654442
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt refuses verify.sh before Docker, credentials or release alignment
|
||||
ok 59 - halt refuses verify.sh before Docker, credentials or release alignment
|
||||
---
|
||||
duration_ms: 122.064969
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt refuses agent.sh before Docker, credentials or release alignment
|
||||
ok 60 - halt refuses agent.sh before Docker, credentials or release alignment
|
||||
---
|
||||
duration_ms: 120.632718
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a nonzero Docker client exit retains its unresolved runtime receipt (task)
|
||||
ok 61 - a nonzero Docker client exit retains its unresolved runtime receipt (task)
|
||||
---
|
||||
duration_ms: 93.3117
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a nonzero Docker client exit retains its unresolved runtime receipt (container-cli)
|
||||
ok 62 - a nonzero Docker client exit retains its unresolved runtime receipt (container-cli)
|
||||
---
|
||||
duration_ms: 97.234461
|
||||
type: 'test'
|
||||
...
|
||||
1..62
|
||||
# tests 62
|
||||
# suites 0
|
||||
# pass 62
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 1323.026518
|
||||
@@ -0,0 +1,32 @@
|
||||
TAP version 13
|
||||
# Subtest: a sleep past the stop deadline still observes an alive run after both signals
|
||||
not ok 1 - a sleep past the stop deadline still observes an alive run after both signals
|
||||
---
|
||||
duration_ms: 53.588908
|
||||
type: 'test'
|
||||
location: '/mnt/storage/scratch/rocko-r279/packages/processing-control/tests/stop-run.test.mjs:64:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly equal:
|
||||
|
||||
'unknown' !== 'alive'
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected: 'alive'
|
||||
actual: 'unknown'
|
||||
operator: 'strictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r279/packages/processing-control/tests/stop-run.test.mjs:72: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 100.649138
|
||||
@@ -0,0 +1,88 @@
|
||||
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: 36.122553
|
||||
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: 24.270208
|
||||
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.289288
|
||||
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: 27.743499
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a sleep past the stop deadline still observes an alive run after both signals
|
||||
ok 5 - a sleep past the stop deadline still observes an alive run after both signals
|
||||
---
|
||||
duration_ms: 26.252224
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: missing run and another business never borrow a pid or signal
|
||||
ok 6 - missing run and another business never borrow a pid or signal
|
||||
---
|
||||
duration_ms: 9.103416
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt joins an in-progress fire without another TERM or deadline and links its first receipt
|
||||
ok 7 - halt joins an in-progress fire without another TERM or deadline and links its first receipt
|
||||
---
|
||||
duration_ms: 25.918671
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a separate host joins durable first ownership without duplicate signals
|
||||
ok 8 - a separate host joins durable first ownership without duplicate signals
|
||||
---
|
||||
duration_ms: 99.095272
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: corrupt and symlinked final receipts refuse as unknown rather than signalling again
|
||||
ok 9 - corrupt and symlinked final receipts refuse as unknown rather than signalling again
|
||||
---
|
||||
duration_ms: 23.700708
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a later exit gets separate halt evidence while the original alive result stays immutable
|
||||
ok 10 - a later exit gets separate halt evidence while the original alive result stays immutable
|
||||
---
|
||||
duration_ms: 55.938599
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: root aliases join but a different root never shares the run stop
|
||||
ok 11 - root aliases join but a different root never shares the run stop
|
||||
---
|
||||
duration_ms: 42.386018
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a changed runtime record cannot replace the original stop identity after TERM
|
||||
ok 12 - a changed runtime record cannot replace the original stop identity after TERM
|
||||
---
|
||||
duration_ms: 7.168469
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: identity is rechecked at actual signal delivery, not just at the loop observation
|
||||
ok 13 - identity is rechecked at actual signal delivery, not just at the loop observation
|
||||
---
|
||||
duration_ms: 6.715559
|
||||
type: 'test'
|
||||
...
|
||||
1..13
|
||||
# tests 13
|
||||
# suites 0
|
||||
# pass 13
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 445.396108
|
||||
@@ -0,0 +1 @@
|
||||
{"id":"M1","file":"packages/processing-control/src/runtimes.mjs","old":" for (;;) {\n try { record = runtime(root, record.id); }","new":" while (now() <= deadline) {\n try { record = runtime(root, record.id); }","test":"packages/processing-control/tests/stop-run.test.mjs","pattern":"a sleep past the stop deadline still observes an alive run after both signals","required":"named assertion kill, never syntax or timeout"}
|
||||
@@ -0,0 +1,20 @@
|
||||
6d33d35302499d66036145976f8f777a4c438c6b3f5c7b613fcfa5651c49faab BUILD.md
|
||||
8521af77deed1d3b2c722f30b776159eb373df407a2a698ee116ef4174125f01 base.txt
|
||||
74efdcea480e6c089f586b7330591b66164a3aa338fb1e1a60b74f6b7b9f3b0a build.patch
|
||||
312ed511d8f1cff826ee2fd547de8368923130c7bfe06236d8e29d8655b80aaa candidate-manifest.sha256
|
||||
960c366a732e4dc20133bf490d842cc42a345fefad1432ca6eba6dbd0058f337 candidate.txt
|
||||
3099049a5acd051207fb5631b7bb6362dd84c246191009a44eca1ba696d27d73 changed-paths.txt
|
||||
960c366a732e4dc20133bf490d842cc42a345fefad1432ca6eba6dbd0058f337 commit.txt
|
||||
98a4da4e1c62f7e4ed5f50adc16cf99462a5e012e9736e658441e8eda3db894c logs/M1.log
|
||||
7f878c13be43640f501b72d36e8d9e0c8d844bc5451f969154cd75d8fab0ee42 logs/processing-control-whole.log
|
||||
62f83e511ad860d0f7da42d87e063eb691a95f29a0806b88784912374d2e7c47 logs/regression-red.log
|
||||
4de8028674472cc75f61dc78cac624c336c8db785580a189e0b705584f80d636 logs/stop-run-whole.log
|
||||
7669ce9f04189f659dbc23dae6b635d5ffac354e03d00fbae10e6fadc7e98791 mutant-plan.json
|
||||
84f1080a9689374f7000197119074e53b459641cd709745f95eb0ebe37b5ef2f reader-search.txt
|
||||
dd6b2ea95782154e1c9839bae7a6d76a60566762a2374901ae64b22f9a7f54f7 results.json
|
||||
b9408890f30a72451d9171d8ceec40eb4f4f671811b30ba74f50f178e0eb8350 run-step.py
|
||||
b8140e3919a5d342eb92c1cd34b13bbb62d6419c6a5a01a9cb4870e9a94c64ed scan-result.json
|
||||
9e5c2e8cd2b59ac4559b0d67348a2a98e59b44a71eec7359018ea7a84d7a4082 skips.txt
|
||||
43d77fe5bffa916f444a77bacda5b4ddbe78035b6ca7631378056c3158aa3253 source-scope-check.json
|
||||
37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570 sweep.json
|
||||
74b3eae937fdcc92cf2b0dd15fb67f4f6addd4a046f74e934febc565e4b5d2ed tmpdir.txt
|
||||
@@ -0,0 +1,12 @@
|
||||
packages/processing-control/tests/runtimes.test.mjs:9:const api = await import('../src/runtimes.mjs').catch(e => { if (e.code === 'ERR_MODULE_NOT_FOUND') return {}; throw e; });
|
||||
packages/processing-control/tests/runtimes.test.mjs:139: new URL('../src/runtimes.mjs', import.meta.url).href, new URL('../src/state.mjs', import.meta.url).href, root], { encoding: 'utf8' });
|
||||
packages/processing-control/tests/effects.test.mjs:60: const { listRuntimes } = await import('../src/runtimes.mjs');
|
||||
packages/processing-control/tests/coordinator.test.mjs:9:import { registerRuntime, updateRuntime, processIdentity, unregisterRuntime } from '../src/runtimes.mjs';
|
||||
packages/processing-control/tests/containers.test.mjs:62: const { updateRuntime } = await import('../src/runtimes.mjs');
|
||||
packages/processing-control/tests/task.test.mjs:12: const runtimes = new URL('../src/runtimes.mjs', import.meta.url).href;
|
||||
packages/processing-control/tests/stop-run.test.mjs:7:import { registerRuntime, updateRuntime, runtime, stopRun, sweepRuntimes, readStops } from '../src/runtimes.mjs';
|
||||
packages/processing-control/tests/stop-run.test.mjs:104: const f = setup(t), source = new URL('../src/runtimes.mjs', import.meta.url).href;
|
||||
packages/processing-control/tests/stop-run.test.mjs:148: const { processIdentity } = await import('../src/runtimes.mjs');
|
||||
packages/bus/tests/processing-runtime.test.mjs:10:import { registerRuntime, updateRuntime, processIdentity } from '../../processing-control/src/runtimes.mjs';
|
||||
packages/discord/tests/processing-stop.test.mjs:35: const { registerRuntime, updateRuntime } = await import('../../processing-control/src/runtimes.mjs');
|
||||
packages/discord/tests/processing.test.mjs:68: const { listRuntimes, sweepRuntimes, processIdentity } = await import('../../processing-control/src/runtimes.mjs');
|
||||
@@ -0,0 +1,62 @@
|
||||
[
|
||||
{
|
||||
"step": "regression-red",
|
||||
"start": "2026-10-22T05:56:03Z",
|
||||
"end": "2026-10-22T05:56:03Z",
|
||||
"exit": 1,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"skipped": 0
|
||||
},
|
||||
"failing": [
|
||||
"a sleep past the stop deadline still observes an alive run after both signals"
|
||||
],
|
||||
"log": "/mnt/storage/scratch/rocko-r279/agents/rocko/work/queue-279/logs/regression-red.log"
|
||||
},
|
||||
{
|
||||
"step": "stop-run-whole",
|
||||
"start": "2026-10-22T05:57:47Z",
|
||||
"end": "2026-10-22T05:57:47Z",
|
||||
"exit": 0,
|
||||
"counts": {
|
||||
"tests": 13,
|
||||
"pass": 13,
|
||||
"fail": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"failing": [],
|
||||
"log": "/mnt/storage/scratch/rocko-r279/agents/rocko/work/queue-279/logs/stop-run-whole.log"
|
||||
},
|
||||
{
|
||||
"step": "processing-control-whole",
|
||||
"start": "2026-10-22T05:57:47Z",
|
||||
"end": "2026-10-22T05:57:49Z",
|
||||
"exit": 0,
|
||||
"counts": {
|
||||
"tests": 62,
|
||||
"pass": 62,
|
||||
"fail": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"failing": [],
|
||||
"log": "/mnt/storage/scratch/rocko-r279/agents/rocko/work/queue-279/logs/processing-control-whole.log"
|
||||
},
|
||||
{
|
||||
"step": "M1",
|
||||
"start": "2026-10-22T05:58:03Z",
|
||||
"end": "2026-10-22T05:58:03Z",
|
||||
"exit": 1,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"skipped": 0
|
||||
},
|
||||
"failing": [
|
||||
"a sleep past the stop deadline still observes an alive run after both signals"
|
||||
],
|
||||
"log": "/mnt/storage/scratch/rocko-r279/agents/rocko/work/queue-279/logs/M1.log"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
from pathlib import Path
|
||||
import subprocess,signal,os,json,time,sys,re
|
||||
root=Path('/mnt/storage/scratch/rocko-r279');p=root/'agents/rocko/work/queue-279';logs=p/'logs';logs.mkdir(exist_ok=True);name=sys.argv[1];command=sys.argv[2:]
|
||||
start=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime());log=logs/(name+'.log')
|
||||
with log.open('w') as out:
|
||||
child=subprocess.Popen(command,cwd=root,env=os.environ,stdout=out,stderr=subprocess.STDOUT,start_new_session=True)
|
||||
try:rc=child.wait(timeout=120)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(child.pid,signal.SIGKILL);child.wait();rc=124
|
||||
text=log.read_text();counts={k:int(v) for k,v in re.findall(r'^# (tests|pass|fail|skipped) (\d+)\s*$',text,re.M)};failing=re.findall(r'^not ok \d+ - (.*)$',text,re.M)
|
||||
row=dict(step=name,start=start,end=time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()),exit=rc,counts=counts,failing=failing,log=str(log));result=p/'results.json';rows=json.loads(result.read_text()) if result.exists() else [];rows.append(row);result.write_text(json.dumps(rows,indent=2)+'\n');print(json.dumps(row));print(text[-3200:])
|
||||
if name in ['regression-red','M1']:
|
||||
ok=rc==1 and failing==['a sleep past the stop deadline still observes an alive run after both signals'] and "actual: 'unknown'" in text and "expected: 'alive'" in text
|
||||
print('EXPECTED_UNKNOWN_VS_ALIVE',ok);raise SystemExit(0 if ok else 1)
|
||||
raise SystemExit(rc)
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"secret_matches": [],
|
||||
"binary_files": []
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
None. All four TAP records report skipped 0.
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"base": "f40f2e770e0d8be394f1ef53b404045fd6a49c0e",
|
||||
"only_product_change": "while (now() <= deadline) -> for (;;)",
|
||||
"other_product_bytes_unchanged": true,
|
||||
"trailing_unknown_fallback": "unchanged; unreachable"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1 @@
|
||||
/mnt/storage/scratch/tmp/gwxq
|
||||
@@ -0,0 +1,222 @@
|
||||
# Slim packet and full-evidence location
|
||||
|
||||
This packet keeps the build note, candidate manifest and patch, summaries,
|
||||
per-run counts/durations/exit statuses, mutant definitions and cleanup
|
||||
records. Raw TAP logs, per-attempt config/log trees, diagnostic byte dumps,
|
||||
runner scripts and transient TMPDIR notes remain only in the full packet.
|
||||
References below to those files resolve at the full evidence commit.
|
||||
|
||||
Full evidence is retained permanently at commit
|
||||
`d6c924764d387ea320365c4bb160b4b57ed8d635` on
|
||||
`origin/rocko/row280-da5d-20261022`, under
|
||||
`agents/rocko/work/queue-280/`. Its 3,531-entry inventory SHA256 is
|
||||
`7d155092cb6edb547dbb7c8ba2fbabb1dd74f966c5c20965ef4fe6af2e7121b8`.
|
||||
The full branch is retained; this slim packet does not replace its evidence.
|
||||
The inventory in this directory describes only this slim packet.
|
||||
|
||||
Sage-2541 requested this doc-only follow-up on b765f5cb8 after the green
|
||||
owner gate: bus 656/0/0, grant-executor 41/0/0, host-run 26/0/0 (#1785 c30431).
|
||||
No source or test byte changes and no additional test run.
|
||||
|
||||
## Portability limit and carry notes
|
||||
|
||||
Duckworth N2: M2's demonstrated kill holds only while the host's ephemeral
|
||||
range has lo <= 32768 <= hi. On another range, the picker test still checks
|
||||
the outside-range property, but the old-picker mutant may not be killed by
|
||||
a fixed sample. The evidence here applies to this host's 32768-60999 range.
|
||||
|
||||
For the next row touching scratch-sshd.mjs, carry these non-blocking notes:
|
||||
- N3: add one helper comment line naming MOSAIC_SSHD_EVIDENCE.
|
||||
- N4: remove the double blank lines in both owning test files.
|
||||
|
||||
These are follow-ups, not changes to the tested candidate in this packet.
|
||||
|
||||
---
|
||||
|
||||
# Row 280: scratch sshd owns its port
|
||||
|
||||
Design before code; Sage assignment sage-2530, mos-1270. Base
|
||||
`da5d3651d75bff113b27d91abefc824c077dabbe`. Claim rev 3148,
|
||||
`rocko-280-start-r1`. Reviewer Duckworth; gate owner Sage.
|
||||
|
||||
## Proposed change
|
||||
|
||||
Both helpers currently return on `listening(port)` alone
|
||||
(grant-executor.test.mjs:86 and host-run.test.mjs:91). A foreign listener
|
||||
can satisfy that probe while our daemon fails its bind. Move only their
|
||||
common launch/retry/readiness loop and port selection into a shared test
|
||||
helper, `packages/bus/tests/scratch-sshd.mjs`; retain their distinct key,
|
||||
authorized-key and command-stub setup in their existing files.
|
||||
|
||||
For each of the existing five attempts, write a fresh, attempt-specific
|
||||
sshd log, start the child, and accept only after all three checks hold:
|
||||
a successful loopback connect, the exact `Server listening on 127.0.0.1
|
||||
port N.` line from this attempt's log, and the child's exitCode/signalCode
|
||||
still null after the asynchronous connect. No previous attempt's log can
|
||||
prove a later attempt. Early exit or failed bind advances to a fresh pick;
|
||||
kill and await an unsuccessful child before retry. Exhaustion keeps the
|
||||
existing `scratch sshd did not start` error, never a skip. Register cleanup
|
||||
for the successful child as well.
|
||||
|
||||
Read and validate `/proc/sys/net/ipv4/ip_local_port_range`. Select uniformly
|
||||
from eligible ports 1024 through 65535 outside the inclusive range, refusing
|
||||
invalid input or an empty eligible set. No fixed 32768 assumption. Expose a
|
||||
first-attempt port override only to the regression, then use the normal picker.
|
||||
|
||||
In each owning file, a regression opens a plain TCP listener, supplies that
|
||||
occupied port for attempt one, and asserts the helper rejects it and returns
|
||||
a different port. The helper's log/liveness proof still governs that returned
|
||||
port. Register TCP listener cleanup before awaiting startup.
|
||||
Add a deterministic picker assertion using injected random samples spanning
|
||||
[0,1); every selected port must be unprivileged and outside the actual proc
|
||||
range. M2 restores the old picker. If the actual host range makes M2 escape
|
||||
this assertion, record it and ask Duckworth to adjudicate as the brief allows.
|
||||
|
||||
## X5 closure
|
||||
|
||||
DEFERRED.md:311-321 binds the `sleep 30` after the grant in G22/X5.
|
||||
Use the fixture's existing fake-command PATH: a `sleep` stub delegates every
|
||||
other argument to /usr/bin/sleep, but for 30 waits in short intervals only
|
||||
while its scratch directory exists. The existing `sleep 30` argv, grant,
|
||||
sudo-log assertion and revoke assertions stay unchanged. Remote cleanup no
|
||||
longer leaves a 30-second child after the fixture directory is removed.
|
||||
Append a closing line to the DEFERRED entry; do not rewrite its history.
|
||||
|
||||
## Evidence and slot request plan
|
||||
|
||||
No run before Sage CLEAR. First run the new occupied-port regressions red
|
||||
against the old acceptance, then green after the helper change. Run both
|
||||
whole files, M1 (old connect-only acceptance) and M2 (old random picker),
|
||||
with each killing test named. Do not classify a timeout or syntax error as
|
||||
an assertion kill. Save process-group timeout output and restore hashes.
|
||||
|
||||
After the whole-file durations are known, refine the repeat estimate.
|
||||
Provisional estimate: allow 60-90 minutes for both 30-run whole-file repeats
|
||||
under continuous CLI-suite load, with per-run counts and elapsed time saved.
|
||||
The load and repeats are intentional concurrency within one granted slot;
|
||||
all other commands stay sequential. Both files must complete all 30 without
|
||||
timeout, and the CLI load must remain green. Any unexpected red stops for
|
||||
Sage/Mos. Use a fresh short scratch TMPDIR, dead Docker socket, no live
|
||||
configuration, no Docker or browser, and kill owned process groups on timeout.
|
||||
Sweep owned node and sshd/stub processes and restore candidate hashes before
|
||||
returning the slot. Sage runs the whole bus suite in the owner gate.
|
||||
|
||||
Owned paths: the two existing test files, the shared test helper,
|
||||
`docs/plans/DEFERRED.md`, and this packet. No product change, no live action.
|
||||
|
||||
## Phase 1 runner setup correction
|
||||
|
||||
The initial red-grant invocation at 07:22:02Z ran no test: packet runner
|
||||
parents[5] selected the scratch parent instead of the worktree. The invalid
|
||||
record and log stay unchanged. Sage-2534 approved parents[4], a git-root
|
||||
and test-file existence preflight, and a fresh CLEAR. No source or test
|
||||
byte changed for this correction; it is not a test red.
|
||||
|
||||
## Accepted design conditions and first test stop
|
||||
|
||||
Sage-2533 and duckworth-0120 approved the design. C1 samples are
|
||||
[0, 0.25, 0.5, 0.6384, 0.75, 0.999999]; M2 uses rand(), never Math.random().
|
||||
C2 checks attempt-zero's config names the occupied port and its log has no
|
||||
listening proof for that port. C3's 30/60-second stub delegates other
|
||||
arguments to absolute /usr/bin/sleep, so hold-add/remove's 0.05 sleeps do
|
||||
not recurse. The same stub covers host-run's remote long sleeps (N1).
|
||||
Attempt logs are sshd-N.log; exhaustion names the final log (N2).
|
||||
|
||||
After sage-2534's runner correction, both old-acceptance regression runs
|
||||
failed exactly on returned port equaling the occupied listener port.
|
||||
The first green regression then failed: no accepted sshd after five
|
||||
attempts (07:22:56Z–07:23:09Z, 12.193 s), while the picker assertion passed.
|
||||
The exact log comparison may be rejecting line framing, but this is only a
|
||||
hypothesis: fixture cleanup removed the raw attempt logs. Stopped without
|
||||
retry and requested diagnostic CLEAR to capture the logs before cleanup.
|
||||
No later whole-file run or mutant was executed. Slot returned 07:23:34Z;
|
||||
own cwd node/sshd sweep is empty. This evidence is not a passing gate.
|
||||
|
||||
## Confirmed log framing and correction
|
||||
|
||||
Sage-2535 granted a diagnostic rerun and the conditional CR-only correction.
|
||||
The saved diagnostic TAP and diagnostic-bytes.json prove the occupied-port
|
||||
bind refusal, then four listening lines with trailing hex 0d before newline.
|
||||
Only the comparison changed: the exact expected line, optionally followed
|
||||
by one CR, is accepted. No trim or substring match is used. The temporary
|
||||
dump is removed. Both focused files pass 2/2 after this correction.
|
||||
The earlier diagnostic red is retained as build evidence, not hidden.
|
||||
|
||||
## Phase 1 complete
|
||||
|
||||
Whole grant-executor: 41/41 in 20.673 s; whole host-run: 26/26 in
|
||||
14.367 s. Focused regressions: 2/2 in each file. No skips or timeouts.
|
||||
M1 and M2 are each assertion-killed by the named regression in both files
|
||||
(4/4 executions, two distinct mutant sites). M2 deterministically hits
|
||||
32768 at injected sample 0.6384. Source restored. Post-run pgrep finds no
|
||||
process naming the fixture TMPDIR; cwd node/sshd sweep is empty, proving
|
||||
the long-sleep stub cleanup requested in C3. Logs retain the prior invalid
|
||||
runner invocation and the diagnosed CRLF build failure separately.
|
||||
|
||||
## Repeat preparation (sage-2536)
|
||||
|
||||
Before the repeat commit, the shared helper adds optional fixture evidence
|
||||
output via MOSAIC_SSHD_EVIDENCE. It saves each attempted config/log before
|
||||
accepting or abandoning that attempt, so test cleanup cannot erase the
|
||||
startup evidence. The repeat runner assigns a packet directory per run.
|
||||
No acceptance, picker, stub or existing assertion changes for this capture.
|
||||
The repeated full files verify this final committed helper.
|
||||
|
||||
Repeat source commit: b765f5cb812e60789e4129ed2ab8009f5439a5be. Four-path manifest SHA256: 27806c0579c76d0999d2e6be4180c3867570df0268af9d7e0d7b971af799e6f5.
|
||||
All four blobs are rechecked before and after each run. No candidate
|
||||
bytes may change after this point.
|
||||
|
||||
## Final committed-byte evidence
|
||||
|
||||
Sage-2536 granted the repeat on committed bytes, with no changes carried
|
||||
through the run. Source commit b765f5cb812e60789e4129ed2ab8009f5439a5be;
|
||||
manifest 27806c0579c76d0999d2e6be4180c3867570df0268af9d7e0d7b971af799e6f5.
|
||||
Repeat window: 2026-10-22T07:29:03Z to 07:48:38Z. Both whole files ran
|
||||
sequentially under continuous CLI-suite load, with full output, exit status,
|
||||
counts and durations retained per run in repeat-results.json/repeat-logs/.
|
||||
Each run checks HEAD and all four hashes before and after. The load is the
|
||||
actual complete CLI glob, not a substitute stress loop. Sage received the
|
||||
required 15-minute report at 07:44:01Z.
|
||||
|
||||
| Repeat file | Whole runs | Pass | Fail | Skip | Timeout |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| grant-executor.test.mjs | 30 | 1230 | 0 | 0 | 0 |
|
||||
| host-run.test.mjs | 30 | 780 | 0 | 0 | 0 |
|
||||
| CLI load, two whole cycles | 2 | 1588 | 0 | 0 | 0 |
|
||||
|
||||
Repeat assertions total 2010/2010; load assertions 1588/1588. The in-progress
|
||||
second CLI load was allowed to finish whole before stopping. No skips.
|
||||
Each repeat has a 180 s process-group timeout; each load a 900 s timeout.
|
||||
Neither bound fired. Attempt config/log evidence was saved before cleanup
|
||||
under attempt-logs/<run>/<fixture>/; these are generated fixture paths only.
|
||||
|
||||
grant-executor: seconds min 20.611, median 20.811, max 26.419.
|
||||
|
||||
host-run: seconds min 14.207, median 14.408, max 14.609.
|
||||
|
||||
cli-load: seconds min 587.109, median 587.409, max 587.709.
|
||||
|
||||
Sage-2537 granted final M1/M2 replays after the complete CLI load exited,
|
||||
because the optional attempt-log capture was added before the repeat commit.
|
||||
07:48:56Z–07:48:57Z: four focused executions, all assertion kills:
|
||||
|
||||
| Mutant | Named killer, in both owning files | Result |
|
||||
| --- | --- | --- |
|
||||
| M1: connect-only acceptance | row 280: an occupied TCP port is attempted, refused, and retried with our sshd | Both fail returned-port inequality |
|
||||
| M2: 20000 + Math.floor(rand() * 20000) | row 280: every injected picker sample is unprivileged and outside the ephemeral range | Both fail at sample 0.6384, port 32768 |
|
||||
|
||||
No timeout or syntax failure is counted as a kill. Phase-1 runner, table and
|
||||
logs remain separately recorded; final replays use the exact b765 helper.
|
||||
Restored-hashes.json records 4/4 with no drift. Final-sweep.json records no
|
||||
node, chromium or sshd under owned cwd paths and no process naming either
|
||||
owned TMPDIR. This also supplies C3's post-test long-sleep cleanup evidence.
|
||||
Slot returned at 07:49:17Z. No source byte changed during the repeat; the
|
||||
final mutations were restored under sage-2537. No live file, unit or config
|
||||
was touched. The whole bus suite remains Sage's owner gate.
|
||||
|
||||
Existing assertions/refusals remain unchanged (289 grant lines, 125 host
|
||||
lines); assertion-preservation.json records the static check. The reader
|
||||
search found only the intended shared-helper imports and no outside reader
|
||||
of either owning file. Duckworth also confirmed no owning-file hash pin.
|
||||
The two extra blank-line pairs are acknowledged style nits, left unchanged
|
||||
after the committed repeat (sage-2537).
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"packages/bus/tests/grant-executor.test.mjs": {
|
||||
"existing_assertion_refusal_lines": 289,
|
||||
"all_preserved": true
|
||||
},
|
||||
"packages/bus/tests/host-run.test.mjs": {
|
||||
"existing_assertion_refusal_lines": 125,
|
||||
"all_preserved": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
da5d3651d75bff113b27d91abefc824c077dabbe
|
||||
@@ -0,0 +1,333 @@
|
||||
diff --git a/docs/plans/DEFERRED.md b/docs/plans/DEFERRED.md
|
||||
index 197a29ecb..7945a6829 100644
|
||||
--- a/docs/plans/DEFERRED.md
|
||||
+++ b/docs/plans/DEFERRED.md
|
||||
@@ -319,6 +319,10 @@ at every gate. Started 2026-09-12 during the control board MVP.
|
||||
waits 30 s before it calls a lone `sleep 30` a leak. The row 197
|
||||
lanes scan only at lane end, about 15 minutes after pkg-bus, so they
|
||||
can't see it (mos-1169). (2026-10-21)
|
||||
+ Closing note, row 280 (2026-10-22): both scratch sshd fixtures install a
|
||||
+ bounded 30/60-second sleep stub that exits when its test directory is gone;
|
||||
+ other sleep arguments exec /usr/bin/sleep unchanged. Evidence is recorded
|
||||
+ in agents/rocko/work/queue-280/BUILD.md.
|
||||
|
||||
- **Row 66 live checks: the unslop notice on a live managed session, both harnesses.**
|
||||
(mos-1186, Vikunja #454.) Row 66's gate asked for one landing session per
|
||||
diff --git a/packages/bus/tests/grant-executor.test.mjs b/packages/bus/tests/grant-executor.test.mjs
|
||||
index 1992f1525..b27d91dc4 100644
|
||||
--- a/packages/bus/tests/grant-executor.test.mjs
|
||||
+++ b/packages/bus/tests/grant-executor.test.mjs
|
||||
@@ -6,9 +6,9 @@
|
||||
// reaches another host.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
-import { execFileSync, spawn } from 'node:child_process';
|
||||
+import { execFileSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
-import { connect } from 'node:net';
|
||||
+import { createServer } from 'node:net';
|
||||
import { appendFileSync, chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir, userInfo } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
@@ -19,6 +19,8 @@ import { hostExecutor, runHost } from '../src/host-run.mjs';
|
||||
import { Store } from '../src/store.mjs';
|
||||
import { grantDecision, hostRecord } from './host-fixtures.mjs';
|
||||
|
||||
+import { ephemeralRange, installSleepStub, pickPort, startScratchSshd } from './scratch-sshd.mjs';
|
||||
+
|
||||
const SSHD = '/usr/bin/sshd';
|
||||
const USER = userInfo().username;
|
||||
const AT = '2026-10-21T00:00:00.000Z';
|
||||
@@ -35,12 +37,7 @@ const DEFINITIONS = {
|
||||
pm: { roleVersion: 2, credentials: [] },
|
||||
ops: { roleVersion: 2, credentials: [{ service: 'host' }] },
|
||||
};
|
||||
-const listening = (port) =>
|
||||
- new Promise((resolve) => {
|
||||
- const s = connect(port, '127.0.0.1');
|
||||
- s.on('connect', () => (s.destroy(), resolve(true)));
|
||||
- s.on('error', () => resolve(false));
|
||||
- });
|
||||
+
|
||||
async function until(fn, ms = 10000) {
|
||||
const end = Date.now() + ms;
|
||||
while (!fn()) {
|
||||
@@ -54,12 +51,13 @@ async function until(fn, ms = 10000) {
|
||||
// Both also stop when the test dir is gone, since a held stub on the sshd side outlives its test (#1750).
|
||||
// wrap.sh logs every request. The sudo stub logs its arguments to sudo.log and runs the command as the
|
||||
// test's user.
|
||||
-async function sshd(t, dir) {
|
||||
+async function sshd(t, dir, options) {
|
||||
if (!existsSync(SSHD)) throw new Error(`${SSHD} is missing: the executor tests need it and fail, never skip`);
|
||||
keygen(join(dir, 'hostkey'));
|
||||
keygen(join(dir, 'installer'));
|
||||
writeFileSync(join(dir, 'authorized_keys'), readFileSync(join(dir, 'installer.pub')), { mode: 0o600 });
|
||||
mkdirSync(join(dir, 'fake'));
|
||||
+ installSleepStub(dir);
|
||||
const stub = (name, body) => writeFileSync(join(dir, 'fake', name), `#!/bin/sh\nD='${dir}'\n${body}\n`, { mode: 0o755 });
|
||||
stub('list', '[ -e "$D/fail-list" ] && exit 1\n[ -e "$D/granted" ] && grep " mosaic-[0-9a-f]*$" "$D/granted"\nexit 0');
|
||||
stub(
|
||||
@@ -76,26 +74,42 @@ async function sshd(t, dir) {
|
||||
`#!/bin/sh\nprintf '%s\\n' "$SSH_ORIGINAL_COMMAND" >> '${dir}/requests.log'\nPATH=${dir}/fake:/usr/bin:/bin; export PATH\nexec /bin/sh -c "$SSH_ORIGINAL_COMMAND"\n`,
|
||||
{ mode: 0o755 },
|
||||
);
|
||||
- for (let attempt = 0; attempt < 5; attempt++) {
|
||||
- const port = 20000 + Math.floor(Math.random() * 20000);
|
||||
- writeFileSync(
|
||||
- join(dir, 'sshd_config'),
|
||||
- [`Port ${port}`, 'ListenAddress 127.0.0.1', `HostKey ${dir}/hostkey`, `AuthorizedKeysFile ${dir}/authorized_keys ${dir}/granted`,
|
||||
- `PidFile ${dir}/sshd.pid`, 'UsePAM no', 'StrictModes no', 'PasswordAuthentication no', 'KbdInteractiveAuthentication no',
|
||||
- `ForceCommand ${dir}/wrap.sh`, ''].join('\n'),
|
||||
- );
|
||||
- const daemon = spawn(SSHD, ['-D', '-f', join(dir, 'sshd_config'), '-E', join(dir, 'sshd.log')], { stdio: 'ignore' });
|
||||
- t.after(() => daemon.kill('SIGKILL'));
|
||||
- const deadline = Date.now() + 3000;
|
||||
- while (Date.now() < deadline && daemon.exitCode === null) {
|
||||
- if (await listening(port)) return port;
|
||||
- await sleep(30);
|
||||
- }
|
||||
- daemon.kill('SIGKILL');
|
||||
- }
|
||||
- throw new Error('scratch sshd did not start');
|
||||
+ return startScratchSshd(t, dir, [
|
||||
+ 'ListenAddress 127.0.0.1', `HostKey ${dir}/hostkey`,
|
||||
+ `AuthorizedKeysFile ${dir}/authorized_keys ${dir}/granted`, `PidFile ${dir}/sshd.pid`,
|
||||
+ 'UsePAM no', 'StrictModes no', 'PasswordAuthentication no', 'KbdInteractiveAuthentication no',
|
||||
+ `ForceCommand ${dir}/wrap.sh`,
|
||||
+ ], options);
|
||||
}
|
||||
|
||||
+
|
||||
+test('row 280: an occupied TCP port is attempted, refused, and retried with our sshd', async t => {
|
||||
+ const dir = mkdtempSync(join(tmpdir(), 'sshd280-'));
|
||||
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
+ const server = createServer(socket => socket.end());
|
||||
+ await new Promise((resolve, reject) => {
|
||||
+ server.once('error', reject);
|
||||
+ server.listen(0, '127.0.0.1', resolve);
|
||||
+ });
|
||||
+ t.after(() => new Promise(resolve => server.close(resolve)));
|
||||
+ const occupied = server.address().port;
|
||||
+ const port = await sshd(t, dir, { firstPort: occupied });
|
||||
+ assert.match(readFileSync(join(dir, 'sshd-0.conf'), 'utf8'), new RegExp(`^Port ${occupied}\\n`));
|
||||
+ assert.equal(readFileSync(join(dir, 'sshd-0.log'), 'utf8').includes(`Server listening on 127.0.0.1 port ${occupied}.`), false);
|
||||
+ assert.notEqual(port, occupied);
|
||||
+ assert.ok([1, 2, 3, 4].some(n => existsSync(join(dir, `sshd-${n}.log`)) &&
|
||||
+ readFileSync(join(dir, `sshd-${n}.log`), 'utf8').includes(`Server listening on 127.0.0.1 port ${port}.`)));
|
||||
+});
|
||||
+
|
||||
+test('row 280: every injected picker sample is unprivileged and outside the ephemeral range', () => {
|
||||
+ const [lo, hi] = ephemeralRange();
|
||||
+ for (const sample of [0, 0.25, 0.5, 0.6384, 0.75, 0.999999]) {
|
||||
+ const port = pickPort(() => sample);
|
||||
+ assert.ok(port >= 1024 && port <= 65535);
|
||||
+ assert.ok(port < lo || port > hi, `sample ${sample}: ${port} inside ${lo}-${hi}`);
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
async function setup(t, opts = {}) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'g235x-'));
|
||||
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
diff --git a/packages/bus/tests/host-run.test.mjs b/packages/bus/tests/host-run.test.mjs
|
||||
index 6f5949b5c..f7eebd45e 100644
|
||||
--- a/packages/bus/tests/host-run.test.mjs
|
||||
+++ b/packages/bus/tests/host-run.test.mjs
|
||||
@@ -4,7 +4,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync, spawn } from 'node:child_process';
|
||||
-import { connect, createServer } from 'node:net';
|
||||
+import { createServer } from 'node:net';
|
||||
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir, userInfo } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
@@ -19,6 +19,8 @@ import { hostGrant } from '../src/host-grant.mjs';
|
||||
import { startBroker } from '../src/runtime.mjs';
|
||||
import { businessDoc, rolesCopy, systemFor, writeJson } from '../../business/tests/helpers.mjs';
|
||||
|
||||
+import { ephemeralRange, installSleepStub, pickPort, startScratchSshd } from './scratch-sshd.mjs';
|
||||
+
|
||||
const SSHD = '/usr/bin/sshd';
|
||||
const REPO = fileURLToPath(new URL('../../../', import.meta.url));
|
||||
const USER = userInfo().username;
|
||||
@@ -50,22 +52,18 @@ async function until(fn, ms = 5000) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
-const listening = (port) =>
|
||||
- new Promise((resolve) => {
|
||||
- const s = connect(port, '127.0.0.1');
|
||||
- s.on('connect', () => (s.destroy(), resolve(true)));
|
||||
- s.on('error', () => resolve(false));
|
||||
- });
|
||||
+
|
||||
|
||||
// A scratch sshd. ForceCommand puts the fake sudo first on PATH and hands the command to /bin/sh, so
|
||||
// the remote side parses the command line the way a real host's POSIX shell does.
|
||||
-async function sshd(t, dir) {
|
||||
+async function sshd(t, dir, options) {
|
||||
if (!existsSync(SSHD)) throw new Error(`${SSHD} is missing: the executor tests need it and fail, never skip`);
|
||||
keygen(join(dir, 'hostkey'));
|
||||
keygen(join(dir, 'otherhost'));
|
||||
keygen(join(dir, 'user'));
|
||||
writeFileSync(join(dir, 'authorized_keys'), readFileSync(join(dir, 'user.pub')), { mode: 0o600 });
|
||||
mkdirSync(join(dir, 'fake'));
|
||||
+ installSleepStub(dir);
|
||||
writeFileSync(
|
||||
join(dir, 'fake', 'sudo'),
|
||||
`#!/bin/sh\n'${process.execPath}' -e 'require("fs").appendFileSync(process.argv[1], JSON.stringify(process.argv.slice(2)) + "\\n")' '${dir}/argv.json' "$@"\nshift 2\nexec "$@"\n`,
|
||||
@@ -75,26 +73,42 @@ async function sshd(t, dir) {
|
||||
writeFileSync(join(dir, 'fake', 'fail255'), '#!/bin/sh\necho "Host key verification failed." >&2\nexit 255\n', { mode: 0o755 });
|
||||
writeFileSync(join(dir, 'wrap.sh'), `#!/bin/sh\nPATH=${dir}/fake:/usr/bin:/bin; export PATH\nexec /bin/sh -c "$SSH_ORIGINAL_COMMAND"\n`);
|
||||
chmodSync(join(dir, 'wrap.sh'), 0o755);
|
||||
- for (let attempt = 0; attempt < 5; attempt++) {
|
||||
- const port = 20000 + Math.floor(Math.random() * 20000);
|
||||
- writeFileSync(
|
||||
- join(dir, 'sshd_config'),
|
||||
- [`Port ${port}`, 'ListenAddress 127.0.0.1', `HostKey ${dir}/hostkey`, `AuthorizedKeysFile ${dir}/authorized_keys`,
|
||||
- `PidFile ${dir}/sshd.pid`, 'UsePAM no', 'StrictModes no', 'PasswordAuthentication no', 'KbdInteractiveAuthentication no',
|
||||
- `ForceCommand ${dir}/wrap.sh`, ''].join('\n'),
|
||||
- );
|
||||
- const daemon = spawn(SSHD, ['-D', '-f', join(dir, 'sshd_config'), '-E', join(dir, 'sshd.log')], { stdio: 'ignore' });
|
||||
- t.after(() => daemon.kill('SIGKILL'));
|
||||
- const deadline = Date.now() + 3000;
|
||||
- while (Date.now() < deadline && daemon.exitCode === null) {
|
||||
- if (await listening(port)) return port;
|
||||
- await sleep(30);
|
||||
- }
|
||||
- daemon.kill('SIGKILL');
|
||||
- }
|
||||
- throw new Error('scratch sshd did not start');
|
||||
+ return startScratchSshd(t, dir, [
|
||||
+ 'ListenAddress 127.0.0.1', `HostKey ${dir}/hostkey`,
|
||||
+ `AuthorizedKeysFile ${dir}/authorized_keys`, `PidFile ${dir}/sshd.pid`,
|
||||
+ 'UsePAM no', 'StrictModes no', 'PasswordAuthentication no', 'KbdInteractiveAuthentication no',
|
||||
+ `ForceCommand ${dir}/wrap.sh`,
|
||||
+ ], options);
|
||||
}
|
||||
|
||||
+
|
||||
+test('row 280: an occupied TCP port is attempted, refused, and retried with our sshd', async t => {
|
||||
+ const dir = mkdtempSync(join(tmpdir(), 'sshd280-'));
|
||||
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
+ const server = createServer(socket => socket.end());
|
||||
+ await new Promise((resolve, reject) => {
|
||||
+ server.once('error', reject);
|
||||
+ server.listen(0, '127.0.0.1', resolve);
|
||||
+ });
|
||||
+ t.after(() => new Promise(resolve => server.close(resolve)));
|
||||
+ const occupied = server.address().port;
|
||||
+ const port = await sshd(t, dir, { firstPort: occupied });
|
||||
+ assert.match(readFileSync(join(dir, 'sshd-0.conf'), 'utf8'), new RegExp(`^Port ${occupied}\\n`));
|
||||
+ assert.equal(readFileSync(join(dir, 'sshd-0.log'), 'utf8').includes(`Server listening on 127.0.0.1 port ${occupied}.`), false);
|
||||
+ assert.notEqual(port, occupied);
|
||||
+ assert.ok([1, 2, 3, 4].some(n => existsSync(join(dir, `sshd-${n}.log`)) &&
|
||||
+ readFileSync(join(dir, `sshd-${n}.log`), 'utf8').includes(`Server listening on 127.0.0.1 port ${port}.`)));
|
||||
+});
|
||||
+
|
||||
+test('row 280: every injected picker sample is unprivileged and outside the ephemeral range', () => {
|
||||
+ const [lo, hi] = ephemeralRange();
|
||||
+ for (const sample of [0, 0.25, 0.5, 0.6384, 0.75, 0.999999]) {
|
||||
+ const port = pickPort(() => sample);
|
||||
+ assert.ok(port >= 1024 && port <= 65535);
|
||||
+ assert.ok(port < lo || port > hi, `sample ${sample}: ${port} inside ${lo}-${hi}`);
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
const GRANT = [['true'], ['false'], ['echo'], ['sleep'], ['head'], ['cat'], ['systemctl', 'status']];
|
||||
async function hostFixture(t, { commands = GRANT, reviewerHosts, wrongHostKey = false, coderWeb2 = false } = {}) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'h210-'));
|
||||
diff --git a/packages/bus/tests/scratch-sshd.mjs b/packages/bus/tests/scratch-sshd.mjs
|
||||
new file mode 100644
|
||||
index 000000000..2dde26de3
|
||||
--- /dev/null
|
||||
+++ b/packages/bus/tests/scratch-sshd.mjs
|
||||
@@ -0,0 +1,88 @@
|
||||
+// Row 280: fixture-only sshd startup. A TCP connect alone does not prove ownership.
|
||||
+import { spawn } from 'node:child_process';
|
||||
+import { once } from 'node:events';
|
||||
+import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
+import { connect } from 'node:net';
|
||||
+import { basename, join } from 'node:path';
|
||||
+
|
||||
+export const ephemeralRange = () => {
|
||||
+ const text = readFileSync('/proc/sys/net/ipv4/ip_local_port_range', 'utf8').trim();
|
||||
+ if (!/^\d+\s+\d+$/.test(text)) throw Error('invalid ephemeral port range');
|
||||
+ const range = text.split(/\s+/).map(Number);
|
||||
+ if (range[0] < 1 || range[1] > 65535 || range[0] > range[1]) throw Error('invalid ephemeral port range');
|
||||
+ return range;
|
||||
+};
|
||||
+
|
||||
+export function pickPort(rand = Math.random) {
|
||||
+ const [lo, hi] = ephemeralRange();
|
||||
+ const below = Math.max(0, Math.min(lo, 65536) - 1024);
|
||||
+ const above = Math.max(0, 65535 - Math.max(hi, 1023));
|
||||
+ const sample = rand();
|
||||
+ if (!below && !above) throw Error('no scratch sshd port available');
|
||||
+ if (!(sample >= 0 && sample < 1)) throw Error('invalid random sample');
|
||||
+ const offset = Math.floor(sample * (below + above));
|
||||
+ return offset < below ? 1024 + offset : Math.max(hi + 1, 1024) + offset - below;
|
||||
+}
|
||||
+
|
||||
+const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
+const listening = port => new Promise(resolve => {
|
||||
+ const socket = connect(port, '127.0.0.1');
|
||||
+ const done = result => { socket.destroy(); resolve(result); };
|
||||
+ socket.setTimeout(200, () => done(false));
|
||||
+ socket.on('connect', () => done(true));
|
||||
+ socket.on('error', () => done(false));
|
||||
+});
|
||||
+const stop = async daemon => {
|
||||
+ if (daemon.pid === undefined || daemon.exitCode !== null || daemon.signalCode !== null) return;
|
||||
+ const exited = once(daemon, 'exit');
|
||||
+ daemon.kill('SIGKILL');
|
||||
+ await exited;
|
||||
+};
|
||||
+
|
||||
+export async function startScratchSshd(t, dir, config, { firstPort } = {}) {
|
||||
+ let lastLog;
|
||||
+ for (let attempt = 0; attempt < 5; attempt++) {
|
||||
+ const port = attempt === 0 && firstPort !== undefined ? firstPort : pickPort();
|
||||
+ const configFile = join(dir, `sshd-${attempt}.conf`);
|
||||
+ lastLog = join(dir, `sshd-${attempt}.log`);
|
||||
+ writeFileSync(configFile, [`Port ${port}`, ...config, ''].join('\n'));
|
||||
+ writeFileSync(lastLog, '');
|
||||
+ const daemon = spawn('/usr/bin/sshd', ['-D', '-f', configFile, '-E', lastLog], { stdio: 'ignore' });
|
||||
+ t.after(() => stop(daemon));
|
||||
+ const saveLog = () => {
|
||||
+ if (!process.env.MOSAIC_SSHD_EVIDENCE) return;
|
||||
+ const evidence = join(process.env.MOSAIC_SSHD_EVIDENCE, basename(dir));
|
||||
+ mkdirSync(evidence, { recursive: true });
|
||||
+ writeFileSync(join(evidence, basename(lastLog)), readFileSync(lastLog));
|
||||
+ writeFileSync(join(evidence, basename(configFile)), readFileSync(configFile));
|
||||
+ };
|
||||
+ let spawnError;
|
||||
+ daemon.on('error', error => { spawnError = error; });
|
||||
+ const deadline = Date.now() + 3000;
|
||||
+ while (Date.now() < deadline && !spawnError && daemon.exitCode === null && daemon.signalCode === null) {
|
||||
+ if (await listening(port) && daemon.exitCode === null && daemon.signalCode === null &&
|
||||
+ readFileSync(lastLog, 'utf8').split('\n').some(line =>
|
||||
+ (line === `Server listening on 127.0.0.1 port ${port}.` ||
|
||||
+ line === `Server listening on 127.0.0.1 port ${port}.\r`))) { saveLog(); return port; }
|
||||
+ await sleep(30);
|
||||
+ }
|
||||
+ await stop(daemon);
|
||||
+ saveLog();
|
||||
+ }
|
||||
+ throw Error(`scratch sshd did not start: ${lastLog}`);
|
||||
+}
|
||||
+
|
||||
+// Long remote sleeps remain in flight for their original duration, but cannot outlive the fixture.
|
||||
+export function installSleepStub(dir) {
|
||||
+ const script = `const fs = require("node:fs");
|
||||
+const end = Date.now() + Number(process.argv[2]) * 1000;
|
||||
+const timer = setInterval(() => {
|
||||
+ if (!fs.existsSync(process.argv[1]) || Date.now() >= end) { clearInterval(timer); }
|
||||
+}, 50);`;
|
||||
+ writeFileSync(join(dir, 'fake', 'sleep'), `#!/bin/sh
|
||||
+case "$#:$1" in
|
||||
+ 1:30|1:60) exec '${process.execPath}' -e '${script}' '${dir}' "$1" ;;
|
||||
+ *) exec /usr/bin/sleep "$@" ;;
|
||||
+esac
|
||||
+`, { mode: 0o755 });
|
||||
+}
|
||||
@@ -0,0 +1,4 @@
|
||||
1cb0971e745745e3db329c7c75f36197664d7bf0687def33c0819134f121975f packages/bus/tests/grant-executor.test.mjs
|
||||
140ec358fff212b747412691e3cb5952844c13c7fe43d3ff1013d24f0df886d2 packages/bus/tests/host-run.test.mjs
|
||||
3b647e59f09e7fc43b41d072e46c3cea5ed7641d5d4a95c5b39c240f73cdb75e packages/bus/tests/scratch-sshd.mjs
|
||||
94c21723cd42f551bbd6249d5133cbfaa12ac25efef5a72bf525998ac3c52100 docs/plans/DEFERRED.md
|
||||
@@ -0,0 +1 @@
|
||||
b765f5cb812e60789e4129ed2ab8009f5439a5be
|
||||
@@ -0,0 +1,4 @@
|
||||
packages/bus/tests/grant-executor.test.mjs
|
||||
packages/bus/tests/host-run.test.mjs
|
||||
packages/bus/tests/scratch-sshd.mjs
|
||||
docs/plans/DEFERRED.md
|
||||
@@ -0,0 +1 @@
|
||||
b765f5cb812e60789e4129ed2ab8009f5439a5be
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"cwd_processes": [],
|
||||
"pgrep_checks": [
|
||||
{
|
||||
"tmpdir": "/mnt/storage/scratch/tmp/gInD",
|
||||
"exit": 1,
|
||||
"matches": []
|
||||
},
|
||||
{
|
||||
"tmpdir": "/mnt/storage/scratch/tmp/gVtU",
|
||||
"exit": 1,
|
||||
"matches": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"helper_sha256": "3b647e59f09e7fc43b41d072e46c3cea5ed7641d5d4a95c5b39c240f73cdb75e",
|
||||
"restored": true
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "M1",
|
||||
"old": " if (await listening(port) && daemon.exitCode === null && daemon.signalCode === null &&\n readFileSync(lastLog, 'utf8').split('\\n').some(line =>\n (line === `Server listening on 127.0.0.1 port ${port}.` ||\n line === `Server listening on 127.0.0.1 port ${port}.\\r`))) { saveLog(); return port; }",
|
||||
"new": " if (await listening(port)) return port;",
|
||||
"pattern": "row 280: an occupied TCP port"
|
||||
},
|
||||
{
|
||||
"id": "M2",
|
||||
"old": "export function pickPort(rand = Math.random) {\n const [lo, hi] = ephemeralRange();\n const below = Math.max(0, Math.min(lo, 65536) - 1024);\n const above = Math.max(0, 65535 - Math.max(hi, 1023));\n const sample = rand();\n if (!below && !above) throw Error('no scratch sshd port available');\n if (!(sample >= 0 && sample < 1)) throw Error('invalid random sample');\n const offset = Math.floor(sample * (below + above));\n return offset < below ? 1024 + offset : Math.max(hi + 1, 1024) + offset - below;\n}\n",
|
||||
"new": "export function pickPort(rand = Math.random) {\n return 20000 + Math.floor(rand() * 20000);\n}\n",
|
||||
"pattern": "row 280: every injected picker sample"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
d5d82d7cb679332e7d90ed26220cd7d3170931d2e8aa2ccdfc35ceea12147c11 BUILD.md
|
||||
0e8f1e76f8c13d7de5cbea1ae8b8c2f178ca5aa0283ac0dbd00bffcd4c39bea5 assertion-preservation.json
|
||||
052e9e79aa3c9ba1b6f8dedc7af00440e104d308ed9451a076e872c903704c7d base.txt
|
||||
5898d3c45b4274a9cbf117018811f2b6206987e3849e97ce171c3aaa42d314da build.patch
|
||||
27806c0579c76d0999d2e6be4180c3867570df0268af9d7e0d7b971af799e6f5 candidate-manifest.sha256
|
||||
634e46d73bbcdbdea70660954fcc8290dc3d6c8d15554218d59c78feafbd0b08 candidate.txt
|
||||
84f2091c0eb1b55c34b4462775160b5f808a6843e68b3e4cb54e1f7a2421eaf3 changed-paths.txt
|
||||
634e46d73bbcdbdea70660954fcc8290dc3d6c8d15554218d59c78feafbd0b08 commit.txt
|
||||
eb9f55fd5de0ec370742702f15d737fb91e35542dbda727d43502adce92903a9 final-sweep.json
|
||||
f9a578b95bd6f9e07dc231002a28ea9c3ae14fe2e269f3fbca50c4fc97ba61e0 mutant-restore.json
|
||||
72cfbf942b4437720d6b92ddef0bc138d27bd6ecef1f1da0b32a2b0250b3cd61 mutants.json
|
||||
b435b731ea4ccff12f90e1b53023cbc54bf65c08ad0f7feaf8a810088156a0b6 post-phase1-pgrep.txt
|
||||
9f8458487a1c5146230e70d40428b6fb218c93f378fd1a8e8607fab80c446b2c reader-search.txt
|
||||
634e46d73bbcdbdea70660954fcc8290dc3d6c8d15554218d59c78feafbd0b08 repeat-commit.txt
|
||||
42e240961767b817fb5f1d183bc949074b3b90c72335d65c3e0278a3db4294ea repeat-results.json
|
||||
e26b6ecefd1a6db69fbaaa38c4f08abe0ad69035ecaa8e2e29f5da2961e5da15 repeat-summary.json
|
||||
3e1903ee103b0a9ef8febeb4d2073848aca0e42fd4ecd1c586381f2ab7404be2 restored-hashes.json
|
||||
f38eaa23861b8acb1738ed082f25a0e6661d976b87da140993373de41bc4053d results.json
|
||||
76c5c03767ca34bc38a7f69c14fbf1c06e8ebcc93120bc577a44e2271f6fdb16 scan-result.json
|
||||
e7f560ce5fc957432ddcad231897a5decd33be06f1201926adabb62d3c218c83 skips.txt
|
||||
37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570 sweep-phase1.json
|
||||
@@ -0,0 +1 @@
|
||||
No process names the owned fixture TMPDIR.
|
||||
@@ -0,0 +1,28 @@
|
||||
Search: packages/bus/tests/grant-executor.test.mjs
|
||||
(none)
|
||||
|
||||
Search: grant-executor.test.mjs
|
||||
(none)
|
||||
|
||||
Search: packages/bus/tests/host-run.test.mjs
|
||||
(none)
|
||||
|
||||
Search: host-run.test.mjs
|
||||
(none)
|
||||
|
||||
Search: packages/bus/tests/scratch-sshd.mjs
|
||||
(none)
|
||||
|
||||
Search: scratch-sshd.mjs
|
||||
packages/bus/tests/grant-executor.test.mjs:22:import { ephemeralRange, installSleepStub, pickPort, startScratchSshd } from './scratch-sshd.mjs';
|
||||
packages/bus/tests/host-run.test.mjs:22:import { ephemeralRange, installSleepStub, pickPort, startScratchSshd } from './scratch-sshd.mjs';
|
||||
|
||||
Search: docs/plans/DEFERRED.md
|
||||
packages/queue/tests/fixtures/genesis-render.md:35:| 30 | Open gaps from the MVP work | unassigned | — | parked | — | Jason reopens (jason) | `docs/plans/DEFERRED.md` § Open | Fixed only when a gate needs them |
|
||||
packages/queue/tests/fixtures/queue-marked.md:25:Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
|
||||
|
||||
Search: DEFERRED.md
|
||||
packages/queue/tests/fixtures/genesis-render.md:35:| 30 | Open gaps from the MVP work | unassigned | — | parked | — | Jason reopens (jason) | `docs/plans/DEFERRED.md` § Open | Fixed only when a gate needs them |
|
||||
packages/queue/tests/fixtures/queue-marked.md:25:Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
|
||||
packages/queue/tests/fixtures/queue-marked.md:83:| Open gaps from the MVP work | Fixed only when a gate needs them | DEFERRED.md, "Open" |
|
||||
packages/queue/tests/fixtures/queue-marked.md:92:- 2026-09-13 — created; rows 1 to 8 taken from CURRENT.md, DEFERRED.md and the plan page.
|
||||
@@ -0,0 +1 @@
|
||||
b765f5cb812e60789e4129ed2ab8009f5439a5be
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"head": "b765f5cb812e60789e4129ed2ab8009f5439a5be",
|
||||
"manifest_sha256": "27806c0579c76d0999d2e6be4180c3867570df0268af9d7e0d7b971af799e6f5",
|
||||
"start": "2026-10-22T07:29:03Z",
|
||||
"end": "2026-10-22T07:48:38Z",
|
||||
"failed": false,
|
||||
"repeat_runs": 60,
|
||||
"load_runs": 2,
|
||||
"counts": {
|
||||
"repeat": {
|
||||
"tests": 2010,
|
||||
"pass": 2010,
|
||||
"fail": 0,
|
||||
"skipped": 0,
|
||||
"cancelled": 0
|
||||
},
|
||||
"cli-load": {
|
||||
"tests": 1588,
|
||||
"pass": 1588,
|
||||
"fail": 0,
|
||||
"skipped": 0,
|
||||
"cancelled": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"paths": 4,
|
||||
"drift": []
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
[
|
||||
{
|
||||
"step": "red-grant",
|
||||
"start": "2026-10-22T07:22:02Z",
|
||||
"end": "2026-10-22T07:22:02Z",
|
||||
"seconds": 0.029,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "red-grant-actual",
|
||||
"start": "2026-10-22T07:22:42Z",
|
||||
"end": "2026-10-22T07:22:43Z",
|
||||
"seconds": 0.111,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "red-host-actual",
|
||||
"start": "2026-10-22T07:22:43Z",
|
||||
"end": "2026-10-22T07:22:43Z",
|
||||
"seconds": 0.143,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "green-grant-regressions",
|
||||
"start": "2026-10-22T07:22:56Z",
|
||||
"end": "2026-10-22T07:23:09Z",
|
||||
"seconds": 12.193,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 2,
|
||||
"pass": 1,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280:",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "diagnostic-log-bytes",
|
||||
"start": "2026-10-22T07:24:22Z",
|
||||
"end": "2026-10-22T07:24:34Z",
|
||||
"seconds": 12.196,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "green-grant-regressions-crlf",
|
||||
"start": "2026-10-22T07:25:03Z",
|
||||
"end": "2026-10-22T07:25:03Z",
|
||||
"seconds": 0.178,
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 2,
|
||||
"pass": 2,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280:",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "green-host-regressions-crlf",
|
||||
"start": "2026-10-22T07:25:03Z",
|
||||
"end": "2026-10-22T07:25:03Z",
|
||||
"seconds": 0.212,
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 2,
|
||||
"pass": 2,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280:",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "grant-whole",
|
||||
"start": "2026-10-22T07:25:03Z",
|
||||
"end": "2026-10-22T07:25:24Z",
|
||||
"seconds": 20.673,
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 41,
|
||||
"pass": 41,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "host-whole",
|
||||
"start": "2026-10-22T07:25:24Z",
|
||||
"end": "2026-10-22T07:25:38Z",
|
||||
"seconds": 14.367,
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 26,
|
||||
"pass": 26,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M1-grant-executor",
|
||||
"start": "2026-10-22T07:26:25Z",
|
||||
"end": "2026-10-22T07:26:25Z",
|
||||
"seconds": 0.113,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M1-host-run",
|
||||
"start": "2026-10-22T07:26:25Z",
|
||||
"end": "2026-10-22T07:26:26Z",
|
||||
"seconds": 0.146,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M2-grant-executor",
|
||||
"start": "2026-10-22T07:26:26Z",
|
||||
"end": "2026-10-22T07:26:26Z",
|
||||
"seconds": 0.095,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: every injected picker sample",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M2-host-run",
|
||||
"start": "2026-10-22T07:26:26Z",
|
||||
"end": "2026-10-22T07:26:26Z",
|
||||
"seconds": 0.128,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: every injected picker sample",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M1-final-grant-executor",
|
||||
"start": "2026-10-22T07:48:56Z",
|
||||
"end": "2026-10-22T07:48:57Z",
|
||||
"seconds": 0.12,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M1-final-host-run",
|
||||
"start": "2026-10-22T07:48:57Z",
|
||||
"end": "2026-10-22T07:48:57Z",
|
||||
"seconds": 0.164,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: an occupied TCP port",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M2-final-grant-executor",
|
||||
"start": "2026-10-22T07:48:57Z",
|
||||
"end": "2026-10-22T07:48:57Z",
|
||||
"seconds": 0.108,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: every injected picker sample",
|
||||
"packages/bus/tests/grant-executor.test.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step": "M2-final-host-run",
|
||||
"start": "2026-10-22T07:48:57Z",
|
||||
"end": "2026-10-22T07:48:57Z",
|
||||
"seconds": 0.138,
|
||||
"exit": 1,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 1,
|
||||
"pass": 0,
|
||||
"fail": 1,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
},
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"--test-name-pattern=row 280: every injected picker sample",
|
||||
"packages/bus/tests/host-run.test.mjs"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"binary_files": [],
|
||||
"secret_matches": []
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
None. All 60 repeat files and both complete CLI load cycles report skipped 0.
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -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,24 @@
|
||||
# V502 rerun on 980cec88c
|
||||
|
||||
Sage-2609 authorized the clean picks; Dewey-0834 carries approval subject
|
||||
to this new-base rerun. Source 407c8d7be, packet 04a86cf33; both patch-ids
|
||||
match 8e3c6a79b and 9bf4683c6. No conflicts; stop-run auto-merged with row
|
||||
279's landed overshoot regression. Original packet and inventory remain
|
||||
historical, unchanged; this subpacket records current-base hashes/results.
|
||||
|
||||
Sage-2616 CLEAR supersedes sage-2614's wait. Run the three whole touched
|
||||
Node files and Python check only, sequentially. No suites, mutants, Docker,
|
||||
browser or live action. scripts/mosaic-task.mjs does not change, so the
|
||||
DEFERRED N7 runs-suite trigger does not apply. A red stops without retry.
|
||||
Short scratch TMPDIR, process-group timeout, pipefail and tee; final source
|
||||
hash check and cwd sweep precede the slot return.
|
||||
|
||||
## Rerun result
|
||||
|
||||
2026-10-22T11:27:32Z–11:27:34Z on 04a86cf33: whole stop-run 13/13,
|
||||
whole processing HTTP 6/6, whole bus tasks 9/9; Node total 28/28. Python
|
||||
regressions 4/4. No skips, cancelled tests or timeouts. Commands used
|
||||
pipefail and tee with a 120-second owned process-group deadline. All five
|
||||
candidate hashes match before/after and cwd node/chromium sweep is empty.
|
||||
The original five assertion-killed mutant results carry because both
|
||||
approved patch-ids match; no mutant rerun or suite was taken in this CLEAR.
|
||||
@@ -0,0 +1 @@
|
||||
980cec88c276f3af6636c140c531d0e6008a0a20
|
||||
@@ -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
|
||||
a26e5c15daff43b946aed8235158eb4037a21eaa67e8241f74a75b42b456fc62 packages/processing-control/tests/stop-run.test.mjs
|
||||
28caf153da5d16949c5a3e1cedf8c91613e7230840d9df65d5dc4bdc95e72bf5 packages/webui/tests/processing.test.mjs
|
||||
@@ -0,0 +1 @@
|
||||
04a86cf338930ffb4ef528fc4b81a5b7a2ebd237
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,65 @@
|
||||
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.535421
|
||||
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: 10.077554
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a bad entry refuses the whole record
|
||||
ok 3 - a bad entry refuses the whole record
|
||||
---
|
||||
duration_ms: 11.903011
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: taskView reads the projection for one business
|
||||
ok 4 - taskView reads the projection for one business
|
||||
---
|
||||
duration_ms: 12.856767
|
||||
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.237027
|
||||
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: 295.239472
|
||||
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.741274
|
||||
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: 48.57706
|
||||
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: 229.152599
|
||||
type: 'test'
|
||||
...
|
||||
1..9
|
||||
# tests 9
|
||||
# suites 0
|
||||
# pass 9
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 793.825413
|
||||
V502_STEP_META {"command": ["node", "--test", "--test-reporter=tap", "packages/bus/tests/tasks.test.mjs"], "start": "2026-10-22T11:27:33Z", "end": "2026-10-22T11:27:34Z", "exit": 0, "timeout": false}
|
||||
@@ -0,0 +1,47 @@
|
||||
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: 18.400047
|
||||
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.897097
|
||||
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.981101
|
||||
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: 3.994511
|
||||
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.204597
|
||||
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.562412
|
||||
type: 'test'
|
||||
...
|
||||
1..6
|
||||
# tests 6
|
||||
# suites 0
|
||||
# pass 6
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 140.239653
|
||||
V502_STEP_META {"command": ["node", "--test", "--test-reporter=tap", "packages/webui/tests/processing.test.mjs"], "start": "2026-10-22T11:27:33Z", "end": "2026-10-22T11:27:33Z", "exit": 0, "timeout": false}
|
||||
@@ -0,0 +1,6 @@
|
||||
....
|
||||
----------------------------------------------------------------------
|
||||
Ran 4 tests in 0.011s
|
||||
|
||||
OK
|
||||
V502_STEP_META {"command": ["python3", "agents/rocko/work/vikunja-502/tests/test_timeout_cause.py"], "start": "2026-10-22T11:27:34Z", "end": "2026-10-22T11:27:34Z", "exit": 0, "timeout": false}
|
||||
@@ -0,0 +1,89 @@
|
||||
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: 33.307311
|
||||
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: 23.800235
|
||||
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.579582
|
||||
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: 28.626285
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a sleep past the stop deadline still observes an alive run after both signals
|
||||
ok 5 - a sleep past the stop deadline still observes an alive run after both signals
|
||||
---
|
||||
duration_ms: 25.884332
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: missing run and another business never borrow a pid or signal
|
||||
ok 6 - missing run and another business never borrow a pid or signal
|
||||
---
|
||||
duration_ms: 8.820752
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: halt joins an in-progress fire without another TERM or deadline and links its first receipt
|
||||
ok 7 - halt joins an in-progress fire without another TERM or deadline and links its first receipt
|
||||
---
|
||||
duration_ms: 25.62055
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a separate host joins durable first ownership without duplicate signals
|
||||
ok 8 - a separate host joins durable first ownership without duplicate signals
|
||||
---
|
||||
duration_ms: 102.133976
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: corrupt and symlinked final receipts refuse as unknown rather than signalling again
|
||||
ok 9 - corrupt and symlinked final receipts refuse as unknown rather than signalling again
|
||||
---
|
||||
duration_ms: 23.157638
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a later exit gets separate halt evidence while the original alive result stays immutable
|
||||
ok 10 - a later exit gets separate halt evidence while the original alive result stays immutable
|
||||
---
|
||||
duration_ms: 57.055196
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: root aliases join but a different root never shares the run stop
|
||||
ok 11 - root aliases join but a different root never shares the run stop
|
||||
---
|
||||
duration_ms: 42.230422
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: a changed runtime record cannot replace the original stop identity after TERM
|
||||
ok 12 - a changed runtime record cannot replace the original stop identity after TERM
|
||||
---
|
||||
duration_ms: 7.372218
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: identity is rechecked at actual signal delivery, not just at the loop observation
|
||||
ok 13 - identity is rechecked at actual signal delivery, not just at the loop observation
|
||||
---
|
||||
duration_ms: 6.951622
|
||||
type: 'test'
|
||||
...
|
||||
1..13
|
||||
# tests 13
|
||||
# suites 0
|
||||
# pass 13
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 445.400398
|
||||
V502_STEP_META {"command": ["node", "--test", "--test-reporter=tap", "packages/processing-control/tests/stop-run.test.mjs"], "start": "2026-10-22T11:27:32Z", "end": "2026-10-22T11:27:33Z", "exit": 0, "timeout": false}
|
||||
@@ -0,0 +1,14 @@
|
||||
0a8e6bc20b2ba835adef447093a653f2dff364c6ad040229d866660a63ba2a71 BUILD.md
|
||||
ecec3a1a2e175702e6ed6370550e0d5c8efdbce87c0d9b4d7851301d33877f39 base.txt
|
||||
5758d699f04ecc81f7fed2a1f14af035130cceb6f99847cd954d89b111802257 candidate-manifest.sha256
|
||||
cd0fe73242c88babb31499654bc104d5b75c47fed37b8d0cf84979c7232af8d5 candidate.txt
|
||||
37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570 cwd-sweep.json
|
||||
dea8596ba0cde895238a9d8dce11d43e02604592a7e810e96e0e609f0eb97050 logs/bus-tasks-whole.log
|
||||
bdfe555de3365f1067fd53839ae488177d23b0f1f1950688e7b0ded89df7e6ea logs/processing-http-whole.log
|
||||
33ffbf98d5c5e910d1605868b4beeb6955d764ee092879a3cf3ea846641cbc63 logs/python-regressions.log
|
||||
1b6f920baa4aa071f51b8820efb465dc6d514bf3d6b95f192b48020f235e0e74 logs/stop-run-whole.log
|
||||
043f3cd860db3c9c6e5249ea33eb6fa2d8538365382d68ce2e4b2be780922ce8 range-diff.txt
|
||||
c90b6a02dfaa0cb64e6fab5be9f655f030222770ed694e2042854566bca28d65 restored-hashes.json
|
||||
07d020c6a1410acf45961589c37f6bfff1f7d5df5c7b273e11d856bfa37e84dc results.json
|
||||
76c5c03767ca34bc38a7f69c14fbf1c06e8ebcc93120bc577a44e2271f6fdb16 scan-result.json
|
||||
061e5c8904f40545d3552c1caa08ee961545b23d559b01ba4e95f4e7fac202e2 tmpdir.txt
|
||||
@@ -0,0 +1,2 @@
|
||||
1: 8e3c6a79b = 1: 407c8d7be test: harden processing failure diagnostics and cleanup
|
||||
2: 9bf4683c6 = 2: 04a86cf33 docs: record Vikunja 502 focused verification and rebase evidence
|
||||
@@ -0,0 +1 @@
|
||||
{"paths": 5, "drift": []}
|
||||
@@ -0,0 +1,79 @@
|
||||
[
|
||||
{
|
||||
"log": "bus-tasks-whole.log",
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"packages/bus/tests/tasks.test.mjs"
|
||||
],
|
||||
"start": "2026-10-22T11:27:33Z",
|
||||
"end": "2026-10-22T11:27:34Z",
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 9,
|
||||
"pass": 9,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"log": "processing-http-whole.log",
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"packages/webui/tests/processing.test.mjs"
|
||||
],
|
||||
"start": "2026-10-22T11:27:33Z",
|
||||
"end": "2026-10-22T11:27:33Z",
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 6,
|
||||
"pass": 6,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"log": "python-regressions.log",
|
||||
"command": [
|
||||
"python3",
|
||||
"agents/rocko/work/vikunja-502/tests/test_timeout_cause.py"
|
||||
],
|
||||
"start": "2026-10-22T11:27:34Z",
|
||||
"end": "2026-10-22T11:27:34Z",
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 4,
|
||||
"pass": 4,
|
||||
"fail": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"log": "stop-run-whole.log",
|
||||
"command": [
|
||||
"node",
|
||||
"--test",
|
||||
"--test-reporter=tap",
|
||||
"packages/processing-control/tests/stop-run.test.mjs"
|
||||
],
|
||||
"start": "2026-10-22T11:27:32Z",
|
||||
"end": "2026-10-22T11:27:33Z",
|
||||
"exit": 0,
|
||||
"timeout": false,
|
||||
"counts": {
|
||||
"tests": 13,
|
||||
"pass": 13,
|
||||
"fail": 0,
|
||||
"cancelled": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"binary_files": [],
|
||||
"secret_matches": []
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/mnt/storage/scratch/tmp/g8zT
|
||||
@@ -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"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user