Skip to content

feat(dispatch): synchronous workflow_call event dispatch (ADR 41)#1611

Draft
ifireball wants to merge 3 commits into
mainfrom
feat/adr-41-sync-dispatch
Draft

feat(dispatch): synchronous workflow_call event dispatch (ADR 41)#1611
ifireball wants to merge 3 commits into
mainfrom
feat/adr-41-sync-dispatch

Conversation

@ifireball

@ifireball ifireball commented May 27, 2026

Copy link
Copy Markdown
Member

Summary

  • Implements ADR 41: per-org path is shim → dispatch.ymlreusable-*.yml@v0 (synchronous workflow_call), replacing # fullsend-stage: scanning and gh workflow run.
  • Removes thin stage workflows from scaffold; prioritize.yml gains workflow_call with declared secrets.
  • Ports per-stage concurrency into upstream reusable workflows.
  • E2e smoke test polls fullsend.yaml on the enrolled repo (caller run includes dispatch + stage jobs).

Commit structure

  1. feat(dispatch) — workflow/scaffold/reusable/e2e changes
  2. docs — architecture + skills (finding-agent-runs, retro-analysis)
  3. fix(cli) — org-scoped inference credentials + enable/disable repo access sync

Review feedback addressed

  • Per-org stage jobs: explicit OIDC permissions; kill-switch ternary; code|fixcoder role mapping
  • Shim: secrets: inherit into dispatch.yml; dispatch declares required GCP workflow_call secrets
  • Per-repo reusable-dispatch: prioritize job; fail-closed reusable-fix bot eligibility
  • Skills: trace agent runs on enrolled-repo fullsend.yaml (not separate .fullsend dispatch.yml runs)
  • CLI: provision GCP secrets/vars at org scope; admin enable/disable updates selected-repo access (same pattern as FULLSEND_MINT_URL)

Migration

Audience Action
Default orgs After merge, run fullsend admin install <org> (or admin enable repos to refresh org secret visibility).
Custom # fullsend-stage: workflows Add explicit workflow_call job in dispatch.yml; remove markers.

Test plan

  • make go-test / scaffold + layers unit tests
  • CI including e2e on upstream branch

e2e note: Shim may still fail until reusable workflow changes are on @v0 (post-merge release) or #1278 lands for PR-branch pinning.

Supersedes closed #1586.

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown

Site preview

Preview: https://5a45ee95-site.fullsend-ai.workers.dev

Commit: e44b70f5471f9471f6925162477dd659ccb25c6f

@fullsend-ai-review

