Skip to content

feat(gh-post): rerun failed jobs once per run attempt - #1055

Open
tbhb-agent wants to merge 5 commits into
mainfrom
feat/gh-post-rerun-failed
Open

tbhb-agent wants to merge 5 commits into
mainfrom
feat/gh-post-rerun-failed

Conversation

@tbhb-agent

@tbhb-agent tbhb-agent commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add gh-post run rerun-failed <run-id> --repo owner/repo for completed failed, cancelled, or timed-out CI runs. Persist the attempt reservation and audit it before requesting failed jobs. An uncertain outcome triggers a read-back without repeating the write.

Why

Agents need a permitted route to recover failed CI on an unchanged head. GitHub permits the posting identity to use this endpoint, but the Go command allowlist refused it. A durable receipt prevents a later caller from repeating the request after interruption.

Verification

Criteria Evidence
AC1 Invalid-argument rejection before GitHub calls and a reader that fails on stdin access
AC2 Eligible conclusions, refused states, run identity mismatches, and posting identity failures
AC3 Each case injects a sync callback to assert the order of receipt creation, file sync, supported directory syncs, and POST on every platform. Windows skips directory handle syncs. Sync failure cases keep receipts and prevent writes, while concurrent reservations and later-attempt tests prove the reservation boundaries.
AC4 Accepted, unchanged, and unreadable outcomes with one POST, one read-back, and refusal of a repeated invocation
AC5 Audit field assertions, reservation and audit failure refusals, and diagnostics without raw write errors
AC6 Help, posting guide, existing wrapper acceptance, and raw PR write refusal

Passed focused checks:

  • mise exec -- go test -race ./internal/ghpost/...
  • mise exec -- golangci-lint run ./internal/ghpost/...
  • GOOS=windows mise exec -- golangci-lint run ./internal/ghpost/...

Windows build checks passed:

  • GOOS=windows mise exec -- go vet ./internal/ghpost/...

Local checks cross-compile Windows tests without executing them. Their children take a shared lock only during execution to avoid contending on coverage metadata while the subtests remain parallel. Other platforms use a no-op lock. The lock test checks mutual exclusion across concurrent Windows callers and verifies the no-op behavior on other platforms. Local verification compiled Windows tests without executing them.

Repository verification passed mise run check, mise run lint, mise run test, and the protected commit hooks. Coverage checks passed mise run cover-go, mise run cover-go-check, mise run cover-py, and mise run cover-py-check. Coverage floors remain unchanged. mise run cover-go-cobertura passed, and the converter also resolved the profile with Linux file lookup. The Windows directory-sync branch remains uncovered locally.

Risk

Reservations coordinate processes sharing a machine state directory. Another host or external client can race between the read and POST because GitHub doesn't support an expected-attempt parameter. A crash after reservation can withhold a request GitHub never receives. Retaining the receipt prevents an automatic repeat. Live CI reruns weren't part of verification. Windows doesn't allow syncing a directory handle, so the receipt file's own sync is the strongest durability available there. The runtime GOOS branch replaces the build-tagged files because the coverage converter's file lookup is Linux-only.

Loosens: no. The final diff doesn't add a lint suppression, skip a test, weaken an assertion, or change a gate. Its code and prose remain within the issue's size limits.

Compatibility: none

Revert the branch's commits to remove the new verb.

Related

Closes #1052.

Related to incident 1277.

An agent needs a permitted route to recover failed CI without a human
click or another push. Reserving the exact run attempt before requesting
failed jobs prevents repeated writes when GitHub's response is
uncertain. The reservation stays in place even when the request may
not have arrived.

Related to #1052.

Assisted-by: codex:gpt-6
Signed-off-by: Tony Burns <tony@tonyburns.net>
@tbhb-agent tbhb-agent added the change:feat PR adds or changes a feature (Conventional Commit type feat) label Nov 1, 2026
@tbhb-agent
tbhb-agent requested a review from tbhb as a code owner November 1, 2026 17:14

@tbhb-dev-reviews tbhb-dev-reviews Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed head: e1ae166
Review: CHANGES REQUESTED
Loosens: yes — new file-wide nolint:paralleltest at internal/ghpost/rerun_internal_test.go:4. No skipped tests, weakened assertions or other gate changes found. No exact-head owner approval is present.
Owner-tier: no changed protected paths. CODEOWNERS assigns all changed files to @tbhb.

Issue #1052 is Feature. All issue comments were read: there are none, so no separate Refinement verdict is available. Verification cites AC1–AC6 and reports focused tests, race tests, lint and repository gates passing. All 23 check contexts are COMPLETED/SUCCESS. Mergeable: MERGEABLE; merge state: BLOCKED; draft: no.

