Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changelog/ci-2904-pr-body-citations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
section: Changed
---

- **Verify PR-body code citations (refs #2904)** — Check cited source lines, offered evidence, lexer-derived test identifiers, and master-red transcripts against the head tree.
5 changes: 5 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ Closes #NNN — only when every acceptance criterion is met. Otherwise Refs
The reference must ALSO be in the PR title — the title becomes the
merge-commit subject.

Citations: every code fact uses ``path:line``; an offered fenced quote is
checked against the cited source line. Test ids in tables are real `it(` titles;
pre-existing-red claims carry the
`origin/master` transcript.

## Type of change

- [ ] Bug fix
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ migrate-*.js
*.md
!CLAUDE.md
!tests/fixtures/changelog-entries/*.md
!tests/fixtures/ci-pr-bodies/*.md
!.changelog/*.md
!AGENTS.md
!HISTORY.md
Expand Down
24 changes: 24 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,21 @@ Diagnostics have one model-facing surface, `lens_diagnostics`; `source` selects

**PR body structure is advisory-linted.** Keep `Summary`, `Tests`, `Blast radius`, `Class sweep`, and `Observability` populated — plus `Test assessment` whenever the PR touches `tests/` (see "Test assessment and removal" under Test requirements); `scripts/check-pr-body.mjs` also checks runtime diff observability when its local range is available, so reviewers still judge the answers.

The PR-body citation checker uses one path-and-line reader for code and
existing-record citations. CI resolves sources from `HEAD`; `--lint-local`
resolves sources and test references from the working tree, including untracked
files under `tests/`, so uncommitted fixer changes can be cited. Fenced
transcripts are excluded from citation and test-reference scans. Source quotes
must match within ±20 lines of the cited line;
transcript fences after citations are not source quotes. Test references come
from declaration titles and paths under `tests/`, excluding the checker's own
fixture inputs; the checker's own test contributes declaration titles only, and
short identifiers use whole-token matching. The
The test-reference corpus comes from lexer-emitted declaration string spans;
table cells are candidates only under headers matching `/test|probe|case|witness|id/i`.
The master-claim rule splits markdown blocks and sentences without treating dots in
code spans as punctuation; a transcript must be in the next non-blank block.

**Draw the blast radius as a call-tree diff (optional, text only; 2026-09-06).** Prose blast radius keeps missing callers. When a change touches a shared seam, the `Blast radius` section may carry a call-tree diff: the changed symbol, its callers above, its callees below, with `+`/`-` on the lines that moved (`resyncLspFile` / ` touchFile` / `+ getAuxiliaryClientsForFile`). A fix round that changes ordering or control flow shows the before/after as a flow diff of the same shape. The reviewer verifies the tree against grep, which is what the reviewer playbook's neighbourhood rule asks for. Never HTML, Mermaid, or diagrams for their own sake — the smallest text view that makes the reviewer's check mechanical.

## Contributing
Expand Down Expand Up @@ -1730,6 +1745,15 @@ Actions, `scripts/check-pr-body.mjs` fails with `diff unavailable:`; local runs
outside CI retain structural-only fallback. Runtime markers exclude test files,
`__tests__` directories, and TypeScript declaration files.

The PR-body lint verifies every backticked `path:line` against `HEAD`, checks an
offered adjacent fenced quote against source text, checks declaration-backed
test titles and paths under `tests/`, and requires an `origin/master` transcript
in the next non-blank block for master/environment claims. Ranges and
approximate line hints resolve from
their first line. Both CI and `--lint-local` use the real
`origin/master...HEAD` range; the lane remains advisory until ten consecutive
merged PRs pass.

Message-end attribution uses a bounded two-slot session anchor. A primary
`session_start` rotates `lastStableSessionId` into `previousSessionId` because
queued stale events from the replaced session can drain after the boundary;
Expand Down
3 changes: 2 additions & 1 deletion docs/pi-lens-fixer.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ A fix on `clients/lsp/`, the read guard, tool registration, or session lifecycle
`## Test assessment` whenever the diff touches `tests/`; Observability
names a record literal that appears in the runtime diff, and may say
exactly "No new failure path; no record added." only when the diff adds no
failure path (no new catch, fallback or degradation branch). Record: on
failure path (no new catch, fallback or degradation branch). Every code fact
is a citation the check verifies. Record: on
2026-09-10 most open PRs failed the PR-body check on one of these two rules.
- No Git authority unless granted: leave changes uncommitted; hand off
`PR_BODY.md` (template headings, every red and mutation quoted in ≤5 lines)
Expand Down
9 changes: 8 additions & 1 deletion scripts/check-pr-body.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ export declare function normalizePrBodyForChecking(
): { body: string; normalized: boolean };
export declare function lintPrBody(
body?: string,
options?: { requireTestAssessment?: boolean; diff?: string },
options?: {
requireTestAssessment?: boolean;
diff?: string;
cwd?: string;
git?: (args: string[], options?: Record<string, unknown>) => string;
workingTree?: boolean;
headFiles?: Map<string, string>;
},
): {
valid: boolean;
errors: string[];
Expand Down
Loading
Loading