fullsend-ai-review Bot commented May 27, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-*.yml, .pre-commit-config.yaml — PR modifies 6 reusable workflow files and .pre-commit-config.yaml. These are governance/infrastructure files that require human approval regardless of context. The PR provides sufficient justification (ADR 41 implementation, detailed description, supersedes feat(dispatch): synchronous workflow_call event dispatch (ADR 41) #1586).

  • [correctness] .github/workflows/reusable-dispatch.yml:51 — The new prioritize job uses a relative path uses: ./.fullsend/.github/workflows/prioritize.yml. Relative uses: paths resolve relative to the repository containing the calling workflow file. Since reusable-dispatch.yml lives in fullsend-ai/fullsend, this resolves to fullsend-ai/fullsend/.fullsend/.github/workflows/prioritize.yml — a path that does not exist in the upstream repo. The prioritize stage will fail in per-repo installation mode. Compare with the per-org scaffold dispatch.yml, which correctly uses ./.github/workflows/prioritize.yml (relative to the .fullsend config repo where it lives).
    Remediation: Either use an absolute reference (fullsend-ai/fullsend/.github/workflows/prioritize.yml@v0 — but prioritize.yml is org-specific, not upstream) or restructure so per-repo prioritize uses a different path resolution strategy.

Low

  • [style] internal/scaffold/fullsend-repo/templates/shim-workflow-call.yaml:4 — Comment says "No secrets are needed in the enrolled repo — agents fetch scoped tokens from the centralized token mint using GitHub OIDC." This is now inaccurate: the shim passes secrets: inherit (line 44) which flows org-scoped GCP secrets (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID) from the enrolled repo's context through to dispatch.yml. The security model is still sound (pull_request_target prevents PR authors from modifying the shim), but the comment should be updated to reflect that org-scoped secrets now transit the shim.

  • [correctness] internal/layers/inference.go (appendConfigRepoID function) — GetRepo errors are silently swallowed (returns nil error). If the config repo cannot be resolved, callers proceed without it in the visibility list and have no indication of the omission. This is acceptable for best-effort admin operations but could make debugging difficult when org secret visibility appears inconsistent.
    Remediation: Consider logging a warning via the UI printer when GetRepo fails, similar to other best-effort operations in this file.

Info

  • [correctness] internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml:237 — The code|fix role mapping means disabling the "coder" role now disables both code and fix agents (previously, fix used its own implicit role name). This is a deliberate behavior change that aligns with the semantic intent (both agents write code), confirmed by consistent usage in reusable-dispatch.yml.

  • [documentation] docs/ADRs/0031-reusable-workflows-for-action-installed-distribution.md — Multiple ADRs and specs still reference "thin callers" and gh workflow run patterns. These are historical records documenting decisions made at the time and do not require updates. The living documentation (docs/architecture.md) and operational skills (finding-agent-runs, retro-analysis) are correctly updated in this PR.

Previous run

Review

Findings

High

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — 7 protected files modified with no linked issue. The PR description explains the rationale (ADR 41 synchronous dispatch implementation: concurrency groups moved to reusable workflows, comment updates, lint suppression for prioritize.yml workflow_call reference, prioritize job added to reusable-dispatch.yml). However, protected-path changes require a linked issue for traceability. Human approval is required for all protected-path changes regardless of context.
    Remediation: Link a tracking issue to this PR.

Low

  • [correctness] internal/scaffold/scaffold_test.go — The test TestCodeImplementationSkillAPIContractGuidance was removed, but internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md still contains the "Verify API contracts per code path" guidance (step 9). This reduces test coverage for scaffold content integrity.
    Remediation: Either restore the test or confirm the guidance removal is planned for a follow-up.

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md — References gh workflow run triage.yml / code.yml / review.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed. This is a historical design spec (lower impact than living docs), but the references are now misleading.
    Remediation: Add a note at the top of the spec indicating it predates ADR 0041, or update the dispatch mechanism description.

Info

  • [correctness] .github/workflows/reusable-dispatch.yml:52 — Good bug fix: the old stage output expression skipped == 'true' && '' || steps.route.outputs.stage always evaluated to stage regardless of skip status due to short-circuit evaluation semantics (true && '''', then '' || stagestage). The corrected expression skipped != 'true' && steps.route.outputs.stage || '' properly returns empty string when the role is skipped.
Previous run (2)

Review

Findings

High

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — 7 protected files modified with no linked issue. The PR description explains the rationale (ADR 41 implementation) but protected-path changes require a linked issue for traceability. Human approval is required for all protected-path changes regardless of context.
    Remediation: Link a tracking issue to this PR.

Low

  • [correctness] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Removed "Verify API contracts per code path" guidance (item 9 from the planning checklist). This advice helped code agents avoid breaking API calls when modifying parameters. Consider preserving this guidance or documenting why it was removed.

Info

  • [correctness] .github/workflows/reusable-dispatch.yml:52 — Good bug fix: the old stage output expression skipped == 'true' && '' || steps.route.outputs.stage always evaluated to stage regardless of skip status due to short-circuit evaluation semantics (true && '''', then '' || stagestage). The corrected expression skipped != 'true' && steps.route.outputs.stage || '' properly returns empty string when the role is skipped.
Previous run (3)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md — References gh workflow run triage.yml / code.yml / review.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist, and its corresponding test (TestCodeImplementationSkillAPIContractGuidance) was also removed, confirming this is intentional. If this guidance is now redundant with other checks, no action needed.

  • [prior-finding-resolved] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") has been re-added with full guidance on accumulating findings across triage runs and incorporating human-identified problems. The prior info-level finding about its removal is resolved.

  • [prior-finding-resolved] .github/workflows/reusable-fix.yml — The bot-triggered fix path now fails closed on gh pr view failure (exit 1) instead of silently proceeding with empty defaults. This resolves the prior low-severity finding about fail-open behavior.

  • [prior-finding-resolved] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh — The unquoted for kv in ${extra_env} expansion was reverted to while IFS= read -r kv (space-safe). A new test case (force-override-comment-body) covers the COMMENT_BODY=--force override path.

Previous run (4)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md — References gh workflow run triage.yml / code.yml / review.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") was removed. This section guided the triage agent to accumulate findings across re-runs and incorporate human-identified problems. If this behavior is now handled elsewhere, no action needed; otherwise, triage re-runs may lose previously documented causes.

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist. This guidance helped catch issues when code changes affect parameters sent to external APIs. If this was removed intentionally (e.g., redundant with other guidance), no action needed.

  • [prior-finding-resolved] The prioritize.yml workflow_call trigger now correctly declares both FULLSEND_GCP_WIF_PROVIDER and FULLSEND_GCP_PROJECT_ID secrets, matching what dispatch.yml passes explicitly. No runtime failure.

  • [prior-finding-corrected] The low-severity [correctness] finding from the prior review about pre-code-test.sh unquoted expansion was inaccurate — the run_test_stdout function uses while IFS= read -r kv (line 91), which correctly handles values with spaces. No issue exists.

Previous run (5)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md — References gh workflow run triage.yml / code.yml / review.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") was removed. This section guided the triage agent to accumulate findings across re-runs and incorporate human-identified problems. If this behavior is now handled elsewhere, no action needed; otherwise, triage re-runs may lose previously documented causes.

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist. This guidance helped catch issues when code changes affect parameters sent to external APIs. If this was removed intentionally (e.g., redundant with other guidance), no action needed.

  • [prior-finding-resolved] The high-severity [correctness] finding from the prior review (prioritize.yml workflow_call trigger missing secrets: declaration) is resolved — the workflow_call trigger now declares both FULLSEND_GCP_WIF_PROVIDER and FULLSEND_GCP_PROJECT_ID secrets.

  • [prior-finding-corrected] The low-severity [correctness] finding from the prior review about pre-code-test.sh unquoted expansion was inaccurate — the run_test_stdout function uses while IFS= read -r kv (line 91), which correctly handles values with spaces. No issue exists.

Previous run (6)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [correctness] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:89 — The while IFS= read -r kv loop was replaced with for kv in ${extra_env} (unquoted expansion). The old version preserved env values containing spaces; the new version word-splits on whitespace. Current test cases only use space-free values (CODE_FORCE=true), so this doesn't break existing tests, but it's a latent issue if a future test case adds a value with spaces.
    Remediation: Quote the expansion: for kv in "${extra_env[@]}" if it's an array, or keep the read loop if extra_env is a string.

  • [correctness] .github/workflows/reusable-fix.yml:262 — The bot-triggered fix path now silently proceeds with empty defaults ({"labels":[],"author":""}) when gh pr view fails, instead of hard-failing. Since the dispatch routing already checks the fullsend-no-fix label, this is a reasonable redundancy reduction, but it means the fullsend-no-fix guard in the reusable workflow is bypassed on API failures (fail-open for bot fixes).

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md — References gh workflow run triage.yml / code.yml / review.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") was removed. This section guided the triage agent to accumulate findings across re-runs and incorporate human-identified problems. If this behavior is now handled elsewhere, no action needed; otherwise, triage re-runs may lose previously documented causes.

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist. This guidance helped catch issues when code changes affect parameters sent to external APIs. If this was removed intentionally (e.g., redundant with other guidance), no action needed.

  • [prior-finding-resolved] The high-severity [correctness] finding from the prior review (prioritize.yml workflow_call trigger missing secrets: declaration) is resolved — the workflow_call trigger now declares both FULLSEND_GCP_WIF_PROVIDER and FULLSEND_GCP_PROJECT_ID secrets.

Previous run (7)

Review

Findings

High

  • [correctness] internal/scaffold/fullsend-repo/.github/workflows/prioritize.yml:4 — The workflow_call trigger declares inputs: but no secrets: section. However, the prioritize: job in dispatch.yml passes explicit named secrets (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID). GitHub Actions requires called workflows to declare secrets when the caller passes them explicitly (as opposed to secrets: inherit). This will cause the prioritize stage to fail at runtime with an error like the called workflow does not define secret 'FULLSEND_GCP_WIF_PROVIDER'.
    Remediation: Either add a secrets: block to prioritize.yml's workflow_call trigger declaring both secrets, or change the prioritize: job in dispatch.yml to use secrets: inherit instead of explicit secret passing.

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [correctness] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:89 — The while IFS= read -r kv loop was replaced with for kv in ${extra_env} (unquoted expansion). The old version preserved env values containing spaces; the new version word-splits on whitespace. Current test cases only use space-free values (CODE_FORCE=true), so this doesn't break existing tests, but it's a latent issue if a future test case adds a value with spaces.
    Remediation: Quote the expansion: for kv in "${extra_env[@]}" if it's an array, or keep the read loop if extra_env is a string.

  • [correctness] .github/workflows/reusable-fix.yml:262 — The bot-triggered fix path now silently proceeds with empty defaults ({"labels":[],"author":""}) when gh pr view fails, instead of hard-failing. Since the dispatch routing already checks the fullsend-no-fix label, this is a reasonable redundancy reduction, but it means the fullsend-no-fix guard in the reusable workflow is bypassed on API failures (fail-open for bot fixes).

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") was removed. This section guided the triage agent to accumulate findings across re-runs and incorporate human-identified problems. If this behavior is now handled elsewhere, no action needed; otherwise, triage re-runs may lose previously documented causes.

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist. This guidance helped catch issues when code changes affect parameters sent to external APIs. If this was removed intentionally (e.g., redundant with other guidance), no action needed.

Previous run (8)

Review

Findings

Medium

  • [correctness] internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml / internal/scaffold/fullsend-repo/.github/workflows/prioritize.yml — The prioritize job in dispatch.yml passes secrets explicitly (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID), but prioritize.yml's new workflow_call: trigger does not declare a secrets: block. GitHub Actions requires called workflows to declare secrets when callers pass them explicitly — this will cause a workflow parse error at runtime when the prioritize stage is triggered. The upstream reusable workflows (reusable-triage.yml, reusable-code.yml, etc.) correctly declare these secrets. The PR body mentions "secrets: inherit on the prioritize workflow_call job" as a review fix, suggesting the intent was to use secrets: inherit on the caller side instead of explicit mapping.
    Remediation: Either add a secrets: block to prioritize.yml's workflow_call: trigger declaring both secrets, or change the dispatch.yml prioritize job to use secrets: inherit instead of explicit secret mapping.

  • [protected-path] .github/workflows/reusable-{code,dispatch,fix,retro,review,triage}.yml, .pre-commit-config.yaml — This PR modifies 7 files under .github/ and .pre-commit-config.yaml. The changes add concurrency groups to reusable workflows (moved from deleted thin callers), update comments for ADR 0041 terminology, fix the role-skip output expression in reusable-dispatch.yml, and add a lint suppression for the prioritize.yml workflow_call reference. Human approval is always required for protected-path changes, regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md:175 — References gh workflow run triage.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [expression-fix] .github/workflows/reusable-dispatch.yml:38 — The stage output expression was corrected from skipped == 'true' && '' || stage (which always evaluated to stage due to short-circuit on falsy empty string) to skipped != 'true' && stage || '' (correctly returns empty when the role is skipped). Good fix — the old expression silently ignored role-skip decisions.

  • [concurrency-parity] .github/workflows/reusable-*.yml — Concurrency groups in the reusable workflows exactly match the groups previously defined in the thin callers they replace (fullsend-{stage}-${{ inputs.source_repo }}-...). Verified all five stage workflows (triage, code, review, fix, retro) have correct group expressions. No behavioral change.

  • [e2e-timestamp-fix] e2e/admin/admin_test.go:253issueCreatedAt is now captured before issue creation rather than after. This is a correctness improvement — the previous placement could miss fast-starting dispatch runs due to the race between time.Now() and the GitHub API call.

Previous run (9)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [correctness] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:89 — The while IFS= read -r kv loop was replaced with for kv in ${extra_env} (unquoted expansion). The old version preserved env values containing spaces; the new version word-splits on whitespace. Current test cases only use space-free values (CODE_FORCE=true), so this doesn't break existing tests, but it's a latent issue if a future test case adds a value with spaces.
    Remediation: Quote the expansion: for kv in "${extra_env[@]}" if it's an array, or keep the read loop if extra_env is a string.

  • [correctness] .github/workflows/reusable-fix.yml:262 — The bot-triggered fix path now silently proceeds with empty defaults ({"labels":[],"author":""}) when gh pr view fails, instead of hard-failing. Since the dispatch routing already checks the fullsend-no-fix label, this is a reasonable redundancy reduction, but it means the fullsend-no-fix guard in the reusable workflow is bypassed on API failures (fail-open for bot fixes).

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md — References gh workflow run triage.yml / code.yml / review.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") was removed. This section guided the triage agent to accumulate findings across re-runs and incorporate human-identified problems. If this behavior is now handled elsewhere, no action needed; otherwise, triage re-runs may lose previously documented causes.

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist. This guidance helped catch issues when code changes affect parameters sent to external APIs. If this was removed intentionally (e.g., redundant with other guidance), no action needed.

  • [prior-finding-resolved] The high-severity [correctness] finding from the prior review (prioritize.yml workflow_call trigger missing secrets: declaration) is resolved — the workflow_call trigger now declares both FULLSEND_GCP_WIF_PROVIDER and FULLSEND_GCP_PROJECT_ID secrets.

Previous run (10)

Review

Findings

High

  • [correctness] internal/scaffold/fullsend-repo/.github/workflows/prioritize.yml:4 — The workflow_call trigger declares inputs: but no secrets: section. However, the prioritize: job in dispatch.yml passes explicit named secrets (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID). GitHub Actions requires called workflows to declare secrets when the caller passes them explicitly (as opposed to secrets: inherit). This will cause the prioritize stage to fail at runtime with an error like the called workflow does not define secret 'FULLSEND_GCP_WIF_PROVIDER'.
    Remediation: Either add a secrets: block to prioritize.yml's workflow_call trigger declaring both secrets, or change the prioritize: job in dispatch.yml to use secrets: inherit instead of explicit secret passing.

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — Seven protected files are modified. The PR body explains the rationale (ADR 41 synchronous dispatch implementation, concurrency groups moved to reusable workflows, pre-commit ignore for prioritize.yml). Human approval is always required for protected-path changes, regardless of context.

Low

  • [correctness] internal/scaffold/fullsend-repo/scripts/pre-code-test.sh:89 — The while IFS= read -r kv loop was replaced with for kv in ${extra_env} (unquoted expansion). The old version preserved env values containing spaces; the new version word-splits on whitespace. Current test cases only use space-free values (CODE_FORCE=true), so this doesn't break existing tests, but it's a latent issue if a future test case adds a value with spaces.
    Remediation: Quote the expansion: for kv in "${extra_env[@]}" if it's an array, or keep the read loop if extra_env is a string.

  • [correctness] .github/workflows/reusable-fix.yml:262 — The bot-triggered fix path now silently proceeds with empty defaults ({"labels":[],"author":""}) when gh pr view fails, instead of hard-failing. Since the dispatch routing already checks the fullsend-no-fix label, this is a reasonable redundancy reduction, but it means the fullsend-no-fix guard in the reusable workflow is bypassed on API failures (fail-open for bot fixes).

Info

  • [intent-alignment] internal/scaffold/fullsend-repo/agents/triage.md — Step 2d ("Review prior triage analysis") was removed. This section guided the triage agent to accumulate findings across re-runs and incorporate human-identified problems. If this behavior is now handled elsewhere, no action needed; otherwise, triage re-runs may lose previously documented causes.

  • [intent-alignment] internal/scaffold/fullsend-repo/skills/code-implementation/SKILL.md — Step 9 ("Verify API contracts per code path") was removed from the planning checklist. This guidance helped catch issues when code changes affect parameters sent to external APIs. If this was removed intentionally (e.g., redundant with other guidance), no action needed.

Previous run (11)

Review

Findings

Medium

  • [correctness] internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml / internal/scaffold/fullsend-repo/.github/workflows/prioritize.yml — The prioritize job in dispatch.yml passes secrets explicitly (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID), but prioritize.yml's new workflow_call: trigger does not declare a secrets: block. GitHub Actions requires called workflows to declare secrets when callers pass them explicitly — this will cause a workflow parse error at runtime when the prioritize stage is triggered. The upstream reusable workflows (reusable-triage.yml, reusable-code.yml, etc.) correctly declare these secrets. The PR body mentions "secrets: inherit on the prioritize workflow_call job" as a review fix, suggesting the intent was to use secrets: inherit on the caller side instead of explicit mapping.
    Remediation: Either add a secrets: block to prioritize.yml's workflow_call: trigger declaring both secrets, or change the dispatch.yml prioritize job to use secrets: inherit instead of explicit secret mapping.

  • [protected-path] .github/workflows/reusable-{code,dispatch,fix,retro,review,triage}.yml, .pre-commit-config.yaml — This PR modifies 7 files under .github/ and .pre-commit-config.yaml. The changes add concurrency groups to reusable workflows (moved from deleted thin callers), update comments for ADR 0041 terminology, fix the role-skip output expression in reusable-dispatch.yml, and add a lint suppression for the prioritize.yml workflow_call reference. Human approval is always required for protected-path changes, regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md:175 — References gh workflow run triage.yml and per-stage standalone workflows as the dispatch mechanism. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed.
    Remediation: Update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [expression-fix] .github/workflows/reusable-dispatch.yml:38 — The stage output expression was corrected from skipped == 'true' && '' || stage (which always evaluated to stage due to short-circuit on falsy empty string) to skipped != 'true' && stage || '' (correctly returns empty when the role is skipped). Good fix — the old expression silently ignored role-skip decisions.

  • [concurrency-parity] .github/workflows/reusable-*.yml — Concurrency groups in the reusable workflows exactly match the groups previously defined in the thin callers they replace (fullsend-{stage}-${{ inputs.source_repo }}-...). Verified all five stage workflows (triage, code, review, fix, retro) have correct group expressions. No behavioral change.

  • [e2e-timestamp-fix] e2e/admin/admin_test.go:253issueCreatedAt is now captured before issue creation rather than after. This is a correctness improvement — the previous placement could miss fast-starting dispatch runs due to the race between time.Now() and the GitHub API call.

Previous run (12)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-{code,dispatch,fix,retro,review,triage}.yml, .pre-commit-config.yaml — This PR modifies 7 files under .github/ and .pre-commit-config.yaml. The changes add concurrency groups to reusable workflows (moved from deleted thin callers), update comments for ADR 0041 terminology, and add a lint suppression for the prioritize.yml workflow_call reference. Human approval is always required for protected-path changes, regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md:175 — References gh workflow run triage.yml and per-stage dispatch-* jobs as the dispatch mechanism, describing standalone stage workflows as separate dispatch targets. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed. Remediation: update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [concurrency-parity] .github/workflows/reusable-*.yml — Concurrency groups in the reusable workflows exactly match the groups previously defined in the thin callers they replace (fullsend-{stage}-${{ inputs.source_repo }}-...). Verified all five stage workflows (triage, code, review, fix, retro) have identical group expressions. No behavioral change.

  • [e2e-timestamp-fix] e2e/admin/admin_test.go:253issueCreatedAt is now captured before issue creation rather than after. This is a correctness improvement — the previous placement could miss fast-starting dispatch runs due to the race between time.Now() and the GitHub API call. Good fix.

Previous run (13)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-{code,dispatch,fix,retro,review,triage}.yml, .pre-commit-config.yaml — This PR modifies 7 files under .github/ and .pre-commit-config.yaml. The changes add concurrency groups to reusable workflows (moved from deleted thin callers) and update comments and lint suppression rules. The PR links to ADR 41 and explains the rationale (synchronous workflow_call dispatch replacing gh workflow run). Human approval is always required for protected-path changes, regardless of context.

Info

  • [concurrency-parity] .github/workflows/reusable-*.yml — Concurrency groups in the reusable workflows exactly match the groups previously defined in the thin callers they replace (fullsend-{stage}-${{ inputs.source_repo }}-...). Verified all five stage workflows (triage, code, review, fix, retro) have identical group expressions. No behavioral change.

  • [e2e-timestamp-fix] e2e/admin/admin_test.go:253issueCreatedAt is now captured before issue creation rather than after. This is a correctness improvement — the previous placement could miss fast-starting dispatch runs due to the race between time.Now() and the GitHub API call. Good fix.

Previous run (14)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-dispatch.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — This PR modifies protected governance/infrastructure files. The changes are well-justified by ADR 0041 (adding concurrency groups to reusable workflows, updating comments, adding lint-ignore for prioritize.yml workflow_call), but human approval is always required for protected-path changes regardless of context.

Low

  • [documentation-currency] docs/superpowers/specs/2026-04-17-installer-agent-content-design.md:175 — References gh workflow run triage.yml, gh workflow run code.yml, and gh workflow run review.yml as the dispatch mechanism, and describes standalone stage workflows as separate dispatch targets. After ADR 0041, dispatch.yml routes directly to upstream reusable workflows via synchronous workflow_call jobs; standalone stage workflows and gh workflow run are removed. Remediation: update the dispatch mechanism description and scaffold layout to reflect the new direct workflow_call pattern.

Info

  • [prior-findings-resolved] The four documentation-currency findings from the prior review (on docs/architecture.md:42, docs/architecture.md:46, internal/layers/workflows.go:32, .github/workflows/reusable-dispatch.yml:4) are all resolved by the latest commit — the stale "thin caller" and "dispatch workflow mints OIDC tokens" references have been updated to describe the new synchronous workflow_call pattern.
Previous run (15)

Review

Findings

Medium

  • [protected-path] .github/workflows/reusable-code.yml, .github/workflows/reusable-fix.yml, .github/workflows/reusable-retro.yml, .github/workflows/reusable-review.yml, .github/workflows/reusable-triage.yml, .pre-commit-config.yaml — This PR modifies protected governance/infrastructure files. The changes are well-justified (ADR 41 implementation: adding concurrency groups to reusable workflows, updating pre-commit ignore patterns), but human approval is always required for protected-path changes regardless of context.

Low

  • [documentation-currency] docs/architecture.md:42 — Line 42 still references "dispatch workflow mints OIDC tokens exchanged at a central token mint... for scoped GitHub App installation tokens per agent role." This description is now stale: the new dispatch.yml no longer mints OIDC tokens — token minting happens inside each reusable workflow's own mint-token action step.

  • [documentation-currency] docs/architecture.md:46 — Line 46 still describes agent workflows as "thin callers (~40-70 lines) that delegate infrastructure logic to upstream reusable workflows." After this PR, thin callers are removed; dispatch.yml calls upstream reusable workflows directly via workflow_call jobs. This line should reference the new dispatch-to-reusable pattern and cite ADR 0041.

  • [documentation-currency] internal/layers/workflows.go:32 — Comment says "It writes the thin caller workflows" but the layer no longer writes thin caller workflows (they were removed). The comment should reference the current scaffold contents (dispatch.yml, prioritize.yml, repo-maintenance.yml, etc.).

  • [documentation-currency] .github/workflows/reusable-dispatch.yml:4 — Comment describes itself as "the per-repo equivalent of the per-org dispatch.yml + thin caller pair." After this PR, per-org dispatch.yml also uses direct workflow_call jobs (same pattern as reusable-dispatch.yml), so the "thin caller pair" comparison is stale.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label May 27, 2026
@ifireball ifireball requested review from ggallen, ralphbean, rh-hemartin and waynesun09 and removed request for ralphbean and waynesun09 May 27, 2026 17:44
ifireball added a commit to ifireball/fullsend that referenced this pull request May 27, 2026
Update stale references to thin callers and per-org OIDC minting in
dispatch.yml; address review feedback on PR fullsend-ai#1611.

Signed-off-by: Barak Korren <bkorren@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Barak Korren <bkorren@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels May 27, 2026
@waynesun09

Copy link
Copy Markdown
Member

HIGH — Missing fixcoder role mapping in per-org dispatch.yml

internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml:235:

code) STAGE_ROLE="coder" ;;

Compare with reusable-dispatch.yml:265 which correctly maps both:

code|fix) STAGE_ROLE="coder" ;;

When a /fix command runs in per-org mode, STAGE_ROLE stays as "fix", which isn't a valid role in config.yaml. If roles are configured, the fix stage is incorrectly skipped with "role 'fix' not in defaults.roles".

(This line isn't in the PR diff — it's existing code that was carried forward without the fix that reusable-dispatch.yml has.)


HIGH — Same ternary kill-switch bug exists in reusable-dispatch.yml:52

stage: ${{ steps.role-check.outputs.skipped == 'true' && '' || steps.route.outputs.stage }}

Same && '' || issue as the per-org dispatch.yml:28 (see inline review comment there). Empty string is falsy in GHA expressions, so the role-check skip never takes effect. This affects per-repo mode as well.

Suggestion:

stage: ${{ steps.role-check.outputs.skipped != 'true' && steps.route.outputs.stage || '' }}

@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 Squad — 10-agent deep-dive on mint/OIDC impact

1 CRITICAL, 3 HIGH, 3 MEDIUM findings (see inline comments + PR comment for items on unchanged lines).

The central issue: permissions: {} at workflow level in the per-org dispatch.yml scaffold blocks id-token: write from propagating to stage jobs, which breaks OIDC token minting in all reusable workflows. The old flow worked because thin callers were independent workflow runs with their own permission grants — this PR inlines them as jobs under dispatch.yml's permissions: {} umbrella.

Additional issues: ternary expression bug defeats the role-check kill switch (both per-org and per-repo), fix role mapping is missing in per-org, and secrets: inherit on prioritize is inconsistent with other stages.

Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml
Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml Outdated
Comment thread internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml Outdated
Comment thread .github/workflows/reusable-fix.yml
Comment thread e2e/admin/admin_test.go Outdated
ifireball added a commit to ifireball/fullsend that referenced this pull request May 28, 2026
Update stale references to thin callers and per-org OIDC minting in
dispatch.yml; address review feedback on PR fullsend-ai#1611.

Signed-off-by: Barak Korren <bkorren@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Barak Korren <bkorren@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 21e5e0f)
@ifireball ifireball force-pushed the feat/adr-41-sync-dispatch branch from 034b8cb to 3df8b3d Compare May 28, 2026 07:10
@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels May 28, 2026
@ifireball

Copy link
Copy Markdown
Member Author

Addressed review feedback from @waynesun09:

  • Caller permissions: stage jobs in per-org dispatch.yml now explicitly set permissions: { contents: read, id-token: write }, so reusable workflows can mint OIDC tokens.
  • Role-check kill switch: fixed the && '' || expression (empty string is falsy) in both per-org dispatch.yml and per-repo reusable-dispatch.yml.
  • Role mapping: per-org dispatch now maps fix stage to coder role (code|fix → coder) for defaults.roles gating.
  • Secrets: removed secrets: inherit from prioritize and pass explicit FULLSEND_GCP_* secrets for consistency.

Commit: 0c831c67

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels May 28, 2026
@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels May 28, 2026
ifireball and others added 2 commits May 31, 2026 12:46
Update architecture.md and agent skills for synchronous workflow_call:
finding-agent-runs and retro-analysis trace runs on the enrolled-repo
shim (not separate .fullsend dispatch runs). Restore code-implementation
API contract checklist dropped during rebase.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Barak Korren <bkorren@redhat.com>
Provision GCP secrets and region at org scope during install and
refresh selected-repository access on admin enable/disable, matching
FULLSEND_MINT_URL visibility for workflow_call dispatch from enrolled repos.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Barak Korren <bkorren@redhat.com>
@ifireball ifireball force-pushed the feat/adr-41-sync-dispatch branch from bb09458 to e44b70f Compare May 31, 2026 09:47
@ifireball

Copy link
Copy Markdown
Member Author

Rebased onto current main and squashed to three commits:

  1. feat(dispatch): synchronous workflow_call event dispatch (ADR 41) — workflows/scaffold/e2e
  2. docs: align skills and architecture with ADR 41 dispatch — architecture + finding-agent-runs / retro-analysis (ralphbean inline feedback)
  3. fix(cli): sync inference org credentials on repo enrollment — org-scoped GCP secrets + enable/disable visibility sync

@ralphbean — addressed the open skill threads (resolved with replies). @waynesun09 — prior CRITICAL/HIGH items remain in the workflow commit.

Head: e44b70f5

@fullsend-ai-review fullsend-ai-review 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.

See the review comment for full details.

@ralphbean

Copy link
Copy Markdown
Member

Ran into something on #1746 that I think is worth surfacing here.

Greg filed that issue and it got labeled ready-to-code along with four other labels (type/bug, component/dispatch, priority/medium, good-fullsend-issue) in quick succession — all within about 6 seconds. Each label fires a separate issues.labeled event, each triggering a workflow run. They all land in the same concurrency group (fullsend-dispatch-1746).

Here's what happened: of the five runs, three were cancelled (queued runs get bumped when a newer one arrives — GitHub only keeps one pending per concurrency group). The two that survived and ran to completion had TRIGGERING_LABEL set to component/dispatch and good-fullsend-issue. Neither of those matches the routing logic, so both exited with "No stage matched — skipping dispatch." The ready-to-code event — the only one that would have routed to code — was in one of the cancelled runs.

Net result: the code agent never ran, even though the issue has ready-to-code on it. You can see the two completed runs here:

With the current async dispatch model, this is annoying but contained — only the ~2 second dispatch job is affected. In the synchronous workflow_call model this PR implements, the concurrency group would span the entire agent run. So if a code agent is 10 minutes into working an issue and someone adds priority/medium, that non-routing event is now competing in the same concurrency group as a long-running agent job. Depending on cancel-in-progress, it either queues behind it or kills it.

