653fae47a580c60cab1cc8fccaa773f55199f716
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f87e0f878c |
action: minimize pullfrog.yml permissions and drop actions:read (#594)
* action: minimize pullfrog.yml permissions and drop actions:read
The recommended pullfrog.yml workflow asked for a permissions block that's
broader than what the action actually uses with the workflow GITHUB_TOKEN —
all real work (git push, PR comments, reviews) goes through installation
tokens that the action mints via OIDC. Customer security scanners flagged
the workflow-level block as too permissive.
- Move permissions to the job level and reduce to id-token: write,
pull-requests: write, issues: write. contents:read is the implicit default
and covers actions/checkout; contents:write, checks:read are unused by
any GITHUB_TOKEN consumer; actions:read was only used by post-cleanup's
listJobsForWorkflowRun call.
- Replace listJobsForWorkflowRun with a SIGTERM/SIGINT handler in main.ts
that calls core.saveState("cancelled", "true"); post-cleanup reads it
back via core.getState. Same cancel-vs-failure UX, no extra scope needed.
- Sync the docs (headless-action, getting-started, action/README) and the
two dogfood pullfrog.yml workflows to the new minimal block. Update the
post-cleanup wiki to describe the saveState approach.
* action: drop pull-requests/issues from required workflow scopes
Switch postCleanup.ts to mint its own short-lived installation token via OIDC
(acquireNewToken with issues:write + pull_requests:write) instead of using the
workflow GITHUB_TOKEN. Same comment-update behavior, but the workflow no longer
needs those scopes — the only permissions Pullfrog ever asks for are id-token:write
(OIDC exchange) and contents:read (actions/checkout).
Also fixes a bug from the previous commit: setting an explicit permissions block
drops every unlisted scope to none (with metadata as the only exception), so
omitting contents would have broken actions/checkout. Restored at both workflow
and job level.
* action: scope id-token:write to pullfrog job, not workflow level
id-token:write is the powerful one — it lets a job mint OIDC tokens that can
be exchanged for cloud credentials or our installation tokens. Keeping it at
workflow level means any future job added to this file silently inherits it.
Move it to the job level where it's actually used; leave only contents:read
at workflow level as a safe baseline for any future jobs.
* action: move stuck-comment cleanup server-side, drop write perms entirely
The action's post-cleanup step lived inside the runner and used the workflow
GITHUB_TOKEN to update the "Leaping into action…" progress comment when a run
failed/cancelled, requiring pull-requests:write + issues:write at the workflow
level. Move that responsibility to the workflow_run.completed webhook handler:
it already has installation-token access via the GitHub App, runs server-side
(no Pullfrog API dependency loop on failure), and lets us drop both write perms.
Recommended workflow permissions block is now truly minimal:
permissions:
contents: read
jobs:
pullfrog:
permissions:
id-token: write
contents: read
Server side
- handleWorkflowRunCompleted: when conclusion != "success" and the WorkflowRun
has progressCommentId, mint installation octokit and update the stuck comment
in place. Try issues.getComment first, fall back to pulls.getReviewComment on
404 (we don't store comment type — one wasted GET on the rarer review case).
- Reuses buildPullfrogFooter and updateProgressComment from pullfrog/internal,
matching the wording the action used to write client-side.
Client side
- Delete action/utils/postCleanup.ts and action/post.ts.
- Remove post: + post-if: from action/action.yml.
- Drop runPostCleanup wiring from action/commands/gha.ts and action/play.ts.
- Remove the SIGTERM/saveState handler I added in main.ts in the previous commit
(no longer needed; cancel/fail signal comes from the webhook hook payload).
Plumbing
- Extract isLeapingIntoActionCommentBody into action/utils/leapingComment.ts so
the predicate can be re-exported via pullfrog/internal without dragging the
MCP server's transitive type graph into the Next.js app's typecheck.
- mcp/comment.ts re-exports from the new location for backward compat.
Wiki
- Delete wiki/post-cleanup.md (obsolete; cleanup is now a one-liner branch in
the workflow_run webhook handler).
* chore: ignore .worktrees in biome config
Recently-added pnpm worktree feature creates nested git worktrees under
.worktrees/, each with their own biome.jsonc declaring root. Biome's
recursive scan trips on the nested config and fails pnpm lint. Excluding
the directory matches the existing .gitignore entry.
* fix: address PR #594 review findings
Two real bugs caught by code review:
1. handleWorkflowRunWebhook.ts:323 — drop the /m flag on the stuck-comment
detection regex. With /m, ^ matches any line start, so any finalized
progress comment that embeds a task list (report_progress writes
`- [x]`/`- [ ]` lines via todoTracking.ts) would be flagged as "stuck"
and silently overwritten with the "This run croaked" boilerplate
whenever the workflow concluded non-success after the agent's final
summary already landed. Restores the body-start anchoring the original
in-process postCleanup.ts:90 had.
2. action/scripts/check-entrypoint-imports.ts — drop ../post.ts from the
esbuild entry-point list (the file was deleted in aa43b9af). The
`pnpm check:entrypoints` step in test.yml would have failed on every
run with an unresolvable-entry-point error.
Plus three small follow-ups:
- main.ts:580 — comment said "post-cleanup has its own verify-retry loop"
but post-cleanup is gone. Updated to describe the new server-side path.
- mcp/comment.ts:443 — comment said "so post script doesn't think the run
failed". Updated to describe the actual current consumers of wasUpdated.
- commands/gha.ts:84 — `--post` help text said "run post-cleanup flow" but
with the post-cleanup path removed, --post is only valid alongside the
`token` subcommand for installation-token revocation. Updated wording.
* fix(action): scope --post help text to gha token subcommand
Root gha help text was documenting --post, but --post only makes sense
paired with the token subcommand (it's how the post step revokes the
installation token previously acquired in the main step). Move it to a
dedicated gha token help section and add a parser layer that rejects
--post on the bare gha command.
$ pullfrog gha --help
usage: pullfrog gha [subcommand]
...
options:
-h, --help show help
$ pullfrog gha token --help
usage: pullfrog gha token [--post]
...
options:
-h, --help show help
--post revoke the previously-acquired token (post-step usage only)
* webhook: artifact-aware cleanup of stranded leaping comments on success
Previously the workflow_run.completed cleanup only handled non-success
conclusions. Extend it to also catch the rare case where a successful
run leaves a "Leaping into action…" comment stuck (in-process cleanup at
action/main.ts:723 normally handles this, but can be skipped on SIGKILL,
runner host crash, or any exit path that bypasses main()'s finally block).
New behavior in cleanupStuckProgressComment:
- cancelled → update with "cancelled 🛑" body (unchanged)
- failure (other) → update with "croaked 😵" body (unchanged)
- success + artifact recorded → delete the comment (the artifact is the
user-facing surface; the leaping comment
is just stale UI noise at this point)
- success + no artifact recorded → delete the comment AND alert
team@pullfrog.com via emailAlert
The "success + no artifact" path is "should never happen" territory: the
run claims success but produced no review, PR, issue, plan, or summary
comment. The team alert helps us catch in-process cleanup regressions or
artifact-tracking gaps. hasRecordedArtifact reads {review,pr,issue,
planComment,summaryComment}NodeId off the WorkflowRun row to make the call.
* webhook: narrow stuck-comment detection to leaping prefix only
Drop the stranded-todo-pattern branch from cleanupStuckProgressComment.
The leaping prefix is highly specific and impossible to confuse with a
legitimate summary; a leading todo line is not — the agent's
error-reporting paths can produce useful explanatory comments whose
body leads with a checklist (e.g. "here's what I was working on" + the
incomplete todo list), and we don't want to silently overwrite those
with the generic "croaked" boilerplate.
In-process cleanup at action/main.ts:723 still handles the stranded-todo
case in the common path (gated on !finalSummaryWritten with full access
to the in-memory tool state). Missing the rare runner-died-mid-todo case
server-side is a worthwhile trade vs. the false-positive risk on real
explanatory comments.
|
||
|
|
3bacf01e48 |
bump model registry for deepseek v4, kimi k2.6, claude opus 4.7 (#554)
* bump model registry for deepseek v4, kimi k2.6, claude opus 4.7
deepseek released v4 (pro/flash) on 2026-04-24 as a generational replacement
for v3-era reasoner/chat. deepseek will fully retire deepseek-chat and
deepseek-reasoner on 2026-07-24 — both already route server-side to v4-flash.
introduce deepseek-pro (preferred) and deepseek-flash slugs and mark the
legacy aliases deprecated via fallback so existing users transparently
upgrade. mirror on the openrouter side.
also bump moonshotai/kimi to k2.6 (from k2.5, 2026-04-21 release) and bump
the anthropic claude-opus openrouter resolves to 4.7 (we'd already moved the
native side to claude-opus-4-7 but openrouter resolves still pointed at 4.6).
update OSS_PROXY_MODEL fallback and stale doc reference accordingly.
snapshot regenerated; all 111 catalog tests + 66 unit tests pass.
* walk fallback chain when resolving the OSS proxy model
the OSS proxy path in run-context/route.ts read alias.openRouterResolve
directly, bypassing the fallback chain. so an OSS repo configured with
deepseek/deepseek-reasoner kept proxying to openrouter/deepseek/deepseek-v3.2
instead of resolving through the new fallback to openrouter/deepseek-v4-pro.
that worked today (v3.2 routes server-side to V4-Flash) but breaks when
deepseek and openrouter retire v3.2 alongside the 2026-07-24 deprecation.
extract the chain walk into a private resolveTerminalAlias helper and add
resolveOpenRouterModel that mirrors resolveCliModel but returns
openRouterResolve. fallback semantics now apply uniformly across both
runtime resolution paths.
* hide deprecated aliases from model selector dropdowns
aliases with a fallback (currently deepseek-reasoner / deepseek-chat /
openrouter/deepseek-chat) should not be selectable from the model dropdown
or the interactive cli model picker — they're a transition path, not a
choice. but if a repo already has a deprecated slug stored in the db, the
selector trigger still resolves it against the full alias registry so the
display name renders correctly until the user opens the menu and picks a
new model.
verified manually: deepseek submenu shows pro+flash only, openrouter submenu
shows pro+flash but no chat, and a deprecated stored value still renders
its full display name in the trigger.
* ci: run models-live on PRs that touch resolution files
Previously the per-alias smoke matrix only fired on push-to-main, so
resolution-affecting PRs (this one included) shipped without ever
exercising the agent harness against the real provider for each alias.
Loosen the gate on the `aliases` step in the `changes` job to fire
whenever the `models` paths-filter matches (action/models.ts,
action/package.json, action/agents/**) — same set that already drives
the comment about "resolution-affecting files". `models-live` itself
is unchanged: it still keys on a non-empty matrix.
`models-catalog` stays gated to main-push intentionally — its existing
comment justifies that (transient upstream catalog drift shouldn't
block PRs).
* relabel codex aliases as GPT, bump to 5.5 family, add gpt-pro
OpenAI retired the "-codex" model suffix on 2026-07-23 (gpt-5.3-codex,
gpt-5.1-codex-mini, gpt-5.2-codex et al all shut down) and unified the
codex+gpt lines into a single family at gpt-5.4. Per OpenAI's own
deprecation table, every "-codex" substitute is plain gpt-5.x — no
future Codex-suffixed frontier models are coming.
Keep the existing slugs for DB stability (no migration needed) but roll
displayName + resolve forward across openai, opencode, and openrouter:
- openai/gpt-codex → "GPT" → openai/gpt-5.5
- openai/gpt-codex-mini → "GPT Mini" → openai/gpt-5.4-mini
- openai/gpt-pro (new) → "GPT Pro" → openai/gpt-5.5-pro
Same relabel + new gpt-pro slug for opencode/* and openrouter/*.
gpt-5.5 (and gpt-5.5-pro) hit the OpenAI public API on 2026-04-24,
day after launch — both are live on OpenRouter as well.
There's no gpt-5.5-mini yet (analysts speculate late June – mid August
based on the gpt-5.4-mini cycle), so "GPT Mini" stays at gpt-5.4-mini
for now; one-line bump when the smaller variant ships.
Also pick up unrelated upstream catalog drift in the snapshot
(xai/grok-4.3 released 2026-05-01, openrouter/poolside laguna).
* deprecate gpt-codex aliases, mint gpt/gpt-pro/gpt-mini, render terminal alias in UI
The previous commit relabeled gpt-codex/gpt-codex-mini in place ("GPT" /
"GPT Mini") so a single slug carried two different identities. That worked
but was self-contradictory: the slug name no longer described the model.
Switch to the same shape we use for the deepseek V3→V4 transition:
- Mint new live slugs: openai/gpt, openai/gpt-pro, openai/gpt-mini
(mirrored on opencode/* and openrouter/*)
- Restore honest deprecated state on gpt-codex/gpt-codex-mini —
displayName "GPT Codex" / "GPT Codex Mini", original 5.3-codex /
5.1-codex-mini resolves, fallback set to the new gpt / gpt-mini slugs
- resolveCliModel + resolveOpenRouterModel walk the chain (existing
machinery), so DB rows holding "openai/gpt-codex" transparently route
to gpt-5.5 with no migration
UI render contract: display sites resolve to the *terminal* alias so a
deprecated stored slug shows the model the user is actually running, not
the historical name. Three call sites updated:
- components/ModelSelector.tsx (dropdown trigger label + provider label)
- action/utils/buildPullfrogFooter.ts (PR-comment "Using `X`" footer)
- action/commands/init.ts ("using model X" startup line)
Promoted internal resolveTerminalAlias → exported resolveDisplayAlias so
all three sites use the same primitive (also re-exported from external.ts
+ internal/index.ts so the Next.js app can import it).
Selectable lists (dropdown options, init picker) still filter on
!a.fallback so deprecated slugs never appear as fresh choices — only
deprecated stored values render.
wiki/model-resolution.md: replaced the muddled "slug names outlive
product names" bullet with a clear decision table for in-place bump
(generational, e.g. Opus 4.6 → 4.7) vs. deprecate+replace (vendor
restructures, e.g. codex → unified GPT, deepseek V3 → V4). Documents
the UI render contract too.
models-live CI matrix will smoke-test all 6 new slugs (gpt, gpt-pro,
gpt-mini × openai/opencode/openrouter) plus the 6 deprecated codex slugs
(which resolve through fallback to the same terminal targets) — 12 jobs
total against real provider APIs.
* wiki: slugs are evergreen, resolves are versioned
Document the slug-naming rule explicitly so future entries don't repeat
the deepseek-chat/deepseek-reasoner mistake (mirroring an upstream's
versioned/product-line-specific ID into the slug). Slugs should track
brand-style tier names that survive major version bumps; embedding
versions is the resolve string's job.
|
||
|
|
8a6696dd1d |
fix lint errors, consolidate husky hooks into root .husky
action/.husky prepare script was overriding root husky config, so the pre-push hook (lint + typecheck + test) never ran. merged the lockfile sync pre-commit into root .husky/pre-commit and removed action/.husky. also auto-fixed biome format/import-sort errors from last commit. Made-with: Cursor |
||
|
|
23a39d7f4b |
polish CLI init UX, backfill jobId on workflow-run page, bump to 0.0.195
simplify installation-not-found flow by removing ownerHasInstallation field and collapsing the "selected repos" vs "no access" branches into a single message with a confirm prompt. improve spinner/log copy throughout init (secrets, model, workflow, test run). backfill missing jobId on workflow-run redirect page by querying the GitHub API for the pullfrog job when jobId is null. add 600ms delay in handleWorkflowRunInProgress before fetching jobs to avoid racing the job assignment. Made-with: Cursor |
||
|
|
421607cf97 |
fix push-to-action: use CLI direct invocation for token acquisition
the inline `node -e` + `TOKEN=$(...)` approach broke because `core.getIDToken()` in @actions/core writes `::debug::` and `::add-mask::` to stdout, polluting the captured value. `node cli.ts gha token` uses `core.setOutput()` which writes to the $GITHUB_OUTPUT file instead of stdout. Made-with: Cursor |