01e4daa0b5
* checkout_pr: refuse unconditionally on dirty working tree drop the live-HEAD comparison from the guard introduced in #796. any checkout_pr call with staged or unstaged changes now throws, even when HEAD is already on pr-N. no stashing, no idempotent escape hatch. motivation is the zed-industries/cloud (2026-05-18) incident: shared-cwd subagents make "carry edits along" semantics dangerous, and the HEAD-equality predicate let a re-checkout silently inherit working-tree state from a sibling agent. forcing commit/discard before any PR-context operation eliminates the entire carry-forward failure class. error names the PR number, lists dirty paths, and tells the agent to commit/push/restore/clean before retrying. * improve dirty-tree error: precise discard commands copilot caught two sloppy bits in the error string: - "push" alone does not clean a dirty tree (needs commit first) - bare `git clean` is a no-op without `-fd` reword to "commit (then push if needed), or discard with `git restore --staged --worktree .` / `git clean -fd`" so the guidance is actually actionable. * checkout_pr: initial-branch invariant setupGit captures `toolState.initialBranch` at run start via live `git rev-parse --abbrev-ref HEAD`. checkout_pr refuses unless current HEAD matches the run-entry branch or the target `pr-N` (idempotent same-PR re-checkout). uses live rev-parse, not toolState.issueNumber (poisonable per the PR #796 review). refusal error names the current branch, target PR, recovery path (`git checkout <initialBranch>` with the literal branch name), and explicitly states routing around via the `git` tool is not sanctioned. closes the zed-industries/cloud (2026-05-18) shape where a subagent parked HEAD on someone else's `pr-X` and the orchestrator's next checkout_pr inherited that position. * reviewfrog: enforce canonical diff + pre-commit halt; align Build dispatch extend REVIEWER_SYSTEM_PROMPT with two prepended HARD CONSTRAINTS: - first action MUST be `git diff origin/<base>` (single-rev, captures uncommitted). no other diff first; no checkout_pr; no alt-ref fetches; no branch listing; no `gh pr list`. - empty canonical diff + claimed-changes dispatch ⇒ reply exactly with `no changes detected — likely pre-commit Build self-review; orchestrator should commit then re-dispatch` and stop. do not guess PR numbers (the zed thrash that ended in `checkout_pr({2582})`). reshape Build mode reviewfrog dispatch step around a verbatim template that names: (a) the situation is pre-commit, (b) canonical diff command, (c) halt-on-empty-diff rule. orchestrator side now says the same thing as the reviewer's baked-in prompt. delegation-discipline bullets and orchestrator-evaluation guidance kept intact. * checkout_pr: handle detached-HEAD entry in initial-branch invariant pullfrog incremental review caught a defense-in-depth gap: `git rev-parse --abbrev-ref HEAD` returns the sentinel string `"HEAD"` on detached entry, which is the default `actions/checkout` state for `pull_request` events. with the previous string-typed `initialBranch`, both the captured value and the live probe would equal `"HEAD"` on any detached state, trivially satisfying the invariant — including a subagent doing `git checkout --detach <sha>`. discriminate the captured HEAD: probe `git symbolic-ref --short HEAD` first (works on named branches), fall back to `git rev-parse HEAD` (SHA) on detached entry. store as `{ kind: "branch"; name } | { kind: "detached"; sha }`. checkout_pr runs the identical probe at call time and compares like-with-like (branch name vs branch name, SHA vs SHA). refusal error renders both heads via a small `describeHead` helper and chooses the right `git checkout` recovery target (branch name or SHA). no inline-discriminant `as` casts — uses a top-level `headsEqual` that narrows via the discriminator.
122 lines
3.7 KiB
YAML
122 lines
3.7 KiB
YAML
name: Test
|
|
|
|
on:
|
|
pull_request:
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "24"
|
|
cache: "pnpm"
|
|
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm typecheck
|
|
- run: pnpm test
|
|
|
|
agents:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
strategy:
|
|
fail-fast: true
|
|
matrix:
|
|
agent: [claude, opencode]
|
|
test:
|
|
[
|
|
codex-auth,
|
|
mcpmerge,
|
|
nobash,
|
|
restricted,
|
|
skill-invoke-claude,
|
|
skill-invoke-opencode,
|
|
smoke,
|
|
token-exfil,
|
|
# vertex-claude, # disabled: 0 anthropic quota on pullfrog GCP vertex
|
|
vertex-opencode,
|
|
]
|
|
exclude:
|
|
- agent: claude
|
|
test: skill-invoke-opencode
|
|
- agent: claude
|
|
test: codex-auth
|
|
- agent: opencode
|
|
test: skill-invoke-claude
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}
|
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
|
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
|
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
|
AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }}
|
|
AWS_REGION: us-east-1
|
|
BEDROCK_MODEL_ID: us.anthropic.claude-opus-4-6-v1
|
|
VERTEX_SERVICE_ACCOUNT_JSON: ${{ secrets.VERTEX_SERVICE_ACCOUNT_JSON }}
|
|
GOOGLE_CLOUD_PROJECT: pullfrog
|
|
VERTEX_LOCATION: global
|
|
VERTEX_MODEL_ID: gemini-2.5-flash
|
|
PULLFROG_MODEL: ${{ vars.PULLFROG_MODEL }}
|
|
# CI smoke-testing shortcut only — production stores this in Pullfrog's
|
|
# per-org secret store (Postgres), set via `pullfrog auth codex`. GH
|
|
# Actions secrets are immutable at runtime so the post-hook can't write
|
|
# back the rotated refresh token; CI accepts the staleness and we
|
|
# manually re-provision when smoke tests start failing. Do not copy this
|
|
# pattern into user-facing workflows. See wiki/codex-auth.md.
|
|
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "24"
|
|
cache: "pnpm"
|
|
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm runtest ${{ matrix.test }} ${{ matrix.agent }}
|
|
|
|
agnostic:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
strategy:
|
|
fail-fast: true
|
|
matrix:
|
|
test:
|
|
[
|
|
byok-no-keys-fallback,
|
|
git-permissions,
|
|
githooks,
|
|
pkg-json-scripts,
|
|
push-disabled,
|
|
push-enabled,
|
|
push-restricted,
|
|
timeout,
|
|
]
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "24"
|
|
cache: "pnpm"
|
|
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm runtest ${{ matrix.test }}
|