Skip to content

fix(surveyor): step 4 reports false rung-0 breakage when the CI classifier cannot be resolvedΒ #207

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Evidence

On the 2026-09-08 Claude-lane survey (t1624), step 4 reported both of these in one digest:

  • QUERY-UNKNOWN step-4-classifier β€” failed=control:helper-unresolvable. The read-only guard denies find/glob, so the installed classify-default-branch-ci-runs.sh path could not be resolved; default-branch CI was assessed by direct forge reads instead, applying the GitHub-managed routing policy in-band.
  • fleet-gitops: CI red on main @23d0ba842d β€” Apply latest configuration to Fleet failure β€” 40+ CONSECUTIVE red runs … This is what sets nothing_on_fire: false.

The reported breakage does not exist. Independently verified:

Fact Value
fleet-gitops main head 8f514f9d985fe9a616af42809bea93b7dd266c4c
Head of every one of the 40+ failing runs 23d0ba842d (a 2026-05-09 commit)
What 8f514f9d98 is commit "ci: pause automatic Fleet apply while the Fleet server is disabled (#5)", 2026-07-04T18:25:07Z
Last failing run 2026-07-04T07:01:05Z β€” 11 h before that fix
Workflow file at main's head push and schedule triggers commented out; workflow_dispatch retained
Platform state k8s/bases/apps/kustomization.yaml still has # - fleetdm/ β€” server deliberately disabled since 2026-06-03

So the schedule "stopped firing" because it was deliberately removed, and the failures are
historical at a superseded head. The state is coherent by design across both repositories.

Control β€” the sanctioned helper gets it right. Run directly at main's real current head, the
installed classifier emits no red rows and exits 0:

classify-default-branch-ci-runs.sh --repo devantler-tech/fleet-gitops --branch main \
  --head-sha 8f514f9d985fe9a616af42809bea93b7dd266c4c   β†’ exit 0, no rows

That empty result is not a broken invocation: the shipped classify-default-branch-ci-runs.test.sh
passes 18/18, so the helper does emit rows for red runs. The helper is installed, mode 755, and
accepts exactly the documented flag form.

Impact

Two distinct defects, and the second is the one that produces a wrong answer:

  1. helper-unresolvable is reachable even though the helper is present and executable. The
    documented recovery β€” one bare probe through the read-only guard, decode the classifier-path-json:
    hint β€” did not yield a usable path. Step 4 therefore degrades on a normal run.
  2. On that failure the surveyor did not fail closed. The rule is explicit: "A missing, malformed,
    ambiguous, or unusable hint means QUERY-UNKNOWN; do not hunt directories or try other roots."

    Instead it emitted QUERY-UNKNOWN and a confident CI red on <branch> @<sha> verdict derived
    in-band, judged at the last run's sha rather than the branch's current head β€” precisely what
    step 4's opening sentence forbids β€” and set nothing_on_fire: false on it.

This is a fail-open on the rung-0 signal. It manufactures breakage that does not exist, and the
consuming engineer must treat rung 0 as preempting everything, so each affected run spends real
investigation budget re-deriving that the repository is fine. Because the underlying head never
changes, it recurs on every tick. The symmetric risk is worse: an in-band reader that judges at
the wrong sha can equally report green over a genuine current-head failure.

Expected behaviour

  • When the classifier path cannot be resolved, step 4 emits QUERY-UNKNOWN for default-branch CI
    and nothing else β€” no in-band substitute verdict, and no contribution to nothing_on_fire.
  • A red run whose head_sha is not the branch's current head is never reported as current breakage.
    Where that distinction is useful, it belongs in its own non-actionable class (e.g.
    SUPERSEDED-HEAD (NO-ACTION)) rather than as CI red.
  • Path resolution succeeds whenever the helper is installed and executable.

Acceptance criteria

  • A fixture in which the only red runs sit at a non-current head_sha yields no CI red row and
    does not set nothing_on_fire: false.
  • A fixture in which classifier resolution fails yields QUERY-UNKNOWN for step 4 with no
    accompanying default-branch verdict, asserted as the absence of that row.
  • Each new assertion is ablated against the pre-fix agent text and fails there.
  • Path resolution is verified against a real install layout, so helper-unresolvable is not
    reachable with the helper present and executable.

Size: small-to-medium β€” the classification/fail-closed half is an agent-text change plus
assertions; the path-resolution half needs a look at the guard hint mechanism against a real install.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions