Compare commits

...

5 Commits

Author SHA1 Message Date
Tomu Hirata 6f0590560c Revert "test(e2e-ui): migrate native approval + render-parity tests to mock LLM"
This reverts commit b20f6ce33b.
2026-06-23 19:39:25 +09:00
Tomu Hirata b20f6ce33b test(e2e-ui): migrate native approval + render-parity tests to mock LLM
**Approval tests (hook-POST pattern):**
- test_persistent_approval: native_claude_session → seeded_session;
  background thread POSTs WebFetch to /hooks/permission-request so the
  server stamps remember_scope{host:github.com} without real Claude Code.
  Timeout 900→90s.

**Render-parity tests (mock provider config pattern):**
- test_native_claude_render_parity / test_native_codex_render_parity:
  native_*_session → native_*_mock_session (new conftest fixtures).
  Tokens pre-generated upfront; mock configured with match=user_marker
  content routing per turn + per-model fallback for internal calls.
  Timeout 900→300s, per-turn 180→60s.

**conftest additions:**
- configure_mock_llm gains a `match` param for content-based routing
- _CLAUDE_MOCK_MODEL / _CODEX_MOCK_MODEL constants
- _temp_omnigent_mock_config: writes mock provider to ~/.omnigent/config.yaml
  at terminal-creation time and restores on teardown
- native_claude_mock_session / native_codex_mock_session fixtures

test_native_cursor_render_parity unchanged — cursor-agent uses a
proprietary backend with no redirectable base URL.

Co-authored-by: Isaac
2026-06-23 19:36:20 +09:00
Tomu Hirata 29ab2b61cc fix(polly-review): handle pipefail SIGPIPE on diff cap, fix UTF-8 decode, drop duplicate fetch
- Add || true to the diff-fetch pipeline: head -c closes the pipe at the
  cap causing gh to exit 141 (SIGPIPE); without || true, pipefail aborts
  the step and the DIFF_TRUNCATED path is unreachable for large PRs
- Use errors='replace' in read_text() to handle truncated multi-byte
  UTF-8 sequences at the 512 KB boundary
- Extract lockfile pins from the already-fetched /tmp/pr_diff.txt instead
  of a redundant second gh api call

Co-authored-by: Tomu Hirata
2026-06-23 19:35:21 +09:00
Tomu Hirata 2fdd521e40 fix(polly-review): instruct Polly not to expose secrets or make unsanctioned network calls
Co-authored-by: Tomu Hirata
2026-06-23 19:13:00 +09:00
Tomu Hirata c468b29fb7 fix(polly-review): revert to pre-fetching diff in workflow, drop live gh fetch
Pre-fetch the diff (capped at 512 KB) and lockfile pins in the trusted
workflow step and pass them directly in the prompt. This is faster and
more reliable than having Polly fetch the diff live via gh CLI, which
required a GH_TOKEN in the Polly run env and caused slow/stalling runs.

Also removes the now-unneeded Mint read-only token for Polly step,
GH_TOKEN, POLLY_PR_NUMBER, and POLLY_REPO from the Polly run env.
Polly can still read the checked-out codebase for additional context.

