Skip to content

cairn: distinguish git-invocation failures from confirmed-negative freshness checks#54

Merged
quad341 merged 5 commits into
mainfrom
deploy/crn-jrhz-gate
Jul 25, 2026
Merged

cairn: distinguish git-invocation failures from confirmed-negative freshness checks#54
quad341 merged 5 commits into
mainfrom
deploy/crn-jrhz-gate

Conversation

@quad341

@quad341 quad341 commented Jul 25, 2026

Copy link
Copy Markdown
Owner

What this changes

When cairn checks an anchor's freshness (cairn freshness, verify, status, get) and the underlying git invocation itself fails to run — a transient error, a canceled context — that failure was previously indistinguishable from a confirmed negative result (e.g. "this path really isn't tracked at HEAD"). Both cases silently fell into the same stale/unknown bucket.

This adds a new Incomplete status that's reported only when the freshness check genuinely couldn't complete, so a transient git hiccup is no longer conflated with a real negative finding:

  • cairn freshness / cairn verify now hard-error with a message distinct from today's "nothing to verify" wording when the check is incomplete.
  • cairn status gains a new marker (alongside OK /!! /?? ) for incomplete entries.
  • cairn get reports the incomplete status honestly in its output but does not fail the command — freshness is incidental to get's job of returning the entry body.

All previously-confirmed-negative outcomes (missing repo, no commits yet, deleted path, unmatched glob, invalid revision) are unaffected and still classify as Unknown exactly as before.

Review notes

  • Check()'s public signature is unchanged; the new status only affects what value it can return.
  • No dependency changes, no new external input surface — this only changes how an already-internal git() error is threaded through the call stack instead of being discarded.
  • internal/critic's freshness scenario gained a 4th sub-check using a pre-canceled context.Context (not PATH-tampering, since that file runs against live, possibly-concurrent state).

Test plan

@quad341
quad341 enabled auto-merge (squash) July 25, 2026 05:35
quad341 added 5 commits July 25, 2026 02:39
… (crn-fdjc.1.1)

Add Incomplete status constant and new tests at the stable Check/Sweep
entry points asserting the FR-5 failure-class distinction from
crn-fdjc.1's design: a genuine git invocation failure (PATH unreachable,
canceled context) must classify as Incomplete, never collapse into the
same Unknown verdict as a confirmed negative (missing repo, no commits
yet, unmatched glob, staged-but-uncommitted path).

New tests fail against the current implementation, which still folds
every failure into Unknown/"?" — expected RED. Confirmed-negative
regression tests pass unmodified, confirming FR-2 (today's behavior for
those classes is preserved). ComputeFingerprint/objectHash/expand/
untrackedPaths keep their current signatures for now; direct tests of
their error-returning form land in the GREEN commit since they can't
compile until the signatures change.
ComputeFingerprint/objectHash/expand now return an explicit error for a
genuine git invocation failure, distinct from a confirmed negative
(untracked path, no anchor, etc.) which still returns ("", nil). Check
surfaces invocation failures as a new Incomplete status; Sweep's
untrackedPaths enrichment leaves status/detail untouched when the
underlying check was itself incomplete, instead of misreporting it as
a confirmed Unknown. cmd/commands.go: freshnessCmd/verifyCmd now hard-error
on Incomplete/invocation failure; statusCmd adds an "!X " flag; getCmd
needed no change (already prints any status generically).

Implements crn-fdjc.1.1 per architect design in crn-fdjc.1.
…et gate

Rebasing crn-fdjc.1.1 (git-invocation-failure -> Incomplete propagation)
onto crn-0vqk.2's already-merged Check() reorder (never-verified
short-circuit before the git shell-out, for performance) produced a
textual, mechanically-resolvable conflict -- but the obvious resolution
(keep the never-verified check first) silently defeated crn-fdjc.1.1's
own pinning tests: a files anchor that has never been fingerprinted yet
still needs its git invocation attempted, so a genuine failure (broken
PATH, canceled context) surfaces as Incomplete instead of being masked
behind "never verified".

Reordered Check() to attempt ComputeFingerprint (surfacing Incomplete on
error) before the never-verified check, which now runs only after a
successful call -- preserving crn-0vqk.2's original fix against a false
Fresh/Stale null-comparison for a never-verified anchor, while letting
crn-fdjc.1.1's error propagation win first.

freshnessBudget.classify (prime.go) gated its shell-out cap on
`a.Fingerprint != ""`, which was only ever correct under the old
ordering where a never-verified anchor short-circuited for free. Now
that Check unconditionally shells out for any shape-verifiable files
anchor, that clause under-metered the exact cost FR-5's budget cap
exists to bound -- dropped it so every files anchor that will actually
invoke git counts against the cap, never-verified or not.

Also fixes a stale call site in prime_test.go: ComputeFingerprint's
pre-crn-fdjc.1.1 signature returned a single value; the two-return form
landed via this same rebase but prime_test.go (merged to main earlier,
unrelated to this branch) wasn't touched by the rebased commits, so the
break was invisible to git's textual merge.
…post-rebase reconciliation)

Supersedes this branch's earlier gate evaluation (eef42ec), which
assessed criterion 6 as a trivial clean merge before origin/main
advanced again (PR #57) and produced a real feature-interaction
conflict. This document reflects the actual final reconciled state.
@quad341
quad341 force-pushed the deploy/crn-jrhz-gate branch from ba1ad43 to b66f81a Compare July 25, 2026 10:03
@quad341
quad341 merged commit be93ae7 into main Jul 25, 2026
2 checks passed
@quad341
quad341 deleted the deploy/crn-jrhz-gate branch July 25, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant