Skip to content

feat(workflows): extract harness-run into reusable workflow - #6356

Draft
ralphbean wants to merge 10 commits into
mainfrom
extract-harness-run
Draft

feat(workflows): extract harness-run into reusable workflow#6356
ralphbean wants to merge 10 commits into
mainfrom
extract-harness-run

Conversation

@ralphbean

@ralphbean ralphbean commented Aug 19, 2026

Copy link
Copy Markdown
Member

Summary

  • Extracts the harness-run job from reusable-dispatch.yml into a new reusable-harness-run.yml workflow
  • Enables custom pollers in user repos to directly invoke harness agents without going through the full dispatch flow
  • The existing flow through reusable-dispatch.yml remains unchanged

Changes

  1. New file: .github/workflows/reusable-harness-run.yml

    • Accepts a matrix input (JSON string from fullsend dispatch)
    • Takes all necessary configuration inputs (install_mode, mint_url, gcp_region, fullsend_version, runner_image)
    • Accepts required secrets (GCP WIF provider, project ID, OTEL headers)
    • Contains the complete harness-run job with all steps
  2. Modified: .github/workflows/reusable-dispatch.yml

    • Simplified harness-run job to call the new reusable workflow with uses:
    • Passes matrix output from harness-dispatch
    • Forwards all inputs and secrets

Test plan

Fixes #6347

🤖 Generated with Claude Code

Extracts the harness-run job from reusable-dispatch.yml into a new
reusable-harness-run.yml workflow that can be invoked directly from
user repos. This enables custom pollers to invoke harness agents
without going through the full dispatch flow.

Fixes #6347

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean ralphbean added the fullsend-fix Enables automatic bot-triggered fix runs on human-authored PRs label Aug 19, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 12:50 AM UTC · Ended 1:01 AM UTC

Commit: e40ec82 · View workflow run →

@ralphbean
ralphbean marked this pull request as ready for review August 19, 2026 01:00
@ralphbean
ralphbean requested a review from a team as a code owner August 19, 2026 01:00
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Extract harness execution into a reusable workflow

✨ Enhancement ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Extracts harness execution into a reusable, matrix-driven GitHub Actions workflow.
• Allows custom repository pollers to invoke harness agents directly.
• Preserves existing dispatch behavior by forwarding configuration and secrets.
Diagram

sequenceDiagram
  actor Poller as Custom Poller
  participant Dispatch as Dispatch Workflow
  participant Harness as Harness Workflow
  participant Agent as Matrix Agent
  participant Repo as Target Repo
  participant GCP as GCP Services
  alt Existing dispatch flow
    Dispatch->>Harness: Forward matrix
  else Direct poller flow
    Poller->>Harness: Supply matrix
  end
  Harness->>Agent: Start each entry
  Agent->>GCP: Authenticate and execute
  Agent->>Repo: Checkout and report
Loading
High-Level Assessment

Extracting the complete job into a reusable workflow is the appropriate approach because callers need job-level matrix expansion, concurrency, permissions, runner selection, and secrets. A composite action cannot encapsulate those job-level controls, while duplicating the job or adding a separate dispatch API would increase maintenance and alter the existing flow.

Files changed (2) +239 / -161

Enhancement (1) +225 / -0
reusable-harness-run.ymlAdd reusable matrix-driven harness workflow +225/-0

Add reusable matrix-driven harness workflow

• Introduces a callable workflow containing the extracted harness execution job and its input and secret contract. It prepares configuration, mints repository credentials, configures GCP, checks out the target repository, and runs each matrix-selected agent.

.github/workflows/reusable-harness-run.yml

Refactor (1) +14 / -161
reusable-dispatch.ymlDelegate harness execution to the reusable workflow +14/-161

Delegate harness execution to the reusable workflow

• Replaces the inline harness matrix job with a call to 'reusable-harness-run.yml'. It forwards the dispatch-generated matrix plus existing configuration inputs and required secrets, preserving the current dispatch path.

.github/workflows/reusable-dispatch.yml

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:02 AM UTC · Completed 1:21 AM UTC

Commit: e40ec82 · View workflow run →

@qodo-code-review

qodo-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Required WIF secret optional ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new workflow declares FULLSEND_GCP_WIF_PROVIDER optional even though every run passes it to
the GCP authentication action, whose corresponding input is required. Direct callers can therefore
pass workflow validation without the secret and then fail during GCP authentication.
Code

.github/workflows/reusable-harness-run.yml[R54-55]

+      FULLSEND_GCP_WIF_PROVIDER:
+        required: false
Relevance

●●● Strong

Reusable-workflow secret contract mismatches are accepted when downstream actions require explicitly
forwarded secrets.

PR-#3903

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new workflow declares the provider secret optional but unconditionally forwards it to
setup-gcp; that composite action declares gcp_wif_provider required and supplies it to the
authentication step. Existing standalone reusable-code uses the consistent required-secret contract.