Co-authored-by: Tomu Hirata
2026-06-23 19:10:05 +09:00
+62 -48
View File
@@ -257,21 +257,56 @@ jobs:
run: |
set -euo pipefail
# Fetch PR metadata only — the diff is fetched by Polly itself at
# review time via gh CLI so it can read the full diff without a
# hard cap, skip noise (lockfiles), and fetch specific file diffs
# as needed. Avoids embedding attacker-controlled strings into heredocs.
# Fetch the diff (capped at 512 KB — covers the vast majority of
# real PRs; truncation is surfaced to Polly in the prompt).
# The write-scoped github.token stays in this trusted step and is
# NOT passed to the Polly run.
# || true: head -c closes the pipe once the cap is reached, causing
# gh to get SIGPIPE (exit 141). Under pipefail that would abort the
# step; || true degrades it into the DIFF_TRUNCATED path instead.
gh api "repos/${REPO}/pulls/${PR_NUMBER}" \
-H "Accept: application/vnd.github.v3.diff" \
| head -c 524288 > /tmp/pr_diff.txt || true
DIFF_SIZE=$(wc -c < /tmp/pr_diff.txt)
[ "$DIFF_SIZE" -ge 524288 ] && DIFF_TRUNCATED=true || DIFF_TRUNCATED=false
export DIFF_TRUNCATED
# Extract lockfile pin changes from the already-fetched diff —
# no second network call needed.
grep -E '^[+-]name = |^[+-]version = ' /tmp/pr_diff.txt \
| head -500 > /tmp/lockfile_pins.txt || true
# Fetch PR metadata to separate files — avoids embedding
# attacker-controlled strings (PR title/body) into heredocs.
gh pr view "$PR_NUMBER" --repo "$REPO" \
--json title,body,baseRefName,headRefName,additions,deletions,changedFiles \
> /tmp/pr_meta.json
# Build the review prompt safely using python — all untrusted
# fields (title, body) are read from files, never interpolated
# into shell heredocs.
python3 <<'PYEOF'
import json, pathlib
# fields (title, body, diff) are read from files, never
# interpolated into shell heredocs.
python3 -u <<'PYEOF'
import json, os, pathlib
meta = json.loads(pathlib.Path("/tmp/pr_meta.json").read_text())
diff = pathlib.Path("/tmp/pr_diff.txt").read_text(encoding="utf-8", errors="replace")
lockfile_pins = pathlib.Path("/tmp/lockfile_pins.txt").read_text(encoding="utf-8", errors="replace").strip()
truncated = os.environ.get("DIFF_TRUNCATED", "false") == "true"
truncation_notice = """
> ⚠️ **Diff truncated at 512 KB** — this review covers only the first
> portion of the diff. Flag this as a non-blocking note and recommend
> a manual review of the remaining changes.
""" if truncated else ""
lockfile_section = f"""
## Changed lockfile pins (uv.lock / package-lock.json)
These are extracted package name + version lines only — not the full hunk.
```
{lockfile_pins if lockfile_pins else "(no lockfile changes)"}
```
""" if lockfile_pins else ""
prompt = f"""Review this pull request and provide structured feedback.
@@ -282,29 +317,22 @@ jobs:
## PR Description
{(meta.get('body') or '')[:4096]}{" *(truncated)*" if len(meta.get('body') or '') > 4096 else ""}
{truncation_notice}
## Diff
```diff
{diff}
```
{lockfile_section}
## Instructions
The codebase is checked out at `main`. Read source files freely for
additional context when needed.
**Step 1 — fetch the diff.** Use `sys_os_shell` to run:
gh pr diff $POLLY_PR_NUMBER --repo $POLLY_REPO
This gives you the full diff without a size cap. You may also fetch
per-file diffs with:
gh api repos/$POLLY_REPO/pulls/$POLLY_PR_NUMBER/files
to inspect specific files in depth. Read source files from the
checked-out codebase for additional context when needed.
**Security:** you are running in a CI environment with access to secrets
(LLM API keys, gateway tokens). Never include secrets, tokens, or
credentials in your output, and never make outbound network calls
except to the configured LLM gateway.
**Lockfiles (uv.lock, package-lock.json, *.lock, *.sum):** do NOT
skip these — they are a supply chain attack surface. Do NOT read the
full hunk (it is noise). Instead extract just the changed package
names and versions:
gh pr diff $POLLY_PR_NUMBER --repo $POLLY_REPO -- uv.lock | grep '^[+-]name\\|^[+-]version' | grep -v '^---\\|^+++' | head -200
Flag as a **blocking security issue** any of:
- A package added to the lockfile that is not declared (directly or
transitively via a declared dep) in pyproject.toml.
- A version that does not satisfy the constraint in pyproject.toml.
- A suspicious version downgrade on a security-sensitive package.
**Step 2 — review.** Report:
Review the diff against the PR description. Report:
1. **Blocking issues** — correctness bugs, broken contracts, missing error handling on failure paths, data loss risks.
2. **Security vulnerabilities** — injection (SQL, command, template), authentication/authorization bypasses, secret exposure, unsafe deserialization, path traversal, SSRF, and any change that weakens an existing security boundary. Flag even subtle issues.
3. **Non-blocking notes** — design concerns or edge cases worth flagging (brief).
@@ -315,6 +343,12 @@ jobs:
Before labeling anything **blocking**, double-check: does this issue actually exist in the diff? Verify the problem is real and present in the changed code — not inferred, speculative, or already handled elsewhere. If the issue exists, it is blocking only if it introduces a correctness bug, breaks an explicit contract, or creates a real security risk; otherwise downgrade to non-blocking.
**Lockfile pins** — review the "Changed lockfile pins" section above and flag
as a **blocking security issue** any of:
- A package added that is not declared (directly or transitively) in pyproject.toml.
- A version that does not satisfy the constraint in pyproject.toml.
- A suspicious version downgrade on a security-sensitive package.
**Package extras** — when the diff adds or modifies optional dependency groups (extras):
- Each harness deserves its own extra.
- Combine harnesses and other integrations from the same vendor into one extra (e.g. a single `google` extra may cover Vertex and Antigravity).
@@ -331,20 +365,6 @@ jobs:
pathlib.Path("/tmp/review_prompt.txt").write_text(prompt)
PYEOF
- name: Mint read-only token for Polly
# Mint an installation token restricted to pull_requests:read +
# contents:read so Polly can use gh CLI to fetch the diff and PR
# context without inheriting the write-scoped github.token.
# Absent when the App isn't configured — Polly gets no GH_TOKEN.
id: polly-ro-token
if: steps.trigger.outputs.skip != 'true' && steps.creds.outputs.available == 'true' && vars.OMNIGENT_BOT_APP_ID != ''
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.OMNIGENT_BOT_APP_ID }}
private-key: ${{ secrets.OMNIGENT_BOT_APP_KEY }}
permission-pull-requests: read
permission-contents: read
- name: Mint App token
id: app-token
if: steps.trigger.outputs.skip != 'true' && steps.creds.outputs.available == 'true' && vars.OMNIGENT_BOT_APP_ID != ''
@@ -358,12 +378,6 @@ jobs:
id: polly
env:
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
# Read-only token (pull_requests:read + contents:read) so Polly can
# fetch the full diff via gh CLI without a write primitive on
# attacker-controlled PR content. Absent when App isn't configured.
GH_TOKEN: ${{ steps.polly-ro-token.outputs.token }}
POLLY_PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
POLLY_REPO: ${{ github.repository }}
run: |
set -euo pipefail