GitHub Actions doesn't currently support prefilling workflow_dispatch
inputs via URL query params (community/community#51159), so the
"override via workflow_dispatch with this commit SHA" instruction in
the no-backport comment required the reader to go look up the SHA
themselves. Paste the full 40-char SHA into the comment in a fenced
code block so it's one click to copy into the "Commit SHA" input on
the workflow run page.
Major-version refs like `@v2`/`@v5` resolve to mutable refs on the
upstream repos — sometimes a tag, sometimes a branch (e.g. marocchino
keeps `v1`/`v2`/`v3` as branches), and dawidd6 force-pushes the bare
`v6` tag forward outside of releases. A compromised maintainer account
could push new code that our CI picks up on the next run with
GITHUB_TOKEN (or, for changesets/action, NPM_TOKEN) in hand.
Pin all third-party `uses:` references to full commit SHAs with a
trailing version comment so the upstream release is still visible to
reviewers. Dependabot/Renovate can keep these fresh going forward.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(backport): mark not-maintained-on-stable list as exhaustive and require git verification
The backport workflow's AI decision prompt only listed `docs/` (outside
`docs/content/`) and `skills/` as paths not maintained on `stable`, but
didn't flag that list as exhaustive. On at least one commit, the AI
generalized the pattern and incorrectly claimed `tarballs/` was also
not maintained on `stable` (it is) — likely because the commit subject
mentioned "preview tarball" and the changed files included a docs
preview smoke check workflow.
Tighten the prompt to:
- explicitly mark the not-maintained list as exhaustive;
- instruct the AI to run `git ls-tree origin/stable -- <path>` to
verify any other path it wants to cite as main-only;
- warn it not to infer main-only-ness from suggestive names like
"docs", "preview", "tarball", or "workflow".
* ci(backport): link to the backport job run in posted comments and PR body
Each of the comments the backport workflow posts (no-backport,
backport-created, conflict-failure) and the body of the backport PR
it opens now includes a link to the GitHub Actions run that produced
the decision. Makes it easy to jump from the comment straight to the
opencode output, prompt, and AI reasoning when the AI gets a call wrong.
Drops the label-based backport override in favor of workflow_dispatch.
The pull_request_target trigger has security concerns (it runs with
write permissions on PR-controlled events), and we already have a
manual dispatch path that covers the same use case.
Large backport prompts (commit message + diff capped at 200KB) can
exceed Linux's `ARG_MAX` and cause `opencode run` to fail with
"Argument list too long" (exit 126). Redirect the prompt files into
`opencode run`'s stdin instead of passing them on the command line.
* Hoist AI model env, fix opencode external_directory permission, fail loud on AI infra errors
Three related fixes triggered by the failed run on #1935:
1. Hoist the AI model name to a top-level `AI_MODEL` env var
(`anthropic/claude-opus-4.7`); both `opencode run` invocations
now interpolate `vercel/${AI_MODEL}` so the model is specified in
exactly one place.
2. Switch `OPENCODE_PERMISSION` from the bare-string shortcut
`"allow"` to the explicit object form
`{"*":"allow","external_directory":"allow"}`. The shortcut
was observed not to override `external_directory` (which defaults to
"ask" and auto-rejects in non-interactive `opencode run`),
causing the conflict-resolution AI to fail when reading scratch files
it created under `/tmp/`.
3. The `Resolve conflicts with opencode` step no longer uses
`continue-on-error`, and now distinguishes two outcomes via an AI-
written outcome file (`.backport-conflict-outcome.json`):
- `{"status":"resolved"}` — the legitimate clean path; cherry-pick
continues and the backport PR is opened.
- `{"status":"unresolved", ...}` — the legitimate "AI couldn't
do it, hand off to a human" path; `resolved=false` is set and the
conflict-failure comment is posted on the source PR.
- Anything else (missing file, malformed JSON, unknown status) is
treated as an opencode/AI Gateway infra failure: the step exits
non-zero, the workflow fails red, and the misleading
"couldn't resolve" comment is suppressed.
The prompt + scratch files are also moved into the workspace so
opencode never needs `external_directory` access anyway.
* Allow manual workflow_dispatch with ref+model inputs; use AI_MODEL in PR body
Add a `workflow_dispatch` trigger to the backport workflow with two
optional inputs:
- `ref` — commit SHA on `main` to back-port (defaults to `main` HEAD)
- `model` — overrides the default AI model used by opencode for the
decision and conflict-resolution steps (defaults to the workflow's
hardcoded `AI_MODEL`)
The top-level `AI_MODEL` env var now uses
`${{ inputs.model || 'anthropic/claude-opus-4.7' }}` so manual runs
pick up the override without changing anything else.
Manual dispatch (like the `backport-stable` label) always forces a
backport regardless of any AI verdict — the operator's intent is
explicit by virtue of triggering the workflow. The PR body shows
"Triggered manually via `workflow_dispatch`." in that case.
The PR body's conflict-resolution attribution also now interpolates
`${AI_MODEL}` (e.g. "opencode with `anthropic/claude-opus-4.7`")
instead of hardcoding "Claude Opus" so the text stays accurate if the
default model is later changed.
* Address PR review: also detect leftover conflict markers in staged files
The previous `Resolve conflicts with opencode` sanity check used
`git diff --diff-filter=U` to detect unresolved cherry-pick conflicts,
which only catches unmerged index entries. That misses the case where
the AI runs `git add` on a file that still has `<<<<<<<` /
`=======` / `>>>>>>>` markers in its content — git happily stages
the broken file as a normal modification.
Add a second check using `git diff --check --cached`, which emits
`leftover conflict marker` lines when any staged content still has
the standard markers. Grep specifically for that phrase so unrelated
whitespace warnings don't trip the check. Also update the inline
comment to accurately describe what each check covers (per Copilot's
review on #1943).
The repo has an enterprise-level branch ruleset requiring verified
signatures on every ref (`~ALL`), so a normal `git push` of a locally
cherry-picked commit is rejected ("Commits must have verified
signatures"). Replace the `git push` step with a GraphQL
`createCommitOnBranch` mutation, which signs commits automatically
with GitHub's internal key (the same way commits made via the web UI
are signed).
Walks the cherry-pick's diff against the parent (`stable` HEAD),
collects file additions (with binary-safe base64 contents read via
`git cat-file blob`) and deletions, ensures the backport branch
exists on the remote, then runs the mutation. Also updates the manual
conflict-resolution instructions in the failure comment to mention
that local cherry-picks must be signed (`git cherry-pick -S`)
because of the same ruleset.
Caveats:
- Authorship is lost — `createCommitOnBranch` always attributes
commits to the token owner (`github-actions[bot]`). The original
commit SHA is still referenced in the PR body.
- Non-regular files (executable bit, symlinks, submodules) are not
supported by the mutation; the step warns and proceeds with mode
100644 for affected paths.
The previous `OPENCODE_PERMISSION` value (`{"allow":["*"]}`) was the
wrong shape for opencode's permission config and was silently falling
through to defaults. Notably `external_directory` defaults to "ask",
which auto-rejects in non-interactive `opencode run` — causing the
`write` tool to fail when the AI tried to create the decision file
under `/tmp/`. Use the documented form (`"allow"` as the entire
permission config) and also move the decision/diff/prompt files into
the working directory so opencode doesn't need `external_directory`
permission at all.
Additionally, treat any opencode/AI Gateway failure (auth error,
rejected tool call, missing or malformed decision file) as a hard job
failure rather than silently defaulting to "no backport". A previous
expired AI Gateway key produced a green job that simply skipped the
backport without any indication of the underlying infra problem.
* Restructure backport workflow with AI-driven decisions
Run the backport workflow on every push to main and have AI analyze each
commit to decide whether to recommend a backport to stable, instead of
relying on a manual backport-stable label. The action now always opens a
PR for human review and never pushes directly to stable. The
backport-stable label is preserved as a manual override that forces a
backport regardless of the AI verdict.
* Address PR review: randomized output delimiters and updated manual instructions
- Use uuidgen-based delimiters when writing multiline values (PR title,
body, AI reasoning) to $GITHUB_OUTPUT, so user-/model-controlled
content cannot collide with or inject into the heredoc terminator.
- Update the manual conflict-resolution instructions to push a backport
branch and open a PR against stable, matching the new "never push
directly to stable" policy.
- Document the head-commit-only behavior of the push trigger inline in
the workflow.
The Release App has been temporarily removed. Switch the Release and
Backport workflows to use the default GITHUB_TOKEN, and disable the
cross-repo Front dispatch workflow until the App is restored.
Also add a workflow_dispatch trigger to release.yml so the Version
Packages PR can be created/updated manually (since pushes made by
GITHUB_TOKEN do not trigger downstream workflow runs).
* ci: auto-resolve skills/ conflicts in backport to stable
The skills/ directory is not maintained on the stable branch (skill
files are unrelated to npm packaging). Extend the backport workflow's
auto-resolution — and the AI-assisted fallback prompt — to treat
skills/ conflicts the same way docs app conflicts are handled: keep
the stable side and drop the incoming change from main.
Also updates AGENTS.md to document which directories are stable-only
placeholders and how the backport action handles them.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
---------
Signed-off-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Prefer stable version for docs app conflicts in backport workflow
After #1786 restored a minimal Next.js placeholder docs app on stable,
docs app conflicts should resolve to the stable branch version rather
than being deleted. Only docs/content/ is actively maintained on stable,
so conflicts there should still be resolved normally.
Update both the auto-resolution logic in backport.yml and the AI prompt
to reflect the new policy, and update AGENTS.md to match.
* Use git show :2:$file to detect ours-side presence in conflicts
git ls-files --error-unmatch succeeds for unmerged paths even when
the file only exists on the incoming (theirs) side, which would then
fail on git checkout --ours. Use git show :2:$file to specifically
check for a stage-2 entry, which indicates the file exists on the
ours (stable) side.
* ci: upgrade pnpm/action-setup to v6 and read version from package.json
Removes hardcoded pnpm version (10.14.0) from all workflows and instead
reads the version from the packageManager field in package.json, so CI
stays in sync with the version used locally.
* ci: update setup-workflow-dev composite action to use pnpm/action-setup@v6
Also removes the pnpm-version input since the action now reads the
version from package.json#packageManager.
* ci: downgrade pnpm/action-setup to v5
v6 installs pnpm 11 RC/beta, which has a regression
(pnpm/pnpm#11264, pnpm/action-setup#225/#227/#228) that causes
'ERR_PNPM_BROKEN_LOCKFILE: expected a single document in the stream'
when the project's packageManager pins a 10.x pnpm version. v5 is the
latest stable release before v6 and supports reading the version from
package.json#packageManager.
* Auto-resolve docs/ and pnpm-lock.yaml conflicts in backport workflow
The docs/ directory is not maintained on the stable branch. When cherry-picking
from main to stable, any conflicts in docs/ files are now auto-resolved by
deleting them. Lockfile conflicts are resolved by re-running pnpm install.
If these resolve all conflicts, the cherry-pick pushes directly to stable
without needing AI resolution or a separate PR.
* Add --signoff to cherry-pick to pass DCO check
* Preserve docs/content/ in backport conflict resolution
The docs/content/ directory is kept on stable because the markdown
files are bundled into npm packages via prepack scripts. Update the
conflict auto-resolution to only delete docs app files (outside of
docs/content/), and update AGENTS.md accordingly.
* Address review: setup pnpm before cherry-pick, fix grep pipefail, guard lockfile resolution
- Move pnpm/node setup before the cherry-pick step so pnpm install
is available during conflict resolution
- Add || true to the docs grep pipeline to prevent pipefail exit
when there are no non-content docs conflicts
- Only run pnpm install for lockfile conflicts when no other
conflicts remain, to avoid choking on conflict markers
* Let pnpm resolve lockfile conflicts natively
* Remove redundant Setup Node.js step for opencode path
Node.js is now set up unconditionally at the start of the job for
the cherry-pick step's pnpm install, so the conditional setup for
the opencode path is redundant.