[pipeline-dcdad92d][slice-2/12] Determinism boundary: computed... - #198
Conversation
🦋 Changeset detectedLatest commit: 0950bf2 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 |
695fd8b to
f578afc
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>
1faaa0f to
b119a01
Compare
f578afc to
a2dee28
Compare
jeresig
left a comment
There was a problem hiding this comment.
In general seems good but I think we need to have a better flow for helping folks get unstuck when we get into the "holding for human review" state.
| : "Approved — no blocking issues found."; | ||
| break; | ||
| case "REQUEST_CHANGES": | ||
| head = "Changes requested — see inline comments."; |
| break; | ||
| case "HOLD_FOR_HUMAN": | ||
| head = input.hasInlineComments | ||
| ? "Holding for human review — see inline comments." |
There was a problem hiding this comment.
I think we're going to need to provide more context on this. What actions should the creator of this PR take to get their thing un-stuck? (I'm not convinced that we should be going into this other state if we have known blocking changes.)
| switch (input.event) { | ||
| case "APPROVE": | ||
| head = input.hasInlineComments | ||
| ? "Approved — see inline comments." |
There was a problem hiding this comment.
I mentioned this in the other PR but we can leave this empty as we're already leaving comments.
|
Agreed on both. I'll add a precedence flip so known blocking findings always produce REQUEST_CHANGES (the hold state only replaces what would otherwise be an auto-approve on a run where the correctness or skill pass silently produced nothing), and update the hold body to say exactly how to get unstuck: which pass failed, how to re-trigger the run, and who to ping. |
…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.
a2dee28 to
aa98784
Compare
| * verdict and no next action. | ||
| */ | ||
| const HOLD_UNSTUCK_LINES = [ | ||
| "To get unstuck: push a new commit (or re-run the review workflow from the " + |
There was a problem hiding this comment.
This seems reasonable to me, for 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>
…bodies) - Flip verdict precedence to REQUEST_CHANGES > HOLD_FOR_HUMAN > APPROVE: a blocking finding is actionable on its own, so the hold only ever replaces what would otherwise be an auto-approval. Missing-dimension and policy-conflict reasons are still recorded and rendered. - Empty review bodies when inline comments exist (APPROVE and REQUEST_CHANGES); a non-empty body remains only for comment-less reviews and skipped-dimension notes, matching review.md. - HOLD_FOR_HUMAN body now explains what failed and how to get unstuck (retry via push or workflow re-run, ask for a manual review, or opt out with the skip-ai-review label), and renders policy-conflict lines. - Drop planning identifiers from shipped comments.
aa98784 to
0950bf2
Compare
Compute the verdict in code from findings + posted-comment labels (with a hold-for-human path), render Conventional Comments from templates, and hold for a human when a core review dimension produced no output.
Base PR: #196
What's in this PR
Commits (5):
This slice
Determinism boundary: computed verdict, rendering, missing-dimension gate
Files affected:
workflows/review/lib/verdict.tsworkflows/review/lib/render-comment.tsworkflows/review/lib/verdict.test.tsworkflows/review/lib/render-comment.test.tsTasks (4) + acceptance criteria
Stack
pipeline-dcdad92degg/pipeline-dcdad92d/slice-1