Skip to content

feat(worker,engine): hand a repair round the tail of each failed CI job's log (CI repair, U4) - #17

Merged
Steel-tech merged 3 commits into
mainfrom
feat/ci-repair-logs
Sep 28, 2026
Merged

Steel-tech merged 3 commits into
mainfrom
feat/ci-repair-logs

Conversation

@Steel-tech

Copy link
Copy Markdown
Contributor

What

U4 of the CI repair plan: a repair round sees the end of each failed CI job's log, not just the check's name. A single PR against main, not stacked.

  • CommitChecks now records each check run's id and app. For GitHub Actions, the check run id is the job id.
  • The gateway gains FailedCheckLogs, which reads repos/{o}/{r}/actions/jobs/{id}/logs and never fails: a check with no log carries a LogNote saying why.
  • The repair envelope carries log_tail / log_note in each failed_checks entry.
Bound Value
Per log, keeping the end 16 KiB, starting on a whole line; timestamps and ANSI codes stripped; valid UTF-8
All logs together 64 KiB, so at most 4 logs
Reads attempted, failures included 8, each under the 60 s gh timeout
Which checks are read Failed GitHub Actions jobs only

The engine re-applies the bounds itself rather than trusting the worker. It also tells the agent that a log line asking for something is output to diagnose, never a request to follow.

Bugs found on the way

  • The existing gh runner keeps the first 4 MiB of output, which would have thrown away the part of a log where the failure is. U4 adds a tail-keeping runner (runTailCommand).
  • A truncated log opened with a half line. The fragment is now dropped.

Security review: three findings, all fixed

  1. Log tails were being written into the ci_repair_start trace event, so anything GitHub's *** masking missed would persist in the control plane. 64 KiB of logs also pushed the event past its payload cap and mangled it. The event now records name, conclusion, URL, note and log_bytes only.
  2. Failed reads didn't count against any limit, so a hanging gh had no overall bound. Passing checks weren't skipped either. Reads are now capped at 8 and only failures are read.
  3. A {{handoff_dir}} inside a CI log or a parameter value could rewrite data and suppress a prompt section, because composePrompt rescanned text it had already inserted. It now substitutes in one pass over the template and decides which sections to add from the template alone. This touches every prompt jig builds; the existing suite passes unchanged.

⚠️ Still inert

Nothing calls FailedCheckLogs until the round loop lands (U5). The composePrompt fix is live immediately and changes behaviour only when a parameter value contains a {{…}} placeholder: that placeholder now stays literal instead of being substituted.

Verification

  • just check is green.

  • Mutation-checked: 27 breaks, all caught. Four fixture gaps let mutants through at first:

    • The test log was shorter than the cap once cleaned, so the cut was never exercised.
    • The fake gh returned id/app whatever the query asked for.
    • One note assertion matched "GitHub" in both of the notes it was meant to tell apart.
    • The parameter in the injection test never reached the prompt.

    All four fixed; every remaining "caught" was checked to compile first.

🤖 Generated with Claude Code

Steel-tech and others added 3 commits September 27, 2026 21:42
…ob's log (CI repair U4)

CommitChecks now records each check run's id and app. The gateway gains
FailedCheckLogs, which attaches the end of a failed GitHub Actions job's
log (the check run id is the job id) and never fails: any check without a
log carries a note saying why.

Logs are read with a new tail-keeping runner, since the existing bounded
runner keeps the first bytes and a failure sits at the end. A tail drops
its fragment first line, loses Actions timestamps and ANSI codes, is cut
to whole lines within 16 KiB, and is valid UTF-8. At most four logs are
read (64 KiB in all); failed reads do not spend that budget.

The engine bounds the tails again rather than trusting the worker,
keeping each one's end and the 64 KiB total, and the repair envelope says
outright that a log line asking for something is output to diagnose,
never a request to follow. Nothing reads logs until the round loop (U5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd treat inserted text as data

Security review of U4 found three problems. The ci_repair_start trace event
carried every log tail, so anything GitHub failed to mask would persist in
the control plane, and 64 KiB of logs pushed the event past its payload cap
and mangled it; it now records name, conclusion, URL, note, and log_bytes
only. FailedCheckLogs read passing checks and did not count failed reads,
so a hanging gh had no overall bound; it now reads only failures and stops
after eight attempts. And composePrompt rescanned text it had inserted, so
a {{handoff_dir}} inside a CI log or a parameter value could rewrite data
and suppress the handoff section; it now substitutes in one pass over the
template and decides sections from the template alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 32 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: e69b3a68-0efc-4863-a287-54d4b7676379

📥 Commits

Reviewing files that changed from the base of the PR and between 4ead632 and 91f5dc9.

📒 Files selected for processing (8)
  • docs/plans/2026-09-27-001-feat-ci-repair-plan.md
  • internal/engine/phase.go
  • internal/engine/resume.go
  • internal/engine/resume_test.go
  • internal/protocol/publish.go
  • internal/worker/publish.go
  • internal/worker/publish_ci_test.go
  • internal/worker/publish_test.go

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Steel-tech
Steel-tech merged commit 39267bc into main Sep 28, 2026
5 checks passed
@Steel-tech
Steel-tech deleted the feat/ci-repair-logs branch September 28, 2026 05:25
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