.github/workflows/reusable-harness-run.yml[53-57]
.github/workflows/reusable-harness-run.yml[163-168]
.github/actions/setup-gcp/action.yml[4-10]
.github/workflows/reusable-code.yml[44-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The directly callable harness workflow marks its GCP WIF provider secret optional, but the setup action requires it and cannot authenticate without it.

## Issue Context
Other standalone agent workflows declare this secret required. The existing reusable-dispatch declaration may remain separately constrained for compatibility, but the new direct workflow contract should reject incomplete calls before execution.

## Fix Focus Areas
- .github/workflows/reusable-harness-run.yml[53-57]
- .github/actions/setup-gcp/action.yml[4-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Extraction breaks contract test ✓ Resolved 🐞 Bug ☼ Reliability
Description
Moving Run harness agent out of reusable-dispatch.yml leaves
TestReusableDispatchPRHeadSHAPassthrough searching that file and unconditionally failing when the
marker is absent. The repository's Go test suite will therefore fail after this extraction.
Code

.github/workflows/reusable-dispatch.yml[1524]

+    uses: ./.github/workflows/reusable-harness-run.yml
Relevance

●●● Strong

Accepted precedents update scaffold tests when workflow extraction changes markers or contracts.

PR-#5555
PR-#1039

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test reads only reusable-dispatch.yml, then requires the Run harness agent marker to be
present. That marker now exists exclusively in reusable-harness-run.yml, while the standard Go test
target executes all package tests.

internal/scaffold/workflow_call_alignment_test.go[747-782]
.github/workflows/reusable-harness-run.yml[205-225]
Makefile[108-111]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The extraction removes the harness step from reusable-dispatch.yml, but its contract test still requires that step to exist in the old file.

## Issue Context
Load reusable-harness-run.yml for the harness subtest and retain the assertions covering `pr-head-sha` and `matrix.event_payload`.

## Fix Focus Areas
- internal/scaffold/workflow_call_alignment_test.go[747-782]
- .github/workflows/reusable-harness-run.yml[205-225]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Status runs cancel incorrectly ✓ Resolved 🐞 Bug ≡ Correctness
Description
The concurrency key uses the custom poller's github.repository rather than matrix.status_repo.
Direct calls for the same agent and status number in different status repositories can consequently
share a group and cancel each other even though they update different targets.
Code

.github/workflows/reusable-harness-run.yml[R70-72]

+    concurrency:
+      group: fullsend-harness-${{ matrix.agent }}-${{ github.repository }}-${{ matrix.status_number }}
+      cancel-in-progress: true
Relevance

●●● Strong

Recent precedent accepts concurrency keys including the target repository to prevent
cross-repository cancellation collisions.

PR-#603

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ExecutionRef defines StatusRepo independently from SourceRepo, and the action receives
matrix.status_repo as its status target. The new direct-call path instead partitions concurrency
by github.repository, which identifies the poller/caller repository.

.github/workflows/reusable-harness-run.yml[70-72]
.github/workflows/reusable-harness-run.yml[217-225]
internal/harnessdispatch/ref.go[3-12]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Direct harness calls can cancel unrelated executions because the concurrency group identifies the caller repository instead of the repository whose status is being processed.

## Issue Context
The matrix schema explicitly carries `status_repo` and `status_number`, and the harness action reports status using those fields. Include `matrix.status_repo` in the concurrency identity.

## Fix Focus Areas
- .github/workflows/reusable-harness-run.yml[70-72]
- internal/harnessdispatch/ref.go[3-12]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. New workflow bypasses E2E 🐞 Bug ☼ Reliability
Description
The E2E workflow's path and runtime relevance filters enumerate reusable-dispatch.yml but not the
newly extracted reusable-harness-run.yml. Future PRs changing only harness execution can therefore
skip the behavior tests intended to protect this flow.
Code

.github/workflows/reusable-harness-run.yml[R21-24]

+name: Harness run
+
+on:
+  workflow_call:
Relevance

●●● Strong

Recent E2E precedents accept broadening relevance filters to cover changed behavior paths and
workflow files.

PR-#2793
PR-#2398

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both E2E relevance enumerations explicitly include reusable-dispatch.yml but omit the new workflow
that now contains the complete harness execution path. The filters skip behavior tests when no
listed path matches.

.github/workflows/e2e.yml[20-44]
.github/workflows/e2e.yml[225-231]
.github/workflows/reusable-harness-run.yml[63-80]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The newly extracted harness workflow is not recognized as behavior-test-relevant, so isolated changes to it can bypass E2E coverage.

## Issue Context
Update both the workflow trigger path list and the pull-request changed-files expression alongside reusable-dispatch.yml.

## Fix Focus Areas
- .github/workflows/e2e.yml[20-44]
- .github/workflows/e2e.yml[225-231]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 56 rules

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/reusable-harness-run.yml Outdated
Comment thread .github/workflows/reusable-harness-run.yml
Comment thread .github/workflows/reusable-dispatch.yml
Comment thread .github/workflows/reusable-harness-run.yml
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — Both files are under the .github/ protected path. The PR links to issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 and explains the rationale for the change. Human approval is always required for protected-path changes regardless of context.

  • [PR title prefix] The PR title uses feat(workflows). Per COMMITS.md, feat is reserved for user-facing features. COMMITS.md explicitly states "Restructuring internals (extracting a sub-agent, splitting a package) → refactor" and lists feat(ci) as a forbidden combination — workflows serves the same CI domain. Consider refactor(workflows): extract harness-run into reusable workflow or ci(workflows): extract harness-run into reusable workflow.

  • [stale-architecture-description] docs/contributing/workflow-contracts.md:8 — The per-repo chain description states "there is no separate reusable-<agent>.yml hop for per-repo mode; thread new secrets/inputs into the relevant inline job." This PR creates exactly such a hop (reusable-dispatch.ymlreusable-harness-run.yml). The secret-threading guidance needs updating to reflect this new forwarding relationship.

  • [concurrency group behavioral change] .github/workflows/reusable-harness-run.yml:119 — The concurrency group key changed from github.repository to matrix.status_repo. For per-repo installs these are typically identical, but this is a behavioral change for the new direct-call path. If matrix.status_repo is unset for some callers, the concurrency key will differ. Consider a fallback like ${{ matrix.status_repo || github.repository }}.

  • [test coverage gap] internal/scaffold/workflow_call_alignment_test.goTestWorkflowCallInputAlignment validates scaffold thin-callers thread correct inputs/secrets to their reusable workflows, but the new reusable-dispatch.ymlreusable-harness-run.yml caller relationship has no alignment test. If a required input is added to one but not threaded from the other, no test catches the drift.

Low

  • [fail-open] .github/workflows/reusable-harness-run.yml:7 — The workflow documents that agent-enablement checks (kill-switch, role-check, agent-check) from reusable-dispatch.yml are bypassed on the direct-call path. This is an intentional design choice with the mint service as the authorization boundary. Consider documenting this trade-off in an ADR or workflow-contracts.md.

  • [scope creep] .github/workflows/reusable-dispatch.yml:78 — The PR adds JIRA_TOKEN and JIRA_USER_EMAIL secret threading which is not strictly authorized by issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347. The additions are optional (required: false) and harmless when unset, but represent a separate concern from the extraction.

  • [scaffold shim not updated] internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml — The shim template does not thread the new JIRA_TOKEN and JIRA_USER_EMAIL secrets. Functionally correct since both are optional, but per workflow-contracts.md, new secrets should be forwarded through every hop.

  • [secret required-flag inconsistency] .github/workflows/reusable-harness-run.yml:96FULLSEND_GCP_WIF_PROVIDER is required: true here but required: false in reusable-dispatch.yml. This is a pre-existing pattern already documented in workflow-contracts.md and not a regression.

  • [naming-convention] .github/workflows/reusable-harness-run.yml:57 — The workflow name: Harness run doesn't follow the <Stage> Agent naming pattern used by other reusable workflows. Reasonable since this workflow isn't tied to a single stage.

Previous run

Review

Findings

High

  • [test-broken] internal/scaffold/workflow_call_alignment_test.go:807TestReusableDispatchStatusCommentPassthrough has a harness-run subtest that calls extractStepSection(t, s, "Run harness agent") on reusable-dispatch.yml. The PR removes the inline "Run harness agent" step (replacing it with a uses: call), so this test will fail with "expected exactly one step named "Run harness agent", found 0". Other analogous tests (TestOTELHeadersSecretThreading, TestOTELVariableForwarding, TestReusableDispatchPRHeadSHAPassthrough) were correctly updated, but this one was missed.
    Remediation: Rebase onto main and update the harness-run subtest to load reusable-harness-run.yml and extract the step from there, matching the pattern used in the other updated tests.

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — Protected files under .github/ are modified/added. PR links to issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 and explains the rationale. Human approval is always required for protected-path changes, regardless of context.

  • [scope-creep] .github/workflows/reusable-dispatch.yml:76 — JIRA_TOKEN and JIRA_USER_EMAIL secret declarations and threading are added alongside the harness-run extraction. Issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 mentions Jira polling as motivating context but its authorized scope is the extraction, not new secret infrastructure. Consider splitting Jira secret threading into a follow-up PR.

  • [breaking-api] .github/workflows/reusable-harness-run.yml:98FULLSEND_GCP_WIF_PROVIDER is required: true in the new workflow but required: false in reusable-dispatch.yml. Per workflow-contracts.md, required-flag consistency across the chain matters. This is a pre-existing inconsistency (same mismatch exists for reusable-<stage>.yml workflows), but the PR cements it into a new public contract surface.

  • [stale-doc] docs/contributing/workflow-contracts.md:8 — Per-repo chain description states "there is no separate reusable-.yml hop for per-repo mode". This PR adds reusable-harness-run.yml as a workflow_call hop from reusable-dispatch.yml, making this claim inaccurate.

Low

  • [pattern-violation] internal/scaffold/workflow_call_alignment_test.go:177reusableWorkflowRef regex reusable-[a-z]+\.yml cannot match reusable-harness-run.yml (hyphens not in [a-z]). Latent issue — no current test pair exercises it.
  • [missing-secret-threading] .github/workflows/reusable-harness-run.yml — JIRA secrets not forwarded by per-repo shim template (shim-per-repo.yaml). Direct-call path (primary use case per Extract harness-run from reusable-dispatch.yml into a reusable action #6347) bypasses the shim, so impact is limited to per-repo-mode Jira agents.
  • [test-helper-consistency] internal/scaffold/workflow_call_alignment_test.go:789TestReusableDispatchPRHeadSHAPassthrough harness-run subtest uses raw os.ReadFile while other updated tests in the same PR use loadRepoFile() helper.
  • [expression-style] .github/workflows/reusable-harness-run.yml:104 — Job-level if: uses ${{ }} wrapper in the new file but the same PR removes ${{ }} from the equivalent expression in reusable-dispatch.yml.
  • [naming-convention] .github/workflows/reusable-harness-run.yml:57 — Workflow name: is "Harness run" vs the " Agent" convention in other reusable workflows. Harness-run is architecturally distinct, so the divergence may be intentional.
  • [stale-doc] docs/ADRs/0062-dispatch-version-skew.md:90 — ADR 62 consequences state all stage logic is inlined. Harness-run is now extracted, though it is not a "stage" in the ADR's sense.
  • [stale-doc] docs/architecture.md:54 — "inlines stage workflow jobs directly" statement no longer fully accurate for the harness path.
  • [undocumented-contract] .github/workflows/reusable-harness-run.yml:7 — Matrix JSON schema documented in header comments but has no runtime validation step for direct callers.
  • [concurrency-group-change] .github/workflows/reusable-harness-run.yml:63 — Concurrency group uses matrix.status_repo instead of github.repository. Correct for a reusable workflow (where github.repository is the calling repo, not the target).
  • [authorization-boundary-documentation] .github/workflows/reusable-harness-run.yml:58 — Authorization bypass of config.yaml checks is documented in a workflow comment. This security-relevant design decision may warrant an ADR.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

Medium

  • [supply-chain-checkout-integrity] .github/workflows/reusable-harness-run.yml:97 — The "Checkout upstream defaults" step uses repository: ${{ inputs.workflow_repository }} (caller-controlled) instead of the tamper-proof job.workflow_repository. In a workflow_call callee context, job.workflow_repository resolves to the repository hosting the reusable workflow file, which is always the correct checkout target. While ref: ${{ job.workflow_sha }} provides a SHA-pinning mitigation (a checkout from a different repo at that SHA would fail), using the caller-controlled input is unnecessarily fragile when a system-provided alternative exists.
    Remediation: Replace inputs.workflow_repository with job.workflow_repository in the Checkout upstream defaults step. If the workflow_repository input is retained for other uses, it can remain in the interface but should not drive the checkout.

  • [stale-doc] docs/contributing/workflow-contracts.md:8 — The per-repo chain description states "there is no separate reusable-<agent>.yml hop for per-repo mode; thread new secrets/inputs into the relevant inline job." After this PR, harness-run is an exception — it delegates to reusable-harness-run.yml via uses:. Contributors following this guidance when adding secrets to the harness path would look for an inline job rather than the new reusable workflow.
    Remediation: Update the per-repo chain description to note the harness-run exception.

  • [concurrency-group-contract-change] .github/workflows/reusable-harness-run.yml:113 — The concurrency group key changed from fullsend-harness-${{ matrix.agent }}-${{ github.repository }}-${{ matrix.status_number }} to fullsend-harness-${{ matrix.agent }}-${{ matrix.status_repo }}-${{ matrix.status_number }}. This replaces github.repository (the config/dispatch repo) with matrix.status_repo (the target repo). For per-org installs or cross-repo dispatch where these differ, this changes which runs cancel each other. The new behavior is arguably more correct (scoping by target repo), but it is a silent behavioral change.
    Remediation: Confirm matrix.status_repo is the intended scoping dimension and document the change.

  • [stale-reference] internal/scaffold/workflow_call_alignment_test.go:808TestReusableDispatchStatusCommentPassthrough (added to main by PR Prioritize job missing status comment and project_number passthrough (per-repo) #6397) has a harness-run sub-test that calls extractStepSection(t, s, "Run harness agent") on reusable-dispatch.yml. After this PR, that step no longer exists inline (replaced with a uses: call). The test will fail on merge.
    Remediation: Rebase onto main and update TestReusableDispatchStatusCommentPassthrough to load reusable-harness-run.yml and verify the "Run harness agent" step there.

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — This PR modifies files under .github/ which are protected governance/infrastructure paths. The PR links to issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 and explains the rationale (extracting harness-run for direct invocation by custom pollers). Human approval is required for protected-path changes regardless of context.

Low

  • [authorization-bypass] .github/workflows/reusable-harness-run.yml:57 — The direct-call path for custom pollers bypasses agent-enablement checks, role checks, and the kill-switch present in reusable-dispatch.yml. The mint service OIDC token exchange is the sole authorization boundary. This is a documented design tradeoff — custom pollers are repo-owned workflows with full control over dispatch.

  • [scope-creep] .github/workflows/reusable-dispatch.yml:76 — Adding JIRA_TOKEN and JIRA_USER_EMAIL secrets to the workflow_call interface is related to the Jira polling motivation but extends beyond the strict scope of issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 (extract harness-run). The secret threading is a natural dependency of making the extracted workflow functional for Jira-integrated agents.

  • [naming-convention] .github/workflows/reusable-harness-run.yml:56 — The workflow name: is "Harness run" while other reusable agent workflows use the " Agent" pattern (e.g., "Code Agent", "Review Agent"). However, this workflow runs arbitrary agents from a matrix rather than a single named stage, so the naming difference may be intentional.

  • [test-coverage-gap] internal/scaffold/workflow_call_alignment_test.goTestWorkflowCallInputAlignment does not include a callerPair entry for the reusable-dispatch.yml harness-run job calling reusable-harness-run.yml. This PR re-introduces a uses: reference from dispatch, so future input/secret drift between the caller and callee would not be caught by this test.
    Remediation: Add a callerPair entry for dispatch's harness-run job.

  • [secret-threading-gap] internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml — The per-repo shim template does not pass JIRA_TOKEN or JIRA_USER_EMAIL to reusable-dispatch.yml. Per-repo users wanting Jira harness agents through the shim chain will not receive these secrets. This may be intentional if Jira integration is intended only for the direct-call path.

  • [stale-doc] docs/architecture.md:53 — States reusable-dispatch.yml "inlines stage workflow jobs directly." The harness stage is now an exception (delegates via uses: to a separate reusable workflow with a relative path, avoiding the @v0 version-skew concern from ADR 0062).

  • [stale-doc] docs/ADRs/0062-dispatch-version-skew.md:86 — States "Stage workflows are merged into reusable-dispatch.yml." The harness-run extraction partially reverses this decision. ADRs are immutable per project conventions; noted for awareness only.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

High

  • [api-contract] .github/workflows/reusable-dispatch.yml — The caller passes workflow_repository: ${{ github.repository }} to reusable-harness-run.yml, but github.repository resolves to the triggering repository (the user's repo for shim-based calls), not fullsend-ai/fullsend. The original inline job used ${{ job.workflow_repository }}, which correctly resolves to the repository containing the reusable workflow. The new reusable-harness-run.yml "Checkout upstream defaults" step uses repository: ${{ inputs.workflow_repository }} with ref: ${{ job.workflow_sha }}. For non-vendored per-repo installs, the SHA from fullsend-ai/fullsend does not exist in the user's repository, causing the checkout to fail with a 404.
    Remediation: Change the caller to pass workflow_repository: ${{ job.workflow_repository }} instead of ${{ github.repository }}. Alternatively, the callee could use job.workflow_repository directly (as all other reusable workflows in this repo do) and remove the workflow_repository input entirely.

Medium

  • [authorization-bypass] .github/workflows/reusable-harness-run.yml:141 — The "Checkout upstream defaults" step uses inputs.workflow_repository (caller-controlled) instead of job.workflow_repository (system-controlled) to determine which repository to check out for the .defaults directory. This directory contains composite actions (mint-token, setup-gcp) and scripts (setup-agent-env.sh) that are subsequently executed with elevated permissions. All other reusable workflows in this repo use job.workflow_repository for this checkout. On the direct-call path (custom pollers), a caller can set workflow_repository to an arbitrary repository. See also: [api-contract] finding above.
    Remediation: Use job.workflow_repository instead of inputs.workflow_repository for the upstream defaults checkout, consistent with every other reusable workflow in this repository.

  • [scope-creep] .github/workflows/reusable-dispatch.yml — The addition of JIRA_TOKEN and JIRA_USER_EMAIL secrets to reusable-dispatch.yml (and their threading into reusable-harness-run.yml) is not authorized by issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347, which scopes the work to extracting harness-run into a reusable workflow. These secrets are new to reusable-dispatch.yml and belong to the Jira poll integration work (feat(#6339): replace jira-poll HarnessRouter with CEL trigger evaluation #6340). Adding new secrets to a reusable workflow changes its contract and requires tracing all callers per workflow-contracts.md. As a corollary, the per-repo shim template (shim-per-repo.yaml) does not forward these new secrets.
    Remediation: Split the JIRA secret additions into a separate PR linked to feat(#6339): replace jira-poll HarnessRouter with CEL trigger evaluation #6340, which can also update the shim template.

  • [dead-input] .github/workflows/reusable-dispatch.yml — The caller passes workflow_sha: ${{ github.sha }} but reusable-harness-run.yml does not declare a workflow_sha input. GitHub Actions silently drops undeclared inputs to reusable workflows. The callee correctly uses job.workflow_sha (auto-populated). Additionally, github.sha (the triggering commit SHA) differs from job.workflow_sha (the reusable workflow file's commit) in shim-based invocations, so if a future refactor declared and used the input, it would introduce a bug.
    Remediation: Remove the workflow_sha: ${{ github.sha }} line from the caller's with: block.

  • [test-coverage-gap] internal/scaffold/workflow_call_alignment_test.goTestWorkflowCallInputAlignment validates that callers pass all required inputs/secrets and no undeclared ones, but does not cover the reusable-dispatch.ymlreusable-harness-run.yml call path. This gap allowed the workflow_sha undeclared-input bug to go undetected.
    Remediation: Add a callerPair entry for the reusable-dispatch.ymlreusable-harness-run.yml call to TestWorkflowCallInputAlignment.

  • [regex-mismatch] internal/scaffold/workflow_call_alignment_test.go:177 — The reusableWorkflowRef regex (reusable-[a-z]+\.yml) does not match multi-segment names like reusable-harness-run.yml because [a-z]+ excludes hyphens. This blocks extending TestWorkflowCallInputAlignment to cover the new workflow.
    Remediation: Update the regex to reusable-[a-z-]+\.yml to accommodate hyphenated workflow names.

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — This PR modifies files under the protected .github/ path. The PR links to issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 and explains the rationale for extracting harness-run into a reusable workflow. Human approval is required for protected-path changes regardless of context.

Low

  • [stale-doc] docs/contributing/workflow-contracts.md:8 — The per-repo chain description could note that harness-run is an exception to stage inlining, now delegated to reusable-harness-run.yml via workflow_call. The doc specifically names the six stages and is technically accurate, but a clarifying note would help readers understand the new hop in the secret-threading chain.

  • [stale-doc] docs/architecture.md:54 — States reusable-dispatch.yml inlines stage workflow jobs directly. This is partially inaccurate now that harness-run is extracted, though the doc's claim is scoped to "stage workflows" which harness-run is not.

  • [secret-threading] .github/workflows/reusable-harness-run.yml:100FULLSEND_GCP_WIF_PROVIDER is required: true in this workflow but required: false in the upstream caller reusable-dispatch.yml. This is a known inconsistency documented in workflow-contracts.md ("Silent failures and required-flag consistency"), replicated in the new file.

  • [concurrency-change] .github/workflows/reusable-harness-run.yml — The concurrency group changed from github.repository to matrix.status_repo. This is likely intentional for the custom-poller use case where the calling repo differs from the target repo. For existing callers via reusable-dispatch.yml, matrix.status_repo is set to github.repository by harness-dispatch, preserving behavior.

  • [inconsistent-helper] internal/scaffold/workflow_call_alignment_test.go — The new harness-run sub-test in TestReusableDispatchPRHeadSHAPassthrough uses os.ReadFile directly while other new test blocks in the same PR (in TestOTELHeadersSecretThreading and TestOTELVariableForwarding) use the loadRepoFile helper. The parent test function also uses os.ReadFile, so this is internally consistent but diverges from the helper pattern used elsewhere in the PR.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (4)

Review

Findings

High

  • [runtime-context-unavailable] .github/workflows/reusable-dispatch.yml — The harness-run job passes ${{ job.workflow_repository }} and ${{ job.workflow_sha }} via job-level with: inputs to reusable-harness-run.yml. GitHub Actions documentation lists only github, needs, strategy, matrix, and inputs as available contexts in jobs.<job_id>.with. Every other use of job.workflow_* in this file occurs inside steps: blocks where the job context is available. In the job-level with: block, these expressions may evaluate to empty strings, causing the "Checkout upstream defaults" step to fail or check out incorrect content.
    Remediation: Remove the workflow_repository and workflow_sha inputs from the job-level with: block and the reusable-harness-run.yml input declarations. Instead, have reusable-harness-run.yml use job.workflow_repository and job.workflow_sha directly in its own steps — this matches the pattern used by every other reusable stage workflow.

Medium

  • [CI-coverage-gap] .github/workflows/e2e.yml:41 — The e2e.yml push path filter includes reusable-dispatch.yml but does not include the new reusable-harness-run.yml. A push to main that modifies only reusable-harness-run.yml will not trigger e2e tests.
    Remediation: Add .github/workflows/reusable-harness-run.yml to the push.paths filter in e2e.yml.

  • [concurrency-group-manipulation] .github/workflows/reusable-harness-run.yml:120 — The concurrency group key changed from github.repository to matrix.status_repo. Since matrix.status_repo is a caller-controlled input, a caller could set it to match another repository's concurrency group, potentially cancelling in-progress harness runs. Concurrency groups are evaluated before any step runs.
    Remediation: Prefix the concurrency group with github.repository to scope cancellation.

  • [secret-threading-gap] internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml:53 — The Jira secrets (JIRA_TOKEN, JIRA_USER_EMAIL) are declared in reusable-dispatch.yml and forwarded to reusable-harness-run.yml, but the shim template does not forward them. Per workflow-contracts.md, secrets must be explicitly forwarded by every caller — they will silently arrive as empty strings through the standard per-repo dispatch chain.
    Remediation: Either add the Jira secrets to the shim template's secrets block, or defer the Jira secret plumbing to a separate PR that updates the full chain.

  • [commit-convention] PR title — feat(workflows): extract harness-run into reusable workflow. Per COMMITS.md, feat is reserved for user-facing features. Extracting an existing inline job into a reusable workflow is internal restructuring — COMMITS.md explicitly lists this pattern as wrong for feat, recommending refactor instead.
    Remediation: Change to refactor(workflows): extract harness-run into reusable workflow.

  • [regex-pattern-mismatch] internal/scaffold/workflow_call_alignment_test.go:177 — The reusableWorkflowRef regex reusable-[a-z]+\.yml cannot match the hyphenated filename reusable-harness-run.yml because [a-z] excludes hyphens. The regex would match reusable-harness.yml instead, extracting an incorrect filename. This undermines the test infrastructure that workflow-contracts.md relies on for correctness.
    Remediation: Update the regex to reusable-[a-z-]+\.yml.

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — Both changed files are under .github/, a protected path. Issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 authorizes this work, but human approval is always required for protected-path changes.

Low

  • [caller-controlled-code-execution] .github/workflows/reusable-harness-run.yml:106 — The workflow_repository and workflow_sha inputs are used to check out code that is executed via uses: ./.defaults/. On the direct-call path, a caller controls these values. This is a pre-existing pattern (not a regression) mitigated by the required: true constraint on FULLSEND_GCP_WIF_PROVIDER.
    Remediation: Consider pinning workflow_repository to fullsend-ai/fullsend or documenting the trust boundary.

  • [scope-creep] .github/workflows/reusable-harness-run.yml:108 — The Jira secrets (JIRA_TOKEN, JIRA_USER_EMAIL) are not part of the harness-run extraction authorized by issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347. Adding secret plumbing conflates a refactor with a feature addition.
    Remediation: Consider splitting the Jira secrets addition into a separate PR.

  • [stale-nesting-depth-comment] .github/workflows/reusable-dispatch.yml:16 — Header comment states "Nesting: 2 levels of workflow_call" but the harness-run path is now 3 levels: shim → reusable-dispatch.ymlreusable-harness-run.yml.
    Remediation: Update the comment to note the harness-run path adds a third level.

  • [secret-scope] .github/workflows/reusable-harness-run.yml:268 — JIRA_TOKEN and JIRA_USER_EMAIL are forwarded to all harness agents regardless of whether the agent needs Jira access. This mirrors the existing pattern for OTEL secrets.

  • [workflow-name-convention] .github/workflows/reusable-harness-run.yml:59 — Workflow name "Harness run" uses lowercase "r". Other reusable workflows use title case.
    Remediation: Consider "Harness Run" for consistency.

  • [stale-doc] docs/contributing/workflow-contracts.md:8 — The per-repo chain description states stage logic is inlined with "no separate reusable-<agent>.yml hop." This is now incomplete since harness-run is dispatched via reusable-harness-run.yml.
    Remediation: Add a caveat noting harness-run is the exception.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (5)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — Both changed files are under .github/, a protected path. Issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 authorizes this work, but human approval is always required for protected-path changes.

  • [CI-coverage-gap] .github/workflows/e2e.yml:41 — The e2e.yml push path filter includes reusable-dispatch.yml but does not include the new reusable-harness-run.yml. A PR that modifies only reusable-harness-run.yml can land on main without e2e test signal.
    Remediation: Add .github/workflows/reusable-harness-run.yml to the push.paths filter in e2e.yml.

  • [test-coverage-gap] internal/scaffold/workflow_call_alignment_test.go:783TestReusableDispatchPRHeadSHAPassthrough's harness-run subtest verifies the workflow_call and matrix forwarding but does not verify that reusable-harness-run.yml itself passes pr-head-sha to the action. The OTEL tests were updated to cover the new file but pr-head-sha was not given the same treatment.
    Remediation: Add an assertion that reads reusable-harness-run.yml and verifies it contains pr-head-sha: and fromJSON(matrix.event_payload).pull_request.head.sha.

  • [scope-creep] .github/workflows/reusable-dispatch.yml:76 — The Jira secrets (JIRA_TOKEN, JIRA_USER_EMAIL, JIRA_BASE_URL) added to both workflow files are not part of the harness-run extraction authorized by issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347. Adding new secret plumbing is a separate feature concern that conflates a refactor and a feature addition.
    Remediation: Split the Jira secrets addition into a separate PR.

  • [secret-threading-gap] internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml:53 — The Jira secrets are declared in reusable-dispatch.yml and forwarded to reusable-harness-run.yml, but the shim template — the upstream caller — does not forward them. Per workflow-contracts.md, this means the secrets will silently arrive as empty strings through the standard per-repo dispatch chain.
    Remediation: Either add the Jira secrets to the shim template's secrets block, or defer the Jira secret plumbing to a separate PR that updates the full chain.

  • [commit-convention] PR title — feat(workflows): extract harness-run into reusable workflow. Per COMMITS.md, feat is reserved for user-facing features. Extracting an existing inline job into a reusable workflow is internal restructuring — COMMITS.md explicitly lists this pattern as wrong for feat, recommending refactor instead.
    Remediation: Change to refactor(workflows): extract harness-run into reusable workflow.

  • [stale-nesting-depth-comment] .github/workflows/reusable-dispatch.yml:16 — Header comment states "Nesting: 2 levels of workflow_call" but the harness-run path is now 3 levels: shim → reusable-dispatch.ymlreusable-harness-run.yml.
    Remediation: Update the comment to note that the harness-run path adds a third level.

Low

  • [regex-pattern-mismatch] internal/scaffold/workflow_call_alignment_test.go:177 — The reusableWorkflowRef regex reusable-[a-z]+\.yml cannot match the hyphenated filename reusable-harness-run.yml because [a-z] excludes hyphens. Currently latent — no test exercises this path for harness-run.
    Remediation: Update the regex to reusable-[a-z-]+\.yml.

  • [workflow-name-convention] .github/workflows/reusable-harness-run.yml:20 — Workflow name "Harness run" uses lowercase "r". Other reusable workflows use title case.
    Remediation: Consider "Harness Run" for consistency.

  • [stale-doc] docs/contributing/workflow-contracts.md:8 — The per-repo chain description states stage logic is inlined with "no separate reusable-<agent>.yml hop." While harness-run is not a stage agent, the blanket statement is now incomplete since harness-run is dispatched via reusable-harness-run.yml.
    Remediation: Add a caveat noting harness-run is the exception.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (6)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — Both changed files are under .github/, a protected path. Issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 authorizes this work, but human approval is always required for protected-path changes.

  • [stale-nesting-depth-comment] .github/workflows/reusable-dispatch.yml:16 — Header comment states "Nesting: 2 levels of workflow_call" but the harness-run path is now 3 levels: shim → reusable-dispatch.ymlreusable-harness-run.yml.
    Remediation: Update the comment to note that the harness-run path adds a third level, e.g., "Nesting: 2 levels for inline stages, 3 levels for harness-run (delegated to reusable-harness-run.yml)".

  • [CI-coverage-gap] .github/workflows/e2e.yml:41 — The e2e.yml push path filter includes reusable-dispatch.yml but does not include the new reusable-harness-run.yml. A PR that modifies only reusable-harness-run.yml can land on main without e2e test signal.
    Remediation: Add .github/workflows/reusable-harness-run.yml to the paths filter in e2e.yml.

  • [stale-doc] docs/contributing/workflow-contracts.md:8 — The per-repo chain description states stage logic is "inlined directly as jobs per ADR 62" with "no separate reusable-.yml hop". This is now incomplete: harness-run has been extracted to reusable-harness-run.yml, creating a reusable workflow hop. The secret-threading guidance is misleading for the harness stage.
    Remediation: Update the per-repo chain description to note that harness-run is an exception invoked via workflow_call to reusable-harness-run.yml.

Low

  • [secret-declaration-consistency] .github/workflows/reusable-harness-run.yml:59FULLSEND_GCP_WIF_PROVIDER is required: false, consistent with its immediate caller (reusable-dispatch.yml) but differs from standalone agent workflows which use required: true.

  • [workflow-name-convention] .github/workflows/reusable-harness-run.yml:1 — Workflow name "Harness run" uses lowercase "r". Other reusable workflows use title case ("Code Agent", "Fix Agent", etc.). Consider "Harness Run" for consistency.

  • [stale-doc] docs/ADRs/0062-dispatch-version-skew.md:90 — States stage logic is inlined into reusable-dispatch.yml as a consequence; now incomplete for harness-run.

  • [stale-doc] docs/ADRs/0033-per-repo-installation-mode.md:189 — Nesting depth description ("2 levels") was already stale after ADR 62 inlining; this PR adds another dimension.

  • [stale-doc] docs/ADRs/0033-per-repo-installation-mode.md:351 — "Resolved Questions" section references pre-ADR-62 nesting structure.

Previous run (7)

Review

Findings

High

  • [test-breakage] internal/scaffold/workflow_call_alignment_test.go:405TestOTELHeadersSecretThreading will fatally fail: extractStepSection expects exactly 1 match for "Run harness agent" in reusable-dispatch.yml, but this PR moves that step to reusable-harness-run.yml.
    Remediation: Remove "Run harness agent" from the stepMarkers list for reusable-dispatch.yml and add a test case that reads reusable-harness-run.yml.

  • [test-breakage] internal/scaffold/workflow_call_alignment_test.go:462TestOTELVariableForwarding will fatally fail for the same reason: extractStepSection expects the "Run harness agent" step in reusable-dispatch.yml.
    Remediation: Update stepMarkers and add a test for reusable-harness-run.yml verifying all 5 OTEL vars.

  • [test-breakage] internal/scaffold/workflow_call_alignment_test.go:769TestReusableDispatchPRHeadSHAPassthrough "harness-run" subtest will fail: strings.Index returns -1 for "Run harness agent" in reusable-dispatch.yml.
    Remediation: Update the subtest to read reusable-harness-run.yml instead.

Medium

  • [protected-path] .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-harness-run.yml — Both changed files are under .github/, a protected path. Issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 authorizes this work, but human approval is always required for protected-path changes.

  • [scope-divergence] .github/workflows/reusable-harness-run.yml — Issue Extract harness-run from reusable-dispatch.yml into a reusable action #6347 authorized a composite action (.github/actions/harness-run/) but the PR implements a reusable workflow. Composite actions cannot define their own concurrency groups or matrix strategies, making a reusable workflow the better fit. The issue is closed, suggesting acceptance.

  • [secret-declaration-consistency] .github/workflows/reusable-harness-run.yml:59FULLSEND_GCP_WIF_PROVIDER is required: false, consistent with its immediate caller (reusable-dispatch.yml) but differs from standalone agent workflows which use required: true.

  • [stale-nesting-depth-comment] .github/workflows/reusable-dispatch.yml:16 — Header comment states "Nesting: 2 levels of workflow_call" but the harness-run path is now 3 levels: shim → reusable-dispatch.ymlreusable-harness-run.yml.

Low

  • [workflow-name-convention] .github/workflows/reusable-harness-run.yml:24 — Workflow name "Harness run" uses lowercase "r". Consider "Harness Run" for title case consistency.

  • [stale-architectural-description] docs/ADRs/0033-per-repo-installation-mode.md:189 — Nesting depth description ("2 levels") was already stale after ADR 62 inlining; this PR adds another dimension.

  • [stale-architectural-description] docs/ADRs/0033-per-repo-installation-mode.md:351 — "Resolved Questions" section references pre-ADR-62 nesting structure.

  • [stale-workflow-comparison] docs/ADRs/0044-deprecate-per-org-installation-mode.md:157 — Per-repo nesting depth description was already stale after ADR 62.


Labels: PR modifies .github/workflows/ dispatch and harness execution infrastructure


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added component/ci CI pipelines and checks component/dispatch Workflow dispatch and triggers component/harness Agent harness, config, and skills loading labels Aug 19, 2026
@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ❌ Failure · Started 1:22 AM UTC · Completed 1:28 AM UTC

Commit: e40ec82 · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

⚠️ Post-fix script failed — Push rejected — workflows permission (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Security boundary: the coder app intentionally lacks workflows write permission. Changes to .github/workflows/ must be made outside the agent (e.g., via a manual PR). Re-run the agent without workflow file changes, or apply those changes separately.

Workflow run: https://github.com/fullsend-ai/.fullsend/actions/runs/32204629702

Details:
To https://github.com/fullsend-ai/fullsend.git
! [remote rejected] extract-harness-run -> extract-harness-run (refusing to allow a GitHub App to create or update workflow .github/workflows/reusable-dispatch.yml without workflows permission)
error: failed to push some refs to 'https://github.com/fullsend-ai/fullsend.git'
To https://github.com/fullsend-ai/fullsend.git
! [remote rejected] extract-harness-run -> extract-harness-run (refusing to allow a GitHub App to create or update workflow .github/workflows/reusable-dispatch.yml without workflows permission)
error: failed to push some refs to 'https://github.com/fullsend-ai/fullsend.git'
Please check the workflow logs for full details and retry with /fs-fix if appropriate.

Update vendormanifest and workflow alignment tests to account for
harness-run being extracted into reusable-harness-run.yml:

- Add reusable-harness-run.yml to vendored reusable workflows list
- Update OTEL headers/variables tests to check reusable-harness-run.yml
  separately instead of the inline harness agent step
- Update PR head SHA passthrough test to verify the workflow_call to
  reusable-harness-run.yml instead of the inline step

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean
ralphbean force-pushed the extract-harness-run branch from 008072d to f060999 Compare August 19, 2026 02:23
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 2:25 AM UTC · Ended 2:41 AM UTC

Commit: f060999 · View workflow run →

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 19, 2026 02:40

Superseded by updated review

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 19, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:25 AM UTC · Completed 2:41 AM UTC

Commit: f060999 · View workflow run →

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review findings

One HIGH and three MEDIUM findings. Three are inline; the one below could not be anchored inline because the line it concerns sits outside this PR's diff hunks.


MEDIUM — New caller→callee hop escapes TestWorkflowCallInputAlignment, and its regex cannot match the hyphenated filename

internal/scaffold/workflow_call_alignment_test.go:177

Two compounding gaps, both verified at head:

  1. The pattern reusable-[a-z]+\.yml (reusableWorkflowRef, line 177) cannot match reusable-harness-run.yml — after reusable-, [a-z]+ consumes harness and then requires \.yml, but the next char is -; no backtracking recovers.
  2. The pairs list (lines 220-226) contains only the six scaffold thin callers and was not extended with the new reusable-dispatch.yml harness-runreusable-harness-run.yml relationship. The adjacent comment "reusable-dispatch.yml stage jobs are no longer validated here (ADR 62: stages inlined, no external uses:)" is now factually false, since this PR reintroduces exactly such a uses:.

So the one automated guard against caller/callee input+secret drift does not cover the hop this PR creates — 6 inputs and 4 secrets are now threaded by hand across it — and even adding a pair would silently no-op because of the regex. I verified the pair is aligned today (the callee declares matrix/install_mode/mint_url/gcp_region/fullsend_version/runner_image and the 4 secrets; the caller passes exactly those); the risk is unguarded future drift. docs/contributing/workflow-contracts.md:14 explicitly instructs contributors to extend these tests when adding a hop.

Suggestion: Widen the pattern to reusable-[a-z0-9-]+\.yml, add a callerPair entry {"reusable-dispatch.yml", loadRepoFile(".github/workflows/reusable-dispatch.yml"), "harness-run"} (the callerWorkflow YAML struct already parses uses/with/secrets), delete/correct the now-false "no external uses:" comment, and add a regression assertion that the regex matches reusable-harness-run.yml so the next hyphenated workflow is not silently skipped.

Comment thread internal/scaffold/workflow_call_alignment_test.go
Comment thread .github/workflows/reusable-harness-run.yml
EVENT_PAYLOAD: ${{ matrix.event_payload }}
run: |
set -euo pipefail
URL=$(printf '%s' "${EVENT_PAYLOAD}" | jq -r '.issue.html_url // .pull_request.html_url // empty')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

MEDIUM — Motivating poller use case is structurally blocked: the extracted body hard-codes GitHub event/identifier shapes

The PR's stated motivation (and #6347's) is letting a custom poller invoke harness agents directly, and the second test-plan box is checked against run 32202963756. That run failed at "Run harness agent" with ERROR: ISSUE_URL does not match expected pattern: https://stage-redhat.atlassian.net/browse/KONFLUX-15169 plus github api: 404 Not Found status posts against "issue" #6580022 (a Jira id).

That is structural, not incidental: line 192 derives GITHUB_ISSUE_URL from jq -r '.issue.html_url // .pull_request.html_url // empty' and hard-fails when neither is present, and lines 218-224 pass matrix.source_repo/status_repo/status_number to the action as GitHub identifiers — precisely the assumptions a fullsend poll --input-driver jira-poll caller cannot satisfy. So no linked run demonstrates the new capability working.

For accuracy on the other checkbox: run 32203213291 shows dispatch / Harness run / Harness run (${{ matrix.agent }}) as skipped with the literal unexpanded matrix expression, i.e. the harness path never executed there either. The real dispatch-path evidence is the PR's own behaviour run (test-repo-03, run 32208540212), where steps 1-11 including Mint agent token and Setup GCP all succeeded and step 12 died on a podman-download flake (curl: (35)) — that does validate the extraction and its permission inheritance.

Suggestion: Either scope the PR and #6347 explicitly to GitHub-shaped event payloads and file the Jira/non-GitHub payload gap as follow-up, or uncheck the direct-invocation box until a run demonstrates it end to end. Also relink the dispatch-path checkbox to a run where the harness job actually executed (e.g. 32208540212) rather than 32203213291, where it was skipped.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We can come back to this after a few iterations - but this really did work. I may just need to update the example I linked to.

The one thing that's not yet working is how to get the JIRA_TOKEN through from the calling repo to the harness-run workflow and the composite action (unrelated to your thread here, other than its the one thing (I think) that's left to get this working.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've really gotten pre-scripts to work (see here: https://github.com/ralphbean/jira-triage-test/actions/runs/32281375216/job/96160837024 ) but am now hitting unrelated sandbox creation issues.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK - yes, this works.

  1. example run from a jira-poll workflow: https://github.com/ralphbean/jira-triage-test/actions/runs/32496078912/job/96814799790 with direct usage of reusable-harness-run
  2. example run from the same repo with reusable-dispatch (proving it didn't get broken in the process): https://github.com/ralphbean/jira-triage-test/actions/runs/32413923978

Declare JIRA_TOKEN, JIRA_USER_EMAIL, and JIRA_BASE_URL as optional
secrets in both reusable-dispatch.yml and reusable-harness-run.yml.
Expose them as env vars in the harness agent step so they're available
to pre-scripts and the fullsend run environment.

This enables JIRA-based agents (like triage) to authenticate with Jira
APIs without requiring bespoke workflow files. For issue #6359.

Assisted-by: Claude claude-sonnet-4-5@20250929 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:14 PM UTC · Completed 5:34 PM UTC

Commit: 020c495 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot removed the requires-manual-review Review requires human judgment label Aug 19, 2026
- Fix OTEL secret-threading test to extract step section instead of
  matching whole file content (which includes header comment examples)
- Fix OTEL variable-forwarding test with same section extraction
- Remove workflow_sha input and use job.workflow_sha built-in context
- Move permissions block from my-poller to harness-run in example
- Remove workflow_sha: v0 from example (forces source build vs release)

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:41 PM UTC · Completed 6:03 PM UTC

Commit: 9393f44 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ❌ Failure · Started 6:04 PM UTC · Completed 6:12 PM UTC

Commit: 9393f44 · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

⚠️ Post-fix script failed — Pre-commit blocked (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Workflow run: https://github.com/fullsend-ai/fullsend/actions/runs/32401038626

Details:
check toml...........................................(no files to check)Skipped
mixed line ending........................................................Passed
ruff (legacy alias)..................................(no files to check)Skipped
ruff format..........................................(no files to check)Skipped
ty check.............................................(no files to check)Skipped
bandit...............................................(no files to check)Skipped
Detect hardcoded secrets.................................................Passed
shellcheck...........................................(no files to check)Skipped
pinact (SHA-pin check)...................................................Passed
Lint GitHub Actions workflow files.......................................Failed
- hook id: actionlint
- exit code: 1

.github/workflows/reusable-dispatch.yml:1545:7: input "workflow_sha" is not defined in "./.github/workflows/reusable-harness-run.yml" reusable workflow. defined inputs are "fullsend_version", "gcp_region", "install_mode", "matrix", "mint_url", "runner_image" [workflow-call]
     |
1545 |       workflow_sha: ${{ github.sha }}
     |       ^~~~~~~~~~~~~

gofmt....................................................................Passed
go vet...................................................................Passed
lint mint embed sync.................................(no files to check)Skipped
lint interface doc sync..............................(no files to check)Skipped
lint ADR statuses....................................(no files to check)Skipped
lint ADR numbers.....................................(no files to check)Skipped
lint ADR frontmatter.................................(no files to check)Skipped
lint broken symlinks.....................................................Passed
lint docs link scope.................................(no files to check)Skipped
lint markdown links..................................(no files to check)Skipped
lint workflow file size..............................(no files to check)Skipped
lint-staged (web)....................................(no files to check)Skipped

Please check the workflow logs for full details and retry with /fs-fix if appropriate.

The workflow_sha input was removed from reusable-harness-run.yml in commit
9393f44 (it now uses job.workflow_sha directly), but reusable-dispatch.yml
was still trying to pass it. This caused startup_failure when calling the
workflow.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:22 PM UTC · Completed 6:43 PM UTC

Commit: c5c24db · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ❌ Failure · Started 6:45 PM UTC · Completed 6:53 PM UTC

Commit: c5c24db · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

⚠️ Post-fix script failed — Push rejected — workflows permission (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Security boundary: the coder app intentionally lacks workflows write permission. Changes to .github/workflows/ must be made outside the agent (e.g., via a manual PR). Re-run the agent without workflow file changes, or apply those changes separately.

Workflow run: https://github.com/fullsend-ai/fullsend/actions/runs/32404743283

Details:
To https://github.com/fullsend-ai/fullsend.git
! [remote rejected] extract-harness-run -> extract-harness-run (refusing to allow a GitHub App to create or update workflow .github/workflows/reusable-dispatch.yml without workflows permission)
error: failed to push some refs to 'https://github.com/fullsend-ai/fullsend.git'
To https://github.com/fullsend-ai/fullsend.git
! [remote rejected] extract-harness-run -> extract-harness-run (refusing to allow a GitHub App to create or update workflow .github/workflows/reusable-dispatch.yml without workflows permission)
error: failed to push some refs to 'https://github.com/fullsend-ai/fullsend.git'
Please check the workflow logs for full details and retry with /fs-fix if appropriate.

…tory

The workflow_repository input had the same issue as workflow_sha - it was
being passed as github.repository from reusable-dispatch.yml, which refers
to the calling repository (e.g., ralphbean/jira-triage-test), not the
repository containing the workflow (fullsend-ai/fullsend).

This caused checkout failures when trying to fetch a SHA from the fullsend
repo while checking out the jira-triage-test repo.

Solution: Remove the workflow_repository input and use job.workflow_repository
directly in reusable-harness-run.yml, just like we did for workflow_sha.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:01 PM UTC · Completed 8:23 PM UTC

Commit: 698589c · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ✅ Success · Started 8:24 PM UTC · Completed 8:31 PM UTC

Commit: 698589c · View workflow run →

Update the reusableWorkflowRef regex from [a-z]+ to [a-z-]+ so it
matches hyphenated workflow names like reusable-harness-run.yml.
Without this, TestWorkflowCallInputAlignment cannot be extended to
cover the new workflow.

Addresses review feedback on #6356
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

🔧 Fix agent — iteration 1 (bot-triggered)

Fixed 1 of 3 review findings: updated reusableWorkflowRef regex to allow hyphens. Disagreed with 2 findings: the authorization-bypass claim is factually incorrect (code already uses job.workflow_repository), and the secret-threading mismatch is a documented known inconsistency out of scope for this PR.

Fixed (1):

  1. regex-mismatch: reusableWorkflowRef regex excludes hyphens (internal/scaffold/workflow_call_alignment_test.go): Updated regex from reusable-[a-z]+.yml to reusable-[a-z-]+.yml to accommodate hyphenated workflow names like reusable-harness-run.yml

Disagreed (2):

  1. authorization-bypass: Checkout upstream defaults uses inputs.workflow_repository: The code at reusable-harness-run.yml line 134 already uses job.workflow_repository (system-controlled), not inputs.workflow_repository. There is no inputs.workflow_repository defined in this workflow. Prior commit 698589c explicitly removed the workflow_repository input. The reviewer's claim is factually incorrect.
  2. secret-threading: FULLSEND_GCP_WIF_PROVIDER required mismatch: This is a known inconsistency documented in workflow-contracts.md, replicated from the original code. The reviewer flagged it as [low] severity and described it as a known issue. Changing it in this PR would be out of scope.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:33 PM UTC · Completed 8:51 PM UTC

Commit: cc0486c · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed stale reviews from themself August 20, 2026 20:51

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 20, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review sweep findings (2 unique after deduping against existing review threads on this PR).

# with:
# matrix: ${{ needs.my-poller.outputs.matrix }}
# mint_url: ${{ vars.FULLSEND_MINT_URL }}
# gcp_region: ${{ vars.GCP_REGION }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HIGH — Documented caller example uses the wrong GCP region var and omits Jira secret forwarding

The header comment's "Minimal caller example" sets gcp_region: ${{ vars.GCP_REGION }} (this line), but the repo's own actual caller — internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml line 51 — uses vars.FULLSEND_GCP_REGION. A custom poller following the documented example verbatim will pass an unset/empty var for a required input, silently breaking GCP WIF auth. The same example's secrets: block (lines 43-47) also does not forward JIRA_TOKEN/JIRA_USER_EMAIL even though the workflow declares them and the surrounding docs describe Jira usage, so a copy-paste caller gets no working Jira integration either. Confirmed by diffing the header example against the real shim template and the workflow_call.secrets declarations at head cc0486c.

Suggestion: Fix the example to use vars.FULLSEND_GCP_REGION (matching the real shim), and add JIRA_TOKEN/JIRA_USER_EMAIL secret forwards to the example so it matches the actual workflow_call contract.

# id-token: write
# issues: write
# pull-requests: write
# uses: fullsend-ai/fullsend/.github/workflows/reusable-harness-run.yml@v0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HIGH — Reintroduces the ADR-0062 @v0 version-skew pattern for a new, less-controlled external-caller audience

docs/ADRs/0062-dispatch-version-skew.md (Accepted) identifies hardcoded reusable-<stage>.yml@v0 references as the "version skew" problem, rejects re-introducing per-repo uses: refs, and mandates inlining stage workflows into reusable-dispatch.yml specifically to remove the extra workflow_call hop. This PR's header comment instructs a brand-new class of external caller — custom pollers in repos not managed by fullsend install — to hardcode uses: fullsend-ai/fullsend/.github/workflows/reusable-harness-run.yml@v0 (this line), with no update/versioning mechanism proposed for those repos to track changes, i.e. the same version-skew failure mode ADR 0062 eliminated, relocated to a less-controlled, unmanaged audience. This is not acknowledged anywhere in the PR body, the new file, or ADR 0062 itself. Additionally, reusable-dispatch.yml's own header comment ("Nesting: 2 levels of workflow_call (previously 3 before inlining)", line 16) is now stale/incorrect since this PR reintroduces a third hop for the harness-run path specifically.

Suggestion: Amend or supersede ADR 0062 to explicitly scope its rule to the per-repo dispatch flow and document why direct external invocation is a separate risk category with its own versioning story (e.g. real vMAJOR.MINOR.PATCH tags + bump guidance instead of @v0), and fix the stale nesting-depth comment in reusable-dispatch.yml.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, this is a problem. I don't want to amend or supersede ADR 62 just to say "this is ok". This is a real problem. People who pin reusable dispatch to a particular version will still "float" forward in their unpinned indirect reference to reusable-harness-run.

@ralphbean

Copy link
Copy Markdown
Member Author

Putting this one into draft while I think about how to resolve the version skew problem. IMO that's a blocker.

@ralphbean
ralphbean marked this pull request as draft August 21, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/ci CI pipelines and checks component/dispatch Workflow dispatch and triggers component/harness Agent harness, config, and skills loading fullsend-fix Enables automatic bot-triggered fix runs on human-authored PRs requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extract harness-run from reusable-dispatch.yml into a reusable action

2 participants