Skip to content

policy(cache): detect kill-switched layers and green default-branch runs that save nothing (fleet-wide) #7

Closed

Description

@zackees

Context

policy-rust.md already states the intent (RUST-004: a successful Dylint run must not silently skip saving reusable outputs). #1 covers one mechanism, the short-vs-qualified nightly marker mismatch. #6 adds a symptom guard: CACHE-005 refuses saves below a family's min size, and heal deletes a poisoned entry.

Nothing yet detects the causes that leave a repository permanently cold while every signal stays green:

zackees/clud has all three at once in its required Dylint job. This is a fleet checker request, not a clud bug report. Any repository using setup-soldr with zccache-unit / dylint-foundation / dylint-output layers can be in the same state, and a green check never reveals it.

Evidence (zackees/clud)

Kill-switch contradicting the declared layers

  • .github/workflows/_dylint.yml sets ZCCACHE_DISABLE: "1". It arrived incidentally in clud #487 (commit f31368f7, 2026-07-06), a PR about Codex fallback instructions.
  • setup-soldr still logs cache-policy layers=toolchain+cargo-registry+zccache-unit+dylint-foundation+dylint-output, yet zccache never runs: final zccache session stats: missing.
  • The only Dylint entry on main is setup-soldr-buildcache-v2-linux-x64-beb1de84d481fcb7-dylint-fd206842bb772458, 253 bytes. PR restores warn produced an unusable payload: archive=22B extracted_files=0 extracted_bytes=0 and treat it as a miss. feat: make template-python-rust-cmd the lean, machine-checked rust-pypi-app reference (ci.toml v3, ci-lint, tag flows, self-healing cache) #6 records the same entry missing on 4/4 main pushes.

