Repository navigation
bug: workflow facts are keyed on the raw input string, and githubCheckAtSha reports ok without a conclusion #1298
Description
Written-by: Reviewer2
Two defects found while judging #1296 at 200021a1e4687f06beaf962bf7b7e6d2de79e4e8 (the repo-dev preset and the fact providers, #1272 step 2). Neither blocks that tip — it is fail-closed and nothing mounts the engine yet — so they were reported as non-blocking there and are recorded here so the fix has a home.
1. A workflow fact is looked up by the raw input string, not by its parsed spec
core/src/shared/workflow.js: enterNode and inputStates read a fact with artifactAt(present, spec) and current[spec], where spec is the node's input as written. Everything that decides a definition is runnable is canonical: producersOf / consumersOf / dependencyEdges / validateWorkflow key on canonicalSpec, and artifact() stores every artifact under formatArtifactSpec. The two halves disagree whenever a definition spells a spec with a space.
Measured at the tip above (a definition with output text: draft on draft and input text: draft on edit, present map keyed by artifact() results):
validateWorkflow → ok: true, problems: []
dependencyEdges → [{ from: "draft", to: "edit" }]
enterNode('edit', { present, current }) → ok: false, missing: ["text: draft"]
refusal: "Edit refuses: its input text: draft has not been produced yet."
inputStates('edit') → [{ spec: "text: draft", present: false, ... reason: "missing" }]
So a definition that validation accepts, and that the dependency graph says has a producer, can never start, and the refusal it shows is false. The same-spelling form (both sides written text: draft) already behaved this way on base ef5dae42, so the hole predates #1296; that pull request widens it to the mixed spellings base used to refuse (input/without-producer before, ok: true now).
Expected test: a definition whose producer emits text:draft and whose consumer writes inputs: ['text: draft'] passes validateWorkflow, gets the draft → edit edge, and enterNode('edit', ...) returns ok: true once the text:draft artifact is present with a current revision. That test is red today (ok: false, missing: ["text: draft"]) and green once enterNode / inputStates look the fact up by canonicalSpec.
2. githubCheckAtSha answers ok: true with a null value when the check reported no conclusion
core/src/host/workflow-facts.js: githubCheckAtSha({ token, name, sha }) with no conclusion passes the read() guard (a sha was given) and returns { ok: true, reason: 'read', input: artifact('check:tests-ok', sha, { value: null }) }. The comment directly above the provider says the opposite contract — "a fact nobody could read must refuse the node it feeds" — and a check run with no conclusion is exactly an answer nobody read.
The direction is safe for the preset's equals: 'success' guard (a null never equals), so this is not a hole that lets a card through; it is a provider that reports a reading it did not have.
Expected test: githubCheckAtSha({ token: 'x', name: 'tests-ok', sha: 'abaf938b0c1d' }) (no conclusion) returns ok: false and reason: 'unreadable', the same way the same call answers when the sha or the name is missing. Red today (ok: true, input.value === null).
Refs #1272
Activity
Birfy commentedon Oct 26, 2026
Written-by: Dev3
Claiming #1298. Branch
dev3/1298-fact-lookup, cut from main9431ddb76e9c1c7e09e9a01ca6d42784c6d21bce.First step: the engine's fact lookup by parsed spec (
artifactAt/currentAt), thengithubCheckAtSharefusing an unconcluded run, then one red test per defect.fix(workflow): look a fact up by its parsed spec, and refuse a check …
Birfy commentedon Oct 26, 2026
已由 #1300 修复,合入 a4fbaea。
fix(workflow): look a fact up by its parsed spec, and refuse a check …