feat: headless mode + bundled PR creation for CI - #6
Merged
Conversation
Adds --no-interactive, --min-confidence, --create-pr, --base-branch, --allow-foreign-scan to `promote scan`, and --create-pr/--base-branch to `promote <id>`. PR creation prefers gh CLI with octokit fallback (works on both GitHub-hosted and self-hosted runners). When the repo has a PULL_REQUEST_TEMPLATE.md, the LLM (draftingModel) fills its section bodies while preserving checkboxes and unknown sections; the full evidence breakdown is always appended as a "Memory promotion details" section. PR target is always the local origin, never the scanned repo. Digest path moves from .promote/digests/ to docs/promote/digests/ so it can be committed alongside the memory PR for traceability. Ships examples/github-actions/weekly-digest.yml + installation guide and updates `promote init`'s Next steps to mention the new flows. 35 tests pass (new ones cover branch-name idempotency and PR-body generation across template-prefilled / standalone variants).
gyulsbox
force-pushed
the
feat/headless-create-pr
branch
from
May 22, 2026 16:32
62e1fd6 to
06820e6
Compare
- detectDefaultBranch no longer falls back to current branch (was creating
PRs against the previous promote/... or feature branch). Tries
refs/remotes/origin/HEAD first, then 'gh repo view', defaults to 'main'.
- gitStage uses update-ref + symbolic-ref + reset --mixed to create the
promote branch from origin/<base> without disturbing the working tree,
so applyPromotion's already-written files become a clean diff vs main.
- gh path no longer passes --label to 'gh pr create' (hard-fails on
missing labels). Labels are created best-effort with 'gh label create
--force' then attached via 'gh pr edit --add-label', matching the
Octokit path's best-effort behavior.
…fidence Four follow-ups from dogfooding the headless cross-repo flow on trpc/trpc: 1. resolveTargetFile now ignores the LLM's suggestedFile unless it matches the configured layout. For `agents` the suggestion must be a known instruction file (AGENTS.md, CLAUDE.md, GEMINI.md, …); for path_scoped_rule it must sit under config.memoryTargets.pathScoped.preferredDir. Anything else falls back to the configured destination. Foreign scans were leaking scanned-repo paths (e.g. packages/server/src/.../*.ts) into the PR target repo; this guard prevents that. 2. isValidFilePath rejects glob characters (**, *, ?), path traversal (../), and absolute paths. Closes the "literal `**` directory in the committed path" bug. 3. run() now surfaces the child process's stderr/stdout in the thrown error message instead of just "Command failed". gh label/edit calls are wrapped per-step with explicit stderr warnings — previously both were silently swallowed, which hid label-creation failures on real runs. 4. confidence values are formatted via toFixed(2) in PR body and LLM facts. Eliminates artifacts like "confidence 0.9500000000000001" coming from the human-signal +0.05 bump.
… + tests
Addresses PR-review follow-ups (analysis items 1-6).
### 1+4. Atomic flow via prepareBranchForPr / finalizePr / rollbackBranch
Splits createPullRequest into composable phases:
- prepareBranchForPr: switches HEAD to a new branch cut from baseBranch using
update-ref + symbolic-ref + reset --mixed, preserving the working tree.
- finalizePr: stages files, commits, pushes, opens PR (no branch switching).
- rollbackBranch: discards file mods, switches back, deletes the branch.
- restoreOriginalBranch: clean checkout back after PR success.
scan.ts headless --create-pr and promote.ts --create-pr now:
prepareBranchForPr → applyPromotion → finalizePr
→ on success: updateCandidateStatus (atomic) + restoreOriginalBranch
→ on failure: rollbackBranch (working tree restored, branch deleted, DB untouched)
DB status flips are now gated on PR-creation success. If gh/octokit hiccups
mid-flow, the candidate stays as 'candidate' and the next scan can retry.
The legacy createPullRequest wrapper (used by interactive --create-pr) keeps
its apply-then-PR shape and intentionally does NOT rollback files on failure
— in that flow, file mods were user-confirmed and rolling back would destroy
their work.
### 2. updateCandidateStatus moved AFTER PR success (atomic gate)
Headless: applies all eligible → finalizePr → only then loop updateCandidateStatus.
Single-PR (promote <id> --create-pr): apply → finalizePr → updateCandidateStatus.
Interactive --create-pr keeps immediate status updates (each apply is explicit
user confirmation; PR is best-effort bonus).
### 3. needs_human_decision skips now surface in headless output
Previously skipped silently. Now: "N candidate(s) flagged needs_human_decision
— review locally with 'promote review'."
### 5. Unit tests for resolveTargetFile + formatConfidence
src/cli/commands/promote.test.ts: 13 cases covering known-agent acceptance,
foreign-path rejection, glob/traversal/absolute rejection, path_scoped_rule
config-derivation, adr numbering, test stub path.
src/pr/template.test.ts: 3 cases for formatConfidence (float precision,
trailing zeros, boundary rounding).
51 tests total, all green.
### 6. Workflow cache key simplification
examples/github-actions/weekly-digest.yml: key was promote-{repo}-{run_id}
which made every run a unique cache entry — restore-keys fallback worked
but the cache was never re-hit and entries piled up. Single repo-keyed
entry now updates monotonically across runs.
gyulsbox
added a commit
that referenced
this pull request
May 23, 2026
feat: headless mode + bundled PR creation for CI
gyulsbox
added a commit
that referenced
this pull request
May 23, 2026
feat: headless mode + bundled PR creation for CI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the v0.5 deliverable from the PRD:
promote scanandpromote <id>can now run fully headless and open a single bundled PR with the applied memory changes — unlocking CI / GitHub Actions usage.New flags
promote scan:--no-interactive— disables all prompts (auto-enabled in CI or non-TTY)--min-confidence <n>— confidence floor for auto-apply--create-pr— open one bundled PR after applying--base-branch <name>— override PR base--allow-foreign-scan— decouple scanned repo from PR target (testing / upstream-tracking)promote <id>:--create-pr— open a PR for a single applied candidate--base-branch <name>How PR creation works
GITHUB_TOKEN).--allow-foreign-scan..github/, root, ordocs/), thedraftingModelfills its section bodies while preserving checkboxes (- [ ]) verbatim and leaving unknown sections (Screenshots, etc.) untouched. A## Memory promotion detailssection with raw evidence is always appended for verifiability.Path change
Digest moves from
.promote/digests/todocs/promote/digests/so it can be committed alongside the memory PR. This repo's.gitignorestill blocksdocs/promote/digests/so we don't pollute the tool's own history while dogfooding.Files touched
src/index.ts— flag registration + help textsrc/cli/commands/scan.ts— headless detection, prompt-site gating, bundled PR flowsrc/cli/commands/promote.ts—applyPromotionreturnsApplyPromotionResult, single-candidate PR flowsrc/cli/commands/init.ts— Next steps updated for headless / CI usesrc/pr/branch.ts— deterministic branch namessrc/pr/template.ts— PR body builder, template detectionsrc/pr/llm-fill.ts— LLM-driven template section fillingsrc/pr/create.ts— gh-first / octokit-fallback PR creatorexamples/github-actions/weekly-digest.yml+README.md— ready-to-copy workflowTests
35/35 pass. New tests cover branch-name idempotency, PR-body generation across template-prefilled / standalone variants, and template-discovery across the 6 standard locations.
Test plan
pnpm typecheckcleanpnpm testpassespnpm buildcleanpromote scan --no-interactive --create-pr --allow-foreign-scanagainst an external repo