Skip to content

review: honour Gerald .github/NOTIFIED in the reviewer - #264

Merged
jeresig merged 4 commits into
mainfrom
jeresig/review-workflow-notified-144eb8
Jul 23, 2026
Merged

review: honour Gerald .github/NOTIFIED in the reviewer#264
jeresig merged 4 commits into
mainfrom
jeresig/review-workflow-notified-144eb8

Conversation

@jeresig

@jeresig jeresig commented Jul 16, 2026

Copy link
Copy Markdown
Member

Summary

Teaches the AI PR reviewer to honour a repo's Gerald .github/NOTIFIED file: when a PR touches files (or introduces diff content) that someone asked to be notified about, the reviewer adds a ### Notified section to its Review Guidance comment that @-mentions them and tells them the rule label and which changed files matched. This is the notify half of Gerald (the router already handles the reviewer-ownership half via .github/REVIEWERS).

What changed

  • workflows/review/lib/notified.ts (new, + notified.test.ts, 34 tests) — parses .github/NOTIFIED and does the matching, following the same shape as router.ts/provenance.ts (pure functions + an fs-injected runCli). It:
    • reads only the [ON PULL REQUEST] section (discards the docs above the ----…ignored!---- marker and the [ON PUSH WITHOUT PULL REQUEST] section);
    • handles label: prefixes, # comments, path globs (matched against changed-file paths) and quoted "/body/flags" diff regexes (matched against each file's staged unified diff, so a rule fires on added content);
    • groups matches by @mention, keeps each rule's label, and renders a ready-to-insert ### Notified block plus a stable signature.
  • workflows/review/review.md — minimal Step 7 wiring: run the CLI, paste its markdown verbatim into the Review Guidance comment, add notifications as a post trigger, and fold the notification signature into the risksPatternsKey idempotency key (Steps 7 & 9). Almost all logic lives in the script; the prompt just runs it and inserts the result.
  • workflows/review/README.md — documents the .github/NOTIFIED support.
  • .changeset/review-notified.mdreview minor bump.

Design notes

  • Dependency-free matching. The lib runs under npx -y tsx with no install step, so the glob matcher is self-contained — a micromatch subset (**, *, ?, {a,b,c}, […], (a|b), ?(…) *(…) +(…) @(…)) anchored at the repo root (Gerald/micromatch semantics, not the router's basename-anywhere gitattributes convention). An unsupported construct or malformed rule degrades to no match plus a Note: on the PR review — never a crashed review.
  • Base-branch read. .github/NOTIFIED is read from REVIEW_REPO_ROOT (the base-branch copy, like the router reads .github/REVIEWERS), so a PR can't inject notify rules that fire before it merges.
  • Idempotency. The notification set feeds the comment's substance signature, so a repost re-pings people only when the matched set actually changes.

⚠️ Reviewer note: mention delivery

gh-aw's safe-output sanitizer neutralizes @mentions (@user`@user`) unless they're allow-listed. The default mentions.allow-team-members: true already lets repo-collaborator users through, so individual folks are pinged — but team mentions (e.g. @Khan/infra-platform) and non-collaborators are shown-but-not-pinged unless the workflow adds a safe-outputs.mentions: block (allowed-teams needs a token with read:org). Since the targets are dynamic and this is a shared-workflow + token-scope decision, I left that config out of this PR. Happy to add it as a follow-up if we want teams pinged.

Scope

Per the review-feature convention (cf. #262, #225), this ships in the shared source + lib + changeset only. The release-pinned consumer copy (.github/workflows/review.md) and compiled lock are intentionally untouched — they pick this up on the next release + re-pin.

Verification

  • notified.ts exercised end-to-end exactly as the workflow invokes it, against a webapp-style NOTIFIED + staged diff: correctly matched a team via both a deploy/** glob and a model diff-regex, and a user via a *.graphql glob; correctly ignored the push-only rule and above-marker docs; the diff-regex fired on the added line, not the +++ header.
  • Full suite green: 1049 tests pass, prettier --check clean, tsc --noEmit clean, eslint clean.

🤖 Generated with Claude Code

…ub/NOTIFIED in the reviewer

Add `.github/NOTIFIED` handling so the reviewer pings the people and teams
a repo has asked to be notified about matching changes, distinct from the
`.github/REVIEWERS` reviewer ownership the router already handles.

New `workflows/review/lib/notified.ts` (+ tests) parses the file's
`[ON PULL REQUEST]` notify rules (ignoring the docs above the
`----...ignored!----` marker and the `[ON PUSH WITHOUT PULL REQUEST]`
section) and matches them against the PR: path globs against the changed-file
list, quoted `"/body/flags"` diff regexes against each file's staged unified
diff (so a rule can fire on added content). It groups matches by `@mention`,
carries each rule's label, and renders a ready-to-insert `### Notified`
block. Matching is a self-contained micromatch subset anchored at the repo
root, keeping the lib dependency-free (it runs under `npx tsx` with no
install); an unsupported construct or malformed rule degrades to no match
plus a `Note:` on the PR, never a crashed review.

review.md Step 7 runs the CLI and pastes the block into the Review Guidance
comment on approval, telling each matched person the label and which changed
files matched; notifications also feed the comment's post trigger and its
`risksPatternsKey` idempotency signature, so a repost re-pings only when the
match set changes. Most of the logic lives in the script; the prompt just
runs it and inserts the result.

Ships only in the shared source + lib + changeset (per the review-feature
convention); the release-pinned consumer copy and lock are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 56c407f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
review Minor

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

@jeresig
jeresig requested a review from jwbron July 16, 2026 20:18
@khan-actions-bot
khan-actions-bot requested review from a team, jaredly and somewhatabstract and removed request for a team July 16, 2026 20:18
@github-actions

Copy link
Copy Markdown
Contributor

Review Guidance

github-actions (2 files)
File Reason
review.md The shared PR reviewer prompt — it is the reviewer's behavior in every consuming repo; the Step 7 wiring here runs on every release.
notified.ts New deterministic enforcement-layer lib that decides who gets notified on every consumer PR once released; a parse/match bug silently changes notification behavior.
Excluded from review (2 files)

Not individually reviewed — generated, formatting-only, or fully explained by a common pattern above:

  • .changeset/review-notified.md — formatting-only
  • workflows/review/README.md — formatting-only

Comment thread workflows/review/lib/notified.ts
Comment thread workflows/review/lib/notified.ts Outdated
Comment thread workflows/review/lib/notified.ts
Comment thread workflows/review/review.md
Comment thread workflows/review/lib/notified.ts
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review live A/B

Baseline: origin/main (review.md af901654d627); candidate: working tree (review.md 9db6e4d78101).

Ruler: matcher deterministic+arbiter; corpus cc11988c0918 (9 cases).

Metric Baseline Candidate Delta
Must-catch recall 100% 100% +0%
Verdict agreement 89% 100% +11%
Noise (unmatched posted) 59% 57% -2%
Clean false flags 0 0
Judge mean quality 0.86 0.88 +0.01
Cost $10.21 $9.80
Wall clock 1308s 1216s
Cases run / skipped 9 / 0 9 / 0
Misses found-but-dropped 0 0
Findings anchor-snapped 0 0

Adversarial hard gate: PASSED on the candidate arm.

Single-run-stable rows: recall, verdict agreement, regressions, adversarial gate. Judge quality and noise are not: they jitter run-to-run at this corpus size, and a regressed reviewer can score HIGHER on judge quality (fewer, surer comments each read better). Recall against the labeled specs is the load-bearing metric.

Measured noise floor (identical arms, run 29069228968, 2026-07-10, 6 arm-samples, full corpus x3, pre-arbiter; budget skips left the samples on unequal case sets, so these v1 bands also carry case-mix variance): must-catch recall 54%-86% (sd 10%), verdict agreement 75%-100% (sd 9%), noise (unmatched posted) 50%-60% (sd 3%), judge mean quality 82%-86% (sd 2%). A single-run delta whose arms both sit inside a band is indistinguishable from run-to-run wobble; use --repeats to resolve smaller effects.

…viewer feedback (parse fixes + honest docs)

- Fix a section-header false positive: an *unlabeled* rule whose glob leads
  with a character class (`[Dd]ockerfile`, `[1-5]`, `[[:digit:]]`) was matched
  by the `startsWith("[")` header guard, silently dropping that rule and every
  rule after it in `[ON PULL REQUEST]`. Match only genuine all-caps `[SECTION]`
  headers now (`SECTION_HEADER_RE`).
- Fix a silent drop: a quoted diff-regex rule with an invalid body compiled to
  null and was dropped at match time with no warning, contradicting the "a
  malformed rule surfaces a Note:" promise. Validate the regex at parse time so
  it flows through the existing `warnings[]` -> Step 7 `Note:` path.
- Tests: the char-class-header case (rule + trailing rules survive), a leading
  char-class matchGlob, and the malformed-regex warning end-to-end through
  parseNotified and computeNotifiedResult.
- Docs: describe the glob dialect honestly as a practical subset of Gerald's
  micromatch (wildcards match dotfiles; `!(...)` unsupported), and document the
  approval-time delivery model and Gerald coexistence in the README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khan-actions-bot
khan-actions-bot requested a review from a team July 20, 2026 18:33
Comment thread workflows/review/lib/notified.ts
Comment thread workflows/review/README.md
Comment thread workflows/review/lib/notified.ts Outdated

@kevinb-khan kevinb-khan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The changes seem reasonable to me. I tried to focus on reviewing the high-level logic in notified.ts and review.md as well as all of the tests in notified.test.ts.

I like a lot of the logic for this change to the reviewer was encoded using a formal programming language as opposed to trying to do everything within the review.md.

I think there's likely more logic within the review.md that we could likely extract into .ts files. This something to think about for future iterations on the review. I see a couple of potential benefits of having more of the logic living in .ts files:

  • it can be unit tested
  • its deterministic
  • it's easier for humans, or at least myself, to review (I find that reviewing large blocks of logic encoded in English takes longer to parse especially when the sentences include a lot of clauses and parentheticals which is often how LLMs tend to write this kind of stuff).

Comment thread workflows/review/lib/notified.test.ts
Comment thread workflows/review/lib/notified.test.ts
Comment thread workflows/review/lib/notified.test.ts Outdated
Comment thread workflows/review/lib/notified.test.ts Outdated
Comment thread workflows/review/lib/notified.test.ts Outdated
Comment thread workflows/review/lib/notified.test.ts
Comment thread workflows/review/README.md
Comment thread workflows/review/review.md Outdated
Comment thread workflows/review/review.md
Comment thread workflows/review/review.md
… review feedback (tests + docs)

Addresses the follow-up review on #264 (all non-blocking):

- notified.ts: import `./diff` without the `.ts` extension, matching the
  module's other importers (provenance.ts, rereview-mode.ts).
- notified.test.ts: cover the `+(...) *(...) @(...)` extglob branches and
  invalid globs; add a case proving `@mentions` inside a trailing `#` comment
  are ignored; split the combined glob and render/signature describe blocks;
  drive the glob matcher with `it.each`; and snapshot the rendered `### Notified`
  block and the full `runCli` result with `toMatchInlineSnapshot` so the shape
  review.md consumes is visible at a glance.
- review.md: render the notified.json field descriptions as a bulleted list and
  break the long "Notified" rendering instruction into nested bullets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread workflows/review/lib/notified.ts
Comment thread workflows/review/lib/notified.ts
Comment thread workflows/review/lib/notified.ts Outdated
… gaps + fix repost-wording (review feedback)

Follow-up review on #264 (all non-blocking):

- notified.test.ts: add matchGlob rows for the trailing-slash directory glob
  (`deploy/`) and for negated character classes (`[!a-z]`, `[^abc]`) — both
  branches of the glob compiler were previously unexercised.
- notified.ts: correct the delivery-note wording. It claimed review.md
  re-posts "only when the notification signature changes"; in fact Step 7 folds
  that signature into the combined Review Guidance key, so a repost (and its
  re-pings) fires when any component — risk files, patterns, exclusions, or the
  notification set — changes. Reworded to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jeresig
jeresig merged commit 8b4e56d into main Jul 23, 2026
5 of 6 checks passed
@jeresig
jeresig deleted the jeresig/review-workflow-notified-144eb8 branch July 23, 2026 16:48
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.

2 participants