fix(analyzer): workflow-permission push handling + gpt-5 max_completion_tokens (#5)
* fix(analyzer): handle workflow-permission push rejection and gpt-5 params
Second end-to-end run succeeded through passes 1-3 and batch building
(60 GOOD / 14 NEEDS_REVIEW / 5 SLOP cherry-picked), then failed at
push with:
! [remote rejected] sync/upstream/v3.17.4-good -> sync/upstream/v3.17.4-good
(refusing to allow a GitHub App to create or update workflow
.github/workflows/publish-platform.yml without `workflows` permission)
GitHub's default GITHUB_TOKEN cannot push commits that modify files
under .github/workflows/, which upstream frequently touches. The whole
pipeline exited non-zero even though every other stage succeeded.
Also surfaced during run: gpt-5-mini rejected max_tokens with
"Unsupported parameter: use max_completion_tokens instead" — API
contract that differs from gpt-4.x.
Changes:
1. pushBranch() returns PushOutcome discriminated union
("ok" | "blocked-by-workflow-permission" | "failed") instead of
throwing. The pipeline records the outcome on each BatchResult
without aborting subsequent pushes.
2. New commitTouchesWorkflow() inspection flags commits that modify
.github/workflows/*. Each batch now tracks these shas so the
analysis issue can call them out as "needs elevated permissions to
sync".
3. Issue body surfaces push outcome per batch:
- Conflict note (existing)
- Push blocked by workflow permission (new)
- Workflow-touching commit list (new)
4. PR specs skip batches whose push failed, so createPR doesn't
attempt to open a PR against a non-existent remote branch.
5. tokenFieldForModel() picks max_completion_tokens for gpt-5 / o-
series, max_tokens for everything else. Temperature is also
omitted for the reasoning models since they reject it. Prevents
the first fallback attempt from wasting a request on a 400.
6. resetWorkingTree() no longer runs `git clean -fdx`; only
`git reset --hard HEAD` on tracked files. This guarantees the
.analyzer-output/ directory survives (prior runs showed it was
being removed during reset), so artifacts are always available
even on crash.
7. isContextLengthError() narrowed to avoid false positives on the
old max_tokens error message.
Expected behavior on next run: pipeline completes, issue is created
with push-outcome notes per batch, good-batch draft PR opens unless
all its commits touch workflow files (in which case the issue flags
it for manual sync).
* fix(analyzer): address PR #5 review findings
Four issues surfaced by Copilot + Augment reviewers on PR #5:
1. PUSH_BRANCHES=false → PR creation still attempted (medium)
When pushBranches is false, pushOutcome stays undefined. The prior
skip logic treated undefined as success, so PRs were attempted for
branches that never reached origin. Plumb pushAttempted through
PipelineResult → buildWorkflowArtifacts → skipped calculation.
PR now skipped unless pushAttempted && pushOutcome === "ok".
2. tokenFieldForModel matched only prefixed names
/\/(gpt-5|o[0-9])/i missed bare "gpt-5-mini" (no provider prefix).
MODEL_* env vars are not validated, so a user could easily pass an
unprefixed name and get a 400. Broaden to /(^|\/)(gpt-5|o\d+)/i.
3. batches.json written before pushBatches() mutates it
Persisted artifact was always missing pushOutcome. Move the write
after pushIfRequested so the on-disk record is consistent with
pipeline-result.json.
4. Branch: label in issue body misleading when push skipped
Previously rendered "Branch: <name>" identically whether the branch
was on origin or only in the runner. Now appends "(on origin)" or
"(local only — not on origin)" and adds an explicit "push was not
attempted" note when push_branches=false.
Type surface:
* PipelineResult now has pushAttempted: boolean
* IssueReportInput now takes pushAttempted: boolean
* renderBatchSection / renderBatchNotes take pushAttempted
* fix(analyzer): remove em dashes from generated issue content
AGENTS.md anti-patterns: "Never use em dashes, en dashes, or AI filler
phrases in generated content." The upstream-analyzer produces GitHub
issue bodies and PR bodies, which are generated content and fall under
the ban. Flagged by Devin review on PR #5.
Replace 5 em dashes (U+2014) with semantically appropriate ASCII:
* "subject — reason" -> "subject -- reason" (commit summary lines)
* "local only — not on origin" -> "local only; not on origin"
* "rejected — GITHUB_TOKEN cannot..." -> "rejected. GITHUB_TOKEN..."
* "(delta) — reasoning" -> "(delta): reasoning"
* "advisory — no merges" -> "advisory: no merges"
Right-arrow (U+2192 →) is retained; it's semantic (from→to) and not
covered by the em/en dash ban. V
Vacbo committed
9c5361f5dcc43af9495a38cfc9e8cf94cb51b358
Parent: f95402c
Committed by GitHub <noreply@github.com>
on 4/17/2026, 6:06:59 PM