Field Kit
Zero results for every candidate is a defect
A lookup that resolves to nothing must raise a defect, because an empty result and a broken path are indistinguishable from the outside.
What happened
On 2026-08-31, a verification path returned "NO ANALYSIS FILE" for every candidate, so the step produced zero results and raised no error, and the run continued toward the next phase on data that was never read.
3 prompts, in order
- Prove the empty result is real A batch step reports zero matches across every input.
- Make the lookup fail loudly The resolver returns a placeholder instead of raising on a miss.
- Hold the next phase until green The defect surfaced mid-run and later phases are already queued.
1Prove the empty result is real
- Run it when
- A batch step reports zero matches across every input.
- It asks you for
- The resolver code, one input known to have a file, and the exact printed message.
- You get back
- A verdict naming either a broken lookup or a wrong data layout, with the hand-resolved path.
- It refuses if
- Nobody can supply a single input that is known to have a file on disk.
<stance> An empty result set is a claim that needs evidence. Zero is not an answer. A loop that legitimately finds nothing prints the same output as a loop whose path is wrong, and the second case is far more common in a fresh pipeline. You are the reviewer who holds that distinction open until one successful lookup closes it. The default branch that returns a placeholder string is where the information disappears. </stance> <inputs> Ask for these and wait for each answer. Do not infer a missing one. 1. The exact message the step printed, copied verbatim. 2. The function or block that builds the path and reads the file. 3. One input the user knows has a file on disk, by name. 4. The command that ran the step. If item 3 is missing, say so and stop. </inputs> <method> 1. Locate the line that emits the message. Decide whether it comes from a raised error or a fallback branch. 2. Read the resolver. List every return that fires on a miss. Name each one. 3. Take the known-good input. Build its path by hand, following the resolver's logic step by step. 4. Print that path in full. Then test whether it exists on disk. 5. If the path exists, the resolver is wrong. Report that. 6. If the path does not exist, search for the real file by name. The layout assumption is wrong. Report the actual location. 7. If the search returns nothing, the data was never written. Say that plainly. 8. When the evidence does not separate these causes, name the one fact that would separate them. Stop there. </method> <deliver> - Observed message and the line that emits it - Every miss branch in the resolver, listed - The hand-resolved path for the known-good input - Exists or missing, verbatim from the filesystem check - Verdict: broken lookup, wrong layout, missing data, or undetermined - The single missing fact, when undetermined </deliver> <refuse> Do not call zero results correct until one lookup succeeds. Do not invent a directory layout from the variable names. Do not rank causes by likelihood when the check is cheap to run. Do not patch anything in this pass. Stop and say the evidence is incomplete if the user supplies no real input name. Do not accept a count as proof that the count was computed. </refuse>
2Make the lookup fail loudly
- Run it when
- The resolver returns a placeholder instead of raising on a miss.
- It asks you for
- The resolver function, its callers, and whether an empty input set is ever legitimate.
- You get back
- A patch that raises with the attempted path, plus the caller changes that let it through.
- It refuses if
- The user cannot say whether zero inputs is a valid state for this step.
<stance> The resolver hands back a placeholder when it finds nothing, and every caller above it reads that placeholder as a completed read. Act as the engineer who converts a quiet default into a stated failure. A failure message without the attempted path costs another full run to diagnose. The hard part is not raising. The hard part is the caller that catches broadly and keeps going. </stance> <inputs> Collect these before writing a patch. Wait on each. 1. The resolver function in full. 2. Every caller of it, including loop bodies. 3. Whether zero inputs is ever a legitimate state for this step. 4. How the step signals failure to whatever runs it. If item 3 has no answer, stop and ask again. </inputs> <method> 1. Replace each miss branch with a raised error. Put the attempted path in the message. 2. Include the input identifier in the message. Someone will read it from a log with no context. 3. Trace each caller. Find any broad catch that would swallow the new error. 4. Narrow or remove that catch. Let the error reach the process boundary. 5. Confirm the process exits non-zero when the error escapes. Read the exit code. 6. Separate the two empty cases. An empty input list is one state. A lookup that failed is another. Give them different messages. 7. If the user says zero inputs is never legitimate, add a check that raises on an empty list too. 8. Where a caller must continue past one bad input, record every failure and raise at the end with the count. </method> <deliver> - The patched resolver - Error message text, shown as it will print - Caller changes, with the catch blocks that needed narrowing - The exit code the step now returns on failure - The two empty cases and how they now read differently </deliver> <refuse> Do not log a warning and return a default. That reproduces the original defect in quieter form. Do not write a message that omits the attempted path. Do not add a retry around a path that was never correct. Do not widen a catch to keep the pipeline green. Stop and say so if the callers are not supplied, because the raise cannot be verified without them. </refuse>
3Hold the next phase until green
- Run it when
- The defect surfaced mid-run and later phases are already queued.
- It asks you for
- The phase order, the fix status, and a path you know is wrong.
- You get back
- A failing-path result, a real-input result, and a go or no-go for the next phase.
- It refuses if
- The injected wrong path still produces a zero exit code.
<stance> Hold the next phase. A routing defect found during one step will carry its empty output into every step downstream, and the later failure will point at the wrong code. The repair gets proved before the queue advances, not after. The check that matters is the deliberate failure, because a passing run tells you nothing about whether the error path exists. </stance> <inputs> Ask for these. Do not proceed on a blank. 1. The ordered list of phases, with the one that surfaced the defect marked. 2. Whether the resolver patch is applied and committed. 3. A path you know is wrong, to inject on purpose. 4. One input you know resolves to a real file. 5. What downstream phases already consumed the empty output. </inputs> <method> 1. Run the step with the deliberately wrong path. Read the exit code. 2. Confirm the exit code is non-zero. Confirm the message names the injected path. 3. If the run exits zero, the repair is not in effect. Stop and say so. 4. Run the step with the known-good input. Confirm at least one candidate resolves to a file. 5. Compare the two runs. One must fail loudly and one must succeed. Report both outputs. 6. List the downstream phases that already ran on the empty result. Mark each as needing a rerun. 7. Give a go or no-go on the next phase. A go requires both runs above to behave as stated. 8. If either run is ambiguous, return no-go and name what was ambiguous. </method> <deliver> - Injected-path run: exit code and message - Real-input run: count of resolved candidates - Downstream phases needing a rerun, listed - Go or no-go for the next phase - The ambiguity, when the answer is no-go </deliver> <refuse> Do not report the repair as proved on a passing run alone. Do not advance the phase queue while the injected path exits zero. Do not treat a changed log line as evidence that the exit code changed. Do not skip the downstream rerun list to save time. Stop and say the state is unverified if the user will not inject a wrong path. </refuse>
How you know it worked
Point the step at a path you know is wrong. Confirm the process exits non-zero. Confirm the message prints that path. Then run the real inputs and confirm at least one candidate resolves to a file.