Skip to content

feat(#3376): differentiate scaffold PR titles for upgrades vs installs - #6003

Merged
maruiz93 merged 3 commits into
mainfrom
agent/3376-scaffold-upgrade-titles
Aug 10, 2026
Merged

feat(#3376): differentiate scaffold PR titles for upgrades vs installs#6003
maruiz93 merged 3 commits into
mainfrom
agent/3376-scaffold-upgrade-titles

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Scaffold PRs previously used "chore: initialize fullsend per-repo installation" for both fresh installs and version upgrades, producing misleading git history and PR titles. This PR adds version-aware metadata generation so upgrade PRs clearly describe the version transition.

Changes

  • New BuildScaffoldPRMetadata helper in internal/repos/scaffold_metadata.go: detects whether the target repo already has fullsend installed (via the guard variable) and extracts the existing version from the workflow file's traceability comment. Returns appropriate commit message, PR title, PR body, and branch name.
  • CommitScaffoldFiles gains a scaffoldBranch parameter in internal/layers/commit.go: callers can now override the default fullsend/scaffold-install branch name. Empty string preserves the existing default.
  • isKnownScaffoldBranch updated to recognize fullsend/bump-* branches, enabling stale PR cleanup when switching between install and upgrade branches.
  • All four scaffold commit closures updated in internal/cli/admin.go and internal/cli/repos.go to use the new helper instead of hardcoded strings.

Validation criteria

  1. Fresh installs retain "initialize" language in title, body, and branch name
  2. Version upgrade PRs use titles like "chore: bump fullsend from v0.25.2 to v0.28.0"
  3. Branch names differentiate: fullsend/scaffold-install vs fullsend/bump-v0.28.0
  4. Old scaffold PRs from a different branch are closed (stale PR cleanup)

Testing

  • Unit tests for BuildScaffoldPRMetadata covering fresh install, upgrade with both versions, upgrade with new version only, upgrade with no versions, guard=false, guard check error
  • Unit tests for detectExistingVersion covering version found, no version, file not found, prerelease
  • Unit tests for CommitScaffoldFiles with custom branch name, empty branch fallback, and upgrade branch closing old install PR
  • isKnownScaffoldBranch test updated for bump branches
  • All existing tests pass with the new scaffoldBranch parameter
  • go vet clean, gofmt clean, secret scan clean

Checklist


Closes #3376

Post-script verification

  • Branch is not main/master (agent/3376-scaffold-upgrade-titles)
  • Secret scan passed (gitleaks — 63cd2688887a6b6f37c980fc876a4a94615d7079..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 7, 2026 13:07
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 7, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:09 PM UTC · Completed 1:27 PM UTC

Commit: ca8438e · View workflow run →

@codecov

codecov Bot commented Aug 7, 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 commented Aug 7, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [stale-doc] docs/contributing/forge-abstraction.md:27 — The list of well-known scaffold branch names mentions fullsend/onboard and fullsend/scaffold-install but is now incomplete. This PR adds fullsend/bump-* as a recognized scaffold branch pattern via isKnownScaffoldBranch. The security guidance about predictable branch names and ownership verification applies equally to bump branches.
    Remediation: Add fullsend/bump-* to the example list of well-known scaffold branch names.
Previous run

Review

Findings

Medium

  • [inaccurate comment] internal/cli/admin.go:1222 — The comment states "applyPerRepoScaffold is only called from the vendor path, which always runs as a fresh install (no upstreamTag available here)." This is factually incorrect: applyPerRepoScaffold is also called from internal/cli/github.go (the runGitHubSetupPerRepo function), which is not the vendor path and can be invoked on repos that already have fullsend installed. The code behavior is correct because BuildScaffoldPRMetadata dynamically checks the guard variable, but the misleading comment could cause a future maintainer to make incorrect assumptions (e.g., removing the guard check thinking it is unnecessary for this call site).
    Remediation: Update the comment to state that no upstreamTag is available in this code path, without claiming it is only called from the vendor path.

Low

  • [redundant API call] internal/cli/admin.go:1047 — In runPerRepoInstall, the guard variable is already fetched at line 708 but BuildScaffoldPRMetadata at line 1047 will re-fetch it because no ScaffoldMetadataOpts are passed. The ScaffoldMetadataOpts.GuardInstalled field was designed to avoid this scenario.
    Remediation: Pass the already-known guard state via ScaffoldMetadataOpts{GuardInstalled: &installed}.

  • [redundant API call] internal/cli/repos.go:807 — In the upgradeCommitFn closure, BuildScaffoldPRMetadata will call GetRepoVariable to check the guard for every repo being upgraded. These repos were already identified as installed during Phase 1, so the guard check is redundant.
    Remediation: Pass ScaffoldMetadataOpts{GuardInstalled: &installed} since repos in the upgrade path are already known to be installed.

  • [commit-convention-violation] PR title — The PR title uses feat(#3376): but per COMMITS.md, feat is reserved for end-user-recognizable new capability. Issue Scaffold PR title template should differentiate version upgrades from initial installation #3376 describes the current behavior as "misleading" (a usability/clarity defect). COMMITS.md guidance says "when in doubt, prefer refactor or chore over feat or fix." Since the issue describes the existing behavior as a defect, fix is the most accurate prefix.
    Remediation: Consider changing the PR title to fix(#3376): differentiate scaffold PR titles for upgrades vs installs.

  • [naming-convention] internal/repos/scaffold_metadata.go:19DefaultScaffoldPRBody is exported but only used within the repos package (in freshInstallMetadata). Unlike DefaultScaffoldBranch and ScaffoldBumpBranchPrefix (consumed cross-package by internal/layers/commit.go), no external package references DefaultScaffoldPRBody.

  • [stale-doc] docs/contributing/forge-abstraction.md:27 — The list of well-known scaffold branch names mentions fullsend/onboard and fullsend/scaffold-install but is now incomplete. This PR adds fullsend/bump-* as a recognized scaffold branch pattern via isKnownScaffoldBranch. The security guidance about predictable branch names and ownership verification applies equally to bump branches.
    Remediation: Add fullsend/bump-* to the example list of well-known scaffold branch names.


Labels: PR modifies scaffold installation metadata generation in internal/repos, internal/layers, and internal/cli

Previous run (2)

Review

Findings

Medium

  • [commit-convention-violation] PR title — The PR title uses feat(#3376): but per COMMITS.md, feat is reserved for changes an end user would recognize as new capability (a new CLI command, a new behavior they interact with, a new integration). This PR corrects misleading scaffold PR metadata — titles, bodies, and branch names — rather than adding new user-facing capability. Per COMMITS.md: "When in doubt, prefer refactor or chore over feat or fix." Since the issue describes the current behavior as misleading (a defect in how metadata is generated), fix(#3376): is the most accurate prefix.
    Remediation: Change the PR title to fix(#3376): differentiate scaffold PR titles for upgrades vs installs.

Low

  • [error-handling-idiom] internal/repos/scaffold_metadata.go:46BuildScaffoldPRMetadata silently swallows errors from GetRepoVariable (line 48: if err != nil || !guardExists || guardVal != "true") and detectExistingVersion silently swallows GetFileContent errors, treating failures as "fresh install" / "no version detected." While this is a reasonable design choice for cosmetic metadata generation (fail-open for non-critical data), it diverges from the error-propagation pattern used elsewhere in internal/repos. A brief code comment explaining the intentional error-swallowing would improve maintainability.

  • [stale-doc] docs/contributing/forge-abstraction.md:27 — The list of well-known scaffold branch names ("e.g., fullsend/onboard, fullsend/scaffold-install") is now incomplete. This PR adds fullsend/bump-* as a recognized scaffold branch pattern via isKnownScaffoldBranch's new strings.HasPrefix(branch, "fullsend/bump-") check. The security guidance about predictable branch names and ownership verification applies equally to bump branches.
    Remediation: Add fullsend/bump-* to the example list of well-known scaffold branch names.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 7, 2026
Comment thread internal/layers/commit.go Outdated
Comment thread internal/layers/commit.go Outdated
Comment thread internal/repos/scaffold_metadata.go Outdated
Comment thread internal/repos/scaffold_metadata.go
Comment thread internal/repos/scaffold_metadata.go Outdated
@maruiz93

Copy link
Copy Markdown
Contributor

/fs-fix Rebase the branch onto main, then address the review findings:

  1. Consolidate duplicate constantsdefaultScaffoldBranch in internal/layers/commit.go duplicates repos.DefaultScaffoldBranch. Import and use repos.DefaultScaffoldBranch in commit.go instead of maintaining a separate constant. Also extract the "fullsend/bump-" prefix used in isKnownScaffoldBranch into a shared constant from repos rather than a raw string literal.

  2. Reduce CommitScaffoldFiles parameter count — Accept repos.ScaffoldPRMetadata as a single parameter instead of destructuring it into 4 separate positional string args (commitMsg, prTitle, prBody, scaffoldBranch) at every call site. This also moves scaffoldBranch next to the related branch parameters.

  3. Accept optional pre-fetched guard state in BuildScaffoldPRMetadata — In batch flows the guard variable is already known. Add an optional parameter (e.g., guardInstalled *bool) so batch callers can skip the redundant GetRepoVariable API call.

  4. Accept optional pre-fetched old version in BuildScaffoldPRMetadata — In the upgrade flow, upgradeRepo already reads the workflow file. Add an optional oldVersion parameter so the upgrade callback can pass it through and skip the duplicate GetFileContent call.

  5. Fix regex prerelease character class — Add hyphens to versionCommentPattern: change [a-zA-Z0-9.] to [a-zA-Z0-9.-]. Add a test case for v1.0.0-alpha-1 to cover hyphenated prerelease tags.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 9:12 AM UTC · Completed 9:26 AM UTC

Commit: ca8438e · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 10, 2026
- Consolidate duplicate constants: remove defaultScaffoldBranch from
  commit.go in favor of repos.DefaultScaffoldBranch; export
  ScaffoldBumpBranchPrefix and use it in isKnownScaffoldBranch.
- Reduce CommitScaffoldFiles parameter count: accept
  repos.ScaffoldPRMetadata as a single parameter instead of 4 separate
  positional string args (commitMsg, prTitle, prBody, scaffoldBranch).
- Accept optional pre-fetched guard state in BuildScaffoldPRMetadata via
  ScaffoldMetadataOpts.GuardInstalled so batch callers can skip the
  redundant GetRepoVariable API call.
- Accept optional pre-fetched old version in BuildScaffoldPRMetadata via
  ScaffoldMetadataOpts.OldVersion so upgrade callers can skip the
  duplicate GetFileContent call.
- Fix regex prerelease character class: add hyphens to
  versionCommentPattern ([a-zA-Z0-9.] → [a-zA-Z0-9.-]); add test for
  v1.0.0-alpha-1.

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed all 5 review findings. Rebased branch onto main. Consolidated duplicate constants between layers and repos packages. Reduced CommitScaffoldFiles from 4 positional string args to a single ScaffoldPRMetadata struct. Added optional pre-fetched guard state and old version to BuildScaffoldPRMetadata via variadic ScaffoldMetadataOpts. Fixed regex to match hyphenated prerelease versions. All existing and new tests pass.

Fixed (5):

  1. Consolidate duplicate constants (internal/layers/commit.go): Removed defaultScaffoldBranch constant from commit.go; imported and used repos.DefaultScaffoldBranch instead. Exported ScaffoldBumpBranchPrefix from repos package and used it in isKnownScaffoldBranch to replace the raw string literal.
  2. Reduce CommitScaffoldFiles parameter count (internal/layers/commit.go): Changed CommitScaffoldFiles to accept repos.ScaffoldPRMetadata as a single parameter instead of 4 separate positional string args (commitMsg, prTitle, prBody, scaffoldBranch). Updated all call sites in admin.go, repos.go, workflows.go, and all test calls in commit_test.go. Added testMeta helper in tests to reduce boilerplate.
  3. Accept optional pre-fetched guard state in BuildScaffoldPRMetadata (internal/repos/scaffold_metadata.go): Added ScaffoldMetadataOpts struct with GuardInstalled *bool field. BuildScaffoldPRMetadata now accepts variadic ScaffoldMetadataOpts; when GuardInstalled is non-nil, the GetRepoVariable API call is skipped. Added 4 new tests covering pre-fetched guard installed/not-installed, pre-fetched old version, and both together.
  4. Accept optional pre-fetched old version in BuildScaffoldPRMetadata (internal/repos/scaffold_metadata.go): Added OldVersion string field to ScaffoldMetadataOpts. When non-empty, BuildScaffoldPRMetadata skips the detectExistingVersion call (GetFileContent API). Tested via TestBuildScaffoldPRMetadata_PreFetchedOldVersion and TestBuildScaffoldPRMetadata_PreFetchedBothGuardAndVersion.
  5. Fix regex prerelease character class (internal/repos/scaffold_metadata.go): Changed versionCommentPattern character class from [a-zA-Z0-9.] to [a-zA-Z0-9.-] to support hyphenated prerelease tags like v1.0.0-alpha-1. Added test case for v1.0.0-alpha-1 in TestDetectExistingVersion.

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-coder
fullsend-ai-coder Bot force-pushed the agent/3376-scaffold-upgrade-titles branch from ca8438e to d4d9854 Compare August 10, 2026 09:26
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:27 AM UTC · Completed 9:45 AM UTC

Commit: d4d9854 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the component/install CLI install and app setup label Aug 10, 2026
@maruiz93

Copy link
Copy Markdown
Contributor

/fs-fix Rebase onto main, then address the review agent's inline comments.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:17 AM UTC · Completed 10:27 AM UTC

Commit: d4d9854 · View workflow run →

Scaffold PRs previously used "chore: initialize fullsend per-repo
installation" for both fresh installs and version upgrades, creating
misleading git history. Now the scaffold commit function detects
whether the target repo already has fullsend installed (via the
guard variable) and formats PR metadata accordingly:

- Fresh installs retain "initialize" language in title and body
- Upgrades use "chore: bump fullsend from vX to vY" with version
  info extracted from the existing workflow file
- Branch names differentiate: fullsend/scaffold-install for installs,
  fullsend/bump-vX.Y.Z for upgrades

New BuildScaffoldPRMetadata helper in internal/repos centralizes
the detection and metadata generation, replacing hardcoded strings
in four scaffold commit closures across admin.go and repos.go.

CommitScaffoldFiles now accepts a scaffoldBranch parameter so
callers can override the default branch name.
isKnownScaffoldBranch recognizes fullsend/bump-* branches for
stale PR cleanup.

Closes #3376
- Consolidate duplicate constants: remove defaultScaffoldBranch from
  commit.go in favor of repos.DefaultScaffoldBranch; export
  ScaffoldBumpBranchPrefix and use it in isKnownScaffoldBranch.
- Reduce CommitScaffoldFiles parameter count: accept
  repos.ScaffoldPRMetadata as a single parameter instead of 4 separate
  positional string args (commitMsg, prTitle, prBody, scaffoldBranch).
- Accept optional pre-fetched guard state in BuildScaffoldPRMetadata via
  ScaffoldMetadataOpts.GuardInstalled so batch callers can skip the
  redundant GetRepoVariable API call.
- Accept optional pre-fetched old version in BuildScaffoldPRMetadata via
  ScaffoldMetadataOpts.OldVersion so upgrade callers can skip the
  duplicate GetFileContent call.
- Fix regex prerelease character class: add hyphens to
  versionCommentPattern ([a-zA-Z0-9.] → [a-zA-Z0-9.-]); add test for
  v1.0.0-alpha-1.

Addresses review feedback on #6003
- Unexport DefaultScaffoldPRBody → defaultScaffoldPRBody (only used
  within the repos package)
- Pass pre-fetched guard state via ScaffoldMetadataOpts in
  runPerRepoInstall to skip redundant GetRepoVariable API call
- Pass pre-fetched guard state in repos.go upgrade closure (repos
  in the upgrade path are already known to be installed)
- Fix inaccurate comment in applyPerRepoScaffold claiming it is only
  called from the vendor path (also called from github.go)

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Addressed 8 of 9 review findings (5 were already fixed in iteration 1, 3 new fixes applied). Disagreed with 1 finding (threading oldVersion through the commit callback requires signature changes beyond this PR's scope). Rebased onto origin/main as requested.

Fixed (8):

  1. defaultScaffoldBranch duplicates repos.DefaultScaffoldBranch (internal/layers/commit.go): Already addressed in iteration 1 — commit.go already uses repos.DefaultScaffoldBranch and repos.ScaffoldBumpBranchPrefix
  2. 13 positional parameters — accept repos.ScaffoldPRMetadata directly (internal/layers/commit.go): Already addressed in iteration 1 — CommitScaffoldFiles now accepts repos.ScaffoldPRMetadata struct
  3. redundant guard-variable API call in batch flows (internal/cli/admin.go): Pass pre-fetched guard state via ScaffoldMetadataOpts{GuardInstalled: &guardInstalled} in runPerRepoInstall, reusing the guard already fetched at line 705
  4. redundant guard-variable API call in upgrade closure (internal/cli/repos.go): Pass ScaffoldMetadataOpts{GuardInstalled: &guardInstalled} with guardInstalled=true since repos in the upgrade path are already known to be installed
  5. prerelease regex excludes hyphens (internal/repos/scaffold_metadata.go): Already addressed in iteration 1 — regex uses [a-zA-Z0-9.-] which includes hyphens, and test for v1.0.0-alpha-1 was added
  6. inaccurate comment in applyPerRepoScaffold (internal/cli/admin.go): Updated comment to remove incorrect claim that applyPerRepoScaffold is only called from the vendor path. New comment accurately states that no upstreamTag is available in this code path.
  7. redundant API call in runPerRepoInstall (internal/cli/admin.go): Same fix as the batch flows finding — pass pre-fetched guard state via ScaffoldMetadataOpts
  8. DefaultScaffoldPRBody exported but only used within package (internal/repos/scaffold_metadata.go): Unexported DefaultScaffoldPRBody to defaultScaffoldPRBody since it is only referenced within the repos package

Disagreed (1):

  1. detectExistingVersion re-reads workflow file redundantly in upgrade flow: The old version is not available in the upgrade closure's scope (repos.go upgradeCommitFn). Threading it through would require changing the commit callback signature, which is a larger refactor beyond this PR's scope. The ScaffoldMetadataOpts.OldVersion API is ready for callers that do have the version available.

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-coder
fullsend-ai-coder Bot force-pushed the agent/3376-scaffold-upgrade-titles branch from d4d9854 to ee574b3 Compare August 10, 2026 10:27
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:29 AM UTC · Completed 11:02 AM UTC

Commit: ee574b3 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Aug 10, 2026
@maruiz93
maruiz93 added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 2245610 Aug 10, 2026
34 of 41 checks passed
@maruiz93
maruiz93 deleted the agent/3376-scaffold-upgrade-titles branch August 10, 2026 11:41
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ❌ Failure · Started 11:43 AM UTC · Completed 12:00 PM UTC

Commit: ee574b3 · View workflow run →

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

Labels

component/install CLI install and app setup ready-for-merge All reviewers approved — ready to merge ready-for-review Agent PR ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scaffold PR title template should differentiate version upgrades from initial installation

1 participant