Skip to content

feat: implement issue #898 — pr-review self-review deadlock: ci-pending skip + "already reviewed" marker blocks approval once CI goes green - #899

Merged
don-petry merged 153 commits into
mainfrom
dev-lead/issue-898-20260621-2309
Jun 24, 2026
Merged

feat: implement issue #898 — pr-review self-review deadlock: ci-pending skip + "already reviewed" marker blocks approval once CI goes green#899
don-petry merged 153 commits into
mainfrom
dev-lead/issue-898-20260621-2309

Conversation

@don-petry

@don-petry don-petry commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Closes #898

Implemented by dev-lead agent. Please review.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of API rate-limit scenarios with proper retry logic.
    • Enhanced CI-pending detection to defer reviews instead of failing when checks are in progress.
    • Refined skip reason reporting to distinguish between temporary deferrals and permanent skips.
  • Tests

    • Added regression tests for CI-pending skip scenarios and batch review reporting accuracy.
  • Chores

    • Implemented token usage aggregation and monitoring in workflow health checks.

@don-petry
don-petry requested a review from a team as a code owner June 21, 2026 23:35
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@don-petry, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 41 minutes and 50 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3bb605b2-6038-474f-8ef3-e19412829cdb

📥 Commits

Reviewing files that changed from the base of the PR and between 62930d9 and 052fe1a.

📒 Files selected for processing (1)
  • .github/workflows/lint.yml
📝 Walkthrough

Walkthrough

This PR fixes the ci-pending deadlock from issue #898 by rewriting engine.sh's run_writer/run_writer_with_fallback/parse_reset_time functions, adding reason-aware deferred skip reporting to review-batch.sh, and adding Bats regression tests for both paths. It also adds a token-usage artifact aggregation step to the fleet monitor workflow.

Changes

ci-pending Non-Terminal Fix and Deferred Skip Reporting

Layer / File(s) Summary
engine.sh write/rate-limit path rewrite
scripts/engine.sh
parse_reset_time rewritten with today/tomorrow ISO rollover. run_writer simplified to temp-file capture with is_rate_limited check returning exit 2. run_writer_with_fallback rewritten to a minimal ordered-engine retry loop on exits 2 and 127 only. File now contains duplicate extract_verdict_json and parse_reset_time definitions.
review-batch.sh reason-aware deferred skip reporting
scripts/review-batch.sh
Adds run_review_capture (tees stdout to REVIEW_OUT, preserves exit code) and skip_reason_from (parses last "reason" field). Introduces deferred counter and REVIEW_OUT temp file with cleanup trap. Updates all three invocation sites to use run_review_capture. Replaces single "No-op (already reviewed)" message with reason-specific dispatch: ci-pending and related reasons increment deferred; others emit targeted notices. Appends deferred count to final batch summary.
Bats tests: batch skip reason reporting
tests/test_batch_skip_reporting.bats
Four tests using stubbed review-one-pr.sh: verifies ci-pending does not emit "already reviewed"; emits "Deferred"/"CI"; appears in session summary; and already-reviewed-at-head emits "already reviewed" without "deferred".
Bats regression tests: review-one-pr ci-pending non-terminal
tests/test_review_one_pr_ci_pending.bats
Two tests using a stubbed gh that returns an IN_PROGRESS snapshot and logs invocations: verifies exit 100 with "reason":"ci-pending" and fixed incident SHA; verifies gh pr view is called but no gh pr comment or gh pr review calls are made.

Fleet Monitor Token Usage Summarization

Layer / File(s) Summary
Token usage aggregation step
.github/workflows/actions-fleet-monitor.yml
New Summarize token usage by workflow step in health-check job fetches token-usage-* artifacts via gh api, skips expired/out-of-window entries, extracts .jsonl records, aggregates per-workflow token counts, and writes a markdown table or "no artifacts found" message to $GITHUB_STEP_SUMMARY.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • petry-projects/.github-private#914: Changes the sweep workflow to re-trigger on workflow_run: completed to re-evaluate PRs once CI turns green — directly paired with this PR's non-terminal ci-pending fix.
  • petry-projects/.github-private#343: Adds the same "Summarize token usage by workflow" step to actions-fleet-monitor.yml, aggregating token-usage-* artifacts into a GITHUB_STEP_SUMMARY markdown table.
  • petry-projects/.github-private#704: Modifies scripts/engine.sh around extract_verdict_json and parse_reset_time/reset-time handling — the same functions rewritten in this PR.

Suggested labels

needs-human-review

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: implementing issue #898 by preventing the 'already reviewed' marker from blocking re-evaluation when CI transitions from pending to success.
Linked Issues check ✅ Passed The PR comprehensively addresses issue #898's requirements: captures per-PR skip reasons (especially ci-pending), prevents 'already reviewed' markers from blocking re-evaluation, adds regression tests for ci-pending→ci-success transition, and handles all ten skip reason strings.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #898: workflow token-usage monitoring, verdict extraction refactoring, skip-reason reporting, batch loop improvements, and ci-pending tests. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev-lead/issue-898-20260621-2309

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses issue #898 by improving skip reporting in scripts/review-batch.sh. It captures the output of review-one-pr.sh to extract the specific reason for an exit-100 skip (such as pending CI or human escalation) and updates the notices and final summary to reflect deferred reviews. Regression tests have also been added to verify this behavior. The review feedback suggests adding error handling for the mktemp call to prevent failures if temporary file creation fails.

Comment thread scripts/review-batch.sh Outdated
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry
don-petry enabled auto-merge (squash) June 21, 2026 23:41
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 21, 2026
@don-petry
don-petry disabled auto-merge June 21, 2026 23:42
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@don-petry
don-petry enabled auto-merge (squash) June 21, 2026 23:47
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-06-22T00:47:53Z.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-06-22T00:47:55Z.

@don-petry
don-petry disabled auto-merge June 22, 2026 03:03
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@don-petry
don-petry enabled auto-merge (squash) June 22, 2026 03:05
@don-petry
don-petry disabled auto-merge June 22, 2026 03:18
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry
don-petry enabled auto-merge (squash) June 22, 2026 03:19
@don-petry
don-petry disabled auto-merge June 22, 2026 03:21
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@don-petry
don-petry enabled auto-merge (squash) June 22, 2026 03:22
@don-petry
don-petry disabled auto-merge June 22, 2026 05:39
@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

dev-lead will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry
don-petry enabled auto-merge (squash) June 22, 2026 05:44
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-06-22T06:51:11Z.

don-petry added a commit that referenced this pull request Jun 25, 2026
…ing` skip + "already reviewed" marker blocks approval once CI goes green (#899)

* Add scheduled PR review agent

Hourly GitHub Action that enumerates open PRs (authored by or
review-requested from @me), classifies risk via Claude Code, and
auto-approves LOW/MEDIUM PRs that pass quality gates while escalating
HIGH-risk or gated PRs for human review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Use Claude Code OAuth token instead of API key

Routes agent runs through the Max plan subscription rather than
per-token API billing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Enforce single-PR scope and default to dry-run

- Prompt now hard-forbids enumeration commands and acting on any PR
  other than $PR_URL. Earlier dry-run revealed the agent was reviewing
  the entire backlog in a single invocation, ignoring the per-PR loop.
- DRY_RUN now defaults to true unless repo variable LIVE_MODE=true,
  so the hourly cron is safe by default until the operator opts in.
- AGENT.md documents the LIVE_MODE switch and corrects PAT scopes
  (Issues needs write to create the needs-human-review label).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add 3-model council + commit-SHA-aware iterative review

The single-prompt reviewer is replaced by a council of 3 Claude models,
each with a focused lens, and a synthesizer that combines them:

  - Security      (Opus 4.6)   - auth, secrets, injection, supply chain
  - Correctness   (Sonnet 4.6) - logic, issue alignment, tests, CI
  - Maintainability (Haiku 4.5) - standards, conventions, clarity
  - Synthesizer   (Sonnet 4.6) - max risk, escalate-if-any, dedupe, post

Council members run in parallel, write JSON verdicts to /tmp/council/,
and never touch GitHub. The synthesizer is the only actor that posts.

Every posted review embeds an HTML marker on line 1:
  <!-- pr-review-agent v1 sha=<full-sha> decision=... risk=... -->

scripts/review-one-pr.sh checks for that marker before spending tokens:
- Marker SHA == current head SHA -> skip (already reviewed).
- Marker SHA != current head SHA -> re-review (handles iterative cycles).
- No marker -> first review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Upgrade maintainability lens from Haiku 4.5 to Sonnet 4.6

Haiku couldn't follow the agentic instructions — it asked for env vars
instead of reading them and executing the gh commands. All three council
members now use models capable of multi-step tool use.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Go-live improvements for PR review agent (#2)

- Upgrade actions/checkout v4→v5 (Node.js 20 deprecation)
- Add MAX_PRS cap (default 10) to stay within 60-min job timeout
- Filter drafts at enumeration (--draft=false)
- Fix jq operator-precedence bug in idempotency check
- Add truncation notice log, inline jq comments, docs updates

Council-reviewed: 2 rounds (escalated round 1 on jq bug, approved round 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Claude delegation, auto-merge, and rebase handling (#3)

- Post structured fix-request comments on escalated PRs in Claude-enabled orgs
- Claude's workflow trigger picks up the comment automatically (no @claude tag)
- On approval: auto-merge (--auto --squash), rebase if behind, remove label
- Cycle guard (MAX_REVIEW_CYCLES=3) prevents infinite delegation loops
- New repo variables: CLAUDE_ORGS, MAX_REVIEW_CYCLES

Council-reviewed: dispatch hit rate limit but architecture validated against
petry-projects/.github claude-code-reusable.yml workflow — event flow confirmed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Optimize review: small-PR and incremental fast paths (#4)

* Add review mode optimization: small-PR and incremental fast paths

PRs are now routed to the cheapest review path:
- Small PRs (<10 lines): single Opus call (1 invocation vs 4)
- Re-reviews (prior marker at different SHA): single Opus call with
  prior review context, focused on what changed since last review
- First review of non-small PRs: full 3-member council (unchanged)

New files:
- prompts/single-review.md: combined council+synth prompt for single
  reviewer mode, handles both small and incremental paths

New config:
- SMALL_PR_THRESHOLD repo variable (default 10)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Replace parallel council with cascading review tiers

Haiku triage → Sonnet deep review → Opus security audit, where each
tier only fires if the previous one escalated. This replaces the
3-parallel-members + synthesizer architecture.

Cost impact:
- ~80% of PRs: Haiku + Opus confirm (2 calls, ~30s vs 4 calls, ~5 min)
- ~15%: + Sonnet (3 calls, ~2.5 min)
- ~5%: full cascade (4 calls, ~5.5 min)

Haiku works by receiving pre-fetched context (no tool use needed),
which sidesteps the agentic limitations found earlier.

New files:
- prompts/triage.md       — Tier 1: Haiku fast classification
- prompts/deep-review.md  — Tier 2: Sonnet full review
- prompts/security-audit.md — Tier 3: Opus security audit
- prompts/cascade-action.md — posts review from any resolving tier

Re-reviews (prior marker at different SHA) pass prior review context
to the triage, enabling faster incremental checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix review council findings: 4 major + 3 minor issues

Major fixes:
- Replace invalid 'repository' gh field with 'headRepository'/'headRepositoryOwner'
  across script and all prompts
- Add 'triage-approved' as valid REVIEW_MODE in single-review.md with
  dedicated mode description
- Fix PRIOR_REVIEW_BODY selector: head -1 → tail -1 to match most-recent SHA
- Update dangling 'synthesize.md step 9a' reference to cascade-action.md

Minor fixes:
- Validate PRIOR_REVIEW_BODY contains marker before exporting (anti-injection)
- Write prior review body to temp file to avoid E2BIG on large reviews
- Remove dead SONNET_RC=$? variable; add || true for set -e safety
- Delete orphaned prompts/council/ files (no longer referenced by cascade)
- Fix incremental-mode compare command: document how to derive owner/repo
  from headRepository metadata

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Claude Code workflow (#8)

Implement the org-level reusable workflow caller pattern from petry-projects/.github.
This thin caller delegates all logic to claude-code-reusable.yml and triggers on PR events,
issue comments, and labeled issues.

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add Copilot engine support via REVIEW_ENGINE toggle (#5)

* feat: add Copilot engine support via REVIEW_ENGINE toggle

Abstract LLM invocation into scripts/engine.sh so both Claude and Copilot
engines use the same cascade logic, prompts, and actions — just different
CLIs and model families.

Key changes:
- scripts/engine.sh: new LLM abstraction layer with run_triage/run_agentic
  dispatching to claude or copilot CLI based on REVIEW_ENGINE variable
- scripts/review-one-pr.sh: refactored to source engine.sh, engine-agnostic
  variable names (DELEGATION_ORGS, AI_DELEGATION_ENABLED, DEEP/AUDIT tiers)
- All prompts updated: engine-agnostic tier names, ENGINE_LABEL footers,
  removed hardcoded model names from prose
- Workflow: conditional CLI install, REVIEW_ENGINE/COPILOT_GITHUB_TOKEN vars,
  DELEGATION_ORGS with CLAUDE_ORGS backward compat fallback
- AGENT.md/README.md: documented both engine options and setup

Model mapping:
  Triage:   Haiku 4.5      / GPT-5-mini
  Deep:     Sonnet 4.6     / GPT-5.2
  Audit:    Opus 4.6       / GPT-5.4
  Action:   Sonnet/Opus    / GPT-5.2/5.4

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update prompts/cascade-action.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update prompts/cascade-action.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: address PR review comments

- Fix Copilot CLI install to use gh extension instead of npm
- Replace speculative GPT-5 models with verified identifiers (gpt-4o-mini, o1, o1-pro)
- Fix cascade-action.md deprecated alias to reference CLAUDE_ENABLED
- Change 'Claude delegation' to 'AI delegation' in synthesize.md
- Add triage-approved to mode schema in single-review.md
- Align log label with JSON field in review-one-pr.sh
- Remove unused SMALL_PR_THRESHOLD from workflow
- Add $OUTPUT_FILE to inputs in deep-review.md and security-audit.md

Co-authored-by: don-petry <36422719+don-petry@users.noreply.github.com>

* feat: update to latest GPT-5 models for Copilot engine

- Use gpt-5-mini for triage (lightweight, faster)
- Use gpt-5.2 for deep review and action (improved reasoning)
- Use gpt-5.4 for security audit and single reviewer (most advanced)

Based on latest GitHub Copilot model support in 2025-2026.

Co-authored-by: don-petry <36422719+don-petry@users.noreply.github.com>

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>

* feat: inline Claude Code workflow to access repo secrets directly (#9)

Replaces the thin org-level reusable workflow caller with the full
inlined workflow so CLAUDE_CODE_OAUTH_TOKEN and GH_PAT_WORKFLOWS
can be accessed from this repo's own secrets.

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: cross-engine adversarial rubber duck review (#11)

* feat: add cross-engine adversarial "rubber duck" review to PR cascade

At tier 2, the deep review now runs in parallel with an adversarial
"rubber duck" reviewer from the opposite engine (Claude↔Copilot).
A synthesis step merges both verdicts before deciding to approve or
escalate. Different model families catch different blind spots — this
approach closes ~75% of the performance gap between tiers.

Graceful degradation: if the rubber duck fails (missing creds, CLI
unavailable), the cascade continues with the primary review only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address rubber duck review findings and Copilot comments

- Strip opposing engine credentials in run_duck to prevent cross-engine
  secret leakage (CLAUDE_CODE_OAUTH_TOKEN/COPILOT_GITHUB_TOKEN)
- Add timeout 300 to both duck CLI paths to prevent hangs
- Add $OUTPUT_FILE to rubber-duck.md inputs section
- Add TIER2_RESULT alias at tier 3 handoff (documents combined vs deep)
- Fail fast on primary CLI install; || true only for duck engine
- Add default/error branch to run_duck case statement
- Fix final_risk → risk terminology in synthesize-duck.md
- Add explicit severity bump mapping (info→minor, minor→major)
- Set DUCK_VALID=false on synthesis fallback for accurate FINAL_TIER
- Kill duck process early if deep review fails to avoid wasted latency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: clarify PR review batching issue

* feat: CI gate + poll-loop rebase wait across all review prompts (#14)

* feat: CI gate + poll-loop rebase wait across all review prompts

1. CI gate (review-one-pr.sh step 1b): skip PRs with failing or
   in-progress checks before spending any LLM tokens. Exits with
   code 100 (no-op sentinel) so skipped PRs don't count against the
   MAX_PRS budget. Empty statusCheckRollup (no CI) is treated as
   passing.

2. Poll loop (cascade-action, single-review, synthesize): replace the
   fixed sleep with a bounded poll (6×5 s) that exits as soon as
   mergeStateStatus is no longer BEHIND, eliminating the race between
   update-branch and the subsequent auto-merge call.

3. Triage cleanup: remove the now-redundant "no failing CI" criterion
   since failing-CI PRs are blocked by the shell gate before triage
   runs. Renumber remaining criteria.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address Copilot review comments + document bot account setup

review-one-pr.sh:
- Combine headRefOid + statusCheckRollup into a single gh pr view call
  to reduce API calls and rate-limit exposure
- Tighten CI classification to strict-success: pending check first, then
  require ALL items to be SUCCESS/passing — any other conclusion (NEUTRAL,
  SKIPPED, STALE, STARTUP_FAILURE, etc.) now counts as failing rather
  than silently passing through
- Clarify exit-code 100 comment to reflect its broader "skip sentinel"
  meaning (already-reviewed, ci-failing, ci-pending)

prompts (cascade-action, single-review, synthesize):
- Initialise REBASE_STATUS="BEHIND" before the poll loop so the variable
  is always set; add explicit post-loop guard — if still BEHIND after 30s,
  skip auto-merge entirely and let the next cycle retry

AGENT.md:
- Document the reviewer identity constraint (self-approval is blocked)
- Add step-by-step bot account creation and classic PAT setup instructions
- Renumber existing setup steps 2→3, 3→4, 4→5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: filter failing/pending CI PRs at enumeration stage in list-prs.sh

Add --checks passing to both gh search prs queries so PRs with failing
or in-progress CI never enter the candidate pool. Previously they were
fetched, consumed a CANDIDATE_LIMIT slot, and only skipped once
review-one-pr.sh ran the per-PR CI gate.

Two-layer defence now:
  1. list-prs.sh  — --checks passing excludes them from the candidate list
  2. review-one-pr.sh — strict jq CI gate catches anything that slips
     through (e.g. PRs passed via PR_URL_OVERRIDE)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: treat SKIPPED/NEUTRAL CI conclusions as passing; drop admin:org scope

review-one-pr.sh:
- Extend is_success to include SKIPPED and NEUTRAL conclusions alongside
  SUCCESS. SKIPPED covers path-filtered workflow jobs (e.g. claude-issue
  skips on non-issue events); NEUTRAL covers informational checks that
  don't gate merging. Both were previously falling through to "failing",
  permanently blocking review of any PR with a skipped check.

AGENT.md:
- Remove admin:org from the bot PAT scope recommendation. The scope is
  not needed for PR approval or auto-merge — repo scope is sufficient.
  admin:org grants full org administration (member management, billing,
  team settings) which is excessive blast radius for a review bot token.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: DJ <dj@Rachels-MacBook-Air.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update repo references from self to pr-review-agent

* feat: add car-hunt skill for used vehicle research and ranking

CPM-based (cost per remaining mile) ranking of Honda/Toyota listings
across AutoTrader, CarGurus, and Facebook Marketplace. Includes live
reliability research step (Consumer Reports, JD Power, RepairPal,
CarComplaints) before searching, NHTSA recall/complaint API deep-dive,
and optional Google Sheet write-back.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: fix FB Marketplace links, improve AutoTrader fetch, add Sheets export step

- AutoTrader: stop trying to WebFetch JS-rendered search pages; use
  WebSearch site:autotrader.com to find individual listing URLs, then
  WebFetch those (they have real HTML data)
- FB Marketplace: never output generic placeholder links; use real
  item URLs if Google indexes them, otherwise surface a manual search
  URL and one placeholder row explaining why
- Step 6: document CSV→Sheets export via Drive MCP create_file,
  including base64 encoding, column format, and share URL construction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add Craigslist as explicit source, enforce real-URL-only link rule

- Add Craigslist section with regional sites (bham, huntsville,
  chattanooga, atlanta), correct search URL pattern with sort/filter
  params, and WebFetch approach (CL uses plain HTML, works reliably)
- Link integrity rule: every Link column value must be a real listing
  URL from WebFetch/WebSearch or a manual search URL clearly labeled —
  never a homepage, never a constructed/guessed URL
- Update WebSearch fallback section to cover all sources uniformly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: split zip/radius questions, rename MAX_MILES to MAX_ODOMETER

Question 4 was ambiguous — users confused "Max miles?" (meant odometer)
with search radius. Now asks zip and radius as separate questions (#4 and
mileage on the car itself)" clarification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: include self-authored PRs awaiting review in agent enumeration (#16)

* fix: fix Craigslist link approach, add date_posted with 21-day staleness filter

Craigslist search pages are JS-gated so WebFetch fails; switch to
WebSearch (site:craigslist.org) as primary approach, with direct URL
as fallback. Add date_posted field to every listing and drop any listing
posted more than 21 days ago as likely-sold. Also fix MAX_MILES → MAX_ODOMETER
in the Craigslist search URL template. Add Posted column to ranked table
and Step 6 sheet schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: include self-authored PRs awaiting review in agent enumeration

Remove --checks success filter from authored PR search to include PRs
that require code review (e.g., compliance fixes). CI validation still
occurs per-PR in review-one-pr.sh as a second layer of defence.

This enables the review agent to detect and approve self-authored PRs
that are blocked by review requirements but have passing CI checks.

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add doors/transmission fields, update-mode sheet, fix CarGurus/Craigslist approach

- Fix CarGurus URL section: direct URL pattern 404s, switch to WebSearch-first approach
- Fix Craigslist primary: regional page WebFetch with pagination (was site: WebSearch which returns category pages)
- Add doors (2/4) and transmission (Auto/Manual) to data extraction, output table, and sheet schema
- Add exhaustive coverage mandate: paginate all sources, never stop early, report coverage in output
- Add AutoTrader outage handling: note 503 in summary, fall back to WebSearch cache
- Add update mode for Step 6: single persistent sheet, deduplicate by URL, mark stale listings
- Update sheet columns to include Doors/Trans; fix MIME type note (text/csv only, not xlsx)
- Update scheduling defaults: ZIP=35243, RADIUS=200, MAX_PRICE=6000, YEAR_MIN=2002, MAX_ODOMETER=250000

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add hard disqualification filters for finance-speak, title issues, and scam signals

Skip any listing mentioning down/monthly payments, no title/lien/salvage,
or scam contact redirects (broken messenger, email-only, urgency pricing).
Log disqualified listings separately rather than silently dropping them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add Distance to Car field using Nominatim geocoding + Haversine formula

Geocode user ZIP and each listing city via free Nominatim API (no key needed),
compute straight-line distance × 1.3 road correction factor, display as
"~X mi" in output table (Dist column) and sheet schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address low result count — batch URL extraction, preview-filter before fetch, target 50+ raw

Score candidates from search-page preview data first to avoid fetching
every listing page; only deep-fetch those passing price/mileage preview.
Clarifies why ~15 results was happening and mandates 50+ raw candidates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: make cascade-action prompt explicit about gh pr review --approve

The agent was posting comments instead of approval reviews because the
prompt was too high-level. Rewritten step 5 to contain explicit bash code
blocks that the agent can directly execute, with clear use of:
- gh pr review --approve (not gh pr comment)
- Proper body passing via temp file to handle newlines/special chars
- Explicit variable extraction and idempotency checks

This ensures the review posts as an APPROVED review, not a COMMENTED
review, so GitHub branch protection recognizes it and auto-merge triggers.

* add: cleanup script to fix PRs with comment approvals instead of review approvals

Script identifies PRs that have the agent's marker comment but no actual
approval reviews (symptom of the original bug), and posts proper approval
reviews + enables auto-merge.

Usage: bash scripts/fix-stuck-prs.sh [dry_run=true|false]

* fix: cleanup script now requires bot account token

GitHub blocks self-approvals, so cleanup must run with the bot account's
GH_TOKEN (petry-review-bot), not the PR author's token.

* add: workflow to fix stuck PRs using bot account token

Allows running the cleanup script via workflow_dispatch with access to
GH_PAT (bot account token) from repo secrets.

* fix: pass GH_PAT to checkout action so workflow uses bot account

* debug: simplify account check, add authentication debug output

* docs: add comprehensive petry-review-bot setup instructions

* docs: add GitHub App setup guide (recommended approach)

GitHub App is the secure, recommended way to automate PR reviews:
- Fine-grained permissions
- JWT tokens that auto-expire
- No human account needed
- Better audit trail
- GitHub's recommended approach

Replaces the bot user account approach with a more secure alternative.

* docs: add comprehensive GitHub App setup documentation

- SETUP.md: Quick reference guide with configuration and troubleshooting
- IMPLEMENTATION.md: Technical deep dive on architecture and design decisions
- DOCUMENTATION.md: Index of all documentation files
- Updated README.md with status and quick links
- Updated GITHUB_APP_SETUP.md with implementation notes and actual app ID
- Workflows use GitHub App token generation instead of static PATs

Covers the transition from bot user account to GitHub App authentication for improved security and maintainability.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: silence auth check in fix-stuck-prs when using GitHub App token

GitHub App tokens don't have user scope, so 'gh api user' returns 403.
This is fine - the script still works for PR operations. Suppress the error
so the script completes successfully with app-token fallback label.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: use explicit author instead of @me in stuck PR search

GitHub App tokens don't have user identity, so @me search doesn't work.
Use explicit 'don-petry' author instead to find PRs to fix.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: avoid subshell in while loop to preserve variable state

Using pipe in while loop created subshell where PROBLEM_PRS and FIXED_PRS
counters were incremented but changes didn't persist to parent shell.
Fixed by using process substitution for input instead of pipe.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add comprehensive status report for GitHub App setup

- Overview of completed work
- GitHub App authentication details
- 24 stuck PRs successfully fixed with approval reviews
- Architectural decisions and rationale
- Configuration guide and usage examples
- Known limitations and troubleshooting

System is now fully operational with GitHub App token authentication,
comprehensive documentation, and all infrastructure in place.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: document stuck PR cleanup status and script fixes

Added documentation for:
- GitHub App token compatibility fixes in fix-stuck-prs.sh
  * Changed @me to explicit author (app tokens have no identity)
  * Fixed subshell variable scope (process substitution vs pipe)
  * Silenced expected 403 auth check error
- Current status: 24 PRs have approvals but remain OPEN
  * Auto-merge failed due to missing GitHub App permission
  * Approvals satisfy branch protection requirement
  * Next: expand permissions or manually merge

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add investigation report for PR review agent workflow failures

* script: add backfill-approvals to retroactively apply real approvals

Converts PRs with agent approval comments (but no real GitHub approvals)
to have actual APPROVED reviews. Needed for PRs reviewed before the
gh pr review --approve fix was applied.

Usage:
  scripts/backfill-approvals.sh          # dry-run (preview changes)
  scripts/backfill-approvals.sh false    # apply real approvals

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add backfill-approvals workflow to run as GitHub App bot

Runs backfill-approvals.sh via GitHub Actions so the approvals are posted
by the bot identity, not the PR author — GitHub rejects self-approvals.

Also fixes subshell counter bug in the script (piped while loop lost variable
state; switched to process substitution).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: convert inner while loop to process substitution to preserve counters

Both the outer repo loop and inner PR loop were piped subshells, causing
all counter increments (approved/skipped/failed) to be lost. The summary
always showed 0. Fixed by using < <(...) process substitution for both loops.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* car-hunt: add VIN-deep-dive flow, printable checklists, location-tier ranking

- Step 5.5 pre-test-drive flow: NHTSA VIN decode + per-VIN unrepaired-recall
  automation via Chrome MCP (Honda LWC owner portal documented as not
  automatable; NHTSA web form works).
- §F printable test-drive checklist generator (reportlab/Platypus PDF) with
  cold-start, model-specific red flags, negotiation table, walk-away rules.
- §G fraud / paperwork verification checklist (vehicle-agnostic, reusable):
  walk-away triggers, VIN three-location match, curbstoning, odometer fraud,
  bill of sale, title transfer, payment protection, stolen-vehicle, title-
  jumping, after-purchase steps.
- Step 5.25 head-to-head comparison template with information-asymmetry rule.
- Step 3 Location Quality Tier scoring (Birmingham AL metro table A/B/C/D)
  with adj-CPM modifier; sheet schema gains Adj.CPM + Tier columns.
- Hard-disqualification rules added: non-running engine keywords, mileage
  inconsistency, mandatory description scrape.
- Drive MCP overwrite limitation documented; sheet ID now read from memory
  rather than hardcoded.
- FB Marketplace operational facts: Birmingham AL city ID 107739635926718,
  /search? vs /vehicles? query handling, React-controlled inline composer,
  send-button selector, seller-name extraction regex.
- scheduled-tasks/used-car-search-{morning,afternoon}: tier A/B priority
  flag, sheet ID read from memory.
- Reference PDF generator scripts checked in for reuse.

* fix: re-approve PRs where approval predates last commit (stale approval)

Instead of skipping PRs that already have any APPROVED review, skip only
those where reviewDecision is not REVIEW_REQUIRED. This handles PRs where
a bot approval was posted before a new commit was pushed — GitHub's ruleset
engine treats those approvals as stale even with dismiss_stale_reviews=false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: robustly extract JSON verdict from mixed claude --print output (#17)

Claude's --print mode can prefix the JSON verdict with conversational
preamble text, causing jq to fail with parse errors. Add
extract_verdict_json to engine.sh and wire all three cascade action
call sites in review-one-pr.sh to use it.

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: extract_verdict_json — check dest file first (agent Bash-write style)

The cascade-action agent writes its verdict JSON to \$OUTPUT_FILE via a
Bash tool call, then prints a text summary to stdout. The previous fix
only scanned stdout (the .raw file), which contained no JSON. The agent-
written file at \$dest (= \$OUTPUT_FILE) was already correct — just not
checked. Now check \$dest first before falling back to stdout scan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Enforce MAX_REVIEW_CYCLES cap before running the cascade (#18)

* fix: enforce MAX_REVIEW_CYCLES cap with human-escalation marker

Previously MAX_REVIEW_CYCLES only gated AI delegation in
post-pr-review.sh — the cascade itself ran on every cron tick
regardless of how many cycles had accumulated. Real-world result:
a PR could rack up 9+ review cycles (we observed exactly that on
ContentTwin#100) before any cap took effect.

Add a pre-cascade check in review-one-pr.sh: when the count of
existing `<!-- pr-review-agent v1 sha=... -->` markers is at or
above MAX_REVIEW_CYCLES (default 3), post one escalation comment
marked `<!-- pr-review-agent escalation -->`, label
needs-human-review, request don-petry, and exit 100 (skip
sentinel — doesn't burn the MAX_PRS budget). The escalation
marker doubles as the no-spam guard: subsequent runs detect it
and exit 100 immediately.

Also reuse a single `gh pr view` for both the cycle count and the
escalation-marker check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* review feedback: surface escalation-comment failures, drop dead unset

- Drop `2>/dev/null || true` on `gh pr comment` so a failed escalation
  post is visible in the workflow log instead of silently leaving the
  PR with no marker (which would re-trigger the cap path next tick).
- Reword the cycle-count comment from "AI delegation loops" to "review
  loops" — the cap also catches cascade-only loops where every cycle
  approves and a new commit lands before merge.
- Remove the redundant `unset PR_BODIES` — never exported, no leakage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* Remove car-hunt skill and scheduled tasks (moved to don-petry/don-petry) (#21)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: harden Copilot fallback path and remove stale worktrees (#25)

- gh-copilot install: add --force flag and post-install verification
  warning so silent failures due to built-in alias conflict are visible
  in workflow logs instead of being silently swallowed by `|| true`
- fallback pre-flight check: verify `gh extension list | grep copilot`
  before switching to Copilot engine; if unavailable, skip the PR and
  continue the batch rather than session-aborting and dropping all
  remaining candidates (fixes the 27-PR drop on run #503)
- pin actions/create-github-app-token to SHA for v3.1.1 (Node.js 24
  compatible) to prevent hard break on 2026-06-02 when GitHub forces
  Node.js 24 as default runtime
- remove 6 stale Claude Code worktrees from git tracking; add
  .gitignore entry to prevent future worktrees from being committed
  (eliminates exit-128 warning on every actions/checkout post-job sweep)

Closes #24

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: petry-projects-pr-review-agent[bot] <petry-projects-pr-review-agent[bot]@users.noreply.github.com>

* ci: add pre-flight dedup check to prevent duplicate claude-issue PRs (#26)

Before invoking Claude on a labeled issue, a new shell step queries
for any open PR with a branch matching claude/issue-NNN-* (or a body
containing "Closes #NNN"). If one is found it posts a comment on the
issue pointing to the existing PR and skips the Claude step entirely.
A secondary prompt instruction tells Claude to check first and push to
the existing branch rather than opening a new PR.

Motivated by issue petry-projects/google-app-scripts#171, where the
claude label was re-applied three times and each run created a fresh
PR unaware of the prior attempts.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: migrate to machine user PAT auth (closes #27) (#28)

* refactor: migrate from GitHub App to machine user PAT auth

Closes #27

GitHub Apps cannot be listed in CODEOWNERS, blocking PRs in repos with
require_code_owner_review: true. Switch all workflows to use a machine
user account's fine-grained PAT (DON_PETRY_BOT_GH_PAT secret), which can
join an org team listed in CODEOWNERS.

Workflows: removed actions/create-github-app-token steps in pr-review,
fix-stuck-prs, backfill-approvals, and daily-pr-review-health. All now
use secrets.DON_PETRY_BOT_GH_PAT directly.

Docs: renamed GITHUB_APP_SETUP.md to MACHINE_USER_SETUP.md with full
rewrite covering account creation, CODEOWNERS config, PAT generation,
and rotation. Updated auth sections in IMPLEMENTATION.md, SETUP.md,
STATUS.md, DOCUMENTATION.md, README.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor: switch to org-scoped PAT secret DON_PETRY_BOT_PETRY_PROJECT_PAT

Replace DON_PETRY_BOT_GH_PAT with DON_PETRY_BOT_PETRY_PROJECT_PAT — the
new fine-grained PAT scoped to the petry-projects org (resource owner =
petry-projects). The previous PAT was scoped to the donpetry-bot personal
namespace and had no repository access.

The old DON_PETRY_BOT_GH_PAT secret is retained for any future use against
don-petry's personal repos.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: ignore .claude/scheduled_tasks.lock state file

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* refactor: merge approval repair scripts into single automation

- Consolidate backfill-approvals.sh and fix-stuck-prs.sh into repair-pr-approvals.sh
- Iterate all repos in both orgs instead of assuming author
- Verify no existing APPROVED review before posting
- Copy original agent comment as review body
- Enable auto-merge when posting approval if needed
- Single workflow with 30-min timeout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add @mention trigger for on-demand PR reviews (#30)

Adds repository_dispatch support so commenting @petry-review-bot on any
PR fires an immediate review without waiting for the hourly schedule.

- pr-review.yml: new `repository_dispatch` trigger (type: pr-review-mention),
  per-PR concurrency group for mention runs, FORCE_REVIEW and DRY_RUN
  env vars that handle both workflow_dispatch and repository_dispatch paths
- review-one-pr.sh: FORCE_REVIEW=true bypasses idempotency so a mention
  always runs a fresh cascade even if the head SHA hasn't changed
- templates/mention-listener.yml: deploy to petry-projects/.github;
  listens for @petry-review-bot, validates commenter trust, posts ack,
  sends repository_dispatch (requires Contents:write, not Actions:write)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: migrate to org-wide .github-private convention (#41)

* refactor: parametrize hardcoded identity values for org migration

Replace all hardcoded references to don-petry, petry-review-bot, and
don-petry/pr-review-agent with environment variables that default to
the current values. This allows the agent to be configured for different
orgs/users via repo variables.

Changes:
- Scripts use $REVIEWER_USER, $TARGET_ORG, $BOT_USER, $AGENT_REPO
- Prompts no longer reference specific GitHub usernames
- Workflows use org-level GH_PAT_WORKFLOWS secret instead of
  repo-level DON_PETRY_BOT_PETRY_PROJECT_PAT
- Health check uses context.repo.owner/repo for issue creation
- Variables are set in workflow env block with defaults

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add Copilot custom agent profiles for org-wide use

Create /agents/ directory with three agent profiles:
- pr-reviewer: Multi-tier cascading PR review
- feature-ideator: Feature idea generation and prioritization
- compliance-auditor: Org standards compliance checking

These are Copilot custom agent profiles that become available
org-wide in the .github-private repo convention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Squashed 'frameworks/bmad-method/' content from commit e36f219c

git-subtree-dir: frameworks/bmad-method
git-subtree-split: e36f219c81b6010d4aae423ba12f49edb5b6e31a

* Squashed 'frameworks/spec-kit/' content from commit 11f49ebf

git-subtree-dir: frameworks/spec-kit
git-subtree-split: 11f49ebfb2f6af55345cb4bd9a7906acd211e56f

* Squashed 'frameworks/gsd/' content from commit 304c1a13

git-subtree-dir: frameworks/gsd
git-subtree-split: 304c1a1302564c45af252bbba4bcc5350e7dac3a

* docs: update README for .github-private org infrastructure role

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* ci: add dependabot-automerge.yml workflow (#73)

Adds the required dependabot-automerge.yml workflow from the org
standard template (petry-projects/.github/standards/workflows/).
This is a thin caller stub that delegates to the org-level reusable
workflow.

Closes #48

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Don Petry <don-petry@users.noreply.github.com>

* fix(pr-review): unblock queue starvation from self-authored PRs (#96) (#97)

* fix(pr-review): unblock queue starvation from self-authored PRs (#96)

A self-authored PR sorted first in the candidate list and triggered
"Can not approve your own pull request" — which the session-fatal
abort treated like an engine error, skipping all 28 remaining PRs
on every run.

- list-prs.sh: filter out PRs authored by REVIEWER_USER at enumeration
- post-pr-review.sh: catch the GraphQL self-approval error and exit 100
  (no-op) instead of 1, so a stray self-PR can no longer abort the batch
- engine.sh: gh copilot renamed --target to --agent; fix the rubber-duck
  invocations so tier-2 cross-engine review works again
- review-one-pr.sh: stop appending a duplicate "0" to REVIEW_CYCLE when
  grep -c finds no markers under set -o pipefail (was breaking the
  cycle-cap integer comparison)

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* fix(pr-review): authenticate as bot, harden enumeration, address review

- Workflow now runs as AGENT_USER (default don-petry-bot), distinct from
  REVIEWER_USER (the human, don-petry). The self-approval rejection that
  caused #96 came from the agent and the human sharing one identity.
- list-prs.sh filters self-authored PRs against AGENT_USER, and validates
  AGENT_USER against the GitHub username charset before interpolating into
  the jq filter (Copilot review feedback).
- review-one-pr.sh uses printf '%s\n' instead of echo for PR_BODIES, since
  PR body content is user-authored and could begin with -n/-e or contain
  backslash escapes (Copilot review feedback).
- AGENT.md guideline updated to reflect that self-authored PRs are
  intentionally excluded (CodeRabbit review feedback).

Operator note: rotate the GH_PAT_WORKFLOWS secret to a token owned by
don-petry-bot (with read:org added) for the bot-account behavior to take
effect at runtime.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* refactor(pr-review): collapse to single BOT_USER, route escalations via CODEOWNERS

Drop AGENT_USER and REVIEWER_USER. The workflow now has one identity:
BOT_USER (default don-petry-bot), which both owns the repos to scan and
gets filtered out as the self-approval blocker. Human escalation no
longer hard-codes a single reviewer; instead, scripts/request-codeowners-
review.sh parses CODEOWNERS in the PR's repo and requests review from
every @user / @org/team mention.

- list-prs.sh: BOT_USER for both gh-repo-list and self-author filter
- review-one-pr.sh, post-pr-review.sh: replace --user "$REVIEWER_USER"
  with the CODEOWNERS helper
- repair-pr-approvals.sh: same gh-repo-list switch
- pr-review.yml: drop AGENT_USER/REVIEWER_USER, set BOT_USER default
  to don-petry-bot
- AGENT.md: updated guideline to describe single-identity model

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* refactor(pr-review): extract batch loop, hoist env, drop redundant step

Workflow YAML drops from 231 to 111 lines by moving the per-PR review
loop into scripts/review-batch.sh. The script can be syntax-checked and
exercised locally; the workflow now just wires env vars and dispatches.

- Hoist GH_TOKEN, MAX_PRS, CANDIDATE_LIMIT to job-level env (were
  repeated on individual steps).
- Inline `gh auth status` into the install step; remove the standalone
  Verify auth step (its only output was a one-line auth dump).
- Drop the `if: steps.list.outputs.count != '0'` guard and the step's
  `id`/output: review-batch.sh handles empty input as a no-op.
- Collapse the duplicated summary-string branch in the review loop into
  a single template with a conditional fragment.

No behavior change.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* perf(pr-review): cache claude-code CLI install across runs

`npm install -g @anthropic-ai/claude-code` ran on every workflow start
(~30s). Switch to a per-user npm prefix (~/.npm-global) and cache that
directory via actions/cache, keyed on CLAUDE_CODE_VERSION + runner OS.

A `command -v claude` guard makes the install a no-op on cache hit, so
the only cost on subsequent runs is the cache restore.

CLAUDE_CODE_VERSION defaults to 'latest' (cache persists until manually
flushed); set the repo variable to pin a specific version for fully
reproducible caching.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

---------

Co-authored-by: Claude <noreply@anthropic.com>

* chore: rename GH_PAT_WORKFLOWS secret to DON_PETRY_BOT_GH_PAT (#99)

The previous name was generic and didn't tell you which account the PAT
belonged to. The new name makes the binding explicit: this secret is the
PAT owned by don-petry-bot, used as BOT_USER throughout the PR-review
workflows.

Operator follow-up before this can be merged:
- Add a new repo secret DON_PETRY_BOT_GH_PAT containing the bot's PAT
  (with repo, workflow, and read:org scopes).
- After merge, the old GH_PAT_WORKFLOWS secret can be deleted.

Affected workflows:
- pr-review.yml (1 use)
- claude.yml (4 uses, with || github.token fallback)
- daily-pr-review-health.yml (1 use)
- repair-pr-approvals.yml (1 use)

scripts/pr_review_health.sh's error message is updated to point at the
new name as well.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

Co-authored-by: Claude <noreply@anthropic.com>

* chore: remove frameworks directory (#101)

Co-authored-by: Gemini CLI <gemini-cli@example.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore all erroneously deleted files (items 4-34) (#104)

* revert: restore .github/workflows/daily-pr-review-health.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore .github/workflows/repair-pr-approvals.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore agents/pr-reviewer.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/cascade-action.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/deep-review.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/rubber-duck.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/security-audit.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/shared.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/single-review.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/synthesize-duck.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/synthesize.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/triage.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/engine.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/list-prs.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/post-pr-review.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/pr_review_health.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/repair-pr-approvals.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/request-codeowners-review.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/review-batch.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/review-one-pr.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore templates/mention-listener.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore AGENT.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore BOT_SETUP.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore DOCUMENTATION.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore IMPLEMENTATION.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore MACHINE_USER_SETUP.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore PR_REVIEW_AGENT_FAILURE_REPORT.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore PR_REVIEW_FAILURE_INVESTIGATION.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore SETUP.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore STATUS.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore README.md to pre-change state

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore .github/workflows/daily-pr-review-health.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add Gemini Pro support and optimize PR review fallback chain (#102)

* Merge main and resolve conflicts

* Enhance pr-review workflow: add Gemini support and refactor fallback logic

* Address Copilot review comments: tighten regex, fix fallback summary, and align docs

* docs: move agent documentation to docs/ folder and rename files

* docs: align secret names and fix casing in index

* docs: organize pr-review-agent documentation under dedicated folder

* Address final PR comments: tighten regex, add preflight checks, fix bot names, and cleanup docs

---------

Co-authored-by: Gemini CLI <gemini-cli@example.com>

* fix(claude): sync inlined workflow with org standard (#109)

* fix(claude): sync inlined workflow with org standard

Brings the inlined claude.yml up to parity with
petry-projects/.github/.github/workflows/claude-code-reusable.yml.

Changes:
1. Bot allow list for pull_request_review_comment
   Add coderabbitai[bot], Copilot, copilot-pull-request-reviewer[bot],
   and gemini-code-assist[bot] alongside the existing OWNER/MEMBER/
   COLLABORATOR check. These bots have author_association 'NONE' so
   their review comments were always skipped.

2. check_run trigger + claude-ci-fix job
   Port the CI failure auto-fix feature from the reusable. When a check
   fails on a PR, Claude diagnoses and fixes it automatically.
   Adapted to use DON_PETRY_BOT_GH_PAT (this repo's secret name).

3. claude-code-action SHA bump: v1.0.89 → v1.0.119
   Update both the claude and claude-issue jobs.

4. paths-ignore on pull_request trigger
   Prevents the workflow from firing on PRs that only change
   claude.yml itself, avoiding the Anthropic OIDC validation
   failure (workflow file must match default branch at token exchange).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(claude): guard bot allow list against fork PRs

Same fix as petry-projects/.github PR #238 — add same-repo guard
for bot-triggered pull_request_review_comment runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(claude): address ci-fix self-loop and fork PR security

- Replace Claude Code name prefix check with explicit job name list to
  correctly prevent self-loops (check_run names for inlined workflows
  are bare job names, not workflow-prefixed)
- Add fork PR trust gate in Resolve PR number step: verify head repo
  matches target repo before running Claude with privileged credentials

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: replace inlined claude.yml with standard thin-caller stub

The inlined version embedded all three jobs (claude, claude-ci-fix,
claude-issue) directly and threaded DON_PETRY_BOT_GH_PAT through
checkout tokens and github_token, causing the bot's PAT to author
PRs and comments instead of github-actions[bot].

Replace with the standard thin caller that delegates to the org-level
reusable workflow (claude-code-reusable.yml@v1) via secrets: inherit,
which is the correct pattern per:
petry-projects/.github/standards/workflows/claude.yml

* ci: pin dependabot-automerge reusable workflow to SHA (#130)

Pin petry-projects/.github reusable workflow from @v1 to commit SHA
0cb4bba11d7563bf197ad805f12fb8639e4879e4 to satisfy the action-pinning
compliance policy.

Closes #116

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Don Petry <don-petry@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: restore pr-review.yml content (accidentally emptied in revert)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: restore pr-review.yml (file was accidentally emptied)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(deps): bump petry-projects/.github/.github/workflows/dependency-audit-reusable.yml (#163)

Bumps [petry-projects/.github/.github/workflows/dependency-audit-reusable.yml](https://github.com/petry-projects/.github) from 1 to 2.
- [Commits](https://github.com/petry-projects/.github/compare/v1...v2)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/dependency-audit-reusable.yml
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump petry-projects/.github/.github/workflows/claude-code-reusable.yml (#164)

Bumps [petry-projects/.github/.github/workflows/claude-code-reusable.yml](https://github.com/petry-projects/.github) from 1 to 2.
- [Commits](https://github.com/petry-projects/.github/compare/v1...v2)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/claude-code-reusable.yml
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml (#168)

Bumps [petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml](https://github.com/petry-projects/.github) from 0cb4bba11d7563bf197ad805f12fb8639e4879e4 to b79962331f28d0fc26c09b441471231ff2df04b8.
- [Commits](https://github.com/petry-projects/.github/compare/0cb4bba11d7563bf197ad805f12fb8639e4879e4...b79962331f28d0fc26c09b441471231ff2df04b8)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml
  dependency-version: b79962331f28d0fc26c09b441471231ff2df04b8
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(list-prs): search DELEGATION_ORGS and drop --checks success pre-filter

- Add iteration over all orgs in DELEGATION_ORGS (e.g. don-petry) so PRs
  in those orgs enter the candidate pool. Previously only BOT_USER and
  TARGET_ORG were searched.
- Remove --checks success from org repo searches. GitHub excludes PRs from
  repos with no CI configured when this flag is used, silently dropping all
  PRs from repos like .github, .github-private, google-app-scripts, and
  TalkTerm. review-one-pr.sh already enforces CI gating per-PR and treats
  empty statusCheckRollup as passing, so the pre-filter is redundant and
  harmful.
- Add --limit 200 to gh repo list calls to handle org growth beyond gh's
  default 30-repo cap.

* chore(deps): bump petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml (#173)

Bumps [petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml](https://github.com/petry-projects/.github) from b79962331f28d0fc26c09b441471231ff2df04b8 to 761dd4a1a484a239429ed15d44d068de04fe2ee1.
- [Commits](https://github.com/petry-projects/.github/compare/b79962331f28d0fc26c09b441471231ff2df04b8...761dd4a1a484a239429ed15d44d068de04fe2ee1)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml
  dependency-version: 761dd4a1a484a239429ed15d44d068de04fe2ee1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(prompts/dev-lead): add human prompt template

* feat(prompts/dev-lead): add human-pr prompt template

* feat(dev-lead): implement dev-lead agent Phases 0-6

* fix: per-PR isolation and single-review retry (closes #132)

- review-batch.sh: non-rate-limit per-PR failures (exit code 1) no
  longer abort the session. SESSION ABORTED EARLY is now reserved for
  the rate-limit-on-fallback-engine case (exit code 2) only. All other
  failures are counted and logged; remaining candidates continue.

- review-one-pr.sh: single-review step retries up to
  SINGLE_REVIEW_MAX_RETRIES (default 2) times with a
  SINGLE_REVIEW_RETRY_DELAY_SEC (default 15s) gap before giving up.
  On exhaustion, the PR is flagged needs-human-review and the script
  exits with code 1, which the updated batch treats as a non-fatal
  per-PR failure. Raw model output and stderr are logged on each failed
  attempt for post-mortem visibility.

Root cause of run #25707852006: claude-opus-4-7 returned a verbose
non-JSON response for PR #129; the old code treated that as fatal and
skipped 35 remaining candidates.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: rate-limit detection and per-attempt stderr logs in single-review retry

Address inline review comments on PR #133:

- Rate-limit check: after each run_agentic call, inspect both stdout
  (VERDICT_JSON.raw) and stderr (SINGLE_LOG) with is_rate_limited before
  retrying. A rate-limit match exits immediately with code 2 so
  review-batch.sh can trigger engine fallback — consistent with triage and
  deep-review tiers. Previously a rate-limited single-review would burn all
  retries and exit 1 (per-PR failure), silently leaving the batch on the
  same rate-limited engine for all remaining PRs.

- Per-attempt log files: stderr is now written to
  single-review-attempt-N.log rather than a single overwritten file, so
  no earlier-attempt errors are lost. Each attempt logs its own stderr
  inline on failure; the fallback path cats all attempt logs for
  post-mortem visibility.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* feat(dev-lead): Phase 0 test infrastructure and Phase 1 intent stub

Phase 0: full test harness for the dev-lead agent —
  26 event fixtures (all valid JSON with _test_expected_intent),
  stub claude/gemini engines, mock gh binary, CI failure log sample,
  bats helpers (stub-engine, mock-gh, assert-env, prompt-vars),
  7 prompt templates with VARIABLES declarations, preflight script,
  prompt coverage integration test, and test-dev-lead.yml CI workflow.

Phase 1: dev-lead.yml trigger workflow (all 7 event types, dispatch + ci-relay
  jobs) and dev-lead-intent.sh stub (anti-loop guard live; all other events
  emit skip/not-implemented).  14/14 bats unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): add permissions blocks to test workflow jobs (CodeQL)

* fix(ci): auto-fix for lint / eslint [skip ci-relay]

* feat(dev-lead): Phases 2-6 — CI fix, review fix, issue, engine fallback

Phase 2: run_writer/run_writer_with_fallback in engine.sh, full intent
routing in dev-lead-intent.sh, dev-lead-fix-ci.sh handler, workflow wiring.

Phase 3: dev-lead-fix-reviews.sh handles fix-reviews, fix-bot-comment,
human, human-pr, rebase intents. Full review routing in intent classifier.

Phase 5: dev-lead-fix-issue.sh handles the issue intent with dedup guard
and branch/PR creation.

Phase 6: run_writer_with_fallback with claude→gemini→copilot fallback chain.

Tests: 77 unit tests across 8 new .bats files, all passing. Updated Phase 1
stub tests to reflect actual Phase 2+ routing behavior.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): address review comments — dispatch JSON, dead code, portable stub gh

* fix(dev-lead): pre-flight after intent, fork URL check, review-batch exit code, retry comment

* fix(dev-lead): install bats-core from GitHub to avoid root requirement

* fix(security): move event values to env vars to prevent script injection (SonarCloud)

* fix(security): move CLAUDE_CODE_VERSION to env block (SonarCloud script injection)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* feat(dev-lead): Phase 1.5 — reusable workflow for cross-repo adoption + Phase 7 shadow period

* feat(dev-lead): add reusable workflow (Phase 1.5) and begin shadow period (Phase 7)

- Create .github/workflows/dev-lead-reusable.yml: workflow_call entry
  point for other repos. Checks out .github-private scripts/prompts into
  .dev-lead/, then runs the same intent-classify + handler pipeline as
  dev-lead.yml with PROMPTS_DIR=.dev-lead/prompts/dev-lead.
- Add PROMPTS_DIR env-var support to dev-lead-fix-ci.sh,
  dev-lead-fix-reviews.sh, and dev-lead-fix-issue.sh so the reusable
  workflow can point scripts at the sparse-checkout path without changing
  CWD. Defaults to prompts/dev-lead (backwards-compatible for dev-lead.yml).
- Annotate dev-lead.yml with Phase 7 shadow-period window (2026-05-15
  through ~2026-05-29): claude.yml and dev-lead.yml run in parallel until
  regressions clear, then claude.yml is removed.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* chore(dev-lead): update shadow-period tracking issue ref to #180

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): document PAT requirement and pin ref:main on private checkout

* fix(dev-lead): P1 review fixes — ci-relay in reusable, .dev-lead gitignore, export PROMPTS_DIR, trim header

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): rate-limit detection, separate status, and retry cron (#192) (#196)

* chore(dev-lead): decommission claude.yml, bump ACTION_TIMEOUT_SEC to 600s

- Delete .github/workflows/claude.yml — replaced by dev-lead.yml (shadow
  period complete, tracking issue #180).
- scripts/engine.sh: raise ACTION_TIMEOUT_SEC default 300→600s to reduce
  timeout failures on large-repo fix-ci runs (PR #80 had 3 timeouts).
- AGENTS.md: remove claude.yml immutability exemption; note dev-lead.yml
  as the active AI automation workflow and its edit-via-reusable pattern.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor: replace Claude analysis with pure gh/jq telemetry in health check

* refactor: drop Node/Claude steps from daily-pr-review-health workflow

* rename: daily-pr-review-health → actions-fleet-monitor

* rename: daily-pr-review-health → actions-fleet-monitor

* fix(agents-md): clarify dev-lead.yml vs dev-lead-reusable.yml scope

dev-lead.yml in .github-private runs inline steps (not a caller stub).
Behavior changes for this repo go to dev-lead.yml directly; changes that
affect all org repos via the cross-repo reusable go to dev-lead-reusable.yml.

Addresses Copilot review on PR #194.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): rate-limit detection, separate status, and retry cron

Closes #192. Implements all phases from the revised plan.

**Phase 0 — fix run_writer stdout capture (prerequisite)**
- engine.sh: capture stdout via `tee` to a tempfile so `is_rate_limited`
  can inspect the output; old code read `/tmp/dev-lead-writer-stderr`
  which was never written (claude --print outputs to stdout, not stderr).
  Fallback engines were never tried in practice.
- engine.sh: add `parse_reset_time` to extract ISO timestamp from
  `resets H:MMpm (UTC)` in engine output and write it to
  `/tmp/dev-lead-rate-limit-reset` for callers to embed in markers.

**Phase 1 — separate status=rate-limited from status=failed**
- fix-ci.sh: detect engine exit 2 → post `status=rate-limited` (not
  `status=failed`); embed parsed reset time in marker body; exit 2.
- fix-ci.sh: fix `check_idempotency` to treat `status=rate-limited`
  as retriable — only block on terminal statuses (applied, failed,
  no-changes). Previously, rate-limited markers blocked all retries.
- fix-ci.sh: `count_recent_failures` already filtered to `status=failed`;
  add explicit comment confirming rate-limited markers are excluded.
- fix-ci.sh: add `has_rate_limited_marker` dedup check so the same SHA
  never accumulates more than one rate-limited comment.
- fix-reviews.sh: on engine exit 2 for all five intent types
  (fix-reviews, fix-bot-comment, human, human-pr, rebase), post a
  `status=rate-limited` marker with embedded reset time and exit 2.
- fix-reviews.sh: for `human` and `human-pr` intents, also post a
  user-visible acknowledgment comment so users know their request was
  received and will be retried.

**Phase 2 — scheduled retry cron for fix-ci**
- dev-lead-retry.sh: new script scans all open PRs across TARGET_ORG
  for `status=rate-limited` markers on current HEAD SHA; skips PRs
  whose reset time is still in the future; staggered dispatches (30s
  between repos) to prevent cascading org-level rate-limit hits.
- dev-lead-retry.yml: new workflow — scheduled every 2 hours (dual
  offsets to work around GitH…
don-petry added a commit that referenced this pull request Jun 25, 2026
…ing` skip + "already reviewed" marker blocks approval once CI goes green (#899)

* Add scheduled PR review agent

Hourly GitHub Action that enumerates open PRs (authored by or
review-requested from @me), classifies risk via Claude Code, and
auto-approves LOW/MEDIUM PRs that pass quality gates while escalating
HIGH-risk or gated PRs for human review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Use Claude Code OAuth token instead of API key

Routes agent runs through the Max plan subscription rather than
per-token API billing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Enforce single-PR scope and default to dry-run

- Prompt now hard-forbids enumeration commands and acting on any PR
  other than $PR_URL. Earlier dry-run revealed the agent was reviewing
  the entire backlog in a single invocation, ignoring the per-PR loop.
- DRY_RUN now defaults to true unless repo variable LIVE_MODE=true,
  so the hourly cron is safe by default until the operator opts in.
- AGENT.md documents the LIVE_MODE switch and corrects PAT scopes
  (Issues needs write to create the needs-human-review label).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add 3-model council + commit-SHA-aware iterative review

The single-prompt reviewer is replaced by a council of 3 Claude models,
each with a focused lens, and a synthesizer that combines them:

  - Security      (Opus 4.6)   - auth, secrets, injection, supply chain
  - Correctness   (Sonnet 4.6) - logic, issue alignment, tests, CI
  - Maintainability (Haiku 4.5) - standards, conventions, clarity
  - Synthesizer   (Sonnet 4.6) - max risk, escalate-if-any, dedupe, post

Council members run in parallel, write JSON verdicts to /tmp/council/,
and never touch GitHub. The synthesizer is the only actor that posts.

Every posted review embeds an HTML marker on line 1:
  <!-- pr-review-agent v1 sha=<full-sha> decision=... risk=... -->

scripts/review-one-pr.sh checks for that marker before spending tokens:
- Marker SHA == current head SHA -> skip (already reviewed).
- Marker SHA != current head SHA -> re-review (handles iterative cycles).
- No marker -> first review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Upgrade maintainability lens from Haiku 4.5 to Sonnet 4.6

Haiku couldn't follow the agentic instructions — it asked for env vars
instead of reading them and executing the gh commands. All three council
members now use models capable of multi-step tool use.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Go-live improvements for PR review agent (#2)

- Upgrade actions/checkout v4→v5 (Node.js 20 deprecation)
- Add MAX_PRS cap (default 10) to stay within 60-min job timeout
- Filter drafts at enumeration (--draft=false)
- Fix jq operator-precedence bug in idempotency check
- Add truncation notice log, inline jq comments, docs updates

Council-reviewed: 2 rounds (escalated round 1 on jq bug, approved round 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Claude delegation, auto-merge, and rebase handling (#3)

- Post structured fix-request comments on escalated PRs in Claude-enabled orgs
- Claude's workflow trigger picks up the comment automatically (no @claude tag)
- On approval: auto-merge (--auto --squash), rebase if behind, remove label
- Cycle guard (MAX_REVIEW_CYCLES=3) prevents infinite delegation loops
- New repo variables: CLAUDE_ORGS, MAX_REVIEW_CYCLES

Council-reviewed: dispatch hit rate limit but architecture validated against
petry-projects/.github claude-code-reusable.yml workflow — event flow confirmed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Optimize review: small-PR and incremental fast paths (#4)

* Add review mode optimization: small-PR and incremental fast paths

PRs are now routed to the cheapest review path:
- Small PRs (<10 lines): single Opus call (1 invocation vs 4)
- Re-reviews (prior marker at different SHA): single Opus call with
  prior review context, focused on what changed since last review
- First review of non-small PRs: full 3-member council (unchanged)

New files:
- prompts/single-review.md: combined council+synth prompt for single
  reviewer mode, handles both small and incremental paths

New config:
- SMALL_PR_THRESHOLD repo variable (default 10)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Replace parallel council with cascading review tiers

Haiku triage → Sonnet deep review → Opus security audit, where each
tier only fires if the previous one escalated. This replaces the
3-parallel-members + synthesizer architecture.

Cost impact:
- ~80% of PRs: Haiku + Opus confirm (2 calls, ~30s vs 4 calls, ~5 min)
- ~15%: + Sonnet (3 calls, ~2.5 min)
- ~5%: full cascade (4 calls, ~5.5 min)

Haiku works by receiving pre-fetched context (no tool use needed),
which sidesteps the agentic limitations found earlier.

New files:
- prompts/triage.md       — Tier 1: Haiku fast classification
- prompts/deep-review.md  — Tier 2: Sonnet full review
- prompts/security-audit.md — Tier 3: Opus security audit
- prompts/cascade-action.md — posts review from any resolving tier

Re-reviews (prior marker at different SHA) pass prior review context
to the triage, enabling faster incremental checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix review council findings: 4 major + 3 minor issues

Major fixes:
- Replace invalid 'repository' gh field with 'headRepository'/'headRepositoryOwner'
  across script and all prompts
- Add 'triage-approved' as valid REVIEW_MODE in single-review.md with
  dedicated mode description
- Fix PRIOR_REVIEW_BODY selector: head -1 → tail -1 to match most-recent SHA
- Update dangling 'synthesize.md step 9a' reference to cascade-action.md

Minor fixes:
- Validate PRIOR_REVIEW_BODY contains marker before exporting (anti-injection)
- Write prior review body to temp file to avoid E2BIG on large reviews
- Remove dead SONNET_RC=$? variable; add || true for set -e safety
- Delete orphaned prompts/council/ files (no longer referenced by cascade)
- Fix incremental-mode compare command: document how to derive owner/repo
  from headRepository metadata

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add Claude Code workflow (#8)

Implement the org-level reusable workflow caller pattern from petry-projects/.github.
This thin caller delegates all logic to claude-code-reusable.yml and triggers on PR events,
issue comments, and labeled issues.

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add Copilot engine support via REVIEW_ENGINE toggle (#5)

* feat: add Copilot engine support via REVIEW_ENGINE toggle

Abstract LLM invocation into scripts/engine.sh so both Claude and Copilot
engines use the same cascade logic, prompts, and actions — just different
CLIs and model families.

Key changes:
- scripts/engine.sh: new LLM abstraction layer with run_triage/run_agentic
  dispatching to claude or copilot CLI based on REVIEW_ENGINE variable
- scripts/review-one-pr.sh: refactored to source engine.sh, engine-agnostic
  variable names (DELEGATION_ORGS, AI_DELEGATION_ENABLED, DEEP/AUDIT tiers)
- All prompts updated: engine-agnostic tier names, ENGINE_LABEL footers,
  removed hardcoded model names from prose
- Workflow: conditional CLI install, REVIEW_ENGINE/COPILOT_GITHUB_TOKEN vars,
  DELEGATION_ORGS with CLAUDE_ORGS backward compat fallback
- AGENT.md/README.md: documented both engine options and setup

Model mapping:
  Triage:   Haiku 4.5      / GPT-5-mini
  Deep:     Sonnet 4.6     / GPT-5.2
  Audit:    Opus 4.6       / GPT-5.4
  Action:   Sonnet/Opus    / GPT-5.2/5.4

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update prompts/cascade-action.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update prompts/cascade-action.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: address PR review comments

- Fix Copilot CLI install to use gh extension instead of npm
- Replace speculative GPT-5 models with verified identifiers (gpt-4o-mini, o1, o1-pro)
- Fix cascade-action.md deprecated alias to reference CLAUDE_ENABLED
- Change 'Claude delegation' to 'AI delegation' in synthesize.md
- Add triage-approved to mode schema in single-review.md
- Align log label with JSON field in review-one-pr.sh
- Remove unused SMALL_PR_THRESHOLD from workflow
- Add $OUTPUT_FILE to inputs in deep-review.md and security-audit.md

Co-authored-by: don-petry <36422719+don-petry@users.noreply.github.com>

* feat: update to latest GPT-5 models for Copilot engine

- Use gpt-5-mini for triage (lightweight, faster)
- Use gpt-5.2 for deep review and action (improved reasoning)
- Use gpt-5.4 for security audit and single reviewer (most advanced)

Based on latest GitHub Copilot model support in 2025-2026.

Co-authored-by: don-petry <36422719+don-petry@users.noreply.github.com>

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>

* feat: inline Claude Code workflow to access repo secrets directly (#9)

Replaces the thin org-level reusable workflow caller with the full
inlined workflow so CLAUDE_CODE_OAUTH_TOKEN and GH_PAT_WORKFLOWS
can be accessed from this repo's own secrets.

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: cross-engine adversarial rubber duck review (#11)

* feat: add cross-engine adversarial "rubber duck" review to PR cascade

At tier 2, the deep review now runs in parallel with an adversarial
"rubber duck" reviewer from the opposite engine (Claude↔Copilot).
A synthesis step merges both verdicts before deciding to approve or
escalate. Different model families catch different blind spots — this
approach closes ~75% of the performance gap between tiers.

Graceful degradation: if the rubber duck fails (missing creds, CLI
unavailable), the cascade continues with the primary review only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address rubber duck review findings and Copilot comments

- Strip opposing engine credentials in run_duck to prevent cross-engine
  secret leakage (CLAUDE_CODE_OAUTH_TOKEN/COPILOT_GITHUB_TOKEN)
- Add timeout 300 to both duck CLI paths to prevent hangs
- Add $OUTPUT_FILE to rubber-duck.md inputs section
- Add TIER2_RESULT alias at tier 3 handoff (documents combined vs deep)
- Fail fast on primary CLI install; || true only for duck engine
- Add default/error branch to run_duck case statement
- Fix final_risk → risk terminology in synthesize-duck.md
- Add explicit severity bump mapping (info→minor, minor→major)
- Set DUCK_VALID=false on synthesis fallback for accurate FINAL_TIER
- Kill duck process early if deep review fails to avoid wasted latency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: clarify PR review batching issue

* feat: CI gate + poll-loop rebase wait across all review prompts (#14)

* feat: CI gate + poll-loop rebase wait across all review prompts

1. CI gate (review-one-pr.sh step 1b): skip PRs with failing or
   in-progress checks before spending any LLM tokens. Exits with
   code 100 (no-op sentinel) so skipped PRs don't count against the
   MAX_PRS budget. Empty statusCheckRollup (no CI) is treated as
   passing.

2. Poll loop (cascade-action, single-review, synthesize): replace the
   fixed sleep with a bounded poll (6×5 s) that exits as soon as
   mergeStateStatus is no longer BEHIND, eliminating the race between
   update-branch and the subsequent auto-merge call.

3. Triage cleanup: remove the now-redundant "no failing CI" criterion
   since failing-CI PRs are blocked by the shell gate before triage
   runs. Renumber remaining criteria.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address Copilot review comments + document bot account setup

review-one-pr.sh:
- Combine headRefOid + statusCheckRollup into a single gh pr view call
  to reduce API calls and rate-limit exposure
- Tighten CI classification to strict-success: pending check first, then
  require ALL items to be SUCCESS/passing — any other conclusion (NEUTRAL,
  SKIPPED, STALE, STARTUP_FAILURE, etc.) now counts as failing rather
  than silently passing through
- Clarify exit-code 100 comment to reflect its broader "skip sentinel"
  meaning (already-reviewed, ci-failing, ci-pending)

prompts (cascade-action, single-review, synthesize):
- Initialise REBASE_STATUS="BEHIND" before the poll loop so the variable
  is always set; add explicit post-loop guard — if still BEHIND after 30s,
  skip auto-merge entirely and let the next cycle retry

AGENT.md:
- Document the reviewer identity constraint (self-approval is blocked)
- Add step-by-step bot account creation and classic PAT setup instructions
- Renumber existing setup steps 2→3, 3→4, 4→5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: filter failing/pending CI PRs at enumeration stage in list-prs.sh

Add --checks passing to both gh search prs queries so PRs with failing
or in-progress CI never enter the candidate pool. Previously they were
fetched, consumed a CANDIDATE_LIMIT slot, and only skipped once
review-one-pr.sh ran the per-PR CI gate.

Two-layer defence now:
  1. list-prs.sh  — --checks passing excludes them from the candidate list
  2. review-one-pr.sh — strict jq CI gate catches anything that slips
     through (e.g. PRs passed via PR_URL_OVERRIDE)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: treat SKIPPED/NEUTRAL CI conclusions as passing; drop admin:org scope

review-one-pr.sh:
- Extend is_success to include SKIPPED and NEUTRAL conclusions alongside
  SUCCESS. SKIPPED covers path-filtered workflow jobs (e.g. claude-issue
  skips on non-issue events); NEUTRAL covers informational checks that
  don't gate merging. Both were previously falling through to "failing",
  permanently blocking review of any PR with a skipped check.

AGENT.md:
- Remove admin:org from the bot PAT scope recommendation. The scope is
  not needed for PR approval or auto-merge — repo scope is sufficient.
  admin:org grants full org administration (member management, billing,
  team settings) which is excessive blast radius for a review bot token.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: DJ <dj@Rachels-MacBook-Air.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update repo references from self to pr-review-agent

* feat: add car-hunt skill for used vehicle research and ranking

CPM-based (cost per remaining mile) ranking of Honda/Toyota listings
across AutoTrader, CarGurus, and Facebook Marketplace. Includes live
reliability research step (Consumer Reports, JD Power, RepairPal,
CarComplaints) before searching, NHTSA recall/complaint API deep-dive,
and optional Google Sheet write-back.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: fix FB Marketplace links, improve AutoTrader fetch, add Sheets export step

- AutoTrader: stop trying to WebFetch JS-rendered search pages; use
  WebSearch site:autotrader.com to find individual listing URLs, then
  WebFetch those (they have real HTML data)
- FB Marketplace: never output generic placeholder links; use real
  item URLs if Google indexes them, otherwise surface a manual search
  URL and one placeholder row explaining why
- Step 6: document CSV→Sheets export via Drive MCP create_file,
  including base64 encoding, column format, and share URL construction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add Craigslist as explicit source, enforce real-URL-only link rule

- Add Craigslist section with regional sites (bham, huntsville,
  chattanooga, atlanta), correct search URL pattern with sort/filter
  params, and WebFetch approach (CL uses plain HTML, works reliably)
- Link integrity rule: every Link column value must be a real listing
  URL from WebFetch/WebSearch or a manual search URL clearly labeled —
  never a homepage, never a constructed/guessed URL
- Update WebSearch fallback section to cover all sources uniformly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: split zip/radius questions, rename MAX_MILES to MAX_ODOMETER

Question 4 was ambiguous — users confused "Max miles?" (meant odometer)
with search radius. Now asks zip and radius as separate questions (#4 and
mileage on the car itself)" clarification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: include self-authored PRs awaiting review in agent enumeration (#16)

* fix: fix Craigslist link approach, add date_posted with 21-day staleness filter

Craigslist search pages are JS-gated so WebFetch fails; switch to
WebSearch (site:craigslist.org) as primary approach, with direct URL
as fallback. Add date_posted field to every listing and drop any listing
posted more than 21 days ago as likely-sold. Also fix MAX_MILES → MAX_ODOMETER
in the Craigslist search URL template. Add Posted column to ranked table
and Step 6 sheet schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: include self-authored PRs awaiting review in agent enumeration

Remove --checks success filter from authored PR search to include PRs
that require code review (e.g., compliance fixes). CI validation still
occurs per-PR in review-one-pr.sh as a second layer of defence.

This enables the review agent to detect and approve self-authored PRs
that are blocked by review requirements but have passing CI checks.

---------

Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add doors/transmission fields, update-mode sheet, fix CarGurus/Craigslist approach

- Fix CarGurus URL section: direct URL pattern 404s, switch to WebSearch-first approach
- Fix Craigslist primary: regional page WebFetch with pagination (was site: WebSearch which returns category pages)
- Add doors (2/4) and transmission (Auto/Manual) to data extraction, output table, and sheet schema
- Add exhaustive coverage mandate: paginate all sources, never stop early, report coverage in output
- Add AutoTrader outage handling: note 503 in summary, fall back to WebSearch cache
- Add update mode for Step 6: single persistent sheet, deduplicate by URL, mark stale listings
- Update sheet columns to include Doors/Trans; fix MIME type note (text/csv only, not xlsx)
- Update scheduling defaults: ZIP=35243, RADIUS=200, MAX_PRICE=6000, YEAR_MIN=2002, MAX_ODOMETER=250000

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add hard disqualification filters for finance-speak, title issues, and scam signals

Skip any listing mentioning down/monthly payments, no title/lien/salvage,
or scam contact redirects (broken messenger, email-only, urgency pricing).
Log disqualified listings separately rather than silently dropping them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add Distance to Car field using Nominatim geocoding + Haversine formula

Geocode user ZIP and each listing city via free Nominatim API (no key needed),
compute straight-line distance × 1.3 road correction factor, display as
"~X mi" in output table (Dist column) and sheet schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address low result count — batch URL extraction, preview-filter before fetch, target 50+ raw

Score candidates from search-page preview data first to avoid fetching
every listing page; only deep-fetch those passing price/mileage preview.
Clarifies why ~15 results was happening and mandates 50+ raw candidates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: make cascade-action prompt explicit about gh pr review --approve

The agent was posting comments instead of approval reviews because the
prompt was too high-level. Rewritten step 5 to contain explicit bash code
blocks that the agent can directly execute, with clear use of:
- gh pr review --approve (not gh pr comment)
- Proper body passing via temp file to handle newlines/special chars
- Explicit variable extraction and idempotency checks

This ensures the review posts as an APPROVED review, not a COMMENTED
review, so GitHub branch protection recognizes it and auto-merge triggers.

* add: cleanup script to fix PRs with comment approvals instead of review approvals

Script identifies PRs that have the agent's marker comment but no actual
approval reviews (symptom of the original bug), and posts proper approval
reviews + enables auto-merge.

Usage: bash scripts/fix-stuck-prs.sh [dry_run=true|false]

* fix: cleanup script now requires bot account token

GitHub blocks self-approvals, so cleanup must run with the bot account's
GH_TOKEN (petry-review-bot), not the PR author's token.

* add: workflow to fix stuck PRs using bot account token

Allows running the cleanup script via workflow_dispatch with access to
GH_PAT (bot account token) from repo secrets.

* fix: pass GH_PAT to checkout action so workflow uses bot account

* debug: simplify account check, add authentication debug output

* docs: add comprehensive petry-review-bot setup instructions

* docs: add GitHub App setup guide (recommended approach)

GitHub App is the secure, recommended way to automate PR reviews:
- Fine-grained permissions
- JWT tokens that auto-expire
- No human account needed
- Better audit trail
- GitHub's recommended approach

Replaces the bot user account approach with a more secure alternative.

* docs: add comprehensive GitHub App setup documentation

- SETUP.md: Quick reference guide with configuration and troubleshooting
- IMPLEMENTATION.md: Technical deep dive on architecture and design decisions
- DOCUMENTATION.md: Index of all documentation files
- Updated README.md with status and quick links
- Updated GITHUB_APP_SETUP.md with implementation notes and actual app ID
- Workflows use GitHub App token generation instead of static PATs

Covers the transition from bot user account to GitHub App authentication for improved security and maintainability.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: silence auth check in fix-stuck-prs when using GitHub App token

GitHub App tokens don't have user scope, so 'gh api user' returns 403.
This is fine - the script still works for PR operations. Suppress the error
so the script completes successfully with app-token fallback label.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: use explicit author instead of @me in stuck PR search

GitHub App tokens don't have user identity, so @me search doesn't work.
Use explicit 'don-petry' author instead to find PRs to fix.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: avoid subshell in while loop to preserve variable state

Using pipe in while loop created subshell where PROBLEM_PRS and FIXED_PRS
counters were incremented but changes didn't persist to parent shell.
Fixed by using process substitution for input instead of pipe.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add comprehensive status report for GitHub App setup

- Overview of completed work
- GitHub App authentication details
- 24 stuck PRs successfully fixed with approval reviews
- Architectural decisions and rationale
- Configuration guide and usage examples
- Known limitations and troubleshooting

System is now fully operational with GitHub App token authentication,
comprehensive documentation, and all infrastructure in place.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: document stuck PR cleanup status and script fixes

Added documentation for:
- GitHub App token compatibility fixes in fix-stuck-prs.sh
  * Changed @me to explicit author (app tokens have no identity)
  * Fixed subshell variable scope (process substitution vs pipe)
  * Silenced expected 403 auth check error
- Current status: 24 PRs have approvals but remain OPEN
  * Auto-merge failed due to missing GitHub App permission
  * Approvals satisfy branch protection requirement
  * Next: expand permissions or manually merge

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add investigation report for PR review agent workflow failures

* script: add backfill-approvals to retroactively apply real approvals

Converts PRs with agent approval comments (but no real GitHub approvals)
to have actual APPROVED reviews. Needed for PRs reviewed before the
gh pr review --approve fix was applied.

Usage:
  scripts/backfill-approvals.sh          # dry-run (preview changes)
  scripts/backfill-approvals.sh false    # apply real approvals

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add backfill-approvals workflow to run as GitHub App bot

Runs backfill-approvals.sh via GitHub Actions so the approvals are posted
by the bot identity, not the PR author — GitHub rejects self-approvals.

Also fixes subshell counter bug in the script (piped while loop lost variable
state; switched to process substitution).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: convert inner while loop to process substitution to preserve counters

Both the outer repo loop and inner PR loop were piped subshells, causing
all counter increments (approved/skipped/failed) to be lost. The summary
always showed 0. Fixed by using < <(...) process substitution for both loops.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* car-hunt: add VIN-deep-dive flow, printable checklists, location-tier ranking

- Step 5.5 pre-test-drive flow: NHTSA VIN decode + per-VIN unrepaired-recall
  automation via Chrome MCP (Honda LWC owner portal documented as not
  automatable; NHTSA web form works).
- §F printable test-drive checklist generator (reportlab/Platypus PDF) with
  cold-start, model-specific red flags, negotiation table, walk-away rules.
- §G fraud / paperwork verification checklist (vehicle-agnostic, reusable):
  walk-away triggers, VIN three-location match, curbstoning, odometer fraud,
  bill of sale, title transfer, payment protection, stolen-vehicle, title-
  jumping, after-purchase steps.
- Step 5.25 head-to-head comparison template with information-asymmetry rule.
- Step 3 Location Quality Tier scoring (Birmingham AL metro table A/B/C/D)
  with adj-CPM modifier; sheet schema gains Adj.CPM + Tier columns.
- Hard-disqualification rules added: non-running engine keywords, mileage
  inconsistency, mandatory description scrape.
- Drive MCP overwrite limitation documented; sheet ID now read from memory
  rather than hardcoded.
- FB Marketplace operational facts: Birmingham AL city ID 107739635926718,
  /search? vs /vehicles? query handling, React-controlled inline composer,
  send-button selector, seller-name extraction regex.
- scheduled-tasks/used-car-search-{morning,afternoon}: tier A/B priority
  flag, sheet ID read from memory.
- Reference PDF generator scripts checked in for reuse.

* fix: re-approve PRs where approval predates last commit (stale approval)

Instead of skipping PRs that already have any APPROVED review, skip only
those where reviewDecision is not REVIEW_REQUIRED. This handles PRs where
a bot approval was posted before a new commit was pushed — GitHub's ruleset
engine treats those approvals as stale even with dismiss_stale_reviews=false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: robustly extract JSON verdict from mixed claude --print output (#17)

Claude's --print mode can prefix the JSON verdict with conversational
preamble text, causing jq to fail with parse errors. Add
extract_verdict_json to engine.sh and wire all three cascade action
call sites in review-one-pr.sh to use it.

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: extract_verdict_json — check dest file first (agent Bash-write style)

The cascade-action agent writes its verdict JSON to \$OUTPUT_FILE via a
Bash tool call, then prints a text summary to stdout. The previous fix
only scanned stdout (the .raw file), which contained no JSON. The agent-
written file at \$dest (= \$OUTPUT_FILE) was already correct — just not
checked. Now check \$dest first before falling back to stdout scan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Enforce MAX_REVIEW_CYCLES cap before running the cascade (#18)

* fix: enforce MAX_REVIEW_CYCLES cap with human-escalation marker

Previously MAX_REVIEW_CYCLES only gated AI delegation in
post-pr-review.sh — the cascade itself ran on every cron tick
regardless of how many cycles had accumulated. Real-world result:
a PR could rack up 9+ review cycles (we observed exactly that on
ContentTwin#100) before any cap took effect.

Add a pre-cascade check in review-one-pr.sh: when the count of
existing `<!-- pr-review-agent v1 sha=... -->` markers is at or
above MAX_REVIEW_CYCLES (default 3), post one escalation comment
marked `<!-- pr-review-agent escalation -->`, label
needs-human-review, request don-petry, and exit 100 (skip
sentinel — doesn't burn the MAX_PRS budget). The escalation
marker doubles as the no-spam guard: subsequent runs detect it
and exit 100 immediately.

Also reuse a single `gh pr view` for both the cycle count and the
escalation-marker check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* review feedback: surface escalation-comment failures, drop dead unset

- Drop `2>/dev/null || true` on `gh pr comment` so a failed escalation
  post is visible in the workflow log instead of silently leaving the
  PR with no marker (which would re-trigger the cap path next tick).
- Reword the cycle-count comment from "AI delegation loops" to "review
  loops" — the cap also catches cascade-only loops where every cycle
  approves and a new commit lands before merge.
- Remove the redundant `unset PR_BODIES` — never exported, no leakage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* Remove car-hunt skill and scheduled tasks (moved to don-petry/don-petry) (#21)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: harden Copilot fallback path and remove stale worktrees (#25)

- gh-copilot install: add --force flag and post-install verification
  warning so silent failures due to built-in alias conflict are visible
  in workflow logs instead of being silently swallowed by `|| true`
- fallback pre-flight check: verify `gh extension list | grep copilot`
  before switching to Copilot engine; if unavailable, skip the PR and
  continue the batch rather than session-aborting and dropping all
  remaining candidates (fixes the 27-PR drop on run #503)
- pin actions/create-github-app-token to SHA for v3.1.1 (Node.js 24
  compatible) to prevent hard break on 2026-06-02 when GitHub forces
  Node.js 24 as default runtime
- remove 6 stale Claude Code worktrees from git tracking; add
  .gitignore entry to prevent future worktrees from being committed
  (eliminates exit-128 warning on every actions/checkout post-job sweep)

Closes #24

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: petry-projects-pr-review-agent[bot] <petry-projects-pr-review-agent[bot]@users.noreply.github.com>

* ci: add pre-flight dedup check to prevent duplicate claude-issue PRs (#26)

Before invoking Claude on a labeled issue, a new shell step queries
for any open PR with a branch matching claude/issue-NNN-* (or a body
containing "Closes #NNN"). If one is found it posts a comment on the
issue pointing to the existing PR and skips the Claude step entirely.
A secondary prompt instruction tells Claude to check first and push to
the existing branch rather than opening a new PR.

Motivated by issue petry-projects/google-app-scripts#171, where the
claude label was re-applied three times and each run created a fresh
PR unaware of the prior attempts.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: migrate to machine user PAT auth (closes #27) (#28)

* refactor: migrate from GitHub App to machine user PAT auth

Closes #27

GitHub Apps cannot be listed in CODEOWNERS, blocking PRs in repos with
require_code_owner_review: true. Switch all workflows to use a machine
user account's fine-grained PAT (DON_PETRY_BOT_GH_PAT secret), which can
join an org team listed in CODEOWNERS.

Workflows: removed actions/create-github-app-token steps in pr-review,
fix-stuck-prs, backfill-approvals, and daily-pr-review-health. All now
use secrets.DON_PETRY_BOT_GH_PAT directly.

Docs: renamed GITHUB_APP_SETUP.md to MACHINE_USER_SETUP.md with full
rewrite covering account creation, CODEOWNERS config, PAT generation,
and rotation. Updated auth sections in IMPLEMENTATION.md, SETUP.md,
STATUS.md, DOCUMENTATION.md, README.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor: switch to org-scoped PAT secret DON_PETRY_BOT_PETRY_PROJECT_PAT

Replace DON_PETRY_BOT_GH_PAT with DON_PETRY_BOT_PETRY_PROJECT_PAT — the
new fine-grained PAT scoped to the petry-projects org (resource owner =
petry-projects). The previous PAT was scoped to the donpetry-bot personal
namespace and had no repository access.

The old DON_PETRY_BOT_GH_PAT secret is retained for any future use against
don-petry's personal repos.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: ignore .claude/scheduled_tasks.lock state file

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* refactor: merge approval repair scripts into single automation

- Consolidate backfill-approvals.sh and fix-stuck-prs.sh into repair-pr-approvals.sh
- Iterate all repos in both orgs instead of assuming author
- Verify no existing APPROVED review before posting
- Copy original agent comment as review body
- Enable auto-merge when posting approval if needed
- Single workflow with 30-min timeout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add @mention trigger for on-demand PR reviews (#30)

Adds repository_dispatch support so commenting @petry-review-bot on any
PR fires an immediate review without waiting for the hourly schedule.

- pr-review.yml: new `repository_dispatch` trigger (type: pr-review-mention),
  per-PR concurrency group for mention runs, FORCE_REVIEW and DRY_RUN
  env vars that handle both workflow_dispatch and repository_dispatch paths
- review-one-pr.sh: FORCE_REVIEW=true bypasses idempotency so a mention
  always runs a fresh cascade even if the head SHA hasn't changed
- templates/mention-listener.yml: deploy to petry-projects/.github;
  listens for @petry-review-bot, validates commenter trust, posts ack,
  sends repository_dispatch (requires Contents:write, not Actions:write)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: migrate to org-wide .github-private convention (#41)

* refactor: parametrize hardcoded identity values for org migration

Replace all hardcoded references to don-petry, petry-review-bot, and
don-petry/pr-review-agent with environment variables that default to
the current values. This allows the agent to be configured for different
orgs/users via repo variables.

Changes:
- Scripts use $REVIEWER_USER, $TARGET_ORG, $BOT_USER, $AGENT_REPO
- Prompts no longer reference specific GitHub usernames
- Workflows use org-level GH_PAT_WORKFLOWS secret instead of
  repo-level DON_PETRY_BOT_PETRY_PROJECT_PAT
- Health check uses context.repo.owner/repo for issue creation
- Variables are set in workflow env block with defaults

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add Copilot custom agent profiles for org-wide use

Create /agents/ directory with three agent profiles:
- pr-reviewer: Multi-tier cascading PR review
- feature-ideator: Feature idea generation and prioritization
- compliance-auditor: Org standards compliance checking

These are Copilot custom agent profiles that become available
org-wide in the .github-private repo convention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Squashed 'frameworks/bmad-method/' content from commit e36f219c

git-subtree-dir: frameworks/bmad-method
git-subtree-split: e36f219c81b6010d4aae423ba12f49edb5b6e31a

* Squashed 'frameworks/spec-kit/' content from commit 11f49ebf

git-subtree-dir: frameworks/spec-kit
git-subtree-split: 11f49ebfb2f6af55345cb4bd9a7906acd211e56f

* Squashed 'frameworks/gsd/' content from commit 304c1a13

git-subtree-dir: frameworks/gsd
git-subtree-split: 304c1a1302564c45af252bbba4bcc5350e7dac3a

* docs: update README for .github-private org infrastructure role

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* ci: add dependabot-automerge.yml workflow (#73)

Adds the required dependabot-automerge.yml workflow from the org
standard template (petry-projects/.github/standards/workflows/).
This is a thin caller stub that delegates to the org-level reusable
workflow.

Closes #48

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Don Petry <don-petry@users.noreply.github.com>

* fix(pr-review): unblock queue starvation from self-authored PRs (#96) (#97)

* fix(pr-review): unblock queue starvation from self-authored PRs (#96)

A self-authored PR sorted first in the candidate list and triggered
"Can not approve your own pull request" — which the session-fatal
abort treated like an engine error, skipping all 28 remaining PRs
on every run.

- list-prs.sh: filter out PRs authored by REVIEWER_USER at enumeration
- post-pr-review.sh: catch the GraphQL self-approval error and exit 100
  (no-op) instead of 1, so a stray self-PR can no longer abort the batch
- engine.sh: gh copilot renamed --target to --agent; fix the rubber-duck
  invocations so tier-2 cross-engine review works again
- review-one-pr.sh: stop appending a duplicate "0" to REVIEW_CYCLE when
  grep -c finds no markers under set -o pipefail (was breaking the
  cycle-cap integer comparison)

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* fix(pr-review): authenticate as bot, harden enumeration, address review

- Workflow now runs as AGENT_USER (default don-petry-bot), distinct from
  REVIEWER_USER (the human, don-petry). The self-approval rejection that
  caused #96 came from the agent and the human sharing one identity.
- list-prs.sh filters self-authored PRs against AGENT_USER, and validates
  AGENT_USER against the GitHub username charset before interpolating into
  the jq filter (Copilot review feedback).
- review-one-pr.sh uses printf '%s\n' instead of echo for PR_BODIES, since
  PR body content is user-authored and could begin with -n/-e or contain
  backslash escapes (Copilot review feedback).
- AGENT.md guideline updated to reflect that self-authored PRs are
  intentionally excluded (CodeRabbit review feedback).

Operator note: rotate the GH_PAT_WORKFLOWS secret to a token owned by
don-petry-bot (with read:org added) for the bot-account behavior to take
effect at runtime.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* refactor(pr-review): collapse to single BOT_USER, route escalations via CODEOWNERS

Drop AGENT_USER and REVIEWER_USER. The workflow now has one identity:
BOT_USER (default don-petry-bot), which both owns the repos to scan and
gets filtered out as the self-approval blocker. Human escalation no
longer hard-codes a single reviewer; instead, scripts/request-codeowners-
review.sh parses CODEOWNERS in the PR's repo and requests review from
every @user / @org/team mention.

- list-prs.sh: BOT_USER for both gh-repo-list and self-author filter
- review-one-pr.sh, post-pr-review.sh: replace --user "$REVIEWER_USER"
  with the CODEOWNERS helper
- repair-pr-approvals.sh: same gh-repo-list switch
- pr-review.yml: drop AGENT_USER/REVIEWER_USER, set BOT_USER default
  to don-petry-bot
- AGENT.md: updated guideline to describe single-identity model

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* refactor(pr-review): extract batch loop, hoist env, drop redundant step

Workflow YAML drops from 231 to 111 lines by moving the per-PR review
loop into scripts/review-batch.sh. The script can be syntax-checked and
exercised locally; the workflow now just wires env vars and dispatches.

- Hoist GH_TOKEN, MAX_PRS, CANDIDATE_LIMIT to job-level env (were
  repeated on individual steps).
- Inline `gh auth status` into the install step; remove the standalone
  Verify auth step (its only output was a one-line auth dump).
- Drop the `if: steps.list.outputs.count != '0'` guard and the step's
  `id`/output: review-batch.sh handles empty input as a no-op.
- Collapse the duplicated summary-string branch in the review loop into
  a single template with a conditional fragment.

No behavior change.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

* perf(pr-review): cache claude-code CLI install across runs

`npm install -g @anthropic-ai/claude-code` ran on every workflow start
(~30s). Switch to a per-user npm prefix (~/.npm-global) and cache that
directory via actions/cache, keyed on CLAUDE_CODE_VERSION + runner OS.

A `command -v claude` guard makes the install a no-op on cache hit, so
the only cost on subsequent runs is the cache restore.

CLAUDE_CODE_VERSION defaults to 'latest' (cache persists until manually
flushed); set the repo variable to pin a specific version for fully
reproducible caching.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

---------

Co-authored-by: Claude <noreply@anthropic.com>

* chore: rename GH_PAT_WORKFLOWS secret to DON_PETRY_BOT_GH_PAT (#99)

The previous name was generic and didn't tell you which account the PAT
belonged to. The new name makes the binding explicit: this secret is the
PAT owned by don-petry-bot, used as BOT_USER throughout the PR-review
workflows.

Operator follow-up before this can be merged:
- Add a new repo secret DON_PETRY_BOT_GH_PAT containing the bot's PAT
  (with repo, workflow, and read:org scopes).
- After merge, the old GH_PAT_WORKFLOWS secret can be deleted.

Affected workflows:
- pr-review.yml (1 use)
- claude.yml (4 uses, with || github.token fallback)
- daily-pr-review-health.yml (1 use)
- repair-pr-approvals.yml (1 use)

scripts/pr_review_health.sh's error message is updated to point at the
new name as well.

https://claude.ai/code/session_01EacTxiHSUhR6kxppXpmxig

Co-authored-by: Claude <noreply@anthropic.com>

* chore: remove frameworks directory (#101)

Co-authored-by: Gemini CLI <gemini-cli@example.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: deprecate pr-review-agent — remove all traces

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore all erroneously deleted files (items 4-34) (#104)

* revert: restore .github/workflows/daily-pr-review-health.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore .github/workflows/repair-pr-approvals.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore agents/pr-reviewer.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/cascade-action.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/deep-review.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/rubber-duck.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/security-audit.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/shared.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/single-review.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/synthesize-duck.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/synthesize.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore prompts/triage.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/engine.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/list-prs.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/post-pr-review.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/pr_review_health.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/repair-pr-approvals.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/request-codeowners-review.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/review-batch.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore scripts/review-one-pr.sh

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore templates/mention-listener.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore AGENT.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore BOT_SETUP.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore DOCUMENTATION.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore IMPLEMENTATION.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore MACHINE_USER_SETUP.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore PR_REVIEW_AGENT_FAILURE_REPORT.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore PR_REVIEW_FAILURE_INVESTIGATION.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore SETUP.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore STATUS.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore README.md to pre-change state

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* revert: restore .github/workflows/daily-pr-review-health.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add Gemini Pro support and optimize PR review fallback chain (#102)

* Merge main and resolve conflicts

* Enhance pr-review workflow: add Gemini support and refactor fallback logic

* Address Copilot review comments: tighten regex, fix fallback summary, and align docs

* docs: move agent documentation to docs/ folder and rename files

* docs: align secret names and fix casing in index

* docs: organize pr-review-agent documentation under dedicated folder

* Address final PR comments: tighten regex, add preflight checks, fix bot names, and cleanup docs

---------

Co-authored-by: Gemini CLI <gemini-cli@example.com>

* fix(claude): sync inlined workflow with org standard (#109)

* fix(claude): sync inlined workflow with org standard

Brings the inlined claude.yml up to parity with
petry-projects/.github/.github/workflows/claude-code-reusable.yml.

Changes:
1. Bot allow list for pull_request_review_comment
   Add coderabbitai[bot], Copilot, copilot-pull-request-reviewer[bot],
   and gemini-code-assist[bot] alongside the existing OWNER/MEMBER/
   COLLABORATOR check. These bots have author_association 'NONE' so
   their review comments were always skipped.

2. check_run trigger + claude-ci-fix job
   Port the CI failure auto-fix feature from the reusable. When a check
   fails on a PR, Claude diagnoses and fixes it automatically.
   Adapted to use DON_PETRY_BOT_GH_PAT (this repo's secret name).

3. claude-code-action SHA bump: v1.0.89 → v1.0.119
   Update both the claude and claude-issue jobs.

4. paths-ignore on pull_request trigger
   Prevents the workflow from firing on PRs that only change
   claude.yml itself, avoiding the Anthropic OIDC validation
   failure (workflow file must match default branch at token exchange).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(claude): guard bot allow list against fork PRs

Same fix as petry-projects/.github PR #238 — add same-repo guard
for bot-triggered pull_request_review_comment runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(claude): address ci-fix self-loop and fork PR security

- Replace Claude Code name prefix check with explicit job name list to
  correctly prevent self-loops (check_run names for inlined workflows
  are bare job names, not workflow-prefixed)
- Add fork PR trust gate in Resolve PR number step: verify head repo
  matches target repo before running Claude with privileged credentials

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: replace inlined claude.yml with standard thin-caller stub

The inlined version embedded all three jobs (claude, claude-ci-fix,
claude-issue) directly and threaded DON_PETRY_BOT_GH_PAT through
checkout tokens and github_token, causing the bot's PAT to author
PRs and comments instead of github-actions[bot].

Replace with the standard thin caller that delegates to the org-level
reusable workflow (claude-code-reusable.yml@v1) via secrets: inherit,
which is the correct pattern per:
petry-projects/.github/standards/workflows/claude.yml

* ci: pin dependabot-automerge reusable workflow to SHA (#130)

Pin petry-projects/.github reusable workflow from @v1 to commit SHA
0cb4bba11d7563bf197ad805f12fb8639e4879e4 to satisfy the action-pinning
compliance policy.

Closes #116

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Don Petry <don-petry@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: restore pr-review.yml content (accidentally emptied in revert)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: restore pr-review.yml (file was accidentally emptied)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(deps): bump petry-projects/.github/.github/workflows/dependency-audit-reusable.yml (#163)

Bumps [petry-projects/.github/.github/workflows/dependency-audit-reusable.yml](https://github.com/petry-projects/.github) from 1 to 2.
- [Commits](https://github.com/petry-projects/.github/compare/v1...v2)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/dependency-audit-reusable.yml
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump petry-projects/.github/.github/workflows/claude-code-reusable.yml (#164)

Bumps [petry-projects/.github/.github/workflows/claude-code-reusable.yml](https://github.com/petry-projects/.github) from 1 to 2.
- [Commits](https://github.com/petry-projects/.github/compare/v1...v2)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/claude-code-reusable.yml
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml (#168)

Bumps [petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml](https://github.com/petry-projects/.github) from 0cb4bba11d7563bf197ad805f12fb8639e4879e4 to b79962331f28d0fc26c09b441471231ff2df04b8.
- [Commits](https://github.com/petry-projects/.github/compare/0cb4bba11d7563bf197ad805f12fb8639e4879e4...b79962331f28d0fc26c09b441471231ff2df04b8)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml
  dependency-version: b79962331f28d0fc26c09b441471231ff2df04b8
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(list-prs): search DELEGATION_ORGS and drop --checks success pre-filter

- Add iteration over all orgs in DELEGATION_ORGS (e.g. don-petry) so PRs
  in those orgs enter the candidate pool. Previously only BOT_USER and
  TARGET_ORG were searched.
- Remove --checks success from org repo searches. GitHub excludes PRs from
  repos with no CI configured when this flag is used, silently dropping all
  PRs from repos like .github, .github-private, google-app-scripts, and
  TalkTerm. review-one-pr.sh already enforces CI gating per-PR and treats
  empty statusCheckRollup as passing, so the pre-filter is redundant and
  harmful.
- Add --limit 200 to gh repo list calls to handle org growth beyond gh's
  default 30-repo cap.

* chore(deps): bump petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml (#173)

Bumps [petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml](https://github.com/petry-projects/.github) from b79962331f28d0fc26c09b441471231ff2df04b8 to 761dd4a1a484a239429ed15d44d068de04fe2ee1.
- [Commits](https://github.com/petry-projects/.github/compare/b79962331f28d0fc26c09b441471231ff2df04b8...761dd4a1a484a239429ed15d44d068de04fe2ee1)

---
updated-dependencies:
- dependency-name: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml
  dependency-version: 761dd4a1a484a239429ed15d44d068de04fe2ee1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(prompts/dev-lead): add human prompt template

* feat(prompts/dev-lead): add human-pr prompt template

* feat(dev-lead): implement dev-lead agent Phases 0-6

* fix: per-PR isolation and single-review retry (closes #132)

- review-batch.sh: non-rate-limit per-PR failures (exit code 1) no
  longer abort the session. SESSION ABORTED EARLY is now reserved for
  the rate-limit-on-fallback-engine case (exit code 2) only. All other
  failures are counted and logged; remaining candidates continue.

- review-one-pr.sh: single-review step retries up to
  SINGLE_REVIEW_MAX_RETRIES (default 2) times with a
  SINGLE_REVIEW_RETRY_DELAY_SEC (default 15s) gap before giving up.
  On exhaustion, the PR is flagged needs-human-review and the script
  exits with code 1, which the updated batch treats as a non-fatal
  per-PR failure. Raw model output and stderr are logged on each failed
  attempt for post-mortem visibility.

Root cause of run #25707852006: claude-opus-4-7 returned a verbose
non-JSON response for PR #129; the old code treated that as fatal and
skipped 35 remaining candidates.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: rate-limit detection and per-attempt stderr logs in single-review retry

Address inline review comments on PR #133:

- Rate-limit check: after each run_agentic call, inspect both stdout
  (VERDICT_JSON.raw) and stderr (SINGLE_LOG) with is_rate_limited before
  retrying. A rate-limit match exits immediately with code 2 so
  review-batch.sh can trigger engine fallback — consistent with triage and
  deep-review tiers. Previously a rate-limited single-review would burn all
  retries and exit 1 (per-PR failure), silently leaving the batch on the
  same rate-limited engine for all remaining PRs.

- Per-attempt log files: stderr is now written to
  single-review-attempt-N.log rather than a single overwritten file, so
  no earlier-attempt errors are lost. Each attempt logs its own stderr
  inline on failure; the fallback path cats all attempt logs for
  post-mortem visibility.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* feat(dev-lead): Phase 0 test infrastructure and Phase 1 intent stub

Phase 0: full test harness for the dev-lead agent —
  26 event fixtures (all valid JSON with _test_expected_intent),
  stub claude/gemini engines, mock gh binary, CI failure log sample,
  bats helpers (stub-engine, mock-gh, assert-env, prompt-vars),
  7 prompt templates with VARIABLES declarations, preflight script,
  prompt coverage integration test, and test-dev-lead.yml CI workflow.

Phase 1: dev-lead.yml trigger workflow (all 7 event types, dispatch + ci-relay
  jobs) and dev-lead-intent.sh stub (anti-loop guard live; all other events
  emit skip/not-implemented).  14/14 bats unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): add permissions blocks to test workflow jobs (CodeQL)

* fix(ci): auto-fix for lint / eslint [skip ci-relay]

* feat(dev-lead): Phases 2-6 — CI fix, review fix, issue, engine fallback

Phase 2: run_writer/run_writer_with_fallback in engine.sh, full intent
routing in dev-lead-intent.sh, dev-lead-fix-ci.sh handler, workflow wiring.

Phase 3: dev-lead-fix-reviews.sh handles fix-reviews, fix-bot-comment,
human, human-pr, rebase intents. Full review routing in intent classifier.

Phase 5: dev-lead-fix-issue.sh handles the issue intent with dedup guard
and branch/PR creation.

Phase 6: run_writer_with_fallback with claude→gemini→copilot fallback chain.

Tests: 77 unit tests across 8 new .bats files, all passing. Updated Phase 1
stub tests to reflect actual Phase 2+ routing behavior.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): address review comments — dispatch JSON, dead code, portable stub gh

* fix(dev-lead): pre-flight after intent, fork URL check, review-batch exit code, retry comment

* fix(dev-lead): install bats-core from GitHub to avoid root requirement

* fix(security): move event values to env vars to prevent script injection (SonarCloud)

* fix(security): move CLAUDE_CODE_VERSION to env block (SonarCloud script injection)

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* feat(dev-lead): Phase 1.5 — reusable workflow for cross-repo adoption + Phase 7 shadow period

* feat(dev-lead): add reusable workflow (Phase 1.5) and begin shadow period (Phase 7)

- Create .github/workflows/dev-lead-reusable.yml: workflow_call entry
  point for other repos. Checks out .github-private scripts/prompts into
  .dev-lead/, then runs the same intent-classify + handler pipeline as
  dev-lead.yml with PROMPTS_DIR=.dev-lead/prompts/dev-lead.
- Add PROMPTS_DIR env-var support to dev-lead-fix-ci.sh,
  dev-lead-fix-reviews.sh, and dev-lead-fix-issue.sh so the reusable
  workflow can point scripts at the sparse-checkout path without changing
  CWD. Defaults to prompts/dev-lead (backwards-compatible for dev-lead.yml).
- Annotate dev-lead.yml with Phase 7 shadow-period window (2026-05-15
  through ~2026-05-29): claude.yml and dev-lead.yml run in parallel until
  regressions clear, then claude.yml is removed.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* chore(dev-lead): update shadow-period tracking issue ref to #180

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): document PAT requirement and pin ref:main on private checkout

* fix(dev-lead): P1 review fixes — ci-relay in reusable, .dev-lead gitignore, export PROMPTS_DIR, trim header

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): rate-limit detection, separate status, and retry cron (#192) (#196)

* chore(dev-lead): decommission claude.yml, bump ACTION_TIMEOUT_SEC to 600s

- Delete .github/workflows/claude.yml — replaced by dev-lead.yml (shadow
  period complete, tracking issue #180).
- scripts/engine.sh: raise ACTION_TIMEOUT_SEC default 300→600s to reduce
  timeout failures on large-repo fix-ci runs (PR #80 had 3 timeouts).
- AGENTS.md: remove claude.yml immutability exemption; note dev-lead.yml
  as the active AI automation workflow and its edit-via-reusable pattern.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor: replace Claude analysis with pure gh/jq telemetry in health check

* refactor: drop Node/Claude steps from daily-pr-review-health workflow

* rename: daily-pr-review-health → actions-fleet-monitor

* rename: daily-pr-review-health → actions-fleet-monitor

* fix(agents-md): clarify dev-lead.yml vs dev-lead-reusable.yml scope

dev-lead.yml in .github-private runs inline steps (not a caller stub).
Behavior changes for this repo go to dev-lead.yml directly; changes that
affect all org repos via the cross-repo reusable go to dev-lead-reusable.yml.

Addresses Copilot review on PR #194.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(dev-lead): rate-limit detection, separate status, and retry cron

Closes #192. Implements all phases from the revised plan.

**Phase 0 — fix run_writer stdout capture (prerequisite)**
- engine.sh: capture stdout via `tee` to a tempfile so `is_rate_limited`
  can inspect the output; old code read `/tmp/dev-lead-writer-stderr`
  which was never written (claude --print outputs to stdout, not stderr).
  Fallback engines were never tried in practice.
- engine.sh: add `parse_reset_time` to extract ISO timestamp from
  `resets H:MMpm (UTC)` in engine output and write it to
  `/tmp/dev-lead-rate-limit-reset` for callers to embed in markers.

**Phase 1 — separate status=rate-limited from status=failed**
- fix-ci.sh: detect engine exit 2 → post `status=rate-limited` (not
  `status=failed`); embed parsed reset time in marker body; exit 2.
- fix-ci.sh: fix `check_idempotency` to treat `status=rate-limited`
  as retriable — only block on terminal statuses (applied, failed,
  no-changes). Previously, rate-limited markers blocked all retries.
- fix-ci.sh: `count_recent_failures` already filtered to `status=failed`;
  add explicit comment confirming rate-limited markers are excluded.
- fix-ci.sh: add `has_rate_limited_marker` dedup check so the same SHA
  never accumulates more than one rate-limited comment.
- fix-reviews.sh: on engine exit 2 for all five intent types
  (fix-reviews, fix-bot-comment, human, human-pr, rebase), post a
  `status=rate-limited` marker with embedded reset time and exit 2.
- fix-reviews.sh: for `human` and `human-pr` intents, also post a
  user-visible acknowledgment comment so users know their request was
  received and will be retried.

**Phase 2 — scheduled retry cron for fix-ci**
- dev-lead-retry.sh: new script scans all open PRs across TARGET_ORG
  for `status=rate-limited` markers on current HEAD SHA; skips PRs
  whose reset time is still in the future; staggered dispatches (30s
  between repos) to prevent cascading org-level rate-limit hits.
- dev-lead-retry.yml: new workflow — scheduled every 2 hours (dual
  offsets to work around GitH…
don-petry pushed a commit that referenced this pull request Jun 26, 2026
Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  The producer that scripts/lsp_pilot_compare.sh consumes.
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off Grep/Glob/Read/Bash vs LSP-on + the pinned agent-lsp nav allowlist),
  measures each, and renders the comparison into the job summary.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse the
  repo's vetted pins; both legs gated to a modest model to bound cost.
- tests: 17 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline; quality proxy is populated only when the
verification step runs. shellcheck clean; bats green; workflow YAML validates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr
don-petry pushed a commit that referenced this pull request Jun 26, 2026
Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr
don-petry added a commit that referenced this pull request Jun 27, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 2, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 3, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 3, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 3, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 7, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 8, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 8, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 8, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
don-petry added a commit that referenced this pull request Aug 8, 2026
…tor (#952)

Adds the missing #844 execution path: a self-contained, dispatchable workflow
that measures both review variants LIVE on real PRs, so the comparison no longer
depends on the frozen synthetic baseline.

- scripts/lsp_pilot_measure.sh: turns one review's stream-json transcript +
  Token Cost Observatory log into one pilot-schema JSONL record (nav_tokens +
  tool_calls from the transcript; findings/false_positives from #843 verification
  records; cold_start_s from the #846 lsp_cold_start record; real reported usage).
  jq pre-flight in main(); value-flags guard their argument via ${2?…} (set -u safe).
- scripts/lsp_pilot_run.sh: per PR, runs the same navigation-heavy review twice
  (LSP-off vs LSP-on + the pinned agent-lsp nav allowlist), measures each, and
  renders the comparison; the harness exit code is preserved (FAIL LOUD on a
  missing baseline), not masked.
- .github/workflows/lsp-pilot-run.yml: workflow_dispatch driver. Installs
  agent-lsp + bash-language-server (cold-start record), runs the A/B over a real
  smoke corpus (#928, #899), uploads JSONL artifacts. Action SHAs reuse vetted pins.
- tests: 18 unit cases over the pure extractor/driver helpers.

Honest scope: a navigation-COST probe (nav_tokens/tool_calls/cold-start/ET/USD),
not the full review pipeline. shellcheck clean; bats green; workflow YAML validates.


Claude-Session: https://claude.ai/code/session_016Y3xW3cnB5wscv4SmjSNEr

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pr-review self-review deadlock: ci-pending skip + "already reviewed" marker blocks approval once CI goes green

2 participants