Skip to content

github-backup(fetch): fail_days counts every failure day since the last success, not 3 days in a row — intermittent failures weeks apart trip the chronic alarm and latch exit 1 #3257

Closed

Description

@dezren39

Summary

github-backup(fetch): the chronic-failure alarm in infra/github-backup/bin/fetch_mirrors.py does not implement its own contract. The module docstring promises: "A repo failing on 3 distinct days in a row makes the run exit non-zero." The implementation instead counts every distinct failure day since the last success, with no consecutiveness check and no expiry: fail_days is only ever appended to (one entry per calendar day) and is only cleared by a successful fetch. A repo that fails once in January, once in February and once in March — never on consecutive days — is declared a chronic failure, and from then on every daily run exits 1 until that repo next succeeds, even on days when nothing failed.

Evidence (main f4f800e7)

Verified (laptop probe, real fetch_mirrors.py)

Probe: ~/git/fleet/probe-2026-10-31-laneA-fetchchronic/ on the laptop — the real fetch_mirrors.py from the laptop clone (sha256 f0115270…, identical to the sandbox checkout at main f4f800e7), a real bare mirror for the valid_mirror sanity check, then the fetch stubbed to fail (rc=1) with the calendar stubbed to three non-consecutive days:

valid_mirror(real bare) = True
2026-01-05 -> fail fail_days = ['2026-01-05']
2026-02-10 -> fail fail_days = ['2026-01-05', '2026-02-10']
2026-03-20 -> fail fail_days = ['2026-01-05', '2026-02-10', '2026-03-20']
chronic_condition(main's exit-1 test) = True

Three failures spaced weeks/months apart — never 3 days in a row — trip the chronic condition.

Why it matters

The exit code is the systemd-visible alarm for the daily mirror fetch. An alarm that fires for intermittent, self-recovering flakiness (a laptop asleep one day in January, a network blip one day in February, a GitHub incident one day in March) and then stays latched trains the operator to ignore it — the exact failure mode a "3 days in a row" streak rule exists to prevent. Conversely the persisted count means the alarm's state depends on ancient history in state.json, not on the repo's current health.

Acceptance criteria

  • Chronic means a streak: a repo counts as chronic only when its failures fall on 3 consecutive calendar days (per the docstring), or the docstring and implementation are changed together to a clearly stated window rule (e.g. 3 failures within N days) — code and contract must agree.
  • Failure days that fall outside the streak/window no longer count: failures on 2026-01-05, 2026-02-10 and 2026-03-20 do not produce a chronic exit 1.
  • A success still resets the streak, and 3 truly consecutive failure days still exit 1.
  • Regression tests/probe cover: three non-consecutive failure days (no chronic), three consecutive failure days (chronic), failure–failure–success–failure–failure (no chronic), and the no-failure control unchanged.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    effort/sSmall: expected under 2 hours of work
    app/github-backupinfra/github-backup estate mirror system
    on Oct 31, 2026
  2. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: filed

    • Agent: new-issue-creator (new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a, 2026-10-31 18:35 CDT run
    • Found via: filed this run
    • Task: Filed after verifying on the laptop (sha-matched fetch_mirrors.py) that fail_days accumulates every failure day since the last success, so three failures weeks apart trip the '3 distinct days in a row' chronic exit-1 test
    • Expectation: effort/s — make chronic mean a consecutive-day streak (or restate the docstring as a window rule and implement that); regression cases for non-consecutive / consecutive / reset-by-success
    • Links: infra/github-backup/bin/fetch_mirrors.py:29-38,72-75; probe ~/git/fleet/probe-2026-10-31-laneA-fetchchronic/ (laptop)
  3. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Design-architect pass 29 (2026-10-31) — agent note: this issue is ADOPTED as a phase of design #3282 ("a mirror is what it can prove — one validity, outcome and alarm contract for the github-backup estate mirror", design doc: files/designs/code-github-backup-mirror-contract.md in the keep-the-four-repos-healthy goal workspace), linked as a sub-issue of #3282 and labelled source/design-architect. Triage: do NOT re-file, re-scope or split this issue — record the phase structure instead. Phase mapping: Phase 1 (validity & destinations) = #3238 + #3233; Phase 2 (recorded outcomes & the alarm) = #3250 + #3219 + #3270 + #3257; Phase 3 (durable state & plan retention) = #3240 + #3229 + closing audit #3283 (gated on Phases 1–2). The design records the pins that span the phases: validity = bare + HEAD resolves, with no emptiness exception inside the plan (build_plan excludes zero-commit repos); destinations are absent / valid / skeleton (remove + clone) / debris (quarantine to .partial-<timestamp> + clone); every fetch attempt ends in one recorded outcome shared by the daily loop and the drainer, and the drainer acks only on ok; the chronic alarm is a streak of 3 consecutive unavailable days (fail, timeout and missing all count); generated state is written atomically only through ghb_common helpers; reconcile retains a failed owner's previously planned entries. Where an origin's acceptance left a choice open (the streak-vs-window rule, missing-counts-vs-immediate, report-vs-retain, skeleton handling), the design doc records the decision — triage follows the design's pin, not the open alternative. Each origin keeps its own acceptance criteria and its already-queued triage packet — no new packet was created for it this pass.

  4. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  5. 7 remaining items

  6. removed
    task/triageQueue packet kind: verify, reproduce, and route this item
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 1, 2026
  7. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state=handed_off
    worker: repo-worker-code
    task_id: 4760
    issue: #3257
    follow_up_task_id: 5688
    summary: Triage confirmed on current main that nonconsecutive failures incorrectly trigger the chronic condition. Follow-up small-fix task 5688 is queued with the consecutive-day contract and four deterministic regression sequences. Triage task 4760 completed as triaged.
    verification: isolated, network-free probe confirms spaced failures false-positive; consecutive failures remain chronic; success resets; empty state remains non-chronic.

  8. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 2, 2026
  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked-up
    task: 5688 (repo-health-code-pilot, OpenChamber scheduled lane)
    state: Fresh GitHub verification: #3257 is OPEN; no open PR matching #3257 was found; no hard-stop labels are present. The issue is assigned to Drew but only ordinary queue labels were present.
    next: Fetch/fast-forward the primary clone, create or resume worktree 5688, inspect the current implementation/tests, then implement the confirmed consecutive-calendar-day streak fix with focused regression tests.

  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: done
    task: 5688 (repo-health-code-pilot, OpenChamber scheduled lane)
    state: Fixed and merged via PR #4334; merge SHA 714289f. Issue #3257 is CLOSED. Streak tracking now keeps only consecutive local-calendar failure dates, preserves success reset, and handles prior cumulative state.
    verified: test_fetch_mirrors.py — 6 pass; test_drain_queue.py — 54/54 pass; py_compile and diff check pass. No active CI checks were configured; CodeRabbit was skipped.
    bookkeeping: sandbox backlog and sweep.log update deferred to fleet reconciliation as requested.

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

Metadata

Metadata

Assignees

Labels

app/github-backupinfra/github-backup estate mirror systemeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions