Skip to content

fix(review-swarm): expose safe failure diagnostics - #283

Closed
khaliqgant wants to merge 5 commits into
fix/review-swarm-wrapper-guard-0910from
fix/review-swarm-diagnostics-0910
Closed

khaliqgant wants to merge 5 commits into
fix/review-swarm-wrapper-guard-0910from
fix/review-swarm-diagnostics-0910

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Summary

  • render an allowlisted structural Cloud failure identity when a swarm is terminally unsuccessful
  • keep raw error/provider/agent output out of privileged Actions logs
  • tolerate malformed or non-object status payloads

Dependency

This PR extends #265’s candidate-validation workflow and is intentionally based on its branch until #265 merges. The main-owned validator required by the immutable gate is introduced there; targeting main caused the gate to correctly fail closed before launch.

Validation

  • workflow YAML parse
  • jq fixtures for queue timeout and malformed status payloads
  • git diff --check
  • Veto diff review: PASS

Note

Medium Risk
Changes privileged CI behavior (failure reporting and a new pull_request_target gate) for the merge-blocking review swarm, though diagnostics are intentionally constrained and the guard only blocks edits to the wrapper workflow.

Overview
Replaces raw swarm failure logging in review-swarm.yml with a base-owned swarm-status-diagnostic.sh that prints only an allowlisted structural identity (phase / code tokens from .failure) to stderr and the job summary, avoiding untrusted agent/provider text in Actions logs.

Adds a pull_request_target wrapper guard (review-swarm-wrapper-guard.yml + swarm-wrapper-guard.sh) that checks out the PR base and fails closed if the candidate PR touches .github/workflows/review-swarm.yml, so the review wrapper cannot be relaxed from the PR under review.

Reviewed by Cursor Bugbot for commit f27529b. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-10T17:51:01.717988Z e837b32 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 12278374-29dd-4d8d-8a1a-440305714b2d

📥 Commits

Reviewing files that changed from the base of the PR and between 5f17b62 and ff5f5c5.

📒 Files selected for processing (3)
  • .github/workflows/review-swarm.yml
  • .github/workflows/scripts/swarm-definition.sh
  • .github/workflows/scripts/swarm-definition.test.sh

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The review workflow now uses the PR base SHA as its trusted gate, validates changed swarm definitions with new Bash and Ruby checks, tests validator behavior, and reports swarm failures through structured JSON fields.

Changes

Trusted workflow validation

Layer / File(s) Summary
Validator foundation
.github/workflows/review-swarm.yml, .github/workflows/scripts/swarm-definition.sh, .github/workflows/scripts/swarm-definition.test.sh
The workflow checks out validator scripts from the immutable base SHA. The validator enforces YAML, retry, delay, timeout, mapping, and symlink rules. Tests cover accepted and rejected definitions.
Workflow validation gates
.github/workflows/review-swarm.yml
The workflow verifies Ruby and Psych, runs the base validator self-test, and validates changed candidate definitions before running the review.
Structured failure reporting
.github/workflows/review-swarm.yml
Swarm failures now produce JSON diagnostics containing validated status fields and write them to stderr and the step summary.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PR as Pull request
  participant Workflow as review-swarm.yml
  participant Base as Immutable base checkout
  participant Validator as swarm-definition.sh
  participant Swarm as Review swarm
  PR->>Workflow: Modify review-swarm.yaml
  Workflow->>Base: Checkout base SHA
  Workflow->>Validator: Validate candidate against trusted definition
  Validator-->>Workflow: Return validation result
  Workflow->>Swarm: Run review with base definition
  Swarm-->>Workflow: Return status payload
  Workflow-->>PR: Write structured failure diagnostic
Loading

Merge Risk: 🟠 High · up to ff5f5

The new gate filters failure diagnostics and validates selected swarm policy fields, but a candidate can preserve those fields while changing other executable graph behavior. That could weaken later review gates, so the validation gap should be resolved before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: exposing safe failure diagnostics for the review swarm.
Description check ✅ Passed The description directly explains the safe diagnostics, log handling, malformed payload support, dependency context, and validation performed.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/review-swarm-diagnostics-0910

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

I’m a rabbit guarding the gate,
With YAML checks that validate state.
The base stays fixed, the rules stay clear,
Structured failures appear here.
Hop by hop, the swarm runs right,
Safe definitions guide the night.

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

@khaliqgant

Copy link
Copy Markdown
Member Author

@codex review
@cursor review

Please review exact head ff5f5c5. The terminal swarm status log now retains only allowlisted structural diagnostics and removes untrusted raw provider/agent output.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff5f5c5240

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# The introducing PR cannot run a helper that is not on its base
# yet. Once this PR lands, absence is a deletion or checkout bug
# and must fail closed like the verdict self-test above.
if [ "$REVIEW_PR_NUMBER" = 265 ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use the actual PR number for validator bootstrap

At this exact head the introducing change is PR #283, while base commit 4dd9277 does not contain swarm-definition.test.sh; consequently this condition is false and the self-test exits with “main-owned candidate validator is missing” before the swarm launches. The duplicate bootstrap check near line 126 has the same stale 265 value, so both checks must recognize the actual introducing PR.

Useful? React with 👍 / 👎.

Comment on lines +42 to +44
# The base SHA is the immutable definition this PR is judged by. A
# moving `main` ref could change the judge while this run is live.
ref: ${{ github.event.pull_request.base.sha }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the wrapper gate from judging its own changes

Checking out the inner relayflow definition from the base SHA does not make this GitHub Actions wrapper immutable: this PR also changes .github/workflows/review-swarm.yml, whose candidate version supplies the bootstrap checks, launch/wait logic, and final enforcement for its own pull_request run. A candidate can therefore weaken or bypass the wrapper that judges it; this wrapper change needs validation by a gate definition outside the candidate-controlled workflow.

AGENTS.md reference: AGENTS.md:L34-L34

Useful? React with 👍 / 👎.

@khaliqgant
khaliqgant changed the base branch from main to fix/review-swarm-candidate-validation September 10, 2026 17:47
@khaliqgant

Copy link
Copy Markdown
Member Author

@codex review
@cursor review

Please re-review exact head e837b32. The prior run correctly failed closed because this dependent PR was incorrectly targeted at main before #265; it is now based on #265 candidate-validation branch. Source diff is unchanged; Veto PASS.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: e837b32fb4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@khaliqgant
khaliqgant force-pushed the fix/review-swarm-diagnostics-0910 branch from e837b32 to f27529b Compare September 10, 2026 17:53
@khaliqgant
khaliqgant changed the base branch from fix/review-swarm-candidate-validation to fix/review-swarm-wrapper-guard-0910 September 10, 2026 17:55
@github-actions

Copy link
Copy Markdown

Review swarm: maintainability

Maintainability Review: PR #283

PR: fix(review-swarm): expose safe failure diagnostics
Lens: Maintainability
Question: Could a stranger read this in six months and change it safely?

Summary

This PR refactors .github/workflows/scripts/swarm-status-diagnostic.sh to extract structured failure diagnostics from cloud workflow responses. The change replaces free-form error string extraction with a structured object extraction pattern that includes token validation.

Findings

F1: Implicit token validation contract has no explanation

Location: .github/workflows/scripts/swarm-status-diagnostic.sh:6

The jq filter defines a token function that validates strings against the pattern ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$, but there is no comment explaining:

  • What security boundary this validates
  • Why this specific character set is safe
  • What attack or failure mode this prevents
  • Where this validation contract is documented

A maintainer reading this in six months will see sanitization but not understand what they must preserve when modifying the script. The prompt says "expose safe failure diagnostics" but the code does not document what "safe" means here or what would be unsafe.

Impact: A future change could weaken the validation (e.g., by removing it as "unnecessary") without understanding that it's a security boundary. The validation appears to prevent shell injection or GitHub Actions injection, but this is implicit, not stated.

F2: Unclear what input structure is expected

Location: .github/workflows/scripts/swarm-status-diagnostic.sh:5-12

The jq filter expects a JSON response with a specific structure:

{ "failure": { "phase": "...", "code": "..." } }

Or possibly:

{ "result": { "error": "..." } }
{ "error": "..." }

The old code extracted from .result.error // .error, but the new code expects .failure.phase and .failure.code. There is no documentation explaining:

  • What creates this response structure
  • Whether both old and new formats can appear
  • If this is a breaking change in what failure information is surfaced
  • What happens when the structure doesn't match

Looking at .github/workflows/review-swarm.yml:245, the response comes from agent-relay cloud status, but the script provides no guidance on what that API contract is.

Impact: A maintainer cannot safely modify this script without understanding the input contract. If the API response changes, they won't know if the script should adapt or if the API change is breaking.

F3: Silent failure handling obscures problems

Location: .github/workflows/scripts/swarm-status-diagnostic.sh:12,22

The jq command uses 2>/dev/null to suppress errors. If:

  • The response is malformed JSON
  • The jq filter has a syntax error
  • The extraction fails for any reason

The script silently outputs an empty string or {}, and the diagnostic is skipped. The caller at .github/workflows/review-swarm.yml:261 only invokes this when status != completed, so a failure to extract diagnostics is indistinguishable from "no diagnostics available."

Impact: When the diagnostic script breaks, it will fail silently. Debugging will require manually reproducing the failure case. A comment explaining that this is intentionally fail-open (for observability, not correctness) would clarify the design choice.

F4: The relationship between old error extraction and new failure extraction is unclear

Location: .github/workflows/scripts/swarm-status-diagnostic.sh (entire diff)

The old code extracted .result.error // .error // empty and output it as multi-line text. The new code extracts .failure.phase and .failure.code as a JSON object. This raises unanswered questions:

  • Are these two different formats returned by the same API at different times?
  • Did the API change, and this is the migration?
  • Will old-format errors now go unreported?
  • Is there overlap, and if so, which takes precedence?

The commit message says "expose safe failure diagnostics" but doesn't explain what changed in the data model or why.

Impact: A maintainer debugging a missing diagnostic won't know if they should expect the old format, the new format, or both. The migration path is invisible.

F5: Token validation logic is not reusable or testable

Location: .github/workflows/scripts/swarm-status-diagnostic.sh:6

The token validation regex is embedded in a jq filter. If this validation needs to be applied elsewhere (e.g., in other diagnostic scripts, in the swarm-post script, in the swarm-prepare script), it will be duplicated with potential drift.

Additionally, there is no test demonstrating that:

  • Valid tokens pass (e.g., phase_name, code:value, foo-bar.baz)
  • Invalid tokens are rejected (e.g., spaces, shell metacharacters, Unicode)
  • The empty object case is handled correctly
  • Malformed JSON is handled correctly

Impact: Correctness depends on a regex that has no test coverage and no usage examples. A future maintainer cannot validate changes without manually constructing test cases.

F6: Missing failure handling for GITHUB_STEP_SUMMARY

Location: .github/workflows/scripts/swarm-status-diagnostic.sh:15

The script writes to $GITHUB_STEP_SUMMARY without checking:

  • If the variable is set
  • If the file is writable
  • If the append succeeds

If GITHUB_STEP_SUMMARY is unset (e.g., running outside GitHub Actions), the script will fail with:

swarm-status-diagnostic.sh: line 15: $GITHUB_STEP_SUMMARY: ambiguous redirect

This would break set -e and cause the caller at .github/workflows/review-swarm.yml:261 to fail, even though the diagnostic is purely observability.

Impact: Running this script in a test environment or outside GitHub Actions will fail with an unclear error. The diagnostic is not essential to correctness (the caller uses exit 0 after calling it), but the script's set -euo pipefail makes the failure fatal if the redirect fails.

F7: The output format change may break downstream consumers

Location: .github/workflows/scripts/swarm-status-diagnostic.sh:14-15

The old output was:

swarm failure reason:
    <multi-line error text>

The new output is:

swarm failure diagnostic: {"phase":"...","code":"..."}

If any automation, log parser, or human process depends on the old format, this is a breaking change. The PR description and commit message do not indicate whether this was audited.

Impact: Unknown downstream breakage. A maintainer cannot assess the blast radius of this change without searching for all consumers of stderr or GITHUB_STEP_SUMMARY from this script.

Additional observations (not blocking)

  • The variable name change from reason to failure improves clarity, since the new value is a structured object, not a string.
  • The use of jq -c (compact output) is appropriate for a single-line log diagnostic.
  • The with_entries(select(.value != null)) idiom correctly omits null fields, producing a cleaner diagnostic.

Verdict

The change improves structure (moving from free-text to structured diagnostics) but introduces multiple maintainability hazards:

  • Implicit security contracts with no documentation (F1)
  • Unclear input/output contracts and migration story (F2, F4, F7)
  • Silent failures that will obscure future bugs (F3)
  • Untested validation logic with no reuse path (F5)
  • Missing environment variable guards (F6)

A stranger reading this in six months would not be able to:

  • Understand what the token validation protects against
  • Know what response structures are valid
  • Debug why diagnostics are missing in a failure case
  • Safely modify the extraction logic without tests

Recommendations to pass

  1. Add a comment block at the top of the script explaining:

    • What the response structure is (with an example)
    • What the token validation prevents (injection attacks, malformed identifiers)
    • Why failures are silently ignored (observability, not correctness)
  2. Add an explicit check for GITHUB_STEP_SUMMARY or document that this script requires a GitHub Actions environment.

  3. Either remove the old error extraction paths entirely (if the API no longer returns them) or document the migration/fallback behavior.

  4. Add a test (even a comment with example inputs/outputs) demonstrating the token validation cases.

Overall assessment

The code will work as written, but it is not safely maintainable. The implicit contracts, silent failures, and undocumented validation make it fragile for future changes.

REVIEW_FAILED

@github-actions

Copy link
Copy Markdown

Review swarm: history

PR #283 — history review

Reviewed head: f27529b2f26d1ffd5c5b4f7dcf3e7aa3a371fe38.
Scope: whether the supplied PR diff fits repository history and RFC-0001. No blocking finding through this lens.

Input provenance and environment

The requested git log --oneline -40 initially failed with:

fatal: not a git repository: /home/daytona/.project-git

/tmp/pr-283.diff was absent. I used .review-target/pr.diff and .review-target/pr.json, then recovered Git metadata with git -C /tmp clone --bare https://github.com/AgentWorkforce/flows.git /home/daytona/.project-git. I configured that metadata for this worktree, created local review branch review/pr283-history at the supplied head, and initialized its index with git read-tree HEAD; no checkout of tracked files, commit, or push was performed. The existing workspace has executable-mode differences from HEAD; those are not in the supplied PR diff and are not findings against this PR. Only this review is staged.

The supplied diff matches HEAD's parent-to-head patch byte for byte (capture below). The empty preceding commit, 794501ea, merely requests a CI rerun; the code change is in f27529b2. The wrapper guard and helper extraction are ancestor changes, not additions in this supplied diff. This avoids the misleading base-versus-branch comparison documented in DRIVE-LOG's 09:07Z audit.

Historical assessment

  1. The diagnostics change has a real predecessor and an intentional tradeoff. 6077688e (fix(review-swarm): print why the swarm failed, not just that it did #235) introduced failure reasons because an opaque failed status hid a CPU quota failure for days. DRIVE-LOG's 2026-09-10 ~05:1xZ entry subsequently used that output to distinguish seven infrastructure failure modes from code-review objections. This PR removes the free-form .result.error // .error path and emits only .failure.phase and .failure.code, restricted to bounded token-shaped strings. A legacy error-only payload consequently becomes silent. That is a reduction in detail, not evidence that the original quota-diagnosis problem is solved for every response. The purpose is nevertheless consistent with the 2026-09-08 cloud#3459 review: raw recovery errors and response bodies were deliberately excluded because they may carry credentials. The patch does not restore raw-error logging or the untrusted multiline output that fix(review-swarm): print why the swarm failed, not just that it did #235's sanitization addressed. Its historical direction is defensible; compatibility with the deployed Cloud producer was not established by this static review.

  2. Settled decisions remain intact. RFC §6 decision 6 makes the judge immutable to its candidate. Ancestor 8a8ece9b moved diagnostics to a helper invoked from ../gate-files; this patch retains that boundary and changes neither the wrapper nor the verdict parser. Decision 11 separates completion from quality judgment: diagnostics here do not become a verdict. The DRIVE-LOG ~04:5xZ correction explicitly says the wait step's exit 0 is deliberate, with enforcement elsewhere; this patch does not reinterpret it as success. Nothing changes the kernel, journal protocol, tenancy, or replay model. Under decision 16, changes to the Lead's own gates still require human merging; this review is not merge authorization.

  3. The earlier guard/extraction story is preserved. 3563cfc0 guards the wrapper from candidates; 8a8ece9b externalizes reporting; 794501ea is an empty rerun commit; f27529b2 modifies that reporting helper. There is no reversal of the immutable-wrapper policy, retry-delay fix, or fail-closed verdict enforcement in the supplied patch. The stale NEXT brief explicitly says its old validation/documentation work is already done; it supplies no reason to demand unrelated edits. DIRECTIVES contains only its standing-directives introduction and no outstanding specific instruction.

  4. Commit-message truthfulness. “fix(review-swarm): emit safe terminal diagnostics” accurately describes emitting structural diagnostic tokens instead of arbitrary error strings. “Safe” here means constrained fields and characters; this is not a semantic allowlist of known codes or a general secret detector. The rerun commit has no file changes, consistent with its message. One nonblocking wording correction is warranted in the PR body: “tolerate malformed or non-object status payloads” is too broad. Non-object JSON is tolerated, but syntactically invalid JSON exits 5 with no diagnostic. The prior helper also used a failing jq assignment under set -e, so this is an overstatement of validation, not a newly introduced historical regression. Narrow the wording to valid JSON with missing or ill-shaped fields unless invalid-JSON tolerance is separately implemented and demonstrated.

Captured evidence

Commands below were executed for this review. These are static history/source checks and a small diagnostic fixture probe, not a live swarm, full test suite, or mutation verification. The fixture's queue_timeout is illustrative input, not evidence of a deployed Cloud enum.

Command:

git log --oneline -40

Captured stdout/stderr:

f27529b2 fix(review-swarm): emit safe terminal diagnostics
794501ea ci(review-swarm): rerun against candidate base
8a8ece9b refactor(review-swarm): externalize terminal diagnostics
3563cfc0 ci(review-swarm): guard wrapper from candidates
50c5ccdb fix(review-swarm): validate candidate without self-judging
4dd9277e fix(review-swarm): give the lens retry budget a delay that can span a 60s backoff (#259)
d9377d17 ops(drive-log): -0910 online; closed relayfile#492, re-ran flows#258
8790e002 ops(drive-log): corrected flows#260 -- I truncated the quote that disproved it
ecaf6b86 ops(drive-log): lenses never received the diff; filed flows#260
3cfbd061 ops(drive-log): recovered lens transcripts; two lenses passed #259
5fd56fbe ops(drive-log): opened cloud#3527 -- run export 400s for every caller
ec014740 ops(drive-log): gate failure moved off infrastructure onto the agent step
f5f97e53 ops(drive-log): quiet tick, nothing moved
17c413ec ops(drive-log): #259 cannot be validated by its own gate; audit complete
069789bd ops(drive-log): audited remaining PRs -- all three still valid
4c2b0ab1 ops(drive-log): closed cloud#3517 as obsolete -- main deleted what it extended
fb73faf3 ops(drive-log): verified the #3516 classifier claim against three literal inputs
a32dc6d3 ops(drive-log): mount fault CONFIRMED FIXED; two corrections
8ab1ab2b ops(drive-log): the in-flight run shows the wedge signature, not progress
7999b28e ops(drive-log): re-ran the gate to test v0.10.56; in flight past 16 minutes
3bb84add ops(drive-log): v0.10.56 promoted; Khaliq had fixed the transport 3h before I filed
7cecffd8 ops(drive-log): opened cloud#3525 -- guard against an empty snapshot name
4bb9f865 ops(drive-log): named the masking secret -- RELAYFILE_SMOKE_BASE_URL
9b26383d ops(drive-log): root cause -- a secret valued "-" masks every hyphen (cloud#3524)
bdcaf415 ops(drive-log): retracted most of relayfile#492 -- read a 95-commit-stale checkout
c3dfe269 ops(drive-log): relayfile#492 -- the full-reconcile remedy exists, nothing triggers it
b58ce471 ops(drive-log): failures converged on one mode; retracting the rotation claim
4519a701 ops(drive-log): broke #3510's build with backticks in a template literal
7124cade ops(drive-log): caught myself reporting an unpushed fix as pushed
e717971b ops(drive-log): Bugbot findings on #3510 -- fixed the race, contested the heartbeat
74b7eac2 ops(drive-log): opened flows#259 -- lens retries had a 1s delay vs a 60s backoff
9f676c26 ops(drive-log): filed relayfile#492 for the recurring cursor_expired mount failure
b00e77ec ops(drive-log): seven failure modes, none consecutive -- no single fix exists
15c4de59 ops(drive-log): all 9 gate failures are infrastructure, none are code verdicts
576e5ee8 ops(drive-log): opened flows#258; corrected two over-readings of the gate
c6a44ce6 ops(drive-log): review gate fails on non-terminal 'running'; 103-day stranded cohort
eaa171d8 ops(drive-log): brief is stale in all four items; review gate blocks 9/9 flows PRs
01626214 ops(drive-log): queue recovered; filed cloud#3519 dead DISABLE_RELAY env var
0a90b4bb ops(drive-log): quiet tick -- all four PRs green and awaiting human review
ea941a7e ops(drive-log): #3516 green; determined #244's fix shape from the contract

Exit status: 0

Command:

git show -s --format=full 6077688e

Captured stdout/stderr:

commit 6077688e9ad32819933d14727f6083332faf7104
Author: KJGBot <khaliqgant+kjgbot@gmail.com>
Commit: GitHub <noreply@github.com>

    fix(review-swarm): print why the swarm failed, not just that it did (#235)
    
    * fix(review-swarm): print why the swarm failed, not just that it did
    
    The gate polls `agent-relay cloud status --json`, reads `.status` off the
    response and throws the rest away. When a swarm fails, the only thing that
    reaches the GitHub log is the word:
    
        Review swarm did not complete successfully: failed
    
    The actual reason is already in the payload the poll just fetched. For the
    five failing runs on 2026-09-07 it was:
    
        Step "lens-maintainability" failed after 2 retries:
        Total CPU limit exceeded. Maximum allowed: 250.
    
    Nothing in the log said "quota". Diagnosing it meant knowing to pull the run
    id out of the log and query the run by hand, so the cause went uninvestigated
    for days while the failures were attributed to a guess.
    
    Surface `.result.error` in the wait step, to stderr and to the step summary.
    Verified against the real 04da7e48 payload: the expression yields the quota
    text above; a payload without an error yields an empty string rather than the
    literal "null", and an empty response (the first-call-failed path) is safe.
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
    
    * fix(review-swarm): sanitize the failure reason before logging it
    
    cubic P3 on #235, and it is right. The reason string comes from the swarm's
    status response, which can carry agent output, which can carry content from
    the PR under review. Two vectors:
    
      - a line starting with `::` is parsed by Actions as a workflow command
        (`::error::`, `::add-mask::`) — log injection;
      - a line of three backticks closes the fenced block early in the step
        summary and the remainder renders as markup.
    
    Indent every line by four spaces instead of fencing. That defeats both at
    once: Actions only parses a command at the start of a line, and an indented
    block is a Markdown code block with no fence to break.
    
    Verified against a payload carrying both vectors: no output line starts with
    `::` or with a fence, and the real quota text still renders intact.
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
    
    ---------
    
    Co-authored-by: kjgbot <kjgbot@agentrelay.dev>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

Exit status: 0

Command:

git show --format=short 8a8ece9b -- .github/workflows/review-swarm.yml

Captured stdout/stderr:

commit 8a8ece9b299860815dfbbd1e36cac7e84dd62f67
Author: Relayflow Lead <lead@relayflows.local>

    refactor(review-swarm): externalize terminal diagnostics

diff --git a/.github/workflows/review-swarm.yml b/.github/workflows/review-swarm.yml
index e77d5bc7..5e43eadd 100644
--- a/.github/workflows/review-swarm.yml
+++ b/.github/workflows/review-swarm.yml
@@ -258,30 +258,7 @@ jobs:
           done
           echo "swarm_status=$status" >> "$GITHUB_OUTPUT"
           if [ "$status" != completed ]; then
-            # The status word alone does not say why the swarm failed, and the
-            # reason never reaches this log: it sits in the run payload we just
-            # fetched. A quota rejection reads here as a bare "failed", which
-            # sent one reader inferring for days before querying the run by
-            # hand. Print what we already have.
-            reason=$(jq -r '.result.error // .error // empty' <<<"${response:-}" 2>/dev/null)
-            if [ -n "$reason" ]; then
-              # The reason is not fully trusted. It can carry agent output,
-              # which can carry content from the PR under review. Two ways that
-              # bites: a line starting with `::` is parsed by Actions as a
-              # workflow command, and a line of three backticks would close a
-              # fenced block early and render the rest as markup.
-              # Indenting every line defeats both at once — Actions only parses
-              # a command at the start of a line, and an indented block is a
-              # Markdown code block with no fence to break.
-              safe_reason=$(printf '%s\n' "$reason" | sed 's/^/    /')
-              echo "swarm failure reason:" >&2
-              printf '%s\n' "$safe_reason" >&2
-              {
-                echo "### Swarm failure reason"
-                echo
-                printf '%s\n' "$safe_reason"
-              } >> "$GITHUB_STEP_SUMMARY"
-            fi
+            ../gate-files/.github/workflows/scripts/swarm-status-diagnostic.sh "${response:-}"
           fi
           exit 0
 

Exit status: 0

Command:

git show --stat --oneline 794501ea

Captured stdout/stderr:

794501ea ci(review-swarm): rerun against candidate base

Exit status: 0

Command:

git diff HEAD^ HEAD -- .github/workflows/scripts/swarm-status-diagnostic.sh | cmp - .review-target/pr.diff

Captured stdout/stderr:

Exit status: 0

Command:

sed -n '4544,4556p;9266,9283p;9303,9324p;10053,10080p' ops/DRIVE-LOG.md

Captured stdout/stderr:

### 2026-09-08 — reviewed cloud#3459 (now CLEAN); explicitly NOT signing it off

Read the source diff rather than trusting the lane's summary. The change is
sound:

- `RelayAuthIdentityRecoveryError` with `retryable = false` — a 500 from a schema
  mismatch cannot be repaired by redelivering the job, which is precisely the
  loop the worker logs showed.
- It deliberately does not retain raw recovery errors or response bodies because
  they may carry credentials.
- A `v2LaunchClaimed` flag releases the run only when *this attempt* claimed it,
  which is the narrow correct fix rather than releasing unconditionally.
- The ordering (release the run before making the job claimable) names the real
### 2026-09-10 ~04:5xZ — gate diagnosed properly; opened flows#258

Queue: pending=1 (young), 12 running.

**Correcting myself twice from last tick.**

1. I called the wait step "exits 0 without terminal state" as though that were
   the bug. It is not — the `exit 0` is deliberate; the step reports through
   `swarm_status` and `Enforce swarm result` does the failing. That design is
   fine.

2. I implied on #255 that the gate is a defect blocking everything. Checked a
   second PR and the causes DIFFER:
   - **#257**: wait ran **65.0 min exactly** (00:02:24Z -> 01:08:39Z vs a 3900s
     budget) then reported `running`. A real timeout, mislabeled.
   - **#238**: wait ran 5.3 min, `swarm_status=failed`. The swarm genuinely
     failed and the gate reported it **correctly**. Not a gate bug at all.

### 2026-09-10 ~05:1xZ — all nine gate failures are infrastructure; zero are code

Queue: pending=1, running=15. #258 open, CI running.

Pulled the literal `swarm failure reason:` block from every open flows PR's
review run:

    #238 failed  Workflow launch exceeded its queue deadline before bootstrap
    #240 failed  notify-flush ... http 410 cursor_expired (Relayfile mount)
    #242 failed  Relaycast workspace key repair failed: 503
    #244 failed  Workflow sandbox ... is still provisioning (creating)
    #245 failed  mcp-args --register (503) database_overloaded
    #251 failed  Relaycast workspace key repair failed: 503
    #253 failed  Step sandbox dependency install failed
    #256 failed  mcp-args --register rate-limited, retry after 60s, workspace_busy
    #257 running (none) -- the 65-min timeout, see #258

**Nine of nine infrastructure. Zero are a lens objecting to the code.** Seven
distinct failure modes. Eight of nine die in `lens-maintainability`, the first
lens to run -- whatever runs first absorbs whatever is broken that hour.

That corrects my #255 framing again. "Single point of failure for merging
### 2026-09-10 09:07Z — audited my remaining PRs; all three survive

Queue drained. Disk 5.5Gi.

After closing two PRs as superseded in a few hours, audited the rest against
current main rather than waiting to be surprised a third time.

    #3497  target present, change NOT on main   -> still valid
    #3510  reapStuckRuns present, runningStaleMinutes absent -> still valid
    #3516  classifier present, my rule absent   -> still valid

**A misread I caught mid-audit.** `git diff origin/main..my-branch` for #3497
reported *59 files changed, 318 insertions, 3533 deletions* and looked like the
branch would revert half the repo including #3522. It would not -- that diff is
just "main has 3533 lines my branch lacks", not what a merge applies. The
correct question is `git diff $(git merge-base main BRANCH) BRANCH`, which is
1 file and +6/-2. I nearly wrote up a fake catastrophe.

**And a real finding for #3497.** Main already has `--require-mount-probe` twice
-- at lines 789-790, in the **candidate** job. The equivalent calls in the
**promotion** job still omit it. So the lane that CANNOT promote enforces mount
convergence, and the lane that DOES promote treats it as optional. That is
backwards, and it is why the 07:16Z rebuild promoted v0.10.56 after logging
"Skipping relayfile mount convergence probe" twice and going green.

Posted that on #3497, with the caveat unchanged: still only 1 of 5 secrets, so
merging today reds the rebuild lane every run -- and rebuilds are currently how
relayfile fixes reach sandboxes. Land it WITH provisioning, not ahead of it.

Exit status: 0

Command:

sed -n '255,290p' .github/workflows/review-swarm.yml

Captured stdout/stderr:

              completed|failed|cancelled) break ;;
            esac
            sleep 15
          done
          echo "swarm_status=$status" >> "$GITHUB_OUTPUT"
          if [ "$status" != completed ]; then
            ../gate-files/.github/workflows/scripts/swarm-status-diagnostic.sh "${response:-}"
          fi
          exit 0

      - name: Post verdict and transcripts
        if: always() && steps.launch.outputs.run_id != ''
        env:
          GH_TOKEN: ${{ github.token }}
        working-directory: pr-head
        run: ../gate-files/.github/workflows/scripts/swarm-post.sh "${{ steps.launch.outputs.run_id }}" "${{ github.event.pull_request.number }}"

      - name: Enforce swarm result
        if: always() && steps.wait.outputs.swarm_status != 'completed'
        run: |
          echo "Review swarm did not complete successfully: ${{ steps.wait.outputs.swarm_status }}" >&2
          exit 1

Exit status: 0

Command:

python3 - <<'PROBE'
import subprocess, tempfile, os
p='.github/workflows/scripts/swarm-status-diagnostic.sh'
for label,payload in [('structural','{"failure":{"phase":"queue","code":"queue_timeout"}}'),('legacy','{"result":{"error":"Total CPU limit exceeded. Maximum allowed: 250."}}'),('non-object','[]'),('invalid-token','{"failure":{"phase":"queue","code":"::error:: injected"}}'),('malformed-json','{')]:
    with tempfile.NamedTemporaryFile() as summary:
        r=subprocess.run(['bash',p,payload],text=True,capture_output=True,env={**os.environ,'GITHUB_STEP_SUMMARY':summary.name})
        print(label, 'exit='+str(r.returncode), 'stdout='+repr(r.stdout), 'stderr='+repr(r.stderr), 'summary='+repr(open(summary.name).read()))
PROBE

Captured stdout/stderr:

structural exit=0 stdout='' stderr='swarm failure diagnostic: {"phase":"queue","code":"queue_timeout"}\n' summary='- Swarm failure diagnostic: `{"phase":"queue","code":"queue_timeout"}`\n'
legacy exit=0 stdout='' stderr='' summary=''
non-object exit=0 stdout='' stderr='' summary=''
invalid-token exit=0 stdout='' stderr='swarm failure diagnostic: {"phase":"queue"}\n' summary='- Swarm failure diagnostic: `{"phase":"queue"}`\n'
malformed-json exit=5 stdout='' stderr='' summary=''

Exit status: 0

No blocking history finding. The loss of legacy free-form diagnostics and the unverified producer contract are disclosed above; the PR-body wording should be narrowed.

REVIEW_PASSED

@github-actions

Copy link
Copy Markdown

Review swarm: structure

No fresh transcript was produced for run 8bdf80d4-270e-4ee5-808d-d2148f271509 (MISSING).

@github-actions

Copy link
Copy Markdown

Review swarm: FAILED

  • maintainability: FAILED
  • history: PASSED
  • structure: MISSING

Cloud run: 8bdf80d4-270e-4ee5-808d-d2148f271509

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.

2 participants