Skip to content

fix(comments): disallow posting threads GitHub cannot show - #180

Draft
moritzwilksch wants to merge 6 commits into
mainfrom
fix/168-guard-github-post
Draft

moritzwilksch wants to merge 6 commits into
mainfrom
fix/168-guard-github-post

Conversation

@moritzwilksch

@moritzwilksch moritzwilksch commented Sep 14, 2026

Copy link
Copy Markdown
Owner

GitHub accepts a review comment on a file the PR does not change, or on a line past its three context lines, and then shows it nowhere. diffle let such threads into a pending review. Now a thread GitHub cannot show carries a githubBlocker: its Add button and the sidebar's Add all are disabled with the reason, and buildReview skips it and reports why.

Per-thread blocker. CommentStore derives githubBlocker next to stale, at relocation and import, from a ReviewView that now exposes inDiff and a side's onGithub ranges. buildReview skips by the field alone; the client only reads it.

 export const CommentThreadSchema = z.object({
   stale: z.boolean(),
   staleFromLine: z.number().optional(),
+  githubBlocker: z.string().optional(),
 });

 export interface SideView {
   shown: LineRange[] | null;
+  onGithub: LineRange[];
 }
 export interface ReviewView {
   hasFile(path: string): boolean;
+  inDiff(path: string): boolean;
 }

Narrowing to a context. shownRanges(patch, side, context) keeps the lines within context of a change, as a diff generated with that context would show them. GITHUB_CONTEXT_LINES = 3 feeds onGithub.

Two bugs on the way. GitRepo pins diff.suppressBlankEmpty=false: with it on, an empty context line loses its leading space, and the hunk-body walk overran into the next hunk header and dropped later hunks. Import now places a line by the same shows predicate as relocation, so a line the diff does not show gets a 400 instead of going stale on the next refresh.

Open decision. Lines reached by expanding context or the full-file view of a changed file are outside the hunks, so commenting there now fails immediately; before, it was accepted and went stale on the next refresh. Allowing such comments instead, fresh with a GitHub blocker, is a product call.

Closes #168

Screenshots

Fixture: main..feature with a real GitHub PR mocked via gh, one open thread inside the hunk (L5–6) and one three lines past GitHub's context (L12) or on a file outside the diff (README.md).

Before After
Before: the L12 card's Add button is enabled like the L5–6 one After: the L12 card's Add button is disabled; its tooltip reads "Cannot add to the GitHub review: lines outside the pull request diff"
Before: the sidebar's Add all button is enabled with only blocked threads open After: the sidebar's Add all button is disabled; its tooltip reads "GitHub cannot show any open thread"

@github-actions github-actions Bot added the fix label Sep 14, 2026
shownRanges(patch, side, context) walks each hunk body and keeps only the
lines within `context` of a change, as a diff generated with that much
context would show them. GitHub's pull request diff has three context
lines; diffle's default is five, so the two disagree on the outer lines.
A thread on a file opened from the tree, or on a line past GitHub's three
context lines, was not stale, so the export posted it into a pending
review where GitHub never displays it. Relocation now derives
`githubBlocker` alongside `stale`: set for every stale thread, for a
thread on an unchanged file, and for a line thread outside the pull
request diff. Imports get it from the start through the same ReviewView,
which replaces AnchorSource. buildReview skips on the blocker and reports
its reason instead of checking `stale`.

Fixes #168
The card's GitHub button is disabled with the server's reason, and the
panel's all-threads button when no open thread can be shown.
With diff.suppressBlankEmpty=true git writes an empty context line as ''
instead of ' '; shownRanges' hunk-body walk then overruns into the next
header and drops that hunk, flagging every later thread stale.
A stale thread carried 'lines outside the pull request diff' whatever
made it stale; it now carries 'stale'. Fresh threads keep the diff-based
reasons, and the blocker helper no longer takes a nullable side view.
placeImport quoted a range and derived its GitHub blocker without asking
whether the diff shows it, while relocateAll flags exactly such a range
stale. A thread posted on a line outside every hunk was therefore fresh
with 'lines outside the pull request diff' until the next relocation
turned it stale with 'stale'.

Both paths now share one predicate, `shows`: relocation flags a range
stale when it leaves what the diff shows, and an import there is refused
as UnquotableError (400), like a line past the end or a file outside the
review. A new thread never starts where the next relocation would flag
it stale.
@moritzwilksch
moritzwilksch force-pushed the fix/168-guard-github-post branch from 9d53d24 to 59ebac5 Compare September 14, 2026 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Disallow "post on github" on file-/ line comments that cannot be shown on github?

1 participant