-
Notifications
You must be signed in to change notification settings - Fork 1
review: cluster candidate claims by defect identity, not by anchor #312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5ed38c4
93d59dd
531a008
a4a4bd0
5691620
163f8cf
68bf922
7b8bb2c
b102a28
5bb4e3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| --- | ||
| "review": minor | ||
| --- | ||
|
|
||
| Cross-source dedup gains a second tier: a `claim-clusterer` sub-agent names the | ||
| candidate comments that describe ONE defect, and `dedup.ts` verifies that | ||
| assertion and merges them. Several reviewers finding one problem now post once. | ||
|
|
||
| Run 30587343777 (webapp#41204) is the case. Four sources flagged one wrong doc | ||
| comment (`// Keeps at most 10 samples per key.` above `const maxSamples = 25`) at | ||
| window.go :8, :9, :8, :8, and `merges` recorded none of them; it was a FIRST | ||
| review at `depth: full`, so not a re-review artifact. Autofix later satisfied all | ||
| four with one rewritten comment, which is the proof they were one defect. | ||
|
|
||
| Replaying that run's own claims.json showed the similarity tier is not close to | ||
| reaching it. Three of the four share the EXACT anchor and still score | ||
| 0.060-0.068 Jaccard against a 0.14 floor with 0-1 shared bigrams against a floor | ||
| of 4: an order of magnitude below the tier, so no re-derivation from the fixtures | ||
| gets there (the floor that admits 0.06 admits everything). Each reviewer wrote | ||
| the same defect in different words, and the terser the claim the less text | ||
| arithmetic has to work with. Nor can reweighting the text recover the | ||
| discriminator: the pairs dedup deliberately keeps apart share MORE salient | ||
| tokens than the real duplicates do (run 29943085279's AddDate issue and its | ||
| "central behavior never exercised" thought sit on one line and share AddDate, | ||
| MemoryTTLDays, 180, 15). Duplicates are "same ask, different words"; those are | ||
| "same facts, different ask", which is a semantic judgment. | ||
|
|
||
| Asking each finder for its own identity key would cluster deterministically at | ||
| zero dispatch, and it fails for the same reason. A finder mints its key blind to | ||
| the other reviewers, so two of them agreeing on one defect would have to | ||
| independently pick the same string; and where a blind key DOES agree is the case | ||
| that must not merge, since the AddDate bug and its missing-test todo would both | ||
| key on `AddDate`. The clusterer's grounding evidence is not that key: it is | ||
| chosen after reading the candidate set, which is what makes it both possible and | ||
| checkable against every member's text. | ||
|
|
||
| So the unit of identity is now the defect, not the anchor. Tier 2 requires no | ||
| line agreement at all, which is what makes the same-defect-different-anchor shape | ||
| mergeable for the first time (one missing-test defect drew comments at three | ||
| anchors in run 29943085279); the line survives as tier-1 evidence and as the | ||
| survivor's posting anchor. | ||
|
|
||
| The model contributes identity only. Every merge rule stays in code, and the | ||
| clusterer must ground each group in the code element its members share, which is | ||
| then checked: a group whose `evidence` names no identifier, literal, or quoted | ||
| text is discarded, and so is a member whose own text never mentions it. Same | ||
| path and different sources are enforced as in tier 1, and only a NON-BLOCKING | ||
| copy may be absorbed on a model's word — with the survivor always the | ||
| highest-severity copy, a false tier-2 merge can cost an advisory comment and can | ||
| never lose a blocking finding or soften a verdict. The accepted price: one defect | ||
| flagged blocking by two sources in different words still posts twice unless tier | ||
| 1 reaches it. | ||
|
|
||
| Degradation is soft in both directions. The clusterer is dispatched only when the | ||
| candidates hold a pair it could legally merge (two anchored claims on one path | ||
| from two sources, at least one non-blocking), so a run with nothing to find never | ||
| pays for the step. A missing definition or an unusable reply leaves the run on | ||
| tier 1, exactly today's behavior, and surfaces as a run warning plus a | ||
| `clustering` block in `dispatch-result.json` (`candidates`, `proposed`, | ||
| `clusterMerges` per group, `clusterMerged` per absorbed copy, and every rejected | ||
| member with the rule that stopped it) rather | ||
| than as an author-facing note: duplicate hygiene is not a review dimension. Each | ||
| merge in `merges` now carries `via` (`similarity`/`clusterer`/`both`) plus the | ||
| tier and anchor of each absorbed copy, so the merge rate reads off the artifact | ||
| instead of off a PR that autofix has already tidied. | ||
|
|
||
| The "also flagged by" note names a source's line when it differs from the | ||
| survivor's, and quotes the subject of any copy tier 2 absorbed. One edit | ||
| discharging every member's ask does not mean every member asked in the same | ||
| words; run 30587343777's `conventions` copy wanted the symbol-name prefix, not | ||
| the corrected number. Tier 2 is exactly the case where the survivor's own | ||
| prose is known not to restate it (the text floor is what those copies could not | ||
| clear). Tier-1 copies are not quoted: clearing that floor against the survivor is | ||
| the evidence that they say the same thing, and repeating them would move the | ||
| duplicate noise into the surviving comment rather than remove it. | ||
|
|
||
| The live A/B now runs dedup, which it never did: a change to the merge rules was | ||
| unmeasurable by construction before this. Tier 1 runs in both arms (it is shared | ||
| code and production has had it since #245) while tier 2 is carried by each arm's | ||
| own review.md, exactly like the provenance gate's anchor-snap emulation, so the | ||
| arm delta prices the clusterer alone and a false merge shows up as recall loss. | ||
| The report gains a "Cross-source claims merged (of candidates)" row with tier 2's | ||
| share, its dollars and wall-clock, and per-case dedup counts. | ||
|
|
||
| Tier 2 can add merges and never subtract them, and that comes from the ORDER the | ||
| tiers run in rather than from any check on a proposal. Tier 1 settles completely, | ||
| and tier 2 then merges only the comments it left standing; a cluster member tier | ||
| 1 has already absorbed is read at the comment it now posts under, and a comment | ||
| tier 2 absorbs carries its own tier-1 copies along. Screening each proposed | ||
| member before it was unioned was not enough, because a member can be legal in | ||
| every respect and still displace the survivor of a tier-1 group it was clustered | ||
| into, orphaning that group's other copies: with three copies folded into one | ||
| comment and a higher-confidence claim clustered with just one of them, the old | ||
| single-pass merge posted three comments where tier 1 alone posted one. The | ||
| structural pre-screen stays for what it does do (an illegal member cannot | ||
| out-rank the legal ones and take a good proposal down with it), as does the | ||
| per-member re-check against the elected survivor. | ||
|
|
||
| `dispatch.ts` was at its 1000-line cap again, so the clustering step lands in | ||
| `dispatch-cluster.ts` (dispatch, contract parse, telemetry) rather than raising | ||
| the cap, and tier 2's rules in `lib/dedup-cluster.ts` beside the tests that | ||
| already carried the name; the eval's dedup stage splits to | ||
| `eval/live-dedup.ts` on the same principle. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,15 @@ gh workflow run review-eval-ab.yml --ref <branch> \ | |
| gh workflow run review-eval-ab.yml --ref <branch> \ | ||
| -f base_ref=origin/<branch> -f force_arms=true -f full=true -f repeats=3 -f max_usd=220 | ||
|
|
||
| # Powered run for a dedup / duplicate-comment change (~$45): the cases that | ||
| # actually produce multi-source clusters are the `documentation`-enabled ones | ||
| # (that reviewer contributes the extra copy on a comment defect, which is the | ||
| # shape production duplicated in run 30587343777), 5x per arm | ||
| gh workflow run review-eval-ab.yml --ref <branch> \ | ||
| -f base_ref=origin/main \ | ||
| -f cases=golden-documentation-stale-and-narrated,golden-documentation-restated-docstring,golden-documentation-missing-why,golden-documentation-commented-out-code,clean-documentation-earned-comments \ | ||
| -f repeats=5 -f max_usd=50 | ||
|
|
||
| # Pool reports across dispatches (run ids or local paths) | ||
| pnpm dlx tsx workflows/review/eval/aggregate.ts <run-id> <run-id> ... [--out <path>] | ||
| ``` | ||
|
|
@@ -145,6 +154,50 @@ claiming a band. | |
| a few lines off or past a short file's end) is what the gate's | ||
| anchor-snap fallback repairs; a finding still landing in this bucket was | ||
| outside both snap windows. | ||
| - **Cross-source merges:** the report's "Cross-source claims merged (of | ||
| candidates)" row is the duplicate-comment observable, read from the merge | ||
| stage rather than from the posted set (merges happen upstream of every later | ||
| drop, and in production autofix satisfies surviving duplicates with one edit, | ||
| which hides the symptom on the PR). Tier 1, the calibrated text-similarity | ||
| floor, is shared code and runs in BOTH arms; tier 2, the `claim-clusterer` | ||
| agent, is carried by each arm's own review.md, so a baseline built from a ref | ||
| that predates the agent reports `tier 1 only` and the arm delta prices the | ||
| clusterer alone. Read it beside recall: a false merge drops a distinct | ||
| finding, so it shows up as candidate-arm recall loss, not as a better | ||
| duplicate number. The `by clusterer` share counts absorbed COPIES, not groups, | ||
| so a group both tiers contributed to credits tier 2 only with what it actually | ||
| brought; production's `clustering` block records the same per-copy number as | ||
| `clusterMerged` (its `clusterMerges` counts groups), so the artifact and the | ||
| report that graduated the tier cannot be read as disagreeing. The share | ||
| carries tier 2's own dollars and wall-clock beside it, because the dispatch | ||
| precondition is satisfied by most multi-finding reviews: the steady state is a | ||
| serial Sonnet call on nearly every run, and a merge count is a graduation | ||
| argument only next to what those merges cost. `rejected` counts cluster | ||
| MEMBERS the merge rules refused, so one bad | ||
| proposal naming three ids counts three (`unknown-id` there means the clusterer | ||
| named claims that do not exist, which is a prompt or staging failure rather | ||
| than a quiet zero). A dispatch that returned nothing usable is reported as | ||
| `N clusterer failure(s)` rather than folded into the zero: the arm paid and | ||
| measured nothing, which is not the claim that tier 2 found no duplicates. | ||
| - **Tier 2's keep-or-cut bar,** written down before the next powered run so the | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought (non-blocking): The keep-or-cut bar was derived from a corpus that lacks the motivating shape, and it prices dollars but not latency. The bar (0.15 absorbed copies/dispatched case, $0.50/copy, 8% of arm cost) is calibrated on run 30651373253, whose cases carry 2-6 claims and never the 4-way density of run 30587343777 — the queued dense fixture is exactly the evidence the graduation decision was missing. Checked: production's A sketch, not a committable replacement: |
||
| decision is auditable after it. The tier ships enabled in the default | ||
| template, so this is the bar it must keep clearing, not one it must clear to | ||
| arrive. Read on the candidate arm of a `--repeats` run: | ||
| - **Any recall loss cuts it.** One `lost` spec traceable to a tier-2 merge | ||
| ends the tier; no merge rate buys back a dropped finding. Same for a failed | ||
| adversarial gate. This one is not traded off against the others. | ||
| - **Rate floor: 0.15 absorbed copies per dispatched case** (`by clusterer` | ||
| over the cases where the clusterer actually ran). Run 30651373253 measured | ||
| 0.20 (4 over 20 case-runs). Below the floor the steady state is a serial | ||
| Sonnet call on nearly every review that mostly does nothing, and the | ||
| dispatch precondition should be tightened or the tier cut. | ||
| - **Price ceiling: $0.50 per absorbed copy, and 8% of the arm's cost.** That | ||
| run measured $0.34 and +6% ($22.93 -> $24.30). The two are both needed: a | ||
| cheap tier that never fires and an expensive tier that fires often fail in | ||
| different ways. | ||
| - **Failures are not no-ops.** If `clusterer failure(s)` exceeds 10% of | ||
| dispatches the run measured plumbing, not a rate; fix it and re-measure | ||
| rather than reading the rate as a negative result. | ||
| - **Anchor-snap and the arms:** the deterministic pipeline is shared by | ||
| both arms, but the provenance gate emulates each arm's OWN review.md gate | ||
| version, keyed on the literal `anchor-snap` marker in the gate step. A | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.