Repository navigation
feat(gh-post): rerun failed jobs once per run attempt - #1055
tbhb-agent wants to merge 5 commits into
Conversation
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>
There was a problem hiding this comment.
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-checkandcover-py-checkresults. 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>
Summary
Add
gh-post run rerun-failed <run-id> --repo owner/repofor 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
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 passedmise run cover-go,mise run cover-go-check,mise run cover-py, andmise run cover-py-check. Coverage floors remain unchanged.mise run cover-go-coberturapassed, 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
GOOSbranch 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.