#1637 is asking exactly these questions — this is a concrete example of the problem it's pointing at.

@ggallen

ggallen commented Jun 1, 2026

Copy link
Copy Markdown
Member

The issue I opened is #1752.

@ralphbean ralphbean 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.

All the items from my May 29 review are addressed — the skills match the new run topology and the code-implementation checklist is back. LGTM.

@ifireball

Copy link
Copy Markdown
Member Author

@ralphbean

Ran into something on #1746 that I think is worth surfacing here.

Greg filed that issue and it got labeled ready-to-code along with four other labels (type/bug, component/dispatch, priority/medium, good-fullsend-issue) in quick succession — all within about 6 seconds. Each label fires a separate issues.labeled event, each triggering a workflow run. They all land in the same concurrency group (fullsend-dispatch-1746).

Here's what happened: of the five runs, three were cancelled (queued runs get bumped when a newer one arrives — GitHub only keeps one pending per concurrency group). The two that survived and ran to completion had TRIGGERING_LABEL set to component/dispatch and good-fullsend-issue. Neither of those matches the routing logic, so both exited with "No stage matched — skipping dispatch." The ready-to-code event — the only one that would have routed to code — was in one of the cancelled runs.

Net result: the code agent never ran, even though the issue has ready-to-code on it. You can see the two completed runs here:

* https://github.com/fullsend-ai/fullsend/actions/runs/26759986173/job/78870454885

* https://github.com/fullsend-ai/fullsend/actions/runs/26759988154/job/78870462284

With the current async dispatch model, this is annoying but contained — only the ~2 second dispatch job is affected. In the synchronous workflow_call model this PR implements, the concurrency group would span the entire agent run. So if a code agent is 10 minutes into working an issue and someone adds priority/medium, that non-routing event is now competing in the same concurrency group as a long-running agent job. Depending on cancel-in-progress, it either queues behind it or kills it.

#1637 is asking exactly these questions — this is a concrete example of the problem it's pointing at.

I looked into this and found out two things:

  1. I should probably add concurrency settings to the shims - otherwise we will see multiple shim invocations fail on the reusable workflow invocation being cancelled. This is a challenge if we want different settings for different stages, because we don't know the stage in the shim workflow
  2. Depending on what we do about cancel-in-progress - the behaviour might be better in the sync case - if its false first label lauches a flow that keeps on running and and blocks the others while the other labels are being applied - and then another workflow is launched to capture the state after all labels have been applied.

@ifireball

Copy link
Copy Markdown
Member Author

With our current e2e coverage (or lack therof) I cannot merge this in good faith. Moving to draft as I work toward having enough e2e coverage in place.

@rh-hemartin

Copy link
Copy Markdown
Member

With the per-org going away, this can be closed as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants