Skip to content

review: post-merge fold fixes from #365, #370, and #371 - #378

Merged
jwbron merged 1 commit into
mainfrom
jwbron/post-merge-fold-fixes
Aug 25, 2026
Merged

review: post-merge fold fixes from #365, #370, and #371#378
jwbron merged 1 commit into
mainfrom
jwbron/post-merge-fold-fixes

Conversation

@jwbron

@jwbron jwbron commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

The final approvals on #365, #370, and #371 landed after their last pushes, so their folds (12 observations) had no branch left to fix; this carries the ones worth taking.

  • review: post a PR comment when the dispatcher dies without a result #370's correctness catch is the consequential one: the death prose sequenced "report the run incomplete" before "continue at Step 9", and reporting incomplete can end the turn, so the cache compensation the death path exists to trigger (the risksPatternsKey drop) could be skipped with it. Step 9 now comes first, with the reason stated inline, and the guidance-repost claim is scoped to the next full-depth run (a scoped re-review doesn't recompute triage).
  • review: stop recommending gh aw update in the consumer README #371's four test-hardening items: whitespace-folded matching (prose wraps at ~76 columns, so "gh aw\nupdate" slipped the substring check), a positive control (without it, a broadened exclusion passes vacuously), and existence-before-read in the stale-allowlist detector (readFileSync's ENOENT swallowed the prune-it message). The fourth (use git grep -l) is declined with the reasoning in a comment: git grep matches within a line, which is exactly what misses the wrapped form the first fix exists for.
  • review: drop a subject that restates the discussion; stage body-size stats #365's two: foldToken's three length thresholds are documented (they all protect stem recognizability), and the empty-subject-tokens early return is pinned by a test (all-stopword subjects would otherwise be vacuously contained).

Not carried: #370's four standing threads (plan-CLI death, dead-process thought, green-run remedy, report_incomplete's surface) are PRA-49's scope, and #365's HOLD-list length question is what the bodyStats instrumentation that PR staged exists to answer.

1956 tests green locally.

KORE-2498

The consequential one is sequencing: the dispatcher-death prose said
'report the run incomplete, then continue at Step 9', and reporting
incomplete can end the turn, so the cache compensation the death path
exists to trigger could be skipped with it. Step 9 now runs first, and the
guidance-repost claim is scoped to the next full-depth run. The ban test
folds whitespace before matching (a wrapped 'gh aw update' counted as no
mention), gains a positive control so a broadened exclusion fails rather
than passing vacuously, and checks existence before reading so the
stale-allowlist message renders; git grep -l was considered and declined
in a comment, since line-based matching is exactly what misses the wrapped
form. foldToken's three length thresholds are documented and the
empty-subject-tokens early return is pinned.
@changeset-bot

changeset-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 266849c

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

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

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
khan-actions-bot requested review from a team, jaredly and jeresig and removed request for a team August 24, 2026 20:09

@github-actions github-actions Bot 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.

suggestion (non-blocking): The whitespace fold this PR adds is not pinned by any test, so it can regress silently.

Full finding

The whitespace fold this PR adds is not pinned by any test, so it can regress silently. Introduced by this change. mentionsTool is the PR's headline hardening — it exists so a prose-wrapped gh aw\nupdate cannot slip the substring check — but nothing in the file fails if the fold is removed.

Cheap check I ran: reverted mentionsTool to a plain text.includes("gh aw update") and re-ran the sweep over the current tree (git ls-files with the same three exclusions, then read each file). All five ALLOWED files still match and offenders is still empty, so all three tests stay green without the fold. That is because every allowlisted file happens to carry the phrase on one line today; the new positive control only proves the sweep reaches those files, not that it folds whitespace on the way.

So the guarantee is load-bearing on repo content that could change at any doc rewrap. One direct assertion on the helper pins it:

it("counts a line-wrapped mention (the whitespace fold)", () => {
    expect(mentionsTool("never recommend gh aw\nupdate here")).toBe(true);
    expect(mentionsTool("gh aw compile; update the lock")).toBe(false);
});

The negative half matters too — it is what keeps the fold from being widened into a match on any gh aw plus a later update.

review details found by correctness-reviewer
**note (non-blocking):** Gating the incomplete report on the cache CLI alone still leaves Step 9's `out/` artifact upload exposed to the same turn-ending hazard.
Full finding

Gating the incomplete report on the cache CLI alone still leaves Step 9's out/ artifact upload exposed to the same turn-ending hazard. Pre-existing hazard; this change narrows it rather than amplifying it, so this is a note, not a blocker — but the residual is worth a word since the sentence is being rewritten anyway.

Step 9 has two emissions, not one. First the cache-record CLI, then (I read Step 9 to confirm) "Finally, if you wrote any sub-agent outputs to /tmp/gh-aw/review/out/ this run (Step 3), upload that directory as a run-scoped artifact with the upload-artifact safe output." The new wording gates the incomplete report on the CLI only, so an orchestrator that follows it literally may report incomplete between the two — and by the hunk's own premise ("reporting incomplete can end the turn") that drops the out/ upload.

That matters on precisely this path. The motivating incident named two lines down, run 32418662895, was killed during claim validation, i.e. late enough that out/ held most of the sub-agent results. On a death with no plan and no posted review, that directory is the only evidence of what the run actually saw, and the death notice itself carries none of it.

The old ordering put the report ahead of all of Step 9, so both the cache write and the upload were at risk; this change rescues the cache write. Extending the gate to the whole step rescues the artifact too.

review details found by correctness-reviewer
review details review-v1.18.0 | schema 2 | depth full | re-review scoped blocking-only | enable holistic,completeness,test-adequacy,first-principles,conventions,documentation