AC Met on reviewed head Evidence
AC1 Yes internal/ghpost/rerun.go:26 enforces exact arguments and canonical ID; internal/ghpost/state.go:30 avoids stdin; internal/ghpost/rerun_internal_test.go:59 and :271 cover rejection and unread input.
AC2 Yes internal/ghpost/run.go:201 applies the existing identity guard before dispatch; internal/ghpost/rerun.go:54 validates run identity and :74 restricts states; internal/ghpost/rerun_internal_test.go:118 and :141 cover refusals.
AC3 No internal/ghpost/rerun.go:118 atomically creates the receipt, :133 syncs only its file, and :93 sends the sole POST. Directory entries are not synced; see F1. Concurrent and later-attempt coverage: internal/ghpost/rerun_internal_test.go:249 and :207.
AC4 Yes internal/ghpost/rerun.go:94 performs one read-back and exits nonzero without retry; internal/ghpost/rerun_internal_test.go:170 covers accepted, unchanged and unreadable outcomes plus persisted duplicate refusal.
AC5 Yes internal/ghpost/rerun.go:89 audits before POST; :139 encodes the UTC timestamp, operation, URL, attempt and reservation outcome, then syncs; internal/ghpost/rerun_internal_test.go:88 and :220 cover fields and failure blocking. Raw write errors are discarded.
AC6 Yes internal/ghpost/help.go:50 and docs/gh-post.md:122 describe the command and limits; internal/ghpost/rerun_internal_test.go:279 exercises the existing PR guard. No shell file changed.

Size: scc 4.1.0 measured added/removed diff text: 429/3 Go code lines, 32/0 documentation lines; below 800 code / 2000 prose even counting removals. Changed-path owner-tier classification:

Changed path Owner-tier
docs/gh-post.md No
internal/ghpost/args.go No
internal/ghpost/help.go No
internal/ghpost/rerun.go No
internal/ghpost/rerun_internal_test.go No
internal/ghpost/run.go No
internal/ghpost/state.go No

All seven exact-head changed files passed the private-list leak scanner separately with zero hits. Existing gitleaks check passed. Mechanical gates were not rerun.

Findings:

  • F1 [P2] internal/ghpost/rerun.go:133 — Persist the reservation directory entries before allowing the POST. Syncing the receipt file alone does not guarantee that its newly created name, or the newly created parent directories, survives a system interruption. A lost receipt allows a later invocation to POST the same attempt again, contradicting AC3's durable duplicate protection. Sync the necessary directory entries and refuse the POST on sync failure.
  • F2 [P2] internal/ghpost/rerun_internal_test.go:4 — The new file-wide lint suppression loosens the parallel-test gate. The environment fixture explains why these tests are serial, but does not provide exact-head owner approval for the suppression. Remove the suppression through an approach that preserves required coverage, or obtain that approval before merge.

Over-engineering: none found. The parser, reservation and audit helpers each serve explicit ACs; no unnecessary guard, option or layer found.

Definition of done:

  • D1 — Verification names every AC and passing commands, but AC3 remains unmet (F1); the eventual merged head must be verified.
  • D2 — This independent exact-head review requests changes; findings remain unresolved.
  • D3 — All 23 check contexts pass; approval for the new suppression remains unmet (F2).
  • D4 — Existing gitleaks SUCCESS; separate private-list scans of all seven exact-head files returned zero hits; this review is submitted through the content-scanning wrapper.
  • D5 — GitHub verifies the sole commit signature as valid; the future squash signature is pending merge.
  • D6 — docs/gh-post.md:122 and internal/ghpost/help.go:50 document the added operation.
  • D7 — Commit contains Assisted-by; the provenance label is missing, and the future squash trailer is pending.
  • D8 — scc diff size is below both limits, as recorded above.
  • D9 — No deferred implementation work or unrelated follow-ups identified; review findings require resolution before approval.
  • D10 — PR closes #1052, but merge, Done board state and final Status comment remain pending.
  • devtools-1 — Existing lint, tests and coverage checks pass; the supplied Verification does not establish explicit mise run check, cover-go-check and cover-py-check results. Supply evidence; no gates were rerun for this review.
  • devtools-2 — Not applicable: no skill source or generated payload changed.
  • devtools-3 — Adoption documentation describes the consumer-visible CLI at docs/gh-post.md:122; no configuration migration required.

Round: 1 of 3

Syncing a receipt alone doesn't persist its name. After a crash, a
later caller could request the same attempt again. The reservation
must survive interruption before the request leaves the machine.

Environment fixtures need separate processes so their configuration
can't interfere with parallel cases or other tests.

Related to #1052.

Assisted-by: codex:gpt-6
Signed-off-by: Tony Burns <tony@tonyburns.net>
Windows refuses to sync directory handles, preventing eligible reruns
from reaching GitHub. The receipt's own sync is the strongest durability
available there. Other platforms still persist directory entries before
the request.

Related to #1052.

Assisted-by: codex:gpt-6
Signed-off-by: Tony Burns <tony@tonyburns.net>
Parallel test children contend on shared coverage metadata on Windows.
Keep the subtests parallel while allowing only one child to execute at a
time there. Coverage collection and its floor remain unchanged.

Related to #1052.

Assisted-by: codex:gpt-6
Signed-off-by: Tony Burns <tony@tonyburns.net>
The coverage converter resolves profile paths through Linux Go files.
Platform-specific source files prevent conversion when coverage includes
Windows helpers. Runtime platform decisions keep those paths resolvable
while preserving directory syncs and Windows child serialization.

Related to #1052.

Assisted-by: codex:gpt-6
Signed-off-by: Tony Burns <tony@tonyburns.net>
@tbhb-agent
tbhb-agent removed the request for review from tbhb November 1, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feat PR adds or changes a feature (Conventional Commit type feat)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(gh-post): rerun failed CI jobs once per run attempt

1 participant