[pipeline-dcdad92d][slice-5/12] Investigation tooling for reviewers - #208
Merged
Conversation
🦋 Changeset detectedLatest commit: bb93b77 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
khan-actions-bot
requested review from
a team,
jaredly and
kevinb-khan
and removed request for
a team
July 3, 2026 21:38
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-4
branch
from
July 3, 2026 21:42
6f17afc to
7310e0f
Compare
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-5
branch
from
July 3, 2026 21:42
6ea5d72 to
ba44e5a
Compare
…ing (slice-1) R1 (task-1-2): standardize Step 6 review submission on one robust submit-pull-request-review call with a real, non-empty body; remove the empty-body branch and any fallback/retry path. E2 (task-1-3): stage /tmp/gh-aw/review/pr-context.json in Step 1 and wire all six sub-agent read blocks to consume it. Extends #194 diff staging; foundation for slice-7 lenses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
task-1-1: workflows/review/lib/finding-schema.ts - FINDING_SCHEMA_VERSION exported constant; each finding carries schema_version and the validator rejects an unrecognized version (breaking-shape guard). - Finding shape (snake_case wire keys, matching #194 per-run JSON artifacts): id, lens, anchor, severity, confidence, evidence_trace, optional suggested_patch, optional pre_merge_obligation, producing_hunt, model_authored_prose. - Anchor is a discriminated union with the required PR-level type (pr | file | line, line supports side + multi-line start_line..line range). - KNOWN_LENSES (11 specialist + always-on/triage) and SEVERITIES exported for reuse by the slice-3 router and slice-2 verdict; lens/severity validated against them. - validateFinding collects ALL violations (diagnosable per-lens drop rate), plus isValidFinding narrowing wrapper and throwing assertFinding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k-1-4) Covers the exported FINDING_SCHEMA_VERSION constant, well-formed findings across every anchor type (line/range/side, file, PR-level) plus optional suggested_patch/pre_merge_obligation, and malformed findings for every required field. Asserts validateFinding collects ALL violations (per-lens drop-rate diagnosability), and exercises isValidFinding narrowing and assertFinding throwing. 37 tests green under vitest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k-1-4) Covers the exported FINDING_SCHEMA_VERSION constant, well-formed findings across every anchor type (line/range/side, file, PR-level) plus optional suggested_patch/pre_merge_obligation, and malformed findings for every required field. Asserts validateFinding collects ALL violations (per-lens drop-rate diagnosability), and exercises isValidFinding narrowing and assertFinding throwing. 37 tests green under vitest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-4
branch
from
July 3, 2026 21:43
7310e0f to
44cb874
Compare
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-5
branch
from
July 3, 2026 21:43
ba44e5a to
39ffacc
Compare
jeresig
requested changes
Jul 6, 2026
jeresig
left a comment
Member
There was a problem hiding this comment.
This feels kind of half-baked - this tool isn't used anywhere (nor is it called).
(Also, in these PRs there keep being references to R* and "slices" but those are just artifacts of the PRs, not what we care about in the code.)
Comment on lines
+273
to
+286
| **Bounded investigation (R9).** The finding-producing sub-agents — the | ||
| `correctness-reviewer`, the `skill-auditor`, and the specialist lenses added in a later | ||
| slice — and the `claim-validator` when it re-checks a claim may **investigate** on the | ||
| checkout before committing to a finding, rather than guessing from the diff alone: grep | ||
| for callers and definitions, trace a call chain a step or two, and run **one targeted | ||
| cheap read-only check per finding**. Each sub-agent carries this protocol in its own | ||
| prompt (they run isolated and never see this orchestrator prompt), so the rule is | ||
| repeated verbatim in each finding-producing agent below and every slice-7 lens embeds the | ||
| same block. Investigation never leaves the checkout — no GitHub, no network, no writes. | ||
| A **per-finding tool-call cap is enforced in code** (`workflows/review/lib/investigation-cap.ts`), | ||
| sized inside the router's `runBudget` (Step 3) so a high-risk PR gets more investigation | ||
| room and a misrouted one keeps a floor; over-cap calls are refused deterministically, so | ||
| the investigation stays shallow no matter what a sub-agent attempts. | ||
|
|
Member
There was a problem hiding this comment.
Suggested change
| **Bounded investigation (R9).** The finding-producing sub-agents — the | |
| `correctness-reviewer`, the `skill-auditor`, and the specialist lenses added in a later | |
| slice — and the `claim-validator` when it re-checks a claim may **investigate** on the | |
| checkout before committing to a finding, rather than guessing from the diff alone: grep | |
| for callers and definitions, trace a call chain a step or two, and run **one targeted | |
| cheap read-only check per finding**. Each sub-agent carries this protocol in its own | |
| prompt (they run isolated and never see this orchestrator prompt), so the rule is | |
| repeated verbatim in each finding-producing agent below and every slice-7 lens embeds the | |
| same block. Investigation never leaves the checkout — no GitHub, no network, no writes. | |
| A **per-finding tool-call cap is enforced in code** (`workflows/review/lib/investigation-cap.ts`), | |
| sized inside the router's `runBudget` (Step 3) so a high-risk PR gets more investigation | |
| room and a misrouted one keeps a floor; over-cap calls are refused deterministically, so | |
| the investigation stays shallow no matter what a sub-agent attempts. | |
| **Bounded investigation (R9).** The finding-producing sub-agents — the | |
| `correctness-reviewer`, the `skill-auditor`, and the specialist lenses added in a later | |
| slice — and the `claim-validator` when it re-checks a claim may **investigate** on the | |
| checkout before committing to a finding, rather than guessing from the diff alone: grep | |
| for callers and definitions, trace a call chain a step or two, and run **one targeted | |
| cheap read-only check per finding**. Each sub-agent carries this protocol in its own | |
| prompt (they run isolated and never see this orchestrator prompt), so the rule is | |
| repeated verbatim in each finding-producing agent below and every lens embeds the | |
| same block. Investigation never leaves the checkout — no GitHub, no network, no writes. | |
| A **per-finding tool-call cap is enforced in code**, | |
| sized inside the router's `runBudget` (Step 3) so a high-risk PR gets more investigation | |
| room and a misrouted one keeps a floor; over-cap calls are refused deterministically, so | |
| the investigation stays shallow no matter what a sub-agent attempts. | |
…nts) - Restore the empty-review-body convention: with inline comments present the verdict is submitted with an empty body; a non-empty body exists only for comment-less reviews and skipped-dimension notes. Keeps the single-call submission rule. - Deliver workflows/review/lib/ to consumer repos: gh-aw's source: import only copies the .md, so the job checks out Khan/actions at the pinned review-v* release (single version surface for prompt + code) and installs its runtime deps. - Drop planning identifiers (E2, R8, slice numbers, operator-directive note) from shipped file comments.
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-4
branch
from
July 6, 2026 18:19
44cb874 to
bf188a9
Compare
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-5
branch
from
July 6, 2026 18:19
39ffacc to
bc564fc
Compare
jeresig
approved these changes
Jul 7, 2026
jeresig
left a comment
Member
There was a problem hiding this comment.
Great - looks like this is all integrated correctly now!
… rendering (slice-2) task-2-1/2-2 verdict.ts: pure computeVerdict(labels + dimension availability + policy conflicts) -> APPROVE | REQUEST_CHANGES | HOLD_FOR_HUMAN. Consumes #194's mechanical label rule (isBlockingLabel); does not re-implement it. R2 gate: a missing core dimension (correctness/skill-severity) or a policy-named conflict holds for a human; a lost pattern-triage is note-and-continue. Documented default blocking threshold = 1 (tunable, not a HITL gate). task-2-3 render-comment.ts: code-owned Conventional-Comment label taxonomy + deterministic label-wrapping/templating from the finding schema; models own all prose. Review-body rendering mirrors review.md Step 6 + skipped-dimension notes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pshot tests (task-2-4) Truth-table tests for computeVerdict (verdict.test.ts): APPROVE / REQUEST_CHANGES / HOLD_FOR_HUMAN precedence, the R2 core-dimension gate (correctness + skill-severity absence holds; hold dominates a blocking label with all reasons retained), pattern-triage note-and-continue, policy-named conflicts (verbatim detail passthrough), blocking-threshold clamping (>=1) + never-RC-on-zero, and purity (deterministic, non-mutating). Rendering snapshot tests (render-comment.test.ts): isBlockingLabel over the full label taxonomy + unknown-label safe default; labelForFinding severity x lens matrix (conventions => best-practice, specialists => plain); renderComment inline snapshots (label wrap + verbatim prose + optional suggestion block) with verbatim prose/patch passthrough asserted; renderReviewBody inline snapshots for every verdict x inline branch + skipped-dimension notes + non-empty safe-output guard. Fixtures validated through assertFinding so tests can't pass on schema-invalid findings. 50 tests green; full pnpm test suite 253/253; tsc --noEmit clean (node24). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Formatting-only follow-up to b2c4d90: applies @khanacademy/eslint-config prettier rules to verdict.test.ts + render-comment.test.ts so the lint check passes on the changed files. No test logic or inline-snapshot content changed — 50/50 still green under node24; tsc --noEmit clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ema (tester NACK v1)
Formatting-only; no logic change. Applies the repo lint gate's prettier 2.6.2
formatting (eslint-plugin-prettier@4.0.0 binds prettier 2.6.2) which had never
been run against these files:
- render-comment.ts / finding-schema.ts: string-arg + ternary line wrapping.
- typeof-indexed-access type aliases dropped the paren form
((typeof X)[number] -> typeof X[number]) — prettier 2.6.2 emits the
unparenthesized form; the parens were added by prettier 2.7.0. Semantics
unchanged (verified typecheck-clean under the repo tsconfig).
Verified: strict typecheck clean; verdict truth-table + render checks still pass;
prettier diff against these files is now empty except the exact 2.6.2<->2.8.8
typeof-parens delta, which the pinned CI formatter (2.6.2) resolves in favour of
the committed form.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e-3) task-3-1: file classification from .gitattributes (linguist-generated), path->lens mapping (11 specialist lenses), team mapping from REVIEWERS (most-specific-pattern-wins + fallback ranking, subsuming reviewer-mapper), and per-file risk tier. The one diff-direction-dependent judgement is externalised as pendingRiskQuestions for the orchestrator's small-model call (plan §8.6); the core stays pure/deterministic. task-3-2: ONE budget rule scaling by the highest touched tier + ONE floor for misrouted PRs. Documented default caps (DEFAULT_TIER_BUDGETS, 20min/$10 ceiling per operator); maxToolCallsPerFinding lives here for slice-5 (R9). No human-read prose authored in code (R8 tripwire, §8.7). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, budget, floor (task-3-4) Covers the five task-3-4 AC axes over fixtures plus the reviewer-mapper subsumption (R12) and the glob/config parsers routing is built on: - classification: linguist-generated vs source (parse + isGenerated + route). - path->lens: per-file union, deduped lensesToSpawn in canonical order, always-on lenses never spawned as specialists. - tier: default, highest-of-matching, diff-direction-dependent deferral (conservative tier + pendingRiskQuestions), resolvedTiers second pass. - budget scaling: monotonic across tiers; run budget = highest touched tier. - misrouted floor: docs-only PR floored to 'low'; already-higher tier not lowered. - teams: most-specific REVIEWERS rule wins, sorted union + file-count fallback rank. Pure/deterministic core (plan §8.6) — no I/O, no model call. Offline: could not run vitest (node_modules absent, network unreachable); hand-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(slice-3 v2)
Addresses reviewer_code_holistic v1 NACK (BLOCKING-1 + BLOCKING-2):
BLOCKING-1: router.ts now has a CLI entrypoint. review.md Step 3 invokes
`node -r @swc-node/register workflows/review/lib/router.ts` directly, so the
file doubles as the executable: a require.main===module guard runs runCli(),
which reads the Step-1-staged files.json + .gitattributes/.github/REVIEWERS,
calls the (unchanged) pure route(), and writes /tmp/gh-aw/review/routing.json.
node:fs is loaded lazily inside the guard so the module stays import-clean for
the unit tests; runCli takes an injected fs so it is testable without real I/O.
BLOCKING-2: new toRoutingJson() adapter emits the exact shape review.md
consumes — teams:{owners:{path:[team]}, fallback:[{team,files}]} (owners over
source files only, mirroring the old owned-files scope) and perFileTier in the
documented capitalized casing (High|Medium|Low|Trivial), bridged from the
core's lowercase tiers at the I/O boundary. pendingRiskQuestions is carried in
routing.json so the two-invocation resolvedTiers protocol stays intact; the
optional second-pass answer is read from /tmp/gh-aw/review/resolved-tiers.json.
Pure route()/parsers/budget core is byte-for-byte unchanged (reviewers had no
correctness objection); this is purely the I/O+serialization shell. No
human-read prose authored in code (R8 tripwire §8.7).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…/tierFromDisplay/runCli) Extends task-3-4 tests to the coder's v2 additions (34d88d5) — the routing.json I/O boundary review.md Step 3 depends on: - tierFromDisplay: display+lowercase casings, unknown -> low. - toRoutingJson: display-cased perFileTier, owners scoped to source files (generated excluded), fallback + pendingRiskQuestions passthrough. - runCli (fs injected, no real I/O): bare-array & {files} wrapper parsing, misrouted floor, .gitattributes/REVIEWERS parse, resolved-tiers second pass, mkdir + routing.json round-trips to the returned value. Offline (node_modules absent, network unreachable); hand-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Remove the baked-in DEFAULT_LENS_RULES/DEFAULT_RISK_RULES: a repo's routing map now lives in its own .github/aw/review/ROUTING file (REVIEWERS-style line grammar, parsed deterministically in the new routing-config module), complementing the model-facing risk-classification.md. Missing config degrades safely: no specialist lenses, always-on reviewers still run, budget floored, and routing.json carries a loud routingConfig warning the orchestrator surfaces on the PR. - runCli reads repo files under REVIEW_REPO_ROOT so the router can run from the shared-lib checkout against the reviewed repo. - review.md: apply reviewer-suggested edits (drop stale reviewer-mapper references, fix the router-reads-the-diff claim), document the ROUTING format, the checkout-based invocation, and the back-to-back two-pass flow for direction-dependent tiers with a concrete example. - Drop planning identifiers from shipped comments.
…lice-4)
E1: correctness-reviewer names the high-risk trigger + one-line judgment in riskReason.
E3: untrusted-input rule broadened to all PR content; injection attempts to steer the
reviewer are themselves findings (issue (blocking)).
E5: deletions are findings -- flag removals that drop a guard/check/test.
R3b: pre-existing bugs on touched lines are fair to flag, scoped to touched lines,
building on #194 severity (does not reopen it).
E6: stage the full reply chain per bot thread; reconciler weighs author reasoning and
never re-raises a conceded point.
E7: stage open human-thread lines; reconciler emits skipLines and the orchestrator
defers there (verdict + Step 5 kept consistent).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rework the injection rule: an author's request in the PR title/description is legitimate context to weigh and respond to (humans may steer the reviewer); steering text inside code/comments/fixtures is still never followed, but is surfaced as a non-blocking note instead of an auto-blocking issue. - Propagate the touched-lines scoping to the skill auditor: violations anchor on lines the PR adds or modifies, never surrounding context or pre-existing style, keeping the mechanical Step 3 scope filter a backstop. - Strip the remaining plan identifiers (E1/E3/E5/E6/E7/R3b) from the shipped prompt text.
…ewers (task-5-1) Add the R9 bounded-investigation protocol to review.md: finding-producing sub-agents (correctness-reviewer, skill-auditor, the slice-7 lenses) and the claim-validator may grep callers/definitions, trace a call chain a step or two, and run one targeted cheap read-only check per finding before committing to it. Stays read-only and on the checkout (no GitHub, no network, no writes), keeps all read content as untrusted input, and cites/drops per investigation result. Because gh-aw sub-agents are prompt-isolated (each sees only its own body), the protocol is embedded verbatim in each finding-producing agent rather than cross-referenced; slice-7 lenses embed the same block. An orchestrator-level note documents the per-finding tool-call cap enforced in code (workflows/review/lib/investigation-cap.ts, task-5-2), sized inside the router runBudget so a high-risk PR gets more room and a misrouted one keeps a floor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
task-5-2: enforce a cap on tool calls per finding in code, drawing the numbers from the slice-3 run budget (maxToolCallsPerFinding + maxTotalToolCalls) rather than configuring them separately. Pure decideToolCall() + stateful InvestigationCap guard; over-cap calls are refused deterministically with a fixed reason code (no prose about the code under review, per the R8 boundary). Default caps documented (mirror the router 'low' tier / misrouted floor); not a HITL surface. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover the pure decideToolCall verdict (per-finding-before-run-total precedence, headroom clamping, malformed-cap normalisation) and the stateful InvestigationCap guard (per-finding independence, the shared run-total pool, no state mutation on a refusal, deterministic replay, snapshot copy-safety) plus the RunBudget wiring (capsFromRunBudget / fromRunBudget). AC: cap enforced; over-cap calls refused deterministically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…total) — task-5-3 check() is the non-mutating preview half of the InvestigationCap guard; the existing tests only exercised its allowed path. Add two cases asserting check() reports the same refusal request() would (per-finding-cap-exceeded and run-total-cap-exceeded) with correct remaining* headroom and zero mutation (snapshot unchanged). No production code change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…allers) - The cap was a library nothing invoked. Add a CLI entrypoint (investigation-cap.ts request <finding-id>) over an append-only journal shared by the run's sub-agents: caps come from routing.json's runBudget, spent calls are recounted from the journal, allowed requests append, and a refusal exits non-zero so shell callers can gate on it. - Wire the invocation into the three finding-producing prompts (correctness reviewer, skill auditor, claim validator) so the cap is requested before every investigation tool call, per the reviewer suggestion; lenses inherit the same block if a repo enables them. - Drop the R*/slice/task planning identifiers from shipped files.
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-5
branch
from
July 7, 2026 19:04
bc564fc to
3f01827
Compare
jwbron
force-pushed
the
egg/pipeline-dcdad92d/slice-4
branch
from
July 7, 2026 19:04
bf188a9 to
798016b
Compare
Base automatically changed from
egg/pipeline-dcdad92d/slice-4
to
egg/pipeline-dcdad92d/slice-3
July 8, 2026 16:37
…/slice-5 # Conflicts: # workflows/review/review.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Give reviewer sub-agents a bounded investigation protocol (grep callers, trace call chains, one targeted cheap check per finding) and enforce a deterministic per-finding tool-call cap that lives inside the slice-3 run budget.
Base PR: #196
What's in this PR
Commits (5):
This slice
Investigation tooling for reviewers
Files affected:
workflows/review/review.mdworkflows/review/lib/investigation-cap.tsworkflows/review/lib/investigation-cap.test.tsTasks (3) + acceptance criteria
Stack
pipeline-dcdad92degg/pipeline-dcdad92d/slice-4Note: this PR was opened by the operator after the fact; the pipeline's stacked-PR reconciler did not open one for slice-5 (its consensus round required operator-assisted recovery).