diff --git a/agents/darkwing/work/s4-follow-up-review/out-r2/node-bus.txt b/agents/darkwing/work/s4-follow-up-review/out-r2/node-bus.txt new file mode 100644 index 00000000..11b30a04 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/out-r2/node-bus.txt @@ -0,0 +1,75 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (162.015698ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (238.937695ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (226.507639ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (145.199058ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (139.010311ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (108.092373ms) +✔ task action subjects and linked decision trail are complete and ordered (142.820074ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (89.92199ms) +✔ business isolation includes inherited object names and cross-business message references (148.898006ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (217.258457ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (97.466084ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (164.013125ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (99.190667ms) +✔ both arbiters require human resolution when their cross-role route is themselves (159.018969ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (1.7987ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.377177ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (11.178996ms) +✔ expiry refuses use and env references never become client data (2.938494ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.242134ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.255707ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.4647ms) +✔ process reader gets own kernel identity without exposing environment values (0.536978ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.672087ms) +✔ real pid 1 remains inspectable when its environment is protected (0.272176ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (192.11023ms) +✔ missing and foreign-business citations refuse and roll back message and grant (166.47203ms) +✔ cross-role sends still need a matching resolved decision and consume it once (221.690021ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (173.461124ms) +✔ startup token refusal returns safe code without value or partial listening broker (40.501746ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (158.580375ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (160.171734ms) +✔ trusted host registers later launches; socket clients never have a registration verb (162.238268ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (37.813864ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (140.568512ms) +✔ v3b prototype refusals, views and append-only mutations (913.050256ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (229.390347ms) +✔ another run cannot consume an approval; a failed check leaves it usable (191.19976ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (135.096325ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (260.933043ms) +✔ class drift gated to cross-role refuses before consumption (152.990224ms) +✔ class drift cross-role to gated refuses before consumption (176.624868ms) +✔ class drift gated to within-role refuses before consumption (170.263951ms) +✔ class drift cross-role to within-role refuses before consumption (171.751209ms) +✔ class drift within-role to gated refuses before consumption (157.946592ms) +✔ class drift within-role to cross-role refuses before consumption (146.405394ms) +✔ message.send consumes approval and prevents a later send or authorize (161.944811ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (177.732221ms) +✔ creates private WAL store and excludes a second writer until explicit close (108.828781ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (164.292379ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (170.389631ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (141.000448ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (92.582353ms) +✔ async transactions refuse before invoking their function (78.895339ms) +✔ recordTask keeps sync reads and a role write apart (160.637571ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (97.161259ms) +✔ a bad entry refuses the whole record (90.241337ms) +✔ taskView reads the projection for one business (120.609459ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (209.339689ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (377.120673ms) +✔ without an adapter the server refuses every task verb (172.529691ms) +✔ the runtime refuses an invalid adapter and closes a valid one (175.430432ms) +✔ the process loads the S3 adapter from plain-data trackers (214.254327ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (139.247766ms) +✔ two wire claims serialize; a lost reply never automatically retries (160.969295ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (94.867731ms) +✔ client preserves UTF-8 when a response divides a multibyte character (11.579482ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (123.680807ms) +ℹ tests 67 +ℹ suites 0 +ℹ pass 67 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2207.510046 diff --git a/agents/darkwing/work/s4-follow-up-review/out-r2/node-cli.txt b/agents/darkwing/work/s4-follow-up-review/out-r2/node-cli.txt new file mode 100644 index 00000000..4de982cc --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/out-r2/node-cli.txt @@ -0,0 +1,76 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (98.324231ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (120.368235ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (96.733383ms) +✔ decide prints a declining choice as declining (82.379171ms) +✔ an unknown outcome is reported once and never resent (84.806648ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (90.973543ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (87.575421ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (56.105972ms) +✔ every human command refuses inside an agent run before it touches the bus (81.207895ms) +✔ usage errors exit 4; no business and no host is a usage error (87.607774ms) +✔ agents and tasks print through the broker (86.794355ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.700549ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (39.652275ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (35.782793ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (32.193332ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (28.274721ms) +✔ a business without tracker.baseUrl gets no trackers entry (28.329289ms) +✔ an unknown business and a broken system config refuse with exit 3 (55.285833ms) +✔ empty views say so (0.837733ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.22402ms) +✔ tasks print the tracker fields the snapshot carries (0.200943ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (891.652263ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (211.576188ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (118.738702ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (157.896095ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (138.727897ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (106.451196ms) +✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (149.458737ms) +✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (101.246399ms) +✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (158.495929ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (22.143789ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.057949ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (210.363695ms) +✔ bus start refuses with exit 3 without a notifier config (85.767742ms) +✔ bus start runs until bus stop; status reports it while it runs (657.700101ms) +✔ bus-service.sh renders the unit and installs it into a given directory (26.582181ms) +✔ zoned uses the IANA zone across DST (17.121872ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (99.555481ms) +✔ two blocking decisions get two DMs with different nonces (108.930294ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.198262ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (99.005505ms) +✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (85.723331ms) +✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (96.358267ms) +✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (86.751728ms) +✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (145.049471ms) +✔ a restart after the second refusal does not send before that refusal's 30 min are up (110.984735ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (104.664968ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (71.471682ms) +✔ an inbox read failure is logged and the next poll retries (0.644589ms) +✔ no Discord id reaches the journal or the log (60.432747ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (10.692664ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (22.745435ms) +✔ the journal: a whole file that is one torn line truncates to empty (12.160028ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.650783ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.576425ms) +✔ the journal: a line with a wrong type refuses with exit 3 and names the field (1.791869ms) +✔ the journal: a symlinked directory refuses and says it is a link (0.508901ms) +✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.471877ms) +✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.370431ms) +✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.411411ms) +✔ digest content stays within Discord's 2000 characters (0.273224ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.798576ms) +task.close {} answered: invalid-request; fake saw 18 requests, first GET /info 200, GET /projects/1 200, GET /projects/1/views 200 +task.close on a missing task answered: task-not-found; it made GET /tasks/999 404 +✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (349.853585ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (33.941143ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2150.665848ms) +✔ busExit and refuseInsideAgent (0.47624ms) +ℹ tests 66 +ℹ suites 0 +ℹ pass 66 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3338.283638 diff --git a/agents/darkwing/work/s4-follow-up-review/out-r2/node-discord.txt b/agents/darkwing/work/s4-follow-up-review/out-r2/node-discord.txt new file mode 100644 index 00000000..6d4b8e65 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/out-r2/node-discord.txt @@ -0,0 +1,186 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.319218ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (10.341889ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.726016ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.523111ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (26.751513ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (8.335326ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.905778ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.501545ms) +✔ authorize: open channel, listed user (2.129233ms) +✔ authorize: wrong guild (0.209397ms) +✔ authorize: no guild (DM) (0.17105ms) +✔ authorize: unlisted channel (0.190673ms) +✔ authorize: unknown channel, no info (0.244211ms) +✔ authorize: thread of listed parent (0.186345ms) +✔ authorize: thread of unlisted parent (0.264489ms) +✔ authorize: text channel that is not a thread and not listed (0.157821ms) +✔ authorize: unlisted user (0.940363ms) +✔ authorize: no author (0.543602ms) +✔ authorize: bot author (listed id, bot flag) (0.24993ms) +✔ authorize: system author (0.304058ms) +✔ authorize: the bot itself (0.117909ms) +✔ authorize: webhook (0.097568ms) +✔ authorize: mention channel without mention (0.136286ms) +✔ authorize: mention channel with bot mention (0.141821ms) +✔ authorize: mention channel with @everyone only (0.300636ms) +✔ authorize: mention channel mentioning someone else (0.096957ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.110415ms) +✔ authorize: private thread under mention channel, mentioned (0.120272ms) +✔ authorize: private thread under mention channel, not mentioned (0.079172ms) +✔ authorize: thread in another guild per channel info (0.07309ms) +✔ authorize: not an object (0.066659ms) +✔ authorize: no id (0.064212ms) +✔ authorize: oversize content is accepted and flagged (0.070187ms) +✔ authorize: exactly the limit is not oversize (0.064804ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.52028ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.159049ms) +✔ binding: a complete binding validates and is frozen (3.068586ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.388934ms) +✔ binding: empty allowlists refuse (0.391764ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.155231ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.587279ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.209747ms) +✔ binding: file must be 0600, regular, not a symlink (1.974052ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (2.013453ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (88.069044ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.93664ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (294.845783ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (199.288899ms) +✔ cli: run refuses when STOP is present, before any network use (127.966382ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.89722ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.434949ms) +✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (23.024268ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (17.482703ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.579395ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.112008ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.396954ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.112956ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (31.945738ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.216269ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.229429ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (0.81815ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (40.682059ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.989003ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (6.138281ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.763872ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.282296ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.011565ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.230563ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.459549ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (2.530831ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (1.848736ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.468681ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.901839ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (4.590872ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.625169ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (1.674023ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.102774ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.258674ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.470798ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.532689ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.874631ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.768139ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.441234ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (42.414407ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (33.957373ms) +✔ engine: one prompt, one turn, text and usage come back (29.430695ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (332.244955ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (238.394961ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (105.376236ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (229.347905ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (136.426007ms) +✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (214.01799ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.727805ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.358632ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.451687ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (428.877802ms) +✔ engine: a malformed JSONL line fails the turn, not the process (28.369789ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (43.555174ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.422037ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.702045ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.689627ms) +✔ gateway: op 9 resumable resumes (0.343438ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.738513ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.49812ms) +✔ gateway: close() is final and unparseable frames are ignored (0.557496ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (54.06605ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (38.03988ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (65.779339ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.270821ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (77.363486ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (87.415844ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (86.215014ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (208.76542ms) +✔ git: push pushes the named branch only and reports up to date (70.945851ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (102.837324ms) +✔ git: the credential helper answers get over https from a private file and nothing else (206.730441ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (731.848737ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.177512ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (73.644698ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.748285ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.846541ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (35.63151ms) +✔ lock: a live pid whose record carries a malformed or noncanonical start or boot string is unknown, not a mismatch; nothing signals, removes, or claims over it (292.352316ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.44681ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (22.79696ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.994977ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (40.307438ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (142.645709ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (95.799317ms) +✔ notices: a kind is recorded per UTC day and found again (0.483637ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.398814ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.385403ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.743489ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (42.8104ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (30.081535ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (26.370058ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (67.302474ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (670.400393ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (3.699245ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.462425ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.672306ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.361755ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.094646ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.496834ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.793536ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.59691ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.130609ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (20.667767ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (9.116801ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.498466ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.563633ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.602942ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (2.626814ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1011.335094ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.435874ms) +✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (6.088849ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.355094ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.236364ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (3.70358ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (5.188167ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.122393ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.24716ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.611039ms) +✔ tools: listing and search caps hold (8.486574ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.853503ms) +✔ tools: the read uses the checked file itself; a symlink, a swapped file, a FIFO, a grown file or a hard link at read time is refused (5.282273ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.213689ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.334094ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.316392ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.392391ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.278135ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.565372ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.387745ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1026.320493ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (4.455325ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.242803ms) +✔ web: web_search asks the instance for json, returns at most ten clean results, and refuses a bad query, a down instance or an unusable answer (2.759567ms) +✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (2.461068ms) +ℹ tests 178 +ℹ suites 0 +ℹ pass 178 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2623.767118 diff --git a/agents/darkwing/work/s4-follow-up-review/out-r2/node-tasks.txt b/agents/darkwing/work/s4-follow-up-review/out-r2/node-tasks.txt new file mode 100644 index 00000000..3cca3e84 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/out-r2/node-tasks.txt @@ -0,0 +1,59 @@ +✔ the boot config is checked before anything starts (88.370383ms) +✔ a business with no tracker entry refuses task verbs (109.95431ms) +✔ credential.expiring and .expired are recorded once per instance (196.285908ms) +✔ a token file that changes on disk records credential.changed (98.992495ms) +✔ autostart polls, reconciles and retries a startup the tracker was down for (116.522176ms) +✔ a refusal a restart must clear is not retried by the poll (68.938902ms) +✔ a poll that fires while two are queued is dropped (80.786377ms) +✔ close waits for a running verb and refuses one that has not started (193.083833ms) +✔ the bundled Vikunja is the pinned upstream image the runbook names (0.761291ms) +✔ every published port is on 127.0.0.1, and no secret is in the file (0.270456ms) +✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (334.579482ms) +✔ the recorded task bodies pass the checks S3 applies to every read (0.527552ms) +✔ the client works against the fake over real HTTP with the platform fetch (224.546898ms) +✔ a correct install starts, and the first reconcile records tasks that already exist (93.611883ms) +✔ verbs refuse while a business is starting and after startup refused it (120.776979ms) +✔ startup refuses a token that can do more than its role needs (332.454022ms) +✔ startup refuses an unsupported version and flags an untested one (223.024049ms) +✔ startup refuses a board that the runbook did not install (333.513824ms) +✔ startup refuses a project the sync bot cannot read (69.336789ms) +✔ startup refuses a configured label the pm bot cannot see (60.319645ms) +✔ startup refuses an expired credential and a missing sync credential (158.510591ms) +✔ an unreachable tracker refuses with tracker-unavailable (87.544085ms) +✔ an edit in the UI is recorded once, with the fields that changed (183.880792ms) +✔ a move between open buckets is seen on the board, though updated does not change (163.615214ms) +✔ a person's comment is counted and a bot's is not (239.561847ms) +✔ the hourly reconcile catches a comment through comment_count (205.68284ms) +✔ a task closed in the UI leaves the open view with its done bucket (376.737742ms) +✔ a task that leaves the board is recorded as deleted, moved or out of reach (239.781275ms) +✔ a poll that read before a verb wrote does not overwrite the verb (162.559594ms) +✔ a tracker fault during a tick is reported and the next tick catches up (152.896094ms) +✔ a malformed answer refuses the tick with tracker-shape (126.29894ms) +✔ no token value reaches the database, the log or a refusal (251.223863ms) +✔ the first look at a task counts only comments inside the window (147.694916ms) +✔ task.create needs a recorded human request and a requirement id (183.457536ms) +✔ only labels named in the business file can be written (201.788761ms) +✔ task.schedule sets and clears a due date and relations (256.919575ms) +✔ assign and reassign move the role bots and record task.assigned (275.339038ms) +✔ task.update.assigned is for the assignee and records task.state (273.373553ms) +✔ a wrong expected digest records task.conflict and writes nothing (186.357206ms) +✔ a cross-role verb needs a resolved decision, used once (217.013242ms) +✔ task.close needs a verdict; after it every verb refuses with task-done (207.408928ms) +✔ a lost answer is settled by a re-read and never retried (234.351525ms) +✔ a create whose answer is lost is reported uncertain, and the poll finds the task (169.38299ms) +✔ a task the sync bot cannot read refuses and records nothing (116.75312ms) +✔ verbs and polls for one business run one at a time (219.459289ms) +✔ a due date with milliseconds is written to the second (164.713733ms) +✔ every write landed and the final read failed: the verb succeeds and records what it wrote (110.892235ms) +✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (74.56379ms) +✔ a create whose final read fails succeeds and records task.created (106.058791ms) +✔ an edit between the last write and the final read shows as external on the next poll (110.121599ms) +✔ task.created is recorded when a later label write fails (86.010496ms) +ℹ tests 51 +ℹ suites 0 +ℹ pass 51 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3287.736661 diff --git a/agents/darkwing/work/s4-follow-up-review/out-r2/summary.txt b/agents/darkwing/work/s4-follow-up-review/out-r2/summary.txt new file mode 100644 index 00000000..d7bd1894 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/out-r2/summary.txt @@ -0,0 +1,5 @@ +node cli rc=0 ℹ tests 66 ℹ pass 66 ℹ fail 0 +node discord rc=0 ℹ tests 178 ℹ pass 178 ℹ fail 0 +node bus rc=0 ℹ tests 67 ℹ pass 67 ℹ fail 0 +node tasks rc=0 ℹ tests 51 ℹ pass 51 ℹ fail 0 +test-discord rc=0 OK service install with USER unset finishes and names the account for lingering discord suite: 66 passed, 0 failed diff --git a/agents/darkwing/work/s4-follow-up-review/out-r2/test-discord.txt b/agents/darkwing/work/s4-follow-up-review/out-r2/test-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/out-r2/test-discord.txt @@ -0,0 +1,70 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/notify.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/notify.test.mjs +OK syntax: packages/discord/tests/recover.test.mjs +OK syntax: packages/discord/tests/rest.test.mjs +OK syntax: packages/discord/tests/setspark.test.mjs +OK syntax: packages/discord/tests/tools.test.mjs +OK syntax: packages/discord/tests/web.test.mjs +OK syntax: packages/discord/fixtures/claim-worker.mjs +OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs +OK syntax: scripts/discord.sh +OK syntax: scripts/discord-service.sh +OK packages/discord declares no dependencies +OK no bot-token-shaped string in packages/discord +OK fixture binding uses placeholder ids only +OK fixture binding validates +OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool +OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start +OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root +OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token +OK real pi with protocol vault adds reserve_id to the git verbs +OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key +OK real pi refuses a git key on a read-only root (fail closed) +OK real pi with the pilot flags (--no-tools) exposes no tool at all +OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed) +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test packages/discord/tests/ (ℹ pass 178) +OK scripts/discord.sh --help exits 0 +OK scripts/discord.sh check without a binding exits 4 +OK scripts/discord.sh recover without a binding exits 4 +OK scripts/discord.sh reload without a binding exits 4 +OK scripts/discord-service.sh without a command exits 4 +OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP +OK service install writes the rendered unit (0644) and leaves no temp file +OK service install a second time reports unchanged +OK systemd-analyze verify accepts the rendered unit +OK service uninstall removes the unit file +OK service install with an unknown flag exits 4 +OK service install with USER unset finishes and names the account for lingering + +discord suite: 66 passed, 0 failed diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/giveup-time-r2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/giveup-time-r2.txt new file mode 100644 index 00000000..ce0f18de --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/giveup-time-r2.txt @@ -0,0 +1,13 @@ +poll 30 s; sends at minutes after the first: 0.0, 30.0, 60.0, 60.0, 60.5, 61.5, 63.5, 67.5, 75.5, 90.0, 91.5, 120.0 +2026-10-09T12:00:00.000Z notify: dm refused (HTTP 403); retry in 1800 s +2026-10-09T12:30:00.000Z notify: dm refused (HTTP 403); retry in 1800 s +2026-10-09T13:00:00.000Z notify: dm refused (HTTP 403); retry in 1800 s +2026-10-09T13:00:00.000Z notify: digest refused (HTTP 403); retry in 30 s +2026-10-09T13:00:30.000Z notify: digest refused (HTTP 403); retry in 60 s +2026-10-09T13:01:30.000Z notify: digest refused (HTTP 403); retry in 120 s +2026-10-09T13:03:30.000Z notify: digest refused (HTTP 403); retry in 240 s +2026-10-09T13:07:30.000Z notify: digest refused (HTTP 403); retry in 480 s +2026-10-09T13:15:30.000Z notify: digest refused (HTTP 403); retry in 960 s +2026-10-09T13:30:00.000Z notify: dm refused (HTTP 403); retry in 1800 s +2026-10-09T13:31:30.000Z notify: digest refused (HTTP 403); retry in 1800 s +2026-10-09T14:00:00.000Z notify: dm dec-0001 refused 5 times; gave up, not retried diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/host-window.test.mjs b/agents/darkwing/work/s4-follow-up-review/probe-r2/host-window.test.mjs new file mode 100644 index 00000000..16f8f137 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/host-window.test.mjs @@ -0,0 +1,62 @@ +// Row 45 probe: the :144 window through startHost. Both children die at the +// same moment as the start send (as a cgroup-wide kill would do), so the +// broker can be dead while broker.connected is still true. Counts 'error' +// events on the broker and close sends; does not change host.mjs. +import { test } from "node:test"; +import { readFileSync } from "node:fs"; +import { subscribe, unsubscribe } from "node:diagnostics_channel"; +import { bootConfig, loadSystem } from "/home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/src/config.mjs"; +import { startHost } from "/home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/src/host.mjs"; +import { makeDeployment } from "/home/jwoltje/darkwing-scratch/r45b/wt/packages/discord/tests/helpers.mjs"; +import { fixture, tmp } from "/home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/helpers.mjs"; + +const N = Number(process.env.N ?? 30); +const SIG = process.env.SIG ?? "SIGKILL"; +const SPIN = Number(process.env.SPIN ?? 0); // ms of busy-wait between the two kills +const tally = { runs: 0, rejected: {}, closeSends: 0, errorEvents: {} }; + +for (let i = 0; i < N; i++) { + test(`window run ${i}`, async (t) => { + const root = tmp(t); + const f = fixture(root); + makeDeployment(root); + const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: "acme", env: f.env }); + const children = []; + const onChild = ({ process: child }) => { + children.push(child); + const send = child.send; + }; + subscribe("child_process", onChild); + t.after(() => unsubscribe("child_process", onChild)); + let armed = true; + const origSends = []; + const startSpy = ({ process: child }) => { + let send; + Object.defineProperty(child, "send", { configurable: true, get: () => send, set(fn) { + send = function (m, ...rest) { + if (m?.op === "close") tally.closeSends++; + const r = fn.call(this, m, ...rest); + if (m?.op === "start" && armed) { + armed = false; + if (!process.env.NOLISTEN) children[0].on("error", (e) => (tally.errorEvents[e.code] = (tally.errorEvents[e.code] ?? 0) + 1)); + children[0].kill(SIG); + const until = performance.now() + SPIN; + while (performance.now() < until); + // ZOMBIE: wait until the kernel has the broker dead (state Z), so its end of the channel is closed. + if (process.env.ZOMBIE) while (!/\) Z /.test(readFileSync("/proc/" + children[0].pid + "/stat", "utf8"))); + children[1].kill(SIG); + } + return r; + }; + } }); + }; + subscribe("child_process", startSpy); + t.after(() => unsubscribe("child_process", startSpy)); + const started = startHost({ boot, business: "acme", notifier: { binding: "test-seat" }, log: () => {} }); + const r = await started.then((h) => (h.close(0), "started"), (e) => `${e.exitCode}: ${e.message.replace(/\(\d+\)/, "(n)")}`); + await new Promise((r) => setTimeout(r, 20)); + tally.runs++; + tally.rejected[r] = (tally.rejected[r] ?? 0) + 1; + }); +} +test("tally", () => console.log("TALLY " + JSON.stringify({ SIG, SPIN, ...tally }))); diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.0-x.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.0-x.txt new file mode 100644 index 00000000..8fc80773 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.0-x.txt @@ -0,0 +1,50 @@ +✔ window run 0 (141.775913ms) +✔ window run 1 (128.801907ms) +✔ window run 2 (122.224765ms) +✔ window run 3 (120.55733ms) +✔ window run 4 (169.60705ms) +✔ window run 5 (131.958959ms) +✔ window run 6 (126.915052ms) +✔ window run 7 (114.99937ms) +✔ window run 8 (121.182822ms) +✔ window run 9 (120.432767ms) +✔ window run 10 (111.317391ms) +✔ window run 11 (118.376653ms) +✔ window run 12 (116.812163ms) +✔ window run 13 (117.915317ms) +✔ window run 14 (114.568821ms) +✔ window run 15 (120.844385ms) +✔ window run 16 (124.351157ms) +✔ window run 17 (246.938018ms) +✔ window run 18 (135.490176ms) +✔ window run 19 (135.996266ms) +✔ window run 20 (147.561527ms) +✔ window run 21 (147.364625ms) +✔ window run 22 (136.565185ms) +✔ window run 23 (130.609943ms) +✔ window run 24 (129.815264ms) +✔ window run 25 (118.789473ms) +✔ window run 26 (122.300832ms) +✔ window run 27 (121.1387ms) +✔ window run 28 (112.614013ms) +✔ window run 29 (119.452852ms) +✔ window run 30 (123.123535ms) +✔ window run 31 (125.832031ms) +✔ window run 32 (116.596912ms) +✔ window run 33 (121.209475ms) +✔ window run 34 (113.816661ms) +✔ window run 35 (114.378503ms) +✔ window run 36 (121.710931ms) +✔ window run 37 (130.852711ms) +✔ window run 38 (119.459241ms) +TALLY {"SIG":"SIGKILL","SPIN":1,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":34,"errorEvents":{}} +✔ window run 39 (122.578679ms) +✔ tally (0.364378ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5220.718135 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.2-x.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.2-x.txt new file mode 100644 index 00000000..889029bc --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.2-x.txt @@ -0,0 +1,50 @@ +✔ window run 0 (131.341481ms) +✔ window run 1 (128.087357ms) +✔ window run 2 (124.400352ms) +✔ window run 3 (128.290452ms) +✔ window run 4 (167.681471ms) +✔ window run 5 (172.17902ms) +✔ window run 6 (139.660295ms) +✔ window run 7 (123.332112ms) +✔ window run 8 (139.039813ms) +✔ window run 9 (154.833525ms) +✔ window run 10 (131.85663ms) +✔ window run 11 (141.022111ms) +✔ window run 12 (129.008786ms) +✔ window run 13 (129.266634ms) +✔ window run 14 (127.802054ms) +✔ window run 15 (128.21902ms) +✔ window run 16 (148.437164ms) +✔ window run 17 (146.149854ms) +✔ window run 18 (140.390096ms) +✔ window run 19 (130.447972ms) +✔ window run 20 (125.66318ms) +✔ window run 21 (127.645275ms) +✔ window run 22 (134.712369ms) +✔ window run 23 (212.831125ms) +✔ window run 24 (160.811058ms) +✔ window run 25 (151.876243ms) +✔ window run 26 (158.656402ms) +✔ window run 27 (151.846909ms) +✔ window run 28 (138.794384ms) +✔ window run 29 (156.56767ms) +✔ window run 30 (152.019386ms) +✔ window run 31 (152.23568ms) +✔ window run 32 (142.229226ms) +✔ window run 33 (150.701444ms) +✔ window run 34 (141.009523ms) +✔ window run 35 (129.71958ms) +✔ window run 36 (139.951769ms) +✔ window run 37 (148.138706ms) +✔ window run 38 (126.708619ms) +TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":32,"errorEvents":{}} +✔ window run 39 (138.358993ms) +✔ tally (0.453262ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5791.811271 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.2.txt new file mode 100644 index 00000000..33a1059d --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.2.txt @@ -0,0 +1,50 @@ +✔ window run 0 (134.461453ms) +✔ window run 1 (125.454162ms) +✔ window run 2 (120.844259ms) +✔ window run 3 (118.278859ms) +✔ window run 4 (119.420377ms) +✔ window run 5 (123.618231ms) +✔ window run 6 (114.292974ms) +✔ window run 7 (128.563253ms) +✔ window run 8 (116.479373ms) +✔ window run 9 (119.741072ms) +✔ window run 10 (121.673034ms) +✔ window run 11 (126.334584ms) +✔ window run 12 (121.757228ms) +✔ window run 13 (113.524238ms) +✔ window run 14 (116.188965ms) +✔ window run 15 (123.127899ms) +✔ window run 16 (118.355664ms) +✔ window run 17 (123.105738ms) +✔ window run 18 (112.473591ms) +✔ window run 19 (122.378022ms) +✔ window run 20 (121.221472ms) +✔ window run 21 (118.322399ms) +✔ window run 22 (129.595055ms) +✔ window run 23 (117.333743ms) +✔ window run 24 (125.865197ms) +✔ window run 25 (119.018576ms) +✔ window run 26 (126.665665ms) +✔ window run 27 (118.902827ms) +✔ window run 28 (118.759938ms) +✔ window run 29 (123.575605ms) +✔ window run 30 (134.11206ms) +✔ window run 31 (124.494316ms) +✔ window run 32 (174.835845ms) +✔ window run 33 (120.345857ms) +✔ window run 34 (115.834247ms) +✔ window run 35 (118.843564ms) +✔ window run 36 (125.738016ms) +✔ window run 37 (116.990457ms) +✔ window run 38 (118.406161ms) +TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":16,"errorEvents":{}} +✔ window run 39 (121.674051ms) +✔ tally (0.929849ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 4994.732641 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.4-x.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.4-x.txt new file mode 100644 index 00000000..86b4c31b --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.4-x.txt @@ -0,0 +1,50 @@ +✔ window run 0 (150.423142ms) +✔ window run 1 (129.678496ms) +✔ window run 2 (135.64539ms) +✔ window run 3 (134.545777ms) +✔ window run 4 (133.854188ms) +✔ window run 5 (131.422526ms) +✔ window run 6 (146.549266ms) +✔ window run 7 (125.651603ms) +✔ window run 8 (138.88489ms) +✔ window run 9 (138.303077ms) +✔ window run 10 (133.06485ms) +✔ window run 11 (135.114358ms) +✔ window run 12 (135.602968ms) +✔ window run 13 (138.54996ms) +✔ window run 14 (148.336899ms) +✔ window run 15 (129.236753ms) +✔ window run 16 (134.154075ms) +✔ window run 17 (142.141297ms) +✔ window run 18 (140.955466ms) +✔ window run 19 (150.086029ms) +✔ window run 20 (127.725663ms) +✔ window run 21 (134.892136ms) +✔ window run 22 (136.840094ms) +✔ window run 23 (134.386007ms) +✔ window run 24 (132.939513ms) +✔ window run 25 (149.985061ms) +✔ window run 26 (130.637847ms) +✔ window run 27 (126.481832ms) +✔ window run 28 (139.730464ms) +✔ window run 29 (138.867103ms) +✔ window run 30 (138.381503ms) +✔ window run 31 (132.513838ms) +✔ window run 32 (147.811325ms) +✔ window run 33 (134.918279ms) +✔ window run 34 (135.444945ms) +✔ window run 35 (140.115305ms) +✔ window run 36 (134.210217ms) +✔ window run 37 (132.593278ms) +✔ window run 38 (134.326022ms) +TALLY {"SIG":"SIGKILL","SPIN":1.4,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":24,"errorEvents":{}} +✔ window run 39 (157.539671ms) +✔ tally (0.357915ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5596.365423 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.5.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.5.txt new file mode 100644 index 00000000..7a5858cc --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.5.txt @@ -0,0 +1,50 @@ +✔ window run 0 (213.128355ms) +✔ window run 1 (153.415211ms) +✔ window run 2 (152.620388ms) +✔ window run 3 (148.341557ms) +✔ window run 4 (135.356542ms) +✔ window run 5 (153.319339ms) +✔ window run 6 (139.776851ms) +✔ window run 7 (139.392433ms) +✔ window run 8 (141.470615ms) +✔ window run 9 (127.012135ms) +✔ window run 10 (132.152329ms) +✔ window run 11 (129.520072ms) +✔ window run 12 (126.202089ms) +✔ window run 13 (128.173216ms) +✔ window run 14 (137.681637ms) +✔ window run 15 (139.430765ms) +✔ window run 16 (122.512087ms) +✔ window run 17 (129.919929ms) +✔ window run 18 (130.558413ms) +✔ window run 19 (131.36124ms) +✔ window run 20 (129.013316ms) +✔ window run 21 (166.617631ms) +✔ window run 22 (123.012037ms) +✔ window run 23 (135.309034ms) +✔ window run 24 (126.446534ms) +✔ window run 25 (122.134556ms) +✔ window run 26 (125.617635ms) +✔ window run 27 (128.164182ms) +✔ window run 28 (128.70883ms) +✔ window run 29 (122.974159ms) +✔ window run 30 (135.188099ms) +✔ window run 31 (130.358744ms) +✔ window run 32 (121.987698ms) +✔ window run 33 (123.236884ms) +✔ window run 34 (128.304914ms) +✔ window run 35 (124.938594ms) +✔ window run 36 (124.626017ms) +✔ window run 37 (132.164621ms) +✔ window run 38 (141.388114ms) +TALLY {"SIG":"SIGKILL","SPIN":1.5,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":17,"errorEvents":{}} +✔ window run 39 (155.341197ms) +✔ tally (0.317348ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5563.638746 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.8.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.8.txt new file mode 100644 index 00000000..48e2b8d7 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-cand-1.8.txt @@ -0,0 +1,50 @@ +✔ window run 0 (130.998528ms) +✔ window run 1 (119.704397ms) +✔ window run 2 (128.992131ms) +✔ window run 3 (120.791647ms) +✔ window run 4 (121.253779ms) +✔ window run 5 (121.657691ms) +✔ window run 6 (119.084951ms) +✔ window run 7 (120.267738ms) +✔ window run 8 (119.640471ms) +✔ window run 9 (145.253415ms) +✔ window run 10 (118.139956ms) +✔ window run 11 (124.894068ms) +✔ window run 12 (117.727296ms) +✔ window run 13 (136.557691ms) +✔ window run 14 (131.940352ms) +✔ window run 15 (119.838904ms) +✔ window run 16 (125.877707ms) +✔ window run 17 (120.973525ms) +✔ window run 18 (137.067272ms) +✔ window run 19 (130.935893ms) +✔ window run 20 (120.226148ms) +✔ window run 21 (125.321337ms) +✔ window run 22 (120.503036ms) +✔ window run 23 (120.708151ms) +✔ window run 24 (119.972725ms) +✔ window run 25 (122.504684ms) +✔ window run 26 (118.988665ms) +✔ window run 27 (118.134286ms) +✔ window run 28 (119.295177ms) +✔ window run 29 (123.745992ms) +✔ window run 30 (130.268251ms) +✔ window run 31 (122.333084ms) +✔ window run 32 (120.073336ms) +✔ window run 33 (121.821046ms) +✔ window run 34 (122.692467ms) +✔ window run 35 (118.482916ms) +✔ window run 36 (124.584566ms) +✔ window run 37 (116.446765ms) +✔ window run 38 (121.984162ms) +TALLY {"SIG":"SIGKILL","SPIN":1.8,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":3,"errorEvents":{}} +✔ window run 39 (124.246021ms) +✔ tally (0.320662ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5023.504777 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.2.txt new file mode 100644 index 00000000..137d4faa --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.2.txt @@ -0,0 +1,50 @@ +✔ window run 0 (135.122939ms) +✔ window run 1 (130.830505ms) +✔ window run 2 (125.280209ms) +✔ window run 3 (128.350504ms) +✔ window run 4 (130.561417ms) +✔ window run 5 (116.417611ms) +✔ window run 6 (118.605832ms) +✔ window run 7 (121.551752ms) +✔ window run 8 (127.001897ms) +✔ window run 9 (122.214684ms) +✔ window run 10 (130.72462ms) +✔ window run 11 (129.924305ms) +✔ window run 12 (128.178601ms) +✔ window run 13 (121.399729ms) +✔ window run 14 (128.906791ms) +✔ window run 15 (131.373785ms) +✔ window run 16 (122.8896ms) +✔ window run 17 (123.958448ms) +✔ window run 18 (120.251958ms) +✔ window run 19 (121.732404ms) +✔ window run 20 (124.560213ms) +✔ window run 21 (117.381408ms) +✔ window run 22 (122.983482ms) +✔ window run 23 (119.628213ms) +✔ window run 24 (130.017879ms) +✔ window run 25 (144.728019ms) +✔ window run 26 (126.052281ms) +✔ window run 27 (131.412223ms) +✔ window run 28 (127.639003ms) +✔ window run 29 (131.003141ms) +✔ window run 30 (134.270388ms) +✔ window run 31 (136.042648ms) +✔ window run 32 (128.801888ms) +✔ window run 33 (129.968451ms) +✔ window run 34 (118.479997ms) +✔ window run 35 (137.586164ms) +✔ window run 36 (131.670247ms) +✔ window run 37 (153.289132ms) +✔ window run 38 (137.929792ms) +TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":34,"undefined: write EPIPE":6},"closeSends":20,"errorEvents":{}} +✔ window run 39 (135.791186ms) +✔ tally (0.361776ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 23987.426252 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.5.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.5.txt new file mode 100644 index 00000000..df9abc01 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.5.txt @@ -0,0 +1,50 @@ +✔ window run 0 (128.454236ms) +✔ window run 1 (142.346211ms) +✔ window run 2 (143.004179ms) +✔ window run 3 (122.139231ms) +✔ window run 4 (123.584734ms) +✔ window run 5 (122.762427ms) +✔ window run 6 (119.591144ms) +✔ window run 7 (119.323631ms) +✔ window run 8 (120.790452ms) +✔ window run 9 (117.146343ms) +✔ window run 10 (119.426328ms) +✔ window run 11 (114.21452ms) +✔ window run 12 (119.97472ms) +✔ window run 13 (122.748017ms) +✔ window run 14 (116.660207ms) +✔ window run 15 (120.981453ms) +✔ window run 16 (115.099923ms) +✔ window run 17 (118.736257ms) +✔ window run 18 (134.113383ms) +✔ window run 19 (126.189466ms) +✔ window run 20 (118.381386ms) +✔ window run 21 (123.819604ms) +✔ window run 22 (124.274617ms) +✔ window run 23 (119.065609ms) +✔ window run 24 (115.889118ms) +✔ window run 25 (113.572234ms) +✔ window run 26 (122.461122ms) +✔ window run 27 (116.525172ms) +✔ window run 28 (112.16788ms) +✔ window run 29 (124.721334ms) +✔ window run 30 (117.365707ms) +✔ window run 31 (116.529775ms) +✔ window run 32 (118.083284ms) +✔ window run 33 (118.283334ms) +✔ window run 34 (120.27732ms) +✔ window run 35 (120.791974ms) +✔ window run 36 (124.325033ms) +✔ window run 37 (118.66312ms) +✔ window run 38 (122.882066ms) +TALLY {"SIG":"SIGKILL","SPIN":1.5,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":8,"errorEvents":{}} +✔ window run 39 (117.968416ms) +✔ tally (0.348867ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 4927.807788 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.8.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.8.txt new file mode 100644 index 00000000..e99ac3d3 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nocb-1.8.txt @@ -0,0 +1,50 @@ +✔ window run 0 (144.224291ms) +✔ window run 1 (129.396254ms) +✔ window run 2 (128.07545ms) +✔ window run 3 (128.077895ms) +✔ window run 4 (118.253611ms) +✔ window run 5 (120.717634ms) +✔ window run 6 (123.163777ms) +✔ window run 7 (118.897388ms) +✔ window run 8 (119.303061ms) +✔ window run 9 (120.444716ms) +✔ window run 10 (124.18566ms) +✔ window run 11 (118.053805ms) +✔ window run 12 (115.225239ms) +✔ window run 13 (120.643862ms) +✔ window run 14 (122.46287ms) +✔ window run 15 (117.717123ms) +✔ window run 16 (123.773426ms) +✔ window run 17 (120.774322ms) +✔ window run 18 (138.138442ms) +✔ window run 19 (133.690503ms) +✔ window run 20 (125.271627ms) +✔ window run 21 (131.574194ms) +✔ window run 22 (119.464835ms) +✔ window run 23 (125.506749ms) +✔ window run 24 (155.395426ms) +✔ window run 25 (178.540602ms) +✔ window run 26 (126.017958ms) +✔ window run 27 (121.052863ms) +✔ window run 28 (124.295782ms) +✔ window run 29 (126.573336ms) +✔ window run 30 (122.492436ms) +✔ window run 31 (121.830347ms) +✔ window run 32 (117.744637ms) +✔ window run 33 (118.159241ms) +✔ window run 34 (116.895203ms) +✔ window run 35 (121.259299ms) +✔ window run 36 (123.21738ms) +✔ window run 37 (130.53615ms) +✔ window run 38 (131.87434ms) +TALLY {"SIG":"SIGKILL","SPIN":1.8,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":7,"errorEvents":{}} +✔ window run 39 (114.693736ms) +✔ tally (0.315463ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5110.552176 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nolisten-1.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nolisten-1.txt new file mode 100644 index 00000000..3d1c6698 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nolisten-1.txt @@ -0,0 +1,50 @@ +✔ window run 0 (127.58092ms) +✔ window run 1 (162.627319ms) +✔ window run 2 (127.693204ms) +✔ window run 3 (125.629054ms) +✔ window run 4 (119.967212ms) +✔ window run 5 (118.150815ms) +✔ window run 6 (180.734978ms) +✔ window run 7 (140.129292ms) +✔ window run 8 (130.352157ms) +✔ window run 9 (119.357903ms) +✔ window run 10 (144.928105ms) +✔ window run 11 (401.668465ms) +✔ window run 12 (461.248243ms) +✔ window run 13 (650.094085ms) +✔ window run 14 (434.334983ms) +✔ window run 15 (172.035638ms) +✔ window run 16 (128.637775ms) +✔ window run 17 (128.864828ms) +✔ window run 18 (122.010379ms) +✔ window run 19 (123.539318ms) +✔ window run 20 (123.093834ms) +✔ window run 21 (130.221603ms) +✔ window run 22 (122.416697ms) +✔ window run 23 (150.960852ms) +✔ window run 24 (128.917022ms) +✔ window run 25 (134.380817ms) +✔ window run 26 (125.219328ms) +✔ window run 27 (132.753926ms) +✔ window run 28 (122.113979ms) +✔ window run 29 (122.278419ms) +✔ window run 30 (122.297869ms) +✔ window run 31 (120.82054ms) +✔ window run 32 (132.674865ms) +✔ window run 33 (128.975462ms) +✔ window run 34 (125.417454ms) +✔ window run 35 (120.8199ms) +✔ window run 36 (130.788311ms) +✔ window run 37 (128.351075ms) +✔ window run 38 (149.992294ms) +TALLY {"SIG":"SIGKILL","SPIN":2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":0,"errorEvents":{}} +✔ window run 39 (120.503523ms) +✔ tally (0.354952ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 6774.656592 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nolisten-2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nolisten-2.txt new file mode 100644 index 00000000..25d6cb43 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-nolisten-2.txt @@ -0,0 +1,50 @@ +✔ window run 0 (136.774204ms) +✔ window run 1 (127.897477ms) +✔ window run 2 (120.689443ms) +✔ window run 3 (151.123263ms) +✔ window run 4 (155.605552ms) +✔ window run 5 (120.71843ms) +✔ window run 6 (124.778061ms) +✔ window run 7 (135.363919ms) +✔ window run 8 (126.663916ms) +✔ window run 9 (120.581723ms) +✔ window run 10 (123.88765ms) +✔ window run 11 (124.882036ms) +✔ window run 12 (150.339719ms) +✔ window run 13 (123.303618ms) +✔ window run 14 (127.070752ms) +✔ window run 15 (128.472236ms) +✔ window run 16 (118.586597ms) +✔ window run 17 (125.180194ms) +✔ window run 18 (120.451018ms) +✔ window run 19 (128.182541ms) +✔ window run 20 (123.20001ms) +✔ window run 21 (122.100919ms) +✔ window run 22 (119.403525ms) +✔ window run 23 (173.776168ms) +✔ window run 24 (134.439389ms) +✔ window run 25 (130.349125ms) +✔ window run 26 (127.978361ms) +✔ window run 27 (136.333253ms) +✔ window run 28 (120.925399ms) +✔ window run 29 (129.149668ms) +✔ window run 30 (132.693504ms) +✔ window run 31 (123.656819ms) +✔ window run 32 (126.766868ms) +✔ window run 33 (120.573641ms) +✔ window run 34 (135.714167ms) +✔ window run 35 (135.710477ms) +✔ window run 36 (117.249605ms) +✔ window run 37 (119.018592ms) +✔ window run 38 (131.310506ms) +TALLY {"SIG":"SIGKILL","SPIN":2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":0,"errorEvents":{}} +✔ window run 39 (120.801524ms) +✔ tally (0.334269ms) +ℹ tests 41 +ℹ suites 0 +ℹ pass 41 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5251.675554 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin0.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin0.txt new file mode 100644 index 00000000..64fad244 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin0.txt @@ -0,0 +1,40 @@ +✔ window run 0 (132.487146ms) +✔ window run 1 (124.968965ms) +✔ window run 2 (124.213142ms) +✔ window run 3 (120.3321ms) +✔ window run 4 (124.944505ms) +✔ window run 5 (142.543814ms) +✔ window run 6 (127.955434ms) +✔ window run 7 (125.514636ms) +✔ window run 8 (136.942164ms) +✔ window run 9 (138.072103ms) +✔ window run 10 (129.780512ms) +✔ window run 11 (118.296274ms) +✔ window run 12 (122.711134ms) +✔ window run 13 (121.931431ms) +✔ window run 14 (110.439928ms) +✔ window run 15 (117.012974ms) +✔ window run 16 (117.55668ms) +✔ window run 17 (128.429878ms) +✔ window run 18 (122.460641ms) +✔ window run 19 (138.367156ms) +✔ window run 20 (117.440095ms) +✔ window run 21 (126.05533ms) +✔ window run 22 (129.696191ms) +✔ window run 23 (117.591068ms) +✔ window run 24 (118.745547ms) +✔ window run 25 (129.883241ms) +✔ window run 26 (118.206089ms) +✔ window run 27 (118.678953ms) +✔ window run 28 (129.870729ms) +TALLY {"SIG":"SIGKILL","SPIN":0,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":30,"errorEvents":{}} +✔ window run 29 (121.367229ms) +✔ tally (0.391525ms) +ℹ tests 31 +ℹ suites 0 +ℹ pass 31 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3825.283784 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin1.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin1.txt new file mode 100644 index 00000000..4ec63bed --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin1.txt @@ -0,0 +1,40 @@ +✔ window run 0 (123.23705ms) +✔ window run 1 (123.961923ms) +✔ window run 2 (125.332242ms) +✔ window run 3 (119.429363ms) +✔ window run 4 (115.543357ms) +✔ window run 5 (112.826753ms) +✔ window run 6 (116.923424ms) +✔ window run 7 (116.256691ms) +✔ window run 8 (113.931748ms) +✔ window run 9 (123.418455ms) +✔ window run 10 (121.795499ms) +✔ window run 11 (119.200194ms) +✔ window run 12 (120.635779ms) +✔ window run 13 (120.632373ms) +✔ window run 14 (124.328492ms) +✔ window run 15 (121.649256ms) +✔ window run 16 (117.564938ms) +✔ window run 17 (151.390084ms) +✔ window run 18 (117.289768ms) +✔ window run 19 (129.194512ms) +✔ window run 20 (123.363796ms) +✔ window run 21 (142.082497ms) +✔ window run 22 (121.358209ms) +✔ window run 23 (355.482528ms) +✔ window run 24 (119.360699ms) +✔ window run 25 (116.390105ms) +✔ window run 26 (118.809952ms) +✔ window run 27 (126.164074ms) +✔ window run 28 (119.761348ms) +TALLY {"SIG":"SIGKILL","SPIN":1,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":25,"errorEvents":{}} +✔ window run 29 (115.468358ms) +✔ tally (0.338162ms) +ℹ tests 31 +ℹ suites 0 +ℹ pass 31 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3968.902661 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin2.txt new file mode 100644 index 00000000..cf8442a7 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin2.txt @@ -0,0 +1,40 @@ +✔ window run 0 (122.416389ms) +✔ window run 1 (123.205492ms) +✔ window run 2 (121.140654ms) +✔ window run 3 (134.247788ms) +✔ window run 4 (136.490509ms) +✔ window run 5 (120.542247ms) +✔ window run 6 (129.277154ms) +✔ window run 7 (125.004173ms) +✔ window run 8 (120.097454ms) +✔ window run 9 (118.368948ms) +✔ window run 10 (116.042869ms) +✔ window run 11 (127.958125ms) +✔ window run 12 (121.512347ms) +✔ window run 13 (116.729365ms) +✔ window run 14 (124.872986ms) +✔ window run 15 (119.443772ms) +✔ window run 16 (117.00815ms) +✔ window run 17 (116.119014ms) +✔ window run 18 (119.493366ms) +✔ window run 19 (125.80199ms) +✔ window run 20 (126.38055ms) +✔ window run 21 (117.431147ms) +✔ window run 22 (120.440654ms) +✔ window run 23 (127.229032ms) +✔ window run 24 (124.86833ms) +✔ window run 25 (121.312387ms) +✔ window run 26 (121.249029ms) +✔ window run 27 (126.269621ms) +✔ window run 28 (128.307128ms) +TALLY {"SIG":"SIGKILL","SPIN":2,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":0,"errorEvents":{}} +✔ window run 29 (118.851188ms) +✔ tally (0.379132ms) +ℹ tests 31 +ℹ suites 0 +ℹ pass 31 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3758.865814 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin5.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin5.txt new file mode 100644 index 00000000..0543fd9b --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-spin5.txt @@ -0,0 +1,40 @@ +✔ window run 0 (136.105562ms) +✔ window run 1 (126.816338ms) +✔ window run 2 (119.846457ms) +✔ window run 3 (121.782267ms) +✔ window run 4 (128.762498ms) +✔ window run 5 (119.906631ms) +✔ window run 6 (123.896163ms) +✔ window run 7 (119.086444ms) +✔ window run 8 (120.050441ms) +✔ window run 9 (118.135927ms) +✔ window run 10 (118.249898ms) +✔ window run 11 (129.107726ms) +✔ window run 12 (126.131622ms) +✔ window run 13 (124.325597ms) +✔ window run 14 (122.411699ms) +✔ window run 15 (121.811091ms) +✔ window run 16 (122.758996ms) +✔ window run 17 (123.200781ms) +✔ window run 18 (135.185419ms) +✔ window run 19 (147.180265ms) +✔ window run 20 (122.065511ms) +✔ window run 21 (126.140412ms) +✔ window run 22 (124.599734ms) +✔ window run 23 (122.169503ms) +✔ window run 24 (120.563634ms) +✔ window run 25 (123.590214ms) +✔ window run 26 (125.482938ms) +✔ window run 27 (128.60004ms) +✔ window run 28 (138.365322ms) +TALLY {"SIG":"SIGKILL","SPIN":5,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":0,"errorEvents":{}} +✔ window run 29 (120.901641ms) +✔ tally (0.425858ms) +ℹ tests 31 +ℹ suites 0 +ℹ pass 31 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3836.882858 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-sweep.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-sweep.txt new file mode 100644 index 00000000..68d9900a --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2-sweep.txt @@ -0,0 +1,14 @@ +Fri Oct 9 02:13:28 PM UTC 2026 +cand SPIN 1.2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":16,"errorEvents":{}} unhandled=0 +nocb SPIN 1.2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":34,"undefined: write EPIPE":6},"closeSends":20,"errorEvents":{}} unhandled=0 +cand SPIN 1.5 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.5,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":17,"errorEvents":{}} unhandled=0 +nocb SPIN 1.5 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.5,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":8,"errorEvents":{}} unhandled=0 +cand SPIN 1.8 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.8,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":3,"errorEvents":{}} unhandled=0 +nocb SPIN 1.8 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.8,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":7,"errorEvents":{}} unhandled=0 +Fri Oct 9 02:14:18 PM UTC 2026 +cand SPIN 1.0 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":34,"errorEvents":{}} unhandled=0 +cand SPIN 1.2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":23,"errorEvents":{}} unhandled=0 +cand SPIN 1.2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":18,"errorEvents":{}} unhandled=0 +cand SPIN 1.2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":32,"errorEvents":{}} unhandled=0 +cand SPIN 1.4 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1.4,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":24,"errorEvents":{}} unhandled=0 +Fri Oct 9 02:14:52 PM UTC 2026 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2.txt new file mode 100644 index 00000000..e515a0dd --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/hw-r2.txt @@ -0,0 +1,8 @@ + 09:12:51 up 33 days, 10:47, 5 users, load average: 3.31, 2.79, 2.53 +nolisten batch 1 rc=0 TALLY {"SIG":"SIGKILL","SPIN":2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":0,"errorEvents":{}} ℹ fail 0 unhandled=0 +nolisten batch 2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":2,"runs":40,"rejected":{"1: notifier exited (null) before it replied":40},"closeSends":0,"errorEvents":{}} ℹ fail 0 unhandled=0 +listener SPIN 0 rc=0 TALLY {"SIG":"SIGKILL","SPIN":0,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":30,"errorEvents":{}} ℹ fail 0 +listener SPIN 1 rc=0 TALLY {"SIG":"SIGKILL","SPIN":1,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":25,"errorEvents":{}} ℹ fail 0 +listener SPIN 2 rc=0 TALLY {"SIG":"SIGKILL","SPIN":2,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":0,"errorEvents":{}} ℹ fail 0 +listener SPIN 5 rc=0 TALLY {"SIG":"SIGKILL","SPIN":5,"runs":30,"rejected":{"1: notifier exited (null) before it replied":30},"closeSends":0,"errorEvents":{}} ℹ fail 0 + 09:13:19 up 33 days, 10:48, 5 users, load average: 3.33, 2.85, 2.55 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/journal-paths-r2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/journal-paths-r2.txt new file mode 100644 index 00000000..419e5a9a --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/journal-paths-r2.txt @@ -0,0 +1,19 @@ +new dir opens +missing dir, parent 0500 CliError exit=3 code=-: notify journal directory cannot be created (EACCES): /c1/acme +dir 0500 CliError exit=3 code=-: notify journal directory is not writable (EACCES): /c2 +dir 0300 (no read) opens +dir 0755 CliError exit=3 code=-: notify journal directory must be mode 0700 and owned by this user: /c4 +dir symlink to 0700 CliError exit=3 code=-: notify journal directory must not be a symlink: /c6 +dir dangling symlink CliError exit=3 code=-: notify journal directory must not be a symlink: /c7 +dir path is a file CliError exit=3 code=-: notify journal directory must be mode 0700 and owned by this user: /c8 +parent is a file CliError exit=3 code=-: notify journal directory cannot be created (ENOTDIR): /c9/acme +file 0400 CliError exit=3 code=-: notify journal is not writable (EACCES): /c10/sent.jsonl +file 0644 CliError exit=3 code=-: notify journal must be a regular file, mode 0600, owned by this user: /c11/sent.jsonl +file path is a dir CliError exit=3 code=-: notify journal must be a regular file, mode 0600, owned by this user: /c12/sent.jsonl +gave-up dm opens +gave-up digest CliError exit=3 code=-: notify journal line 1 is malformed (outcome): /c14/sent.jsonl +refused dm status 403 opens +refused dm status "403" CliError exit=3 code=-: notify journal line 1 is malformed (status): /c16/sent.jsonl +digest day 2026-13-45 CliError exit=3 code=-: notify journal line 1 is malformed (day): /c17/sent.jsonl +at without ms CliError exit=3 code=-: notify journal line 1 is malformed (at): /c18/sent.jsonl +dm decision 7 CliError exit=3 code=-: notify journal line 1 is malformed (decision): /c19/sent.jsonl diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/late-send-r2.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/late-send-r2.txt new file mode 100644 index 00000000..6dec5f13 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/late-send-r2.txt @@ -0,0 +1,16 @@ +Fri Oct 9 02:14:58 PM UTC 2026 + 20 rc=0 {"delay":"sync","cb":false,"connected":true,"sent":true,"errorEvent":null} + 20 rc=0 {"delay":"immediate","cb":false,"connected":true,"sent":true,"errorEvent":null} + 6 rc=0 {"delay":"1","cb":false,"connected":false,"sent":null,"errorEvent":null} + 11 rc=0 {"delay":"1","cb":false,"connected":true,"sent":true,"errorEvent":null} + 3 rc=1 node:events:505 throw er; // Unhandled 'error' event ^ Error: write EPIPE + 20 rc=0 {"delay":"2","cb":false,"connected":false,"sent":null,"errorEvent":null} + 20 rc=0 {"delay":"5","cb":false,"connected":false,"sent":null,"errorEvent":null} + 20 rc=0 {"delay":"sync","cb":true,"connected":true,"sent":true,"cbErr":null,"errorEvent":null} + 20 rc=0 {"delay":"immediate","cb":true,"connected":true,"sent":true,"cbErr":null,"errorEvent":null} + 8 rc=0 {"delay":"1","cb":true,"connected":false,"sent":null,"errorEvent":null} + 10 rc=0 {"delay":"1","cb":true,"connected":true,"sent":true,"cbErr":"EPIPE","errorEvent":null} + 2 rc=0 {"delay":"1","cb":true,"connected":true,"sent":true,"cbErr":null,"errorEvent":null} + 20 rc=0 {"delay":"2","cb":true,"connected":false,"sent":null,"errorEvent":null} + 20 rc=0 {"delay":"5","cb":true,"connected":false,"sent":null,"errorEvent":null} +Fri Oct 9 02:15:44 PM UTC 2026 diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Mb-nocb150.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Mb-nocb150.txt new file mode 100644 index 00000000..225464ee --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Mb-nocb150.txt @@ -0,0 +1,53 @@ +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (901.128701ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (200.974735ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (143.839337ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (177.949725ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (166.371407ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (109.334297ms) +✖ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (169.53424ms) +✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (113.379595ms) +✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (180.612226ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (25.853366ms) +✔ bus stop refuses to signal a live pid that is not a bus host (203.250525ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (229.322779ms) +✔ bus start refuses with exit 3 without a notifier config (100.568013ms) +✔ bus start runs until bus stop; status reports it while it runs (695.909159ms) +✔ bus-service.sh renders the unit and installs it into a given directory (38.912292ms) +ℹ tests 15 +ℹ suites 0 +ℹ pass 14 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 22035.449879 + +✖ failing tests: + +test at packages/cli/tests/host.test.mjs:271:1 +✖ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (169.53424ms) + AssertionError [ERR_ASSERTION]: The validation function is expected to return "true". Received false + + Caught error: + + Error: write EPIPE + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/host.test.mjs:279:3) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: Error: write EPIPE + at epipe (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/host.test.mjs:113:24) + at file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/host.test.mjs:276:51 + at ChildProcess.send (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/host.test.mjs:94:23) + at startHost (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/src/host.mjs:150:36) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async waitForActual (node:assert:615:5) + at async strict.rejects (node:assert:738:25) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/host.test.mjs:279:3) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7), + operator: 'rejects', + diff: 'simple' + } diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Mc-norefusedat.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Mc-norefusedat.txt new file mode 100644 index 00000000..071bc8b1 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Mc-norefusedat.txt @@ -0,0 +1,57 @@ +✔ zoned uses the IANA zone across DST (13.576673ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (59.501778ms) +✔ two blocking decisions get two DMs with different nonces (53.051795ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.204649ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (47.675935ms) +✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (46.418663ms) +✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (53.547704ms) +✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (46.317859ms) +✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (108.858391ms) +✖ a restart after the second refusal does not send before that refusal's 30 min are up (79.688197ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (55.259672ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (42.118885ms) +✔ an inbox read failure is logged and the next poll retries (0.64199ms) +✔ no Discord id reaches the journal or the log (46.664543ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (10.28155ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (14.793831ms) +✔ the journal: a whole file that is one torn line truncates to empty (9.68584ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.597578ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.571636ms) +✔ the journal: a line with a wrong type refuses with exit 3 and names the field (1.872573ms) +✔ the journal: a symlinked directory refuses and says it is a link (0.302665ms) +✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.455729ms) +✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.364107ms) +✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.452604ms) +✔ digest content stays within Discord's 2000 characters (0.270363ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.198965ms) +ℹ tests 26 +ℹ suites 0 +ℹ pass 25 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 867.94662 + +✖ failing tests: + +test at packages/cli/tests/notifier.test.mjs:201:1 +✖ a restart after the second refusal does not send before that refusal's 30 min are up (79.688197ms) + AssertionError [ERR_ASSERTION]: nothing between the restart and +60 min + + actual - expected + + [ + + 31.5 + - 60 + ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/notifier.test.mjs:208:10) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: [ 31.5 ], + expected: [ 60 ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Md2-nomkdir.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Md2-nomkdir.txt new file mode 100644 index 00000000..35b54ed1 --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/mut-Md2-nomkdir.txt @@ -0,0 +1,55 @@ +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (904.392647ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (195.161563ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (112.904093ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (183.76143ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (167.268086ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (105.710329ms) +✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (170.287733ms) +✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (126.009082ms) +✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (171.530803ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (23.647599ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.494767ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (234.236466ms) +✔ bus start refuses with exit 3 without a notifier config (97.13829ms) +✔ bus start runs until bus stop; status reports it while it runs (674.314841ms) +✖ bus-service.sh renders the unit and installs it into a given directory (19.236597ms) +ℹ tests 15 +ℹ suites 0 +ℹ pass 14 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3481.947593 + +✖ failing tests: + +test at packages/cli/tests/host.test.mjs:396:1 +✖ bus-service.sh renders the unit and installs it into a given directory (19.236597ms) + AssertionError [ERR_ASSERTION]: The input did not match the regular expression /^ {2}mkdir -m 0700 -p \/notify\/ +the notifier refuses a looser directory$/m. Input: + + 'written: /home/jwoltje/darkwing-scratch/tmp/mosaic-cli-BJ6nOZ/mosaic-bus@.service\n' + + 'next, for one business (one per data root):\n' + + ' write /notify//notify.json, mode 0600:\n' + + ' {"notifyVersion": 1, "binding": ""} or "binding": null for no DMs\n' + + ' systemctl --user enable --now mosaic-bus@ start now and at login\n' + + ' systemctl --user status mosaic-bus@\n' + + " journalctl --user -u mosaic-bus@ -f the host's log\n" + + ' systemctl --user stop mosaic-bus@ SIGTERM; restartable\n' + + 'survive logout and reboot only with lingering on: loginctl enable-linger jwoltje\n' + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/host.test.mjs:409:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1402:25) + at Test.processPendingSubtests (node:internal/test_runner/test:974:18) + at Test.postRun (node:internal/test_runner/test:1542:19) + at Test.run (node:internal/test_runner/test:1467:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: `written: /home/jwoltje/darkwing-scratch/tmp/mosaic-cli-BJ6nOZ/mosaic-bus@.service\nnext, for one business (one per data root):\n write /notify//notify.json, mode 0600:\n {"notifyVersion": 1, "binding": ""} or "binding": null for no DMs\n systemctl --user enable --now mosaic-bus@ start now and at login\n systemctl --user status mosaic-bus@\n journalctl --user -u mosaic-bus@ -f the host's log\n systemctl --user stop mosaic-bus@ SIGTERM; restartable\nsurvive logout and reboot only with lingering on: loginctl enable-linger jwoltje\n`, + expected: /^ {2}mkdir -m 0700 -p \/notify\/ +the notifier refuses a looser directory$/m, + operator: 'match', + diff: 'simple' + } diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/restart-wait.mjs b/agents/darkwing/work/s4-follow-up-review/probe-r2/restart-wait.mjs new file mode 100644 index 00000000..b2cc5a0f --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/restart-wait.mjs @@ -0,0 +1,32 @@ +// Row 45 round 2 probe: decision 73's wait across restarts. A fake clock +// polls every POLL_MS against a DM transport that always answers 403; at +// each restart minute a fresh notifier opens the same journal. Prints the +// DM send minutes and the gave-up minute. +// node restart-wait.mjs +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { join } from "node:path"; +const W = process.argv[2]; +const restarts = (process.argv[3] ?? "").split(",").filter(Boolean).map(Number); +const { createNotifier, journalPath, POLL_MS } = await import(`${W}/cli/src/notifier.mjs`); +const { RestOutcome } = await import(`${W}/discord/src/rest.mjs`); +const root = mkdtempSync(join(process.env.TMPDIR, "dw-r45-rw-")); +const t0 = Date.parse("2026-10-09T03:00:00.000Z"); // digest sends are refused too but not counted +let t = t0; +const sends = []; +const make = () => createNotifier({ + business: "acme", dataRoot: root, + inbox: async () => [{ id: "dec-0001-aaaa", blocking: true, action: "approve", question: "q", options: [{ key: "yes", text: "yes" }], created: "2026-10-09T02:00:00.000Z", requester: "r" }], + direct: { send: async (m) => { if (!String(m?.content).startsWith("Mosaic digest")) sends.push(t); throw new RestOutcome("refused", "dm: refused", { status: 403 }); } }, + now: () => new Date(t), log: () => {}, +}); +let n = make(); +const min = (x) => (x - t0) / 60000; +for (let i = 0; i < 400; i++) { + if (restarts.includes(min(t))) n = make(); + await n.tick(); + t += POLL_MS; +} +const lines = readFileSync(journalPath(root, "acme"), "utf8").trim().split("\n").map((l) => JSON.parse(l)); +const gave = lines.filter((l) => l.outcome === "gave-up").map((l) => min(Date.parse(l.at))); +console.log(`restarts at ${restarts.join(", ") || "none"}; dm sends at ${sends.map(min).join(", ")}; gave-up lines at ${gave.join(", ")}`); +rmSync(root, { recursive: true, force: true }); diff --git a/agents/darkwing/work/s4-follow-up-review/probe-r2/restart-wait.txt b/agents/darkwing/work/s4-follow-up-review/probe-r2/restart-wait.txt new file mode 100644 index 00000000..0aa643ea --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/probe-r2/restart-wait.txt @@ -0,0 +1,6 @@ +restarts at none; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 31; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 0.5, 29.5, 59.5; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 89.5, 91, 119.5; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 120.5; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 1, 2, 3, 4, 5, 31, 61, 91, 121; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 diff --git a/agents/darkwing/work/s4-follow-up-review/review-r2.md b/agents/darkwing/work/s4-follow-up-review/review-r2.md new file mode 100644 index 00000000..d8930c7e --- /dev/null +++ b/agents/darkwing/work/s4-follow-up-review/review-r2.md @@ -0,0 +1,176 @@ +# Row 45, S4 follow-up, round 2 review (Darkwing) + +Issue #1527, request comment 26882, queue rev 216 (`19dcc553`). +Packet: `agents/rocko/work/s4-follow-up/` at `af1377d7`, base `d539d8d2`, +8 files, +635/-56. Candidate manifest sha256 +`5b067a9dad645c31d99e1ea02575cd2fc1ba547ce011ea81c56b17ae29da0d0e`, +`build.patch` sha256 +`c8cec070da60d8297f1ee5b006a1099ab9376fd3abf32fa4461f967750ad6756`. +Rulings: lead decisions 72 and 73. My round 1: `review-r1.md`. + +Verdict: **approve**, comment 26884. R1 and R2 are fixed and hold +under my probes. Notes 1 to 4 are taken and checked. Rocko's decline of +the type check in `append` is sound. + +## Method + +- A detached worktree at `d539d8d2`, then `git apply --index build.patch` + and `sha256sum -c candidate-manifest.sha256`: 8 OK. Both digests match + `git show af1377d7: | sha256sum`. After the mutants I restored the + files and checked the manifest again: 8 OK. +- I read the diff for `host.mjs`, `notifier.mjs`, `bus-service.sh`, the + cli README and the new tests, against decisions 72 and 73, and Rocko's + round 2 section in `BUILD.md`. +- Probes in `probe-r2/`. `giveup-time.mjs`, `journal-paths.mjs` and + `late-send.mjs` are the round 1 files in `probe/`, unchanged. + `host-window.test.mjs` differs only in its import paths. New: + `restart-wait.mjs`. They import from my scratch worktree by absolute + path, so they won't run as committed without editing the paths. +- Three mutants of my own: Mb drops the callback at `host.mjs:150`, Mc + ignores `refusedAt` in `tick`, Md2 drops the `mkdir` line from the + install message. + +Node is v26.8.1, `TMPDIR=~/darkwing-scratch/tmp`, load about 3.3. + +## Suites + +| Suite | Result | File | +|---|---|---| +| `node --test 'packages/cli/tests/*.test.mjs'` | 66/66 | `out-r2/node-cli.txt` | +| `node --test 'packages/discord/tests/*.test.mjs'` | 178/178 | `out-r2/node-discord.txt` | +| `node --test 'packages/bus/tests/*.test.mjs'` | 67/67 | `out-r2/node-bus.txt` | +| `node --test 'packages/tasks/tests/*.test.mjs'` | 51/51 | `out-r2/node-tasks.txt` | +| `scripts/test-discord.sh` | 66 passed, 0 failed | `out-r2/test-discord.txt` | + +Sage's gate covers the rest. + +## R1: the close and stop sends (closed) + +`host.mjs:144`, `:150` and `:188` now read +`if (broker.connected) broker.send({ op: "close" }, () => {});`, and +`:184` sends `stop` to the notifier the same way. The three new host tests +use the deterministic form from round 1: `failSends` passes `EPIPE` to the +callback if there is one, and otherwise emits `'error'` on the next tick. + +Through `startHost` (`probe-r2/host-window.test.mjs`), the SPIN 2 window +from round 1 didn't open this time. All 80 runs at SPIN 2 with no listener +made 0 close sends. Timing drifts with load, so I swept SPIN from 1.0 to +1.8 and ran a no-callback control beside the candidate +(`probe-r2/hw-r2-sweep.txt`): + +| SPIN | Candidate, 40 runs | No-callback control, 40 runs | +|---|---|---| +| 1.2 | 16 close sends, 0 `EPIPE` | 20 close sends, 6 reject with a raw `write EPIPE` | +| 1.5 | 17 close sends, 0 `EPIPE` | 8 close sends, 0 `EPIPE` | +| 1.8 | 3 close sends, 0 `EPIPE` | 7 close sends, 0 `EPIPE` | + +Five more candidate batches at SPIN 1.0, 1.2, 1.2, 1.2 and 1.4 made 34, +23, 18, 32 and 24 close sends. Across all 520 candidate runs (`hw-r2.txt` +and the sweep), 222 close sends went out. Every run rejected with the +notifier's error, none with `write EPIPE`, and none left an unhandled +rejection. The control shows the window was reachable at the same time, +so the zero is the fix and not a quiet machine. + +The bare fork (`probe-r2/late-send-r2.txt`) still shows the Node +behaviour the fix relies on. At 1 ms after SIGKILL, 3 of 20 runs with no +callback crash on an unhandled `'error'` (`write EPIPE`). With a callback, +10 of 20 pass `EPIPE` to it, with 0 `'error'` events and 0 crashes. This +probe tests Node, not the candidate. + +Mutant Mb (no callback at `:150`) fails the host test "a close send that +fails with EPIPE after the notifier refuses still gives the notifier's +refusal, exit 3": pass 14, fail 1 (`probe-r2/mut-Mb-nocb150.txt`). +Rocko's G144cb, G150cb, G184 and G188 cover the other sites. + +## R2: decision 73's wait (closed) + +A definite refusal now waits `BACKOFF_MAX_MS`, 30 min. `count()` keeps +`refusedAt`, the `at` of the last definite refusal, and `tick` skips a +decision until `refusedAt` plus 30 min. The wait therefore comes from the +journal, not from memory. + +`probe-r2/giveup-time-r2.txt`, every send refused 403: the DM goes out at +0, 30, 60, 90 and 120 min, each with "retry in 1800 s", then +"dm dec-0001 refused 5 times; gave up, not retried" at 120 min. That +matches decision 73's 2 h span. + +`probe-r2/restart-wait.mjs` starts a fresh notifier on the same journal +at given minutes and counts only DM sends (`restart-wait.txt`): + +``` +restarts at none; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 31; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 0.5, 29.5, 59.5; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 89.5, 91, 119.5; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 120.5; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +restarts at 1, 2, 3, 4, 5, 31, 61, 91, 121; dm sends at 0, 30, 60, 90, 120; gave-up lines at 120 +``` + +No restart schedule shortens a wait or adds a send, and a restart after +the give-up writes no second `gave-up` line. + +Mutant Mc (`tick` ignores `refusedAt`) fails "a restart after the second +refusal does not send before that refusal's 30 min are up": pass 25, +fail 1 (`probe-r2/mut-Mc-norefusedat.txt`). + +The README now says recovery after a give-up is manual: the decision +stays in `mosaic inbox` and the digest, and the operator decides it with +`mosaic decide`. Round 1 asked for that line under option 2. + +## Round 1 notes + +1. **Raw errors (taken).** Each path in `probe-r2/journal-paths-r2.txt` + is now a `CliError` with exit 3. A dangling symlink gives the symlink + message, a parent that is a file gives "cannot be created (ENOTDIR)", + and a `sent.jsonl` that is a directory gives the regular-file message. + The round 1 paths are unchanged. +2. **The `mkdir` line (taken).** Mutant Md2 drops it and fails "bus-service.sh + renders the unit and installs it into a given directory": pass 14, + fail 1 (`probe-r2/mut-Md2-nomkdir.txt`). In round 1 the same mutant + passed everything. +3. **The day check (taken).** `isDay` checks the shape, then + `Date.parse`, then a `toISOString` round trip. `2026-13-45` refuses as + `malformed (day)`. +4. **The README sentence (taken).** It now names three states in quotes. +5. **`accessSync` (unchanged).** It was a note with no change asked. + +## Rocko's decline + +Filbert asked for a type check in `append`. Rocko declined, and I agree. +In `attempt` (`notifier.mjs:271`), the `confirmed` append sits in the +`try` after `direct.send` returns. A throw from it lands in the `catch`, +which journals `unknown` for a DM that Discord already delivered, and +every later tick would send it again. `rest.mjs` already throws `unknown` +on a 2xx without a string id, so the writer can't produce that line +today. If it ever does, the open-time check refuses it with exit 3, which +is the loud failure. + +## Notes (not blocking) + +1. A digest refused with a definite 403 retries without a limit, with + doubling up to 30 min (`giveup-time-r2.txt`, 13:00Z on). That follows + decision 72, which limits DMs only. A digest refused for a bad token + then retries every 30 min forever, and each retry logs a line. One + sentence in the README or the header comment would say this is meant. +2. `refusedAt` comes from the journal's `at`. If the clock steps back + after a refusal, the wait grows by the step. The fail direction is + "wait longer", which is safe. I note it only. +3. A journal directory path that is a regular file gives "must be mode + 0700 and owned by this user". The refusal is right; the message could + say "not a directory". Cosmetic. + +## Files + +- `review-r2.md`, this file. +- `out-r2/`: the suite outputs and `summary.txt`. +- `probe-r2/host-window.test.mjs` and `hw-r2*.txt`: the `startHost` + window, the sweep and its no-callback control. +- `probe-r2/late-send-r2.txt`: the bare fork, run with `probe/late-send.mjs`. +- `probe-r2/giveup-time-r2.txt`: decision 73 timing, run with + `probe/giveup-time.mjs`. +- `probe-r2/restart-wait.mjs`, `probe-r2/restart-wait.txt`: restarts + against the wait. +- `probe-r2/journal-paths-r2.txt`: journal error paths and types, run + with `probe/journal-paths.mjs`. +- `probe-r2/mut-Mb-nocb150.txt`, `mut-Mc-norefusedat.txt`, + `mut-Md2-nomkdir.txt`: the mutants.