A green default-branch run saves nothing

  • main push run 36463271709, job 109067163056 ("Dylint / Dylint", conclusion success, 18:10:45→18:14:51) logs:
    • dylint-cache: no matching successful Dylint marker - skipping save
    • dylint-output-cache: Dylint did not complete successfully - skipping save
    • final cache summary: … build=miss/saved id=-1
  • Mechanism so far:
    • soldr writes SOLDR_DYLINT_SUCCESS_MARKER (content plan.cache_identity()) only on the cargo front-door path (crates/soldr-cli/src/cargo_front_door/run.rs ~L888 → dylint_toolchain.rs::write_success_marker, gated on dylint_entrypoint).
    • setup-soldr src/post.ts ~L2330 requires the marker text to equal its own cacheIdentity.
    • clud invokes soldr dylint --all …. Not yet determined: whether that path writes no marker, or writes one whose identity mismatches (the fix: detect Dylint recompilation and cache identity drift in fast CI #1 class).
  • Either way, the lint passes and the cache is never written.

Cost: every run on every branch re-checks the whole dependency graph cold. From PR clud#1541, run 36467947289, which adds Windows/macOS cross-target passes:

Pass Dependency units checked First workspace unit at Pass finished at
host x86_64-unknown-linux-gnu 405 112.8 s 144.0 s
--target x86_64-pc-windows-msvc 336 113.3 s 147.2 s
--target aarch64-apple-darwin 278 31.5 s 60.2 s

Proposal: candidate rules

These IDs continue after CACHE-001…009 (#6), RUST-008…012 and GEN-009…010.

Candidate ID Mechanical signal
CACHE-010 A workflow or job sets a cache-layer kill-switch while its setup step declares that layer active. Examples: ZCCACHE_DISABLE, SOLDR_NO_CACHE, --no-cache on the compile step, cache: false for the matching family. Runtime confirmation: the setup log lists the layer in cache-policy layers=… but the job logs final zccache session stats: missing or zero compilations. Static detection is from YAML; runtime detection is from logs.
CACHE-011 A successful default-branch run, the writer flow, ends with a declared layer unsaved or saved below its min size. Log signals: save skipped for any reason other than exact-hit or PR policy; no matching successful … marker; did not complete successfully while the job conclusion is success; saved id=-1. Reported per family with the job URL. CACHE-005 refuses the tiny save; CACHE-011 names why the writer produced nothing.
CACHE-012 A family's newest default-branch entry has been restored as unusable payload / treated-as-miss on N consecutive runs (proposed N=2) and no writer run has replaced it. This is the steady-state detector for (c): it pairs with #6's heal and flags repositories where heal never fires because the writer itself is broken (CACHE-011).

Checker behavior:

  • Report cold-vs-warm evidence per the existing timing rules in policy-general.md: compilation counts and the dependency/workspace time split for Dylint passes, not only job duration. Label a run "warm" only when the log shows restored units that were actually used.

  • Fleet sweep: for every repository whose workflows call setup-soldr with dylint: true or zccache layers:

    • read the workflow YAML for kill-switches;
    • read the latest successful default-branch job log for each declared layer's save outcome;
    • list the family's cache entries with sizes.

    Do not rely on GitHub code search: it did not return clud's _dylint.yml for ZCCACHE_DISABLE.

Acceptance criteria

  • CACHE-010–012 are added to policy-general.md (or the feat: make template-python-rust-cmd the lean, machine-checked rust-pypi-app reference (ci.toml v3, ci-lint, tag flows, self-healing cache) #6 cache section), each with its mechanical signal and fixture.
  • RED fixture: clud main run 36463271709 (green Dylint, both Dylint saves skipped, 253-byte build-cache entry) together with PR run 36467947289 (the 22-byte restore treated as a miss, and 405/336/278 cold dependency units). The checker must emit CACHE-010 (static ZCCACHE_DISABLE plus session stats: missing), CACHE-011 and CACHE-012 with those run and job URLs.
  • GREEN fixture: a default-branch run that saves a non-trivial Dylint/zccache dependency payload (above the family min), followed by a run that restores it with measurable dependency-unit hits. The cold-vs-warm report must show the dependency portion of each pass collapsing.
  • The fleet sweep produces one finding per repository and family; repositories without these layers are pass, and inaccessible logs are insufficient evidence.

Decisions

Open questions

  • Should CACHE-011 treat a marker mismatch differently from a missing marker? The fix owner differs: a soldr path that never writes the marker, versus a setup-soldr/soldr identity contract (fix: detect Dylint recompilation and cache identity drift in fast CI #1).
  • Should the kill-switch list be owned by setup-soldr as a machine-readable manifest of env and inputs that disable each layer, so the checker does not hard-code names?
  • Does zccache's per-unit layer cache nightly check-mode (.rmeta) dependency units well enough for Dylint, or is soldr dylint cook the required layer? This needs a measured cold/warm pair on clud after ZCCACHE_DISABLE is removed.

Related

ci.yml#1, #5, #6; clud#487 (f31368f7), clud#1541; runs 36463271709 (job 109067163056) and 36467947289; soldr#2349 (Dylint cache and skip), soldr#3042 (soldr dylint cook).

Activity

  1. zackees commented on Sep 29, 2026

    @zackees
    OwnerAuthor

    Implemented in #62 (merged):

    • CACHE-010 (static): a workflow/job/step env sets a kill-switch var (ZCCACHE_DISABLE, SOLDR_NO_CACHE, SOLDR_CACHE_DISABLE, SOLDR_DYLINT_NO_CACHE) truthy while ci.toml still declares a [cache.family] via a matching setup-soldr:*/zccache* backend.
    • CACHE-011/CACHE-012 (runtime, ci-lint cache save-check --log <file>... --conclusion <c>, pure filesystem): a successful run's log showing a declared layer's save skipped for a non-exact-hit reason (or saved id=-1), and a layer whose restore reports an unusable 0-extracted payload on >= 2 consecutive logs with no writer save in between.

    RED/GREEN fixtures reproduce this issue's own evidence verbatim (clud main run 36463271709 job 109067163056, PR run 36467947289). selftest passes with and without PyYAML (419 tests). Verified read-only against zackees/template-python-rust-cmd: CACHE-010 does not false-positive there.

    Not done (documented as open in the PR): a live fleet-wide sweep across repositories (no central scanner exists yet -- out of ci_lint's current scope per AGENTS.md), and a machine-readable kill-switch manifest owned by setup-soldr (issue's own open question) -- ci-lint's list is its own reserved allowlist for now.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions