Files

4.6 KiB

Filbert review: row 115 round 1 (#1609): changes

Candidate manifest sha256 f37bc933bbfd2e5f10fd5dd500f60a37e16de324c4e63395bb8d62cc3b5068ab (5 files), as named in comment 28420. I rebuilt it in my own worktree at BASE 94ece491f with build.patch from packet b3fca1f1a. All 5 hashes match, git diff --check is clean, no manifest path has drifted from 94ece491f to HEAD, and the patch's added lines have no dashes, bidi or control characters.

The brief, item by item

  1. The message. It is inserted in the decision.resolve/seen/withdraw case, right after the decision_events row and the human answer event, so it is in the same transaction. to_role is raised_by_role, the class is DECISION, decision is the id, and in_reply_to and corrects are null. The sender is human/human-cli for a human close, and the closing role and run otherwise. The body has the fixed forms, with the option text and an optional Note: line. It is routed through #holder and #delivery the same way message.send routes.
  2. No message. This covers seen, a withdraw by the raising run (s.run !== d.raised_by_run), and supersede and within-role, which write their decision_events rows inside decision.raise and never reach this case.
  3. request. See C1.
  4. Runner. It is unchanged, and the new runner.test.mjs test shows a human resolve becoming a turn that cites the decision and the request.
  5. README. It has the rows and the new section. I agree with scope note 1 on single-use.test.mjs.

The close paths Darkwing asked about

decision_events has three writers in broker.mjs: the supersede withdraw and the within-role resolve inside decision.raise, and this case. The broker never writes expired. In this case, #closed refuses decision-closed before any write, and request() runs each call in one BEGIN IMMEDIATE transaction. A second close therefore refuses, and a refused close rolls back its message. Only the raising run can withdraw as an agent, so an agent withdraw never sends. A resolve comes from the human, or from the arbiter role, which decision.raise never sets to the raiser's own role. I found no path that sends twice or skips the message.

Change requested

  • C1. The fallback's op filter is untested, and dropping it gives the wrong request. In #request, the fallback for a human DECISION message picks the decision's first human.input answer event, filtered to op IN ('resolved','withdrawn'). A human decision.seen on a human-routed decision also writes an answer event with the decision as its subject, with op: 'seen'. I removed AND json_extract(body,'$.op') IN ('resolved','withdrawn') (mutant f4), and decision-close, single-use and runner all still passed, 41/41. No test has the human run seen and then close the same decision. In the no-message test, the human seens toHuman but never closes it.

    The mutant is not equivalent. I added a throwaway probe: the coder raises deploy, the human seens it, then resolves it (or withdraws it), and the coder receives the message. With the filter, request is the resolved (or withdrawn) event. Under f4 it is the seen event in both cases, which breaks brief item 3 ("the id of that close's human.input answer event"). The probe file was deleted afterwards and the manifest hashes rechecked.

    Fix: add a test where the human runs decision.seen and then resolves (or withdraws) a human-routed decision, and assert that the message's request equals the resolve's answer event, not the seen one. The test's answer() helper doesn't filter on op, so it would need to select op='resolved'. The broker needs no change.

Mutants, rerun by me (decision-close, single-use and harness runner together)

Mutant Result
m1, the packet's (no insert) killed, 8 fail
m2, the packet's (to route_to) killed, 7 fail
m3, the packet's (send on the raiser's withdraw) killed, 1 fail
f4: op filter dropped from the fallback survived, 41/41 pass
f5: no fallback (own only) killed, 5 fail
f6: route always unheld killed, 1 fail
f7: Note: line dropped killed, 2 fail

I restored the source after each one and rechecked all 5 hashes.

What I ran (single files, no slot)

bus: decision-close 7/7, single-use 12/12, decisions 7/7, broker 16/16, messages 4/4, review 8/8, tester 11/11. harness: runner 22/22. I also ran the mutants and the probe above. I didn't rerun the full gate and relied on gate-run.log for it (bus 155, harness 81, GATE_PASSED). The worktree is removed and no process is left in it.

Verdict: changes (C1, one test).