* The three length thresholds all protect stem recognizability: a token of
* 3 or fewer chars is returned whole (too short to carry a strippable
* suffix; "les" is a word, not "le"+s), a suffix strip must leave a stem of
* at least 3 chars ("goes" loses "es" only because "go"+es fails the floor

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.

suggestion (non-blocking): The middle threshold's example says "goes" loses "es" but the code (and the same sentence) keeps "goe". The PR's stated #365 item is documenting foldToken's three length thresholds; tracing the loop on "goes" ("es" fails 4-2>=3, no break, then "s" passes 4-1>=3) shows it loses only the "s", which contradicts the clause's own "loses 'es'" wording. The other two threshold explanations check out against the code.

Suggested change
* at least 3 chars ("goes" loses "es" only because "go"+es fails the floor
* at least 3 chars ("goes" keeps its "e": "go"+es fails the floor, so it
* falls through to the "s" strip and folds to "goe"), and the trailing-e
Lower-confidence observations (1)
  • workflows/review/gh-aw-update-ban.test.ts:88 nitpick (non-blocking): Test title leads with a category prefix instead of the file's verb-phrase form. (conventions)
review details found by completeness | also flagged by first-principles: The threshold comment misdescribes the code it documents.; documentation: The added foldToken threshold docstring's "goes" example contradicts what the code does.

* matches within a line, so the wrapped form would slip past it.
*/
const mentionsTool = (text: string): boolean =>
text.replace(/\s+/g, " ").includes("gh aw update");

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.

suggestion (non-blocking): The whitespace fold — this PR's headline test fix — is not pinned by any assertion. I checked all five ALLOWED files with and without the fold applied: each contains the literal gh aw update on one line, so the positive control and the stale-allowlist detector both pass with the fold removed, and grep -rn mentionsTool shows no other caller. A one-line direct assertion on mentionsTool would pin the behavior independently of incidental repo content.

Suggested change
text.replace(/\s+/g, " ").includes("gh aw update");
it("counts a line-wrapped mention (whitespace fold)", () => {
expect(mentionsTool("...recommend gh aw\nupdate to repin...")).toBe(
true,
);
expect(mentionsTool("gh aw update")).toBe(true);
expect(mentionsTool("gh awful updates")).toBe(false);
});
review details found by test-adequacy

drops `risksPatternsKey` so the next run reposts the guidance comment
your death notice collapsed, and leaves the prior fingerprints
and noting that the next push triggers a fresh full review; then skip
Steps 4-8, continue at Step 9, and report the run incomplete only AFTER

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.

question (non-blocking): The reordered death path defers the incomplete report only past Step 9's cache CLI, not past Step 9's upload-artifact step. Pre-existing hazard — the old wording put the report ahead of all of Step 9, so this change narrows it rather than introducing it — but the residual is worth a word since the sentence is being rewritten anyway.

Step 9 has two emissions: cache-record.ts, then "Finally, if you wrote any sub-agent outputs to /tmp/gh-aw/review/out/ this run (Step 3), upload that directory as a run-scoped artifact". lib/dispatch.ts writes each agent's result to out/<agent>.json as it goes, so on the run 32418662895 shape (killed at the Bash ceiling mid-claim-validation) that directory is the only evidence of what the run saw — and by the hunk's own premise ("reporting incomplete can end the turn") an orchestrator that reports right after the cache CLI drops it. Extending the gate to all of Step 9 rescues the artifact too.

review details found by skill-auditor (out-of-lane) | also flagged by holistic

// Without this, a broadened exclusion (or a broken read) would make
// the offenders sweep pass vacuously. The same sweep, WITHOUT the
// allowlist filter, must find every allowlisted file.
const found = sweptFiles().filter((file) =>

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.

suggestion (non-blocking): The positive control's only new claim is sweep membership, which needs no file reads. The third test (lines 100-113) already reads each ALLOWED file and asserts mentionsTool is true, so the read half of the positive control is redundant; the only claim unique to it is that a broadened exclusion in sweptFiles() has not dropped an allowlisted path. Asserting membership in the swept path list alone states that claim exactly and costs one git ls-files.

Suggested change
const found = sweptFiles().filter((file) =>
const swept = new Set(sweptFiles()); for (const file of ALLOWED) { expect(swept, `sweep no longer reaches ${file}`).toContain(file); }
review details found by first-principles

@github-actions

Copy link
Copy Markdown
Contributor

Review live A/B

Baseline: origin/main (review.md 08adcb905dc2); candidate: working tree (review.md c4448c05cdac).

Ruler: matcher deterministic+arbiter; corpus 0441b7c6a581 (10 cases).

Metric Baseline Candidate Delta
Must-catch recall 100% 100% +0%
Verdict agreement 90% 90% +0%
Noise (unmatched posted) 68% 69% +1%
Clean false flags 1 1
Judge mean quality 0.87 0.83 -0.03
Cost $9.74 $9.38
Wall clock 1570s 1438s
Cases run / skipped 10 / 0 10 / 0
Misses found-but-dropped 0 0
Findings anchor-snapped 1 0
Cross-source claims merged (of candidates) 4 / 32 (0 by clusterer at $0.36 / 192s, 10 proposed member(s) rejected) 5 / 35 (0 by clusterer at $0.39 / 199s, 10 proposed member(s) rejected)

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.

@jwbron
jwbron merged commit d384230 into main Aug 25, 2026
11 checks passed
@jwbron
jwbron deleted the jwbron/post-merge-fold-fixes branch August 25, 2026 19:27
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