feat: reusable workflows (ADR 31), centralized routing (ADR 34), layered content (ADR 35) - #792
Conversation
|
fullsend review is working on this — view logs |
Site previewPreview: https://20965d3a-site.fullsend-ai.workers.dev Commit: |
|
fullsend review is working on this — view logs |
ca21f4f to
d3e9134
Compare
|
fullsend review is working on this — view logs |
Review: #792Head SHA: 1cabeda SummaryThis PR implements a major architectural change spanning ADRs 31, 34, and 35 — converting scaffold workflows from full implementations to thin callers that delegate to reusable workflows, centralizing event routing in dispatch.yml, and resolving upstream defaults via sparse checkout at runtime. The security posture is strong: all FindingsCriticalNone. HighNone. Medium
Low
Info
FooterOutcome: comment-only Previous runReview: #792Head SHA: af0bf5b SummaryThis PR implements three ADRs (31, 34, 35) converting the fullsend agent pipeline from full scaffold copies to thin callers delegating to upstream reusable workflows, centralizing event routing in dispatch.yml, and introducing layered content resolution. The security posture has improved: fork PR detection now fails closed (was fail-open), enrollment validation is inlined to prevent override via customized scripts, and routing logic is centralized in the FindingsMedium
Low
Info
FooterOutcome: comment-only Previous run (2)Review: #792Head SHA: b1dbc1d SummaryThis PR implements three coordinated ADRs (31, 34, 35) to restructure the fullsend agent pipeline from full-scaffold deployment to a reusable-workflow architecture with runtime layering. The security model is sound: dispatch routing correctly uses FindingsMedium
Low
Info
FooterOutcome: approve
Previous run (3)Review: #792Head SHA: 2a336a9 SummaryThis is a large, well-structured PR implementing three ADRs (31, 34, 35) that fundamentally reshapes the agent pipeline architecture. The security-critical changes — OIDC mint validation expansion, centralized dispatch routing, fork PR blocking, and expression injection prevention — are all handled correctly. The transition from per-stage shim jobs to centralized routing in dispatch.yml is sound, with proper fail-closed behavior and defense-in-depth (fork checks at both dispatch and reusable workflow layers). The layered content resolution design cleanly separates upstream defaults from org customizations. A few non-blocking observations are noted below. FindingsMedium
Low
Info
FooterOutcome: comment-only Previous run (4)Review: #792Head SHA: c9cc35c SummaryThis is a large, well-structured PR implementing three ADRs (31, 34, 35) to move from full-copy scaffold workflows to thin callers backed by upstream reusable workflows. The architectural direction is sound and the security posture is generally strong — inputs are routed through FindingsHigh
Medium
Low
Info
FooterOutcome: request-changes Previous run (5)Review: #792Head SHA: 264dfa5 SummaryThis PR implements three ADRs (31, 34, 35) that restructure the fullsend agent pipeline from duplicated full workflows into thin callers backed by upstream reusable workflows, centralized event routing in dispatch.yml, and runtime content layering. The architecture is sound and the security-critical changes — mint OIDC validation widening, expression injection prevention via env blocks, fork PR defense-in-depth — are well-executed. No critical or high findings. Several medium and info-level observations regarding spec/test gaps and maintenance burden are noted below. FindingsMedium
Low
Info
FooterOutcome: comment-only Previous run (6)Review: automated reviewOutcome: failure The review agent reviewed commit Previous run (7)Review: #792Head SHA: 6d611f5 SummaryThis PR implements three ADRs (31, 34, 35) to convert agent workflows into thin callers delegating to upstream reusable workflows, centralize event routing in dispatch.yml, and implement layered content resolution. The architecture is sound and the security model is well-considered — expression injection prevention via FindingsHigh
Medium
Low
Info
PR-Specific ChecksPR body injection defense: No prompt injection patterns or non-rendering Unicode detected in the PR description, commit messages, or body text. The PR body contains only legitimate technical documentation. Scope authorization: No linked issue. The PR implements three previously accepted/proposed ADRs (31, 34, 35). The scope is large but coherent — all changes serve the reusable-workflows architecture. The FooterOutcome: request-changes Previous run (8)Review: #792Head SHA: ea67fbf SummaryThis is a well-structured PR implementing three ADRs (30/31, 33/34, 34/35) that converts agent workflows to thin callers delegating to upstream reusable workflows, centralizes event routing in dispatch.yml, and introduces layered content resolution. The security posture is preserved or improved: expression injection is mitigated by using env blocks in dispatch routing, validate-enrollment is correctly inlined to prevent script override attacks, fork PR detection fails closed, and the mint OIDC validation properly extends to accept the upstream fullsend-ai/fullsend/ workflow ref prefix with existing .github/workflows/ path constraints. Two medium findings relate to a tag version inconsistency and a concurrency behavior change that should be verified before merge. FindingsMedium
Low
Info
FooterOutcome: comment-only Previous run (9)Review: #792Head SHA: 72db671 SummaryThis PR implements three ADRs (30/31, 33/34, 34/35) to move from full-copy scaffold workflows to thin callers backed by upstream reusable workflows, centralize event routing in dispatch.yml, and add runtime layered content resolution. The architectural direction is sound — scaffold shrinks from ~82 to ~24 files, infrastructure patches ship once upstream, and expression injection risks are eliminated by moving routing logic from GHA expressions to shell FindingsHigh
Medium
Low
Info
FooterOutcome: request-changes Previous run (10)Review: #792Head SHA: 896eb0e SummaryThis PR implements three coordinated architectural changes: reusable workflows (ADR 31), centralized event routing, and layered content resolution (ADR 35). The overall design is sound — the security model is preserved through defense-in-depth (fork PR blocking at both dispatch and reusable workflow layers, OIDC validation expanded to accept upstream workflow refs with the existing FindingsMedium
Low
Info
FooterOutcome: comment-only Previous run (11)Review: #792Head SHA: 48c6d6d SummaryThis PR implements three ADRs (30/33/34 — titled 31/34/35 in the PR) to move from full-copy scaffold workflows to thin callers backed by reusable workflows, centralized event routing in dispatch.yml, and layered content resolution via runtime sparse-checkout. The architecture is sound, security controls are well-designed (expression-injection-safe routing, inlined enrollment validation, defense-in-depth fork-PR blocking, OIDC mint expansion with proper tests), and tests are comprehensive. However, all reusable workflows and scaffold workflows sparse-checkout upstream defaults at FindingsHigh
Medium
Low
Info
FooterOutcome: request-changes |
|
fullsend fix is working on this — view logs |
🔧 Fix agent — iteration 1 (bot-triggered)Addressed 3 of 6 review findings with code fixes (build verification, reopened PR routing, jsonschema pinning). Disagreed with 3 (OpenShell SHA pin needs external lookup, source_repo validation and cancel-in-progress documentation are info-level). Fixed (3):
Disagreed (3):
Tests: passed Decision points
Updated by fullsend fix agent |
- Add `reopened` to pull_request_target routing in dispatch.yml so reopened PRs trigger review (was a behavioral regression vs old shim) - Pin jsonschema to exact version (4.23.0) instead of open range - Update scaffold test assertion to match new routing pattern Addresses review feedback on #792 Signed-off-by: fullsend-fix <fullsend-code@users.noreply.github.com>
|
fullsend review is working on this — view logs |
55e4295 to
10b9c9d
Compare
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
825a1da to
5118846
Compare
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
|
fullsend review is working on this — view logs |
…SC2001 Signed-off-by: Wayne Sun <gsun@redhat.com>
|
fullsend review is working on this — view logs |
- dispatch.yml: fail-closed when fork status cannot be determined - ADR 35: add env/ to all three customized/layered dir lists, fix gitkeep count - prioritize.yml, repo-maintenance.yml: cp -rP → cp -r for consistency - e2e verifyInstalled: update expected file list to match ADR 35 layered content Signed-off-by: Wayne Sun <gsun@redhat.com>
|
fullsend review is working on this — view logs |
- ADR 31: update to reference ADR 34 centralized routing (dispatch.yml is no longer unchanged) - Remove docs/normative/ — v1 admin-install specs are superseded by ADR 35 layered content resolution and no longer reflect the installed file set Signed-off-by: Wayne Sun <gsun@redhat.com>
|
fullsend review is working on this — view logs |
Update ADRs 0011, 0012, 0013, 0014, and 0024 to remove references to docs/normative/ SPEC.md files that were deleted in the previous commit. Point readers to the Go implementation and test suite instead. Signed-off-by: Wayne Sun <gsun@redhat.com>
|
fullsend review is working on this — view logs |
ralphbean
left a comment
There was a problem hiding this comment.
Review Summary
This is a well-motivated architectural improvement implementing three ADRs (31, 34, 35) that dramatically reduces per-org maintenance burden. The security posture is strong — expression injection avoidance, explicit secret passing, fork PR blocking, and OIDC validation are all correctly implemented.
Since our last review pass (against b1dbc1d), three issues have been fixed:
- ✅ SPEC.md for ADR 0012 removed (was normatively incorrect)
- ✅ Fork PR
|| truechecks now fail closed with explicit empty-string guards - ✅ e2e
verifyInstalledupdated correctly for ADR 35
4 items require changes:
- Mint token validation fails open when
ALLOWED_WORKFLOW_FILESis unset — should fail closed - Retro agent role escalated from
retrotofullsendwithout justification - ADR 0035 miscategorizes
env/as org-only when it's actually a layered directory - Test missing
customized/env/.gitkeepassertion
4 items noted for follow-up:
- Duplicated "Prepare workspace" step across 5 reusable workflows (extract to composite action)
- No test verifying LAYERED_DIRS consistency between Go and YAML
post-run-linkfeedback removed (restore insidefullsend runwith correct app identity)- No automated tests for dispatch routing logic (~100 lines of bash)
| sparse-checkout: | | ||
| internal/scaffold/fullsend-repo/ | ||
|
|
||
| - name: Prepare workspace (upstream defaults + org overrides) |
There was a problem hiding this comment.
[moderate] (noted, deferred) This "Prepare workspace" step is byte-for-byte identical across all 5 reusable workflows (reusable-code, reusable-fix, reusable-review, reusable-triage, reusable-retro), with a slightly different variant in prioritize.yml. If the layering logic or LAYERED_DIRS changes, all 6 must be updated in lockstep. Consider extracting to a composite action (.github/actions/prepare-workspace/action.yml) consistent with the existing mint-token, setup-gcp, and validate-enrollment actions.
| // layeredDirs contain upstream defaults provided at runtime via reusable | ||
| // workflow workspace preparation. The scaffold does not install these — | ||
| // orgs add overrides in customized/<dir>/ instead. See ADR 0035. | ||
| var layeredDirs = []string{ |
There was a problem hiding this comment.
[moderate] (noted, deferred) The layeredDirs list here must stay in sync with LAYERED_DIRS in all 5 reusable workflows. Currently both list agents skills schemas harness policies scripts env, but there's no automated test to catch drift. Consider adding a test that parses LAYERED_DIRS from a reusable workflow YAML and compares it against this Go slice.
|
|
||
| jobs: | ||
| dispatch-triage: | ||
| dispatch: |
There was a problem hiding this comment.
[moderate] (noted, deferred) The old shim's post-run-link job ("fullsend X is working on this — view logs") is removed. This is user-visible — users will no longer get feedback that an agent was dispatched. Plan is to restore this from inside fullsend run so it uses the correct app identity token (not github-actions[bot]) and can provide a better link.
| contents: read | ||
| id-token: write | ||
| steps: | ||
| - name: Determine stage |
There was a problem hiding this comment.
[moderate] (noted, deferred) The routing logic here (~100 lines of bash: command parsing, label matching, author association, bot filtering, fork detection, auto-triage) has no automated test coverage. Any regression silently breaks all dispatch. The old shim used GHA if: expressions which were at least visible in the workflow graph. Consider adding a shell-script test that exercises this routing with various event/input combinations.
…/test gaps - Make ALLOWED_WORKFLOW_FILES fail closed: unset/empty denies all requests; set to "*" to allow any workflow file - Add test for unset ALLOWED_WORKFLOW_FILES scenario - Revert retro workflow from role: fullsend to role: retro (read-only + issues:write is sufficient for retrospective agent) - Fix ADR 0035: move env/ from Org-only to Upstream defaults category, list all 7 subdirs in Org overrides parenthetical - Add missing customized/env/.gitkeep assertion in scaffold test Signed-off-by: Wayne Sun <gsun@redhat.com>
|
fullsend review is working on this — view logs |
ralphbean
left a comment
There was a problem hiding this comment.
Review Summary
All 4 required changes from the previous review have been addressed in 1cabedaf:
- ✅
ALLOWED_WORKFLOW_FILESnow fails closed (unset/empty denies all) - ✅ Retro role reverted from
fullsendtoretro - ✅ ADR 0035
env/moved from Org-only to Upstream defaults - ✅
customized/env/.gitkeepassertion added to scaffold test
The security posture is strong: expression injection avoidance via env: blocks, explicit secret passing (not secrets: inherit), fork PR fail-closed detection, persist-credentials: false on all target-repo checkouts in reusable workflows, and OIDC job_workflow_ref validation with a fail-closed allowlist.
2 items noted for follow-up (not blocking):
dispatch.ymlcheckout missingpersist-credentials: falseon minted token checkout- e2e
verifyInstalledmissingretro.ymlandAGENTS.mdassertions
|
|
||
| - name: Checkout repository | ||
| if: steps.route.outputs.stage != '' | ||
| uses: actions/checkout@v6 |
There was a problem hiding this comment.
[moderate] (noted, deferred) This checkout uses the minted OIDC token but doesn't set persist-credentials: false. The token stays in the git credential helper for the rest of the job. While dispatch.yml only runs trusted shell scripts (not LLM-driven), the reusable workflows correctly set persist-credentials: false on all their target-repo checkouts. For consistency and defense-in-depth, consider adding persist-credentials: false here too.
| "scripts/process-fix-result.py", | ||
| ".github/workflows/dispatch.yml", | ||
| ".github/workflows/repo-maintenance.yml", | ||
| ".github/workflows/prioritize.yml", |
There was a problem hiding this comment.
[moderate] (noted, deferred) This list is missing .github/workflows/retro.yml and AGENTS.md — both are in the scaffold, not in any layered or upstream-only directory, so WalkFullsendRepo will install them. Without assertions here, regressions on those files won't be caught by e2e tests.
Implementation of ADR 31 (reusable workflows), ADR 34 (centralized routing), and ADR 35 (layered content resolution) for the fullsend agent pipeline.
Summary
ADR 31 — Reusable workflows for action-installed distribution
workflow_call) and a root composite action fromfullsend-ai/fullsend, enabling org.fullsendrepos to delegate agent pipeline logic upstream instead of maintaining full scaffold copiesworkflow_call+ explicitsecrets:passthroughmint-token,validate-enrollment,setup-gcp) to.github/actions/for use by reusable workflowsaction.yml(was duplicated at.github/actions/fullsend/and scaffold copy)WorkflowsLayersince thin callers no longer contain CLI download steps — version is now controlled byfullsend_versioninput to reusable workflowsADR 34 — Centralized event routing via dispatch.yml
dispatch.ymlin the.fullsendrepodispatchforwarder viaworkflow_calland astop-fixhandlercaselogic withenv:blocks (no expression injection)fullsend admin installADR 35 — Layered content resolution
.fullsendrepos by the scaffoldfullsend-ai/fullsend@v0at runtime and layer org overrides fromcustomized/on topfullsend-ai/fullsend/.github/actions/*@v0), not installed into.fullsendcustomized/gitkeeps, thin callers, config, env files)New files (repo root, published upstream)
action.yml.github/actions/fullsend/.github/actions/mint-token/action.yml.github/actions/validate-enrollment/action.yml.github/actions/setup-gcp/action.yml.github/workflows/reusable-{triage,code,review,fix,retro}.ymldocs/ADRs/0035-layered-content-resolution.mdModified files
internal/scaffold/.../workflows/{triage,code,review,fix,retro}.ymlinternal/scaffold/.../workflows/dispatch.ymlevent_actioninputinternal/scaffold/.../workflows/{prioritize,prioritize-scheduler,repo-maintenance}.ymlfullsend-ai/fullsend/.github/actions/*@v0) and workspace layering for scriptsinternal/scaffold/.../templates/shim-workflow-call.yamlinternal/scaffold/.../customized/{agents,skills,schemas,harness,policies,scripts,env}/.gitkeepinternal/scaffold/scaffold.go,scaffold_test.gointernal/layers/workflows.gointernal/cli/admin.goNewWorkflowsLayercall sitesinternal/mint/main.go,main_test.gofullsend-ai/fullsend/prefix injob_workflow_reffor cross-orgworkflow_callDeleted files
.github/actions/fullsend/action.ymlaction.ymlinternal/scaffold/fullsend-repo/.github/actions/fullsend/action.ymlArchitecture
ADR 31 properties:
vars.*mapped to explicitworkflow_callinputs (vars don't cross the boundary)secrets:blocks (notsecrets: inherit— doesn't work cross-org)workflow_callnesting (limit is 4)job_workflow_refreports the reusable workflow's repo — mint accepts both{org}/.fullsend/andfullsend-ai/fullsend/prefixesADR 34 routing:
event_action(notstage) to dispatch.ymlgithub.event.*context viaenv:blocks — no expression injection/fixand/retrocommandsretroandprioritizemap tofullsendApp role (no dedicated PEM)ADR 35 layering:
fullsend-ai/fullsend@v0at runtime for upstream defaultscustomized/are layered on top.fullsendTest plan
go test ./internal/...— all 19 packages passgo vet ./internal/...— cleanfullsend admin installagainst nonflux org — 5 apps reused, 25 scaffold files written (down from 77), mint health check passed, enrollment completed (2 repos)customized/override directories all present with.gitkeep--force-mint-deployrequired when upgrading from PR feat: OIDC token mint dispatch — deprecate PAT, migrate to OIDC #503 — the mint'sjob_workflow_refvalidation must acceptfullsend-ai/fullsend/prefix for cross-orgworkflow_call. Without redeployment, reusable workflows get 403 from the old mint.reusable-triage.yml@v0→ sparse-checkout upstream defaults → mint-token action → GCP auth → agent setup — all green. Agent run fails due to openshell gateway incompatibility (Investigate standalone openshell-gateway with Docker/Podman drivers across target environments #780), unrelated to this PR.gcp_auth_mode,FULLSEND_GCP_WIF_SA_EMAIL,FULLSEND_GCP_SA_KEY_JSON)fullsend-ai/.fullsendworkflows with thin callers, trigger dispatch chainIssues found and fixed during testing
/healthendpoint (GCF routing) — CLI polled/healthzwhich GCF Functions Framework v1.9.0 doesn't route to the handler. Fixed: changed to/healthingcf.go.read;workflow_callcaps the called workflow's permissions to the caller's top-levelpermissions:block. Without explicit permissions, reusable workflows gotnonefor write scopes. Fixed: addedpermissions:blocks to all thin caller templates.secrets: inheritdoesn't work cross-org —nonflux/.fullsendcallingfullsend-ai/fullsendis cross-org;secrets: inheritonly works within the same org/enterprise. Fixed: reusable workflows declare explicitsecrets:inputs; thin callers pass each secret individually.uses: ./resolves to caller's checkout — inworkflow_call,uses: ./resolves to the workspace root (caller's.fullsendcheckout), not the reusable workflow's repo. Fixed with upstream action refs (fullsend-ai/fullsend/.github/actions/*@v0).job_workflow_refOIDC validation — mint validatedjob_workflow_refstarts with{org}/.fullsend/, but cross-repoworkflow_callreportsfullsend-ai/fullsend/.github/workflows/reusable-*.yml@.... Fixed: updated mint to also acceptfullsend-ai/fullsend/prefix.nonflux-coder/nonflux-reviewApps created withissues: read, but mint requestsissues: write. Fixed: upgraded manifests intypes.go(33920c8). Existing Apps need manual permission update.fullsendcomposite action not at repo root — reusable workflows referencefullsend-ai/fullsend@v0, consolidated the action to rootaction.yml(removed duplicates at.github/actions/fullsend/and scaffold copy).repo-maintenance.yml,prioritize.yml,prioritize-scheduler.ymlused./.github/actions/mint-token(local ref), but ADR 35 no longer installs composite actions into.fullsend. Fixed: updated to upstream action refs and added workspace layering for scripts.dispatch.ymlmaps stage→role but only handledcode → coder.retroandprioritizeshare thefullsendApp. Fixed: addedretro|prioritize → fullsendmapping and updatedreusable-retro.ymlto mint withrole: fullsend.--skip-mint-deploy— upgrading from PR feat: OIDC token mint dispatch — deprecate PAT, migrate to OIDC #503 to PR feat: reusable workflows (ADR 31), centralized routing (ADR 34), layered content (ADR 35) #792 with--skip-mint-deployleaves the old mint code deployed, which rejectsfullsend-ai/fullsend/injob_workflow_ref. Must use--force-mint-deployon first install after this PR.gcp_auth_mode,FULLSEND_GCP_WIF_SA_EMAIL,FULLSEND_GCP_SA_KEY_JSON. Fixed: aligned all reusable workflows and setup-gcp with WIF-only auth.