Skip to content

fix(bin): defer contribution unavailable wakes until a failure repeats - #6017

Open
dhanu-covlant wants to merge 3 commits into
kunchenguid:mainfrom
dhanu-covlant:fm/fleet-contributions-stale-observation
Open

dhanu-covlant wants to merge 3 commits into
kunchenguid:mainfrom
dhanu-covlant:fm/fleet-contributions-stale-observation

Conversation

@dhanu-covlant

@dhanu-covlant dhanu-covlant commented Sep 28, 2026 •

Copy link
Copy Markdown

Intent

Fix the repeated contribution-observer noise: the contribution poll keeps waking with observation unavailable for older Codeparser and Sentinel PRs even when there is no pending action. The recurring failure is transient and self-clearing, so the goal is to stop momentary read inconsistencies from interrupting supervision while preserving escalation for persistent observation loss.

What Changed

  • bin/fm-contributions.sh poll now saves a failure_streak on each failed record. The first failed forge observation is saved quietly with failure_streak=1. The contributions: observation unavailable line prints only when the next consecutive failure moves the streak to 2. Later failures stay at 2 and print nothing. A successful read, or settling a merged or closed contribution, clears the streak and ends the episode.
  • When several tasks own the same URL, they share one episode that continues from the highest streak among them. A saved error with no failure_streak (an older record) counts as already reported, so it does not wake supervision again. bin/fm-contributions.jq accepts failure_streak only as an integer from 0 to 2, and the script's header comment now describes the new rules.
  • tests/fm-contributions.test.sh has new tests: one failure that clears on the next read without printing anything, a persistent failure that prints once after two failures and again only after a recovery, and a late-joining owner that shares the episode. Existing tests now expect a first failure to be saved quietly with failure_streak == 1.

🤖 Generated with Claude Code

Risk Assessment

✅ Low: The change is a small, clearly bounded debounce and the intent explicitly allows it: a first failure is now recorded silently, and the unavailable line prints once on the second consecutive failure. Success, final settlement, shared owners, late owners and legacy error records each reset or carry the streak correctly. The watcher runs checks every 300s, so a single momentary failure is absorbed while a persistent outage still wakes supervision within one extra check interval.

Testing

I ran the focused contribution test file, which passed. I then ran seven scenarios live against real GitHub through fm-contributions.sh poll in a disposable lab home: a healthy read, a failure that clears on the next poll, a failure that persists, recovery followed by a new episode, a stored record from before failure_streak existed, a URL shared with a task added mid-episode, and a tampered streak value. All seven matched the intended rules: the first failure is recorded without a wake, the second failure in a row wakes exactly once, and a good read clears the episode. The same drive on the base commit reproduced the reported noise (it woke on the first failure, even when the next read succeeded). This is a CLI change with no UI, so the evidence is poll output transcripts rather than screenshots. Lab homes and base-commit temp copies were removed, and the worktree is clean.

  • Live validation: ✅ go - 7 of 7 scenarios driven live against the product
Scenario Result Live Evidence
Healthy real GitHub read records the observation and prints nothing ✅ pass live live-poll-transcript.txt S1: rc=0 stdout=[], state=open head=102726fc7b, error=null
Transient read failure that clears on the next poll never wakes supervision ✅ pass live live-poll-transcript.txt S2: failure poll stdout=[] with error recorded and failure_streak=1; recovery poll stdout=[] with error=null and no failure_streak. The base transcript shows the old code woke…
Persistent observation loss still escalates, exactly once per episode ✅ pass live live-poll-transcript.txt S3: failure #1 silent (streak 1), failure #2 prints 'contributions: observation unavailable for …/pull/6016' (streak 2), failures #3 and #4 silent (streak stays 2)
A good read ends the episode, and a new episode again waits for its second failure ✅ pass live live-poll-transcript.txt S4: recovery clears error and streak; new failure #1 silent, new failure #2 wakes once
A stored error from before failure_streak existed counts as already reported (no wake after upgrade) ✅ pass live live-poll-transcript.txt S5: seeded error with failure_streak=null; the next failing poll prints nothing and sets streak 2
Two tasks owning one PR share one episode and one wake, including a task added mid-episode ✅ pass live live-poll-transcript.txt S6: first failure silent; second failure, with the new owner added, prints one line; both records at streak 2; third failure silent
Adversarial: a tampered out-of-range failure_streak is refused, not trusted ✅ pass live live-poll-transcript.txt S7: stored failure_streak=7 → 'contributions: 1 unreadable durable record(s)' and 'invalid stored contribution record'; the record was not rewritten
Evidence: Live poll transcript (fix) — real gh against #6016, scenarios S1–S7

Source: Live poll transcript (fix) — real gh against kunchenguid/firstmate#6016, scenarios S1–S7


=== S1 healthy real read ===
--- poll healthy (real gh)                      rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:40:51Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}

=== S2 transient failure then recovery ===
--- poll transient failure (bad token)          rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:40:54Z","error":"forge observation unavailable or changed during read","failure_streak":1,"state":"open","head":"102726fc7b"}
--- poll recovery (real gh)                     rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:40:56Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}

=== S3 persistent failure escalates once ===
--- poll failure #1                             rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:40:59Z","error":"forge observation unavailable or changed during read","failure_streak":1,"state":"open","head":"102726fc7b"}
--- poll failure #2                             rc=0 stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:00Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}
--- poll failure #3                             rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:02Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}
--- poll failure #4                             rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:04Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}

=== S4 recovery ends episode; new episode deferred again ===
--- poll recovery                               rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:05Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll new failure #1                         rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:09Z","error":"forge observation unavailable or changed during read","failure_streak":1,"state":"open","head":"102726fc7b"}
--- poll new failure #2                         rc=0 stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:10Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}

=== S5 legacy error record (no failure_streak) stays quiet ===
--- poll recovery                               rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:12Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}
{"note":"seeded legacy record","error":"forge observation unavailable or changed during read","failure_streak":null}
--- poll failure on legacy error                rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:15Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}

=== S6 shared URL, late owner joining an episode ===
--- poll recovery                               rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:17Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll shared failure #1 (one owner)          rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:20Z","error":"forge observation unavailable or changed during read","failure_streak":1,"state":"open","head":"102726fc7b"}
--- poll shared failure #2 (late owner added)   rc=0 stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:22Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}
{"task":"liveduo","rec":{"error":"forge observation unavailable or changed during read","failure_streak":2}}
--- poll shared failure #3                      rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:23Z","error":"forge observation unavailable or changed during read","failure_streak":2,"state":"open","head":"102726fc7b"}

=== S7 adversarial: tampered failure_streak rejected ===
--- poll recovery                               rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:25Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll poll with tampered streak=7            rc=1 stdout=[contributions: 1 unreadable durable record(s)
fm-contributions: invalid stored contribution record]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:25Z","error":"x","failure_streak":7,"state":"open","head":"102726fc7b"}
Evidence: Live poll transcript (base 29213a0) — reproduces a wake on the first failure that then clears

Source: Live poll transcript (base 29213a09) — reproduces a wake on the first failure that then clears


=== S1 healthy real read ===
--- poll healthy (real gh)                      rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:40Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}

=== S2 transient failure then recovery ===
--- poll transient failure (bad token)          rc=0 stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:45Z","error":"forge observation unavailable or changed during read","failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll recovery (real gh)                     rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:47Z","error":null,"failure_streak":null,"state":"open","head":"102726fc7b"}

=== S3 persistent failure escalates once ===
--- poll failure #1                             rc=0 stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:50Z","error":"forge observation unavailable or changed during read","failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll failure #2                             rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:51Z","error":"forge observation unavailable or changed during read","failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll failure #3                             rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:53Z","error":"forge observation unavailable or changed during read","failure_streak":null,"state":"open","head":"102726fc7b"}
--- poll failure #4                             rc=0 stdout=[]
{"url":"https://github.com/kunchenguid/firstmate/pull/6016","checked_at":"2026-09-28T15:41:54Z","error":"forge observation unavailable or changed during read","failure_streak":null,"state":"open","head":"102726fc7b"}

=== S4 recovery ends episode; new episode deferred again ===
Evidence: Live drive script used for both transcripts

Source: Live drive script used for both transcripts

#!/usr/bin/env bash
# Live drive of bin/fm-contributions.sh poll against real GitHub in a disposable lab home.
# Usage: live-drive.sh <repo-root>
set -u
ROOT=$1
LAB=$(mktemp -d "${TMPDIR:-/tmp}/fm-lab.XXXXXX")
trap 'rm -rf "$LAB"' EXIT
"$ROOT/bin/fm-lab-home.sh" create "$LAB" >/dev/null
URL=https://github.com/kunchenguid/firstmate/pull/6016
printf '# Backlog\n\n## Queued\n- [ ] livepr - Contribution %s (repo: firstmate) (kind: ship)\n' "$URL" > "$LAB/data/backlog.md"
clean() { env -u NO_MISTAKES_GATE -u FM_GATE_REFUSE_BYPASS -u FM_ROOT_OVERRIDE -u FM_STATE_OVERRIDE -u FM_DATA_OVERRIDE -u FM_CONFIG_OVERRIDE -u FM_PROJECTS_OVERRIDE FM_HOME="$LAB" "$@"; }
poll() { # label [bad-token]
  local out rc
  if [ "${2:-}" = bad ]; then
    out=$(clean env GH_TOKEN=invalid-token-for-live-test "$ROOT/bin/fm-contributions.sh" poll 2>&1); rc=$?
  else
    out=$(clean "$ROOT/bin/fm-contributions.sh" poll 2>&1); rc=$?
  fi
  printf -- '--- poll %-38s rc=%s stdout=[%s]\n' "$1" "$rc" "$out"
  jq -c '.records[] | {url,checked_at,error,failure_streak,state:.observation.state,head:(.observation.head // "" | .[0:10])}' "$LAB/data/livepr/contributions.json" 2>/dev/null
  sleep 1
}
section() { printf '\n=== %s ===\n' "$1"; }

section 'S1 healthy real read'
poll 'healthy (real gh)'
section 'S2 transient failure then recovery'
poll 'transient failure (bad token)' bad
poll 'recovery (real gh)'
section 'S3 persistent failure escalates once'
poll 'failure #1' bad
poll 'failure #2' bad
poll 'failure #3' bad
poll 'failure #4' bad
section 'S4 recovery ends episode; new episode deferred again'
poll 'recovery' 
poll 'new failure #1' bad
poll 'new failure #2' bad
section 'S5 legacy error record (no failure_streak) stays quiet'
poll 'recovery'
jq '.records[0].error = "forge observation unavailable or changed during read" | del(.records[0].failure_streak)' "$LAB/data/livepr/contributions.json" > "$LAB/x.json" && mv "$LAB/x.json" "$LAB/data/livepr/contributions.json"
chmod 600 "$LAB/data/livepr/contributions.json"
jq -c '.records[] | {note:"seeded legacy record",error,failure_streak}' "$LAB/data/livepr/contributions.json"
poll 'failure on legacy error' bad
section 'S6 shared URL, late owner joining an episode'
poll 'recovery'
poll 'shared failure #1 (one owner)' bad
printf -- '- [ ] liveduo - Duplicate owner %s (repo: firstmate) (kind: ship)\n' "$URL" >> "$LAB/data/backlog.md"
poll 'shared failure #2 (late owner added)' bad
jq -c '{task, rec:(.records[] | {error,failure_streak})}' "$LAB/data/liveduo/contributions.json"
poll 'shared failure #3' bad
section 'S7 adversarial: tampered failure_streak rejected'
poll 'recovery'
jq '.records[0].failure_streak = 7 | .records[0].error = "x"' "$LAB/data/livepr/contributions.json" > "$LAB/x.json" && mv "$LAB/x.json" "$LAB/data/livepr/contributions.json"
chmod 600 "$LAB/data/livepr/contributions.json"
poll 'poll with tampered streak=7'
Evidence: Output of the contribution test file

Source: Output of the contribution test file

ok - only required-captain contributions are rows; other actors are counted
ok - replaced-head verdict is stale and cannot create a captain requirement
ok - unchecked ownership is disclosed and cannot prove silence
ok - newest check with no verdict is distinct from passing and pending
ok - new maintainer comment wakes once and stays pending until acknowledged
ok - new maintainer review wakes once and stays pending until acknowledged
ok - new maintainer inline wakes once and stays pending until acknowledged
ok - ready-for-pr on a filed issue becomes a planning wake
ok - a fresh open issue remains measured maintainer triage
ok - an absent check lane remains missing across repeated observations
ok - mixed freshness retains measured captain work and discloses the gap
ok - malformed durable evidence cannot prove silence
ok - a transient ready-for-pr label wakes and its exact acknowledgement survives replay
ok - recorded judgment keeps its exact head and is stale immediately on a published replacement
ok - a current forge observation refreshes a verdict after a replacement
ok - an unavailable current head leaves verdict freshness unknown
ok - away yolo delivery is fleet work without granting merge authority
actionable: PR https://github.com/o/r/pull/8 is registered but its ready line did not reach the parent channel (rc=3)
ok - cross-home away yolo delivery is fleet work
ok - retired ownership persists and unsupported forge remains visibly unmeasured
ok - unsupported forge coverage is disclosed without inventing fleet work
ok - held unsupported forge coverage remains unmeasured
ok - shared contribution signal wakes once while retaining both acknowledgements
ok - watcher keeps observer diagnostics separate from contribution wakes
ok - expired child unsupported-forge coverage remains unmeasured
ok - watcher surfaces one newly durable contribution signal without re-ringing it
ok - parent consumes measured child coverage and refuses expired child silence
ok - unreadable pending signals refuse an empty-inbox claim
ok - record task identity is the directory dirname/basename named
ok - snapshot and pending create nothing in a home without state
ok - budget exhausted mid-observation (exhaust) keeps the prior record and stays silent
ok - budget exhausted mid-observation (hang) keeps the prior record and stays silent
ok - a first genuine forge failure inside the budget records evidence without waking
ok - a URL owned by two tasks is observed once and every owner receives the result
ok - a merged or closed contribution settles once, is not re-read, and never wakes again
ok - a late owner inherits a terminal observation without a forge read or wake
ok - an open PR linked from a done task keeps being observed
ok - a later URL waits when fewer than fifteen seconds remain for its observation
ok - eight 3-second PR reads complete fresh within one 20-second poll cycle
ok - a read killed at the five-second bound is budget refusal and stays silent
ok - rotation preserves timed-out records and refreshes every slow PR on successive cycles
ok - the effective budget is cut down to the watcher per-check bound with margin
ok - generated checks enforce configured and inherited budgets at runtime
ok - a transient forge failure records evidence and self-clears without waking supervision
ok - a persistently unavailable forge wakes once after two consecutive failures
ok - a late owner shares the same deferred forge failure episode
Evidence: Key before/after lines
BASE: poll transient failure (bad token) stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016]
FIX: poll transient failure (bad token) stdout=[] failure_streak=1
FIX: poll recovery (real gh) stdout=[] error=null failure_streak=null
FIX: poll failure #2 stdout=[contributions: observation unavailable for https://github.com/kunchenguid/firstmate/pull/6016] failure_streak=2
FIX: poll failure #3/#4 stdout=[] failure_streak=2

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ bin/fm-contributions.sh:394 - The legacy-migration branch has no test. In the prior-streak query, (.failure_streak // 2) treats a saved record that has an error but no failure_streak as already reported. That is the case for every error record written before this change. Without that default, the first poll after deployment would re-wake each existing outage once. No test seeds such a record and checks that the next failure stays silent and writes failure_streak == 2. Add one behavioural poll test that starts from a pre-change error record.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 7 of 7 scenarios driven live against the product
Scenario Result Live Evidence
Healthy real GitHub read records the observation and prints nothing ✅ pass live live-poll-transcript.txt S1: rc=0 stdout=[], state=open head=102726fc7b, error=null
Transient read failure that clears on the next poll never wakes supervision ✅ pass live live-poll-transcript.txt S2: failure poll stdout=[] with error recorded and failure_streak=1; recovery poll stdout=[] with error=null and no failure_streak. The base transcript shows the old code woke…
Persistent observation loss still escalates, exactly once per episode ✅ pass live live-poll-transcript.txt S3: failure #1 silent (streak 1), failure #2 prints 'contributions: observation unavailable for …/pull/6016' (streak 2), failures #3 and #4 silent (streak stays 2)
A good read ends the episode, and a new episode again waits for its second failure ✅ pass live live-poll-transcript.txt S4: recovery clears error and streak; new failure #1 silent, new failure #2 wakes once
A stored error from before failure_streak existed counts as already reported (no wake after upgrade) ✅ pass live live-poll-transcript.txt S5: seeded error with failure_streak=null; the next failing poll prints nothing and sets streak 2
Two tasks owning one PR share one episode and one wake, including a task added mid-episode ✅ pass live live-poll-transcript.txt S6: first failure silent; second failure, with the new owner added, prints one line; both records at streak 2; third failure silent
Adversarial: a tampered out-of-range failure_streak is refused, not trusted ✅ pass live live-poll-transcript.txt S7: stored failure_streak=7 → 'contributions: 1 unreadable durable record(s)' and 'invalid stored contribution record'; the record was not rewritten
  • bash tests/fm-contributions.test.sh (only the contribution test file; every case passed, including the new test_transient_forge_failure_recovers_without_wake, test_unavailable_forge_wakes_once_after_two_consecutive_failures and test_late_owner_keeps_failure_episode_suppressed)
  • Live drive live-drive.sh &lt;worktree&gt;: minted a lab home with bin/fm-lab-home.sh create, added a backlog row linking feat(spawn): add opt-in worker launch command that leads the launch prompt #6016, and ran the real bin/fm-contributions.sh poll with the real authenticated gh. Genuine read failures came from a process-local GH_TOKEN=invalid... env var; the credential store was not touched.
  • Live S1: healthy real read records the observation, no output
  • Live S2: one failure, then recovery → no wake, streak 1, then cleared
  • Live S3: four failures in a row → one wake on the 2nd, silent on the 3rd and 4th, streak held at 2
  • Live S4: recovery ends the episode; a new episode again waits for its 2nd failure
  • Live S5: a stored error with no failure_streak (older record format) → the next failure stays quiet (streak 2)
  • Live S6: a second task sharing the URL, added mid-episode → one shared wake, both records at streak 2, then quiet
  • Live S7 (adversarial): stored failure_streak=7 → the record is reported unreadable and no write happens
  • Baseline: the same live drive against git archive 29213a09 (the pre-fix code) to reproduce the reported noise
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Changes failure tracking logic for contribution monitoring.

The PR appears safe to merge; no outstanding findings remain.

Reviews (2) · Last reviewed commit: "no-mistakes(ci): I made both requested G..."

Comment thread bin/fm-contributions.jq Outdated
Comment thread tests/fm-contributions.test.sh
…, and the full contribution test suite passes. I left ci-1 and ci-2 alone, as instructed. Those two checks report `action_required`, which needs someone to approve the workflow runs; no code change can clear them. - **ci-3, `bin/fm-contributions.jq:15`:** the old check turned a stored `false` into 0, so it counted as valid. The check now allows a missing or null `failure_streak`; any value that is present must be a whole number from 0 to 2. I ran the validator on sample records: `null`, `1` and `2` pass, while `false`, `1.5` and `3` are now rejected. - **ci-4, `tests/fm-contributions.test.sh`:** I added `test_legacy_stored_error_counts_as_reported` and put it in the test runner list. It starts from an older record that has an error but no `failure_streak`, runs a poll against a failing forge, and checks two things: nothing is printed, and the record is saved with the same error, the new `checked_at` and `failure_streak == 2`. **Checks run:** - `bash tests/fm-contributions.test.sh` passes every test, including the new one. - To confirm the new test catches a real regression, I temporarily changed the poll so a missing streak counts as 0 instead of 2. The new test failed ("a legacy stored error did not migrate to streak 2") and all other tests still passed. I then restored the file; only the two intended files differ from the original. The changes are not committed. Pushing and rechecking CI are left to the outer executor
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