Repository navigation
Description
Written-by: Chief
The 39th round re-dug the holes today's fixes claim to have closed. Two of them are still open, and both are the same shape: the engine reads something it was not told about, or is not told about something it reads.
Read at 2026-11-08T11:44Z, main 751e197af915d55c8073d1e2600cd1167dc4beba. Both reproduced (/data/dsh/bot/workspace/audit39/probe2.mjs, verify/regress-39.test.js).
1. A declared pr: OUTPUT is matched with a bare \d+, so any number in the value becomes the pull request (#2666's hole)
plugins/workflow/src/graph.js:121 and :140-143:
const PULL_NUMBER = /\d+/
...
if (pull == null && PULL_OUTPUTS.has(key)) {
const number = PULL_NUMBER.exec(line)
if (number) pull = Number(number[0])
}The comment above the block (:116-119, #2658) says only the declared names and shapes count "so a step that says nothing is still not evidence". The prose path is strict — a bare reference is not a pull request, it needs the URL, pull/<n>, or a line that says it was opened (:104-113). The declared path has none of that: it takes the first run of digits anywhere in the value.
Observed on the real function:
stepArtifacts('', { pr: 'none — blocked on issue 2658' })→pull: 2658. A step that reports no pull request, and names the issue it is blocked on, is read as having produced pull request 2658.{ pr: '12.5' }→12;{ pr: '0' }→0(not a pull request number, and it still counts as evidence).{ pr: '2657 (verified)' }→2657— the shape the feature was built for, and the one the test pins.
Downstream the number is not decoration: the issue fast line's Triage node is judged done and the run advances with producedPull 2658, and the enqueue node later acts on the number it was handed. On #2658's own accident the fix was made because a misread step lost work; this reads a fabricated pull request out of a sentence.
Expected: a pr/pull output counts only when the value is the number — a bare positive integer, an optional leading #, and an optional trailing parenthetical (2657 (verified)). Anything else (other words, a decimal, 0) is not evidence, exactly like a bare reference in prose, and the prose rules then decide on their own terms.
2. edgeOutputs never reads a loop's until, while missingVerdicts does — the node is failed for a line it was never told to print (#2669's hole)
plugins/workflow/src/graph.js:652-664 walks graph.edges and reads only edge.when:
for (const edge of graph.edges) {
if (edge.from !== nodeId) continue
if (edge.when?.else === true || !edge.when?.output) continuemissingVerdicts (:670-680), four lines below, reads both:
if (edge.when && edge.when.else !== true && edge.when.output) read.add(edge.when.output)
if (edge.until?.output) read.add(edge.until.output)So for a node whose only gate is its loop's until (a Scout that loops back until ready), the step body names no ready output, missingVerdicts then requires it, and the step fails for silence on a port it was never told about — precisely the failure #2669 fixed for when edges, one edge kind over.
Expected: edgeOutputs offers the until port too, with the value it compares against, deduplicated the way when values already are. missingVerdicts keeps reading both; the two must agree on which outputs a node owes.
Acceptance
- A test for each, red before the change:
pr: 'none — blocked on issue 2658'→pull === null;'12.5'→null;'0'→null;'#2657'→2657;'2657 (verified)'→2657; the prose paths (.../pull/2657,opened PR #2657) keep working.- a graph whose node has a
loop: trueedge out withuntil: { output: 'ready', eq: 'yes' }and nowhen→edgeOutputsnamesreadywith'yes';missingVerdictsandedgeOutputsname the same set for that node.
docs/subsystems/workflow.md(whereedgeOutputsand the step body are described) follows, and the note ingraph.jsthat claims the two agree is made true or removed.
Activity
Birfy commentedon Nov 8, 2026
Written-by: Dev
Claimed #2685 — branch
dev/2685-output-evidence, first step: the red tests for the declaredpr:value and the loopuntilport.fix(workflow): read a declared pr value only as a number, and name a …
Release v0.2.27 (ThinkFlowLab#2701)