feat(checks): warn when --allow-existing-red runs against a stale local base #70

Merged
triform-admin merged 3 commits from agent/checks-stale-base-warn into main 2026-06-24 00:24:09 +00:00

Defect-2a stale-base detection in --allow-existing-red classification.

cargoless checks run --base <ref> --allow-existing-red now warns when the
local tracking ref is behind real origin before re-running checks against
the base worktree. Without the warning, a stale origin/dev silently resolves
to a pre-PYAML merge-base and classify_required_reds_at_base reports phantom
counts for whole-tree fingerprint checks — the wedge PYAML hit on donut
(~5 false-block dev-merge attempts).

What's in this PR

  • base_freshness_warning() in BaseWorktree::create — uses read-only
    git ls-remote origin <short> to compare local tracking-ref tip vs real
    origin. Warns when they differ; classification proceeds regardless.
  • Threshold gating: with rev-list --count succeeding (objects in odb),
    warn only if drift ≥ CARGOLESS_BASE_STALE_THRESHOLD (default 5).
    When count fails (the common stale-clone case — objects not in odb), warn
    regardless, because the user is reasoning against a base they cannot even
    fully describe locally.
  • 5 unit tests under stale_base_tests covering: happy path silent, the
    exact production wedge (objects unavailable → "unknown number of commits"),
    count-known path, threshold honoured, unresolvable base silent.
  • Disable with CARGOLESS_BASE_STALE_CHECK=0.
  • Detect-only: no auto-fetch, no side effects on local refs.

CI

All 4 jobs green on the final commit 1051126: build / test / fmt / clippy.
ra-bench correctly skipped (S1 harness).

Commit history (will squash)

  • 8b48840 feat(checks): warn when classify-at-base runs against a stale local tracking ref
  • cb63c72 fix(checks): clippy clean stale-base helper
  • 1051126 fix(checks): rustfmt edition-2024 single-line if-else
Defect-2a stale-base detection in `--allow-existing-red` classification. `cargoless checks run --base <ref> --allow-existing-red` now warns when the local tracking ref is behind real origin before re-running checks against the base worktree. Without the warning, a stale `origin/dev` silently resolves to a pre-PYAML merge-base and `classify_required_reds_at_base` reports phantom counts for whole-tree fingerprint checks — the wedge PYAML hit on donut (~5 false-block dev-merge attempts). ## What's in this PR - `base_freshness_warning()` in `BaseWorktree::create` — uses read-only `git ls-remote origin <short>` to compare local tracking-ref tip vs real origin. Warns when they differ; classification proceeds regardless. - Threshold gating: with `rev-list --count` succeeding (objects in odb), warn only if drift ≥ `CARGOLESS_BASE_STALE_THRESHOLD` (default 5). When count fails (the common stale-clone case — objects not in odb), warn regardless, because the user is reasoning against a base they cannot even fully describe locally. - 5 unit tests under `stale_base_tests` covering: happy path silent, the exact production wedge (objects unavailable → "unknown number of commits"), count-known path, threshold honoured, unresolvable base silent. - Disable with `CARGOLESS_BASE_STALE_CHECK=0`. - Detect-only: no auto-fetch, no side effects on local refs. ## CI All 4 jobs green on the final commit `1051126`: build / test / fmt / clippy. ra-bench correctly skipped (S1 harness). ## Commit history (will squash) - `8b48840` feat(checks): warn when classify-at-base runs against a stale local tracking ref - `cb63c72` fix(checks): clippy clean stale-base helper - `1051126` fix(checks): rustfmt edition-2024 single-line if-else
feat(checks): warn when classify-at-base runs against a stale local tracking ref
Some checks failed
ci / rust-analyzer latency harness (S1 / AC#2) (push) Has been skipped
ci / fmt (push) Successful in 6m56s
ci / build (push) Successful in 6m56s
ci / test (push) Successful in 6m57s
ci / clippy (push) Failing after 6m57s
8b48840ddc
`cargoless checks run --base <ref> --allow-existing-red` re-runs project
checks against a base worktree to classify required reds as inherited
vs. new. When the local `origin/<ref>` lags real origin, the base
worktree resolves to a pre-merge-base tree and whole-tree fingerprint
checks report phantom diagnostic counts — classifications come back as
"new" because their fingerprints don't match the stale-base run.

This is the wedge the PYAML team hit on donut (~5 false-block dev-merge
attempts, reported as Defect 2 in 2026-06-23 ticket).

`BaseWorktree::create` now calls a read-only `git ls-remote origin
refs/heads/<ref>` before `git worktree add` and compares the remote tip
to the local tracking ref. When tips differ:

  - if `rev-list --count` succeeds (remote objects are in odb), warn
    when drift ≥ CARGOLESS_BASE_STALE_THRESHOLD (default 5);
  - if `rev-list --count` fails (the common stale-clone case), warn
    regardless — the operator is reasoning against a base they cannot
    even fully describe locally.

Disable with CARGOLESS_BASE_STALE_CHECK=0. Detect-only: the warning
suggests `git fetch origin <ref>`; classification continues regardless,
so this never blocks a build. `ls-remote` never advances local refs.

Unit tests use a file:// origin fixture to exercise: silent-when-fresh,
warn-when-objects-missing (the wedge case), warn-with-count-when-objects-
available, threshold-honoured-when-count-known, silent-for-unresolvable-base.

Belongs alongside the existing A1–A4 asks in
tf-multiverse/.docs-discovery/plan/cargoless-source-asks.md as A5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(checks): clippy clean stale-base helper
Some checks failed
ci / rust-analyzer latency harness (S1 / AC#2) (push) Has been skipped
ci / fmt (push) Failing after 2m0s
ci / clippy (push) Successful in 2m0s
ci / test (push) Successful in 2m1s
ci / build (push) Successful in 2m1s
cb63c72562
Three default-warn clippy lints flagged on 8b48840:
- collapsible_if: collapse `if let Some(n) = behind { if n < threshold {...} }`
  to `if behind.is_some_and(|n| n < threshold)` in base_freshness_warning.
- redundant_closure: replace `.map(|s| s.to_string())` with
  `.map(str::to_string)` in resolve_tracking_ref and ls_remote_origin_branch
  (one each). Same behaviour, no allocation difference.
- needless_borrow: drop the extra `&` on `local.parent().unwrap()` in the
  test fixture — the inner `Option<&Path>::unwrap()` is already `&Path`.

The format! call in base_freshness_warning loses redundant `short = short`
style named args (the args are now bound to local lets which the captured
identifier syntax picks up directly). Pure mechanical formatting change.

No behaviour change; this is the clippy gate on the Defect-2a stale-base
detection PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(checks): rustfmt edition-2024 single-line if-else
Some checks failed
ci / rust-analyzer latency harness (S1 / AC#2) (push) Has been skipped
ci / clippy (push) Successful in 1m21s
ci / fmt (push) Successful in 1m46s
ci / test (push) Successful in 2m43s
ci / build (push) Successful in 2m43s
ci / rust-analyzer latency harness (S1 / AC#2) (pull_request) Has been skipped
ci / fmt (pull_request) Successful in 3m47s
ci / test (pull_request) Failing after 3m47s
ci / build (pull_request) Successful in 3m48s
ci / clippy (pull_request) Successful in 3m51s
1051126576
`cargo fmt --check` (workspace edition 2024) collapses
`if x.is_empty() { None } else { Some(name) }` onto one line. My earlier
`rustfmt <file>` (no flag) defaulted to edition 2015 which expands them
to 5 lines — fmt CI on cb63c72 caught the drift. Re-formatted with
`rustfmt --edition 2024` so the source matches what CI checks.

No behaviour change. Clippy/build/test all stayed green on cb63c72;
only fmt was red.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
triform/cargoless!70
No description provided.