HOMELAB-2682: fix(landing): heading context regression from the blockers fix #97

Merged
pi-landing merged 2 commits from plane/HOMELAB-lock-heading-delta into main 2026-11-05 07:52:03 +00:00
Collaborator

Restores the pre-#96 semantic exactly: the heading line is never a finding of its own, but it supplies the blocker severity for the content lines under it, so a line there that states a defect without a blocker token is a finding again (review-measured shape: ## Blockers + - the census crashes when the key file is unreadable).

Guards: negation/documentation/narrative rules unchanged; a heading or horizontal rule ends the section; a whole-line determination of zero (None., None found., Nothing outstanding., 0 remaining.) reports nothing; a line opening with a resolution word is the reviewer's closure note; a labelled bullet (- B2 — ..., 1. ...) keys as its own id so resolved B2 cites it, and blockerIdsIn reads it the same way.

Tests — forgejo/tests/landing-blocker-heading-context.test.ts (11 cases; 5 fail on 299a0c5, 6 pass there):

  • regression shapes (fail before this fix): defect bullet under ## Blockers → finding; plain defect line under it → finding; labelled bullet B2 keys as B2 and resolved B2 closes it; the lock refuses the merge on a defect under the heading; the section does not leak (## Summary, ---, a following heading).
  • #96 pins (green before and after): heading alone, ## Open blockers:, #### Blockers → no finding; empty-heading verdict still lands; ## Blockers: <defect> still a finding and ## Blockers: none still zero; a real **Blocker B1 — ...** under the heading still counts exactly once.

Full suite 24 files / 680 tests green; tsc --noEmit clean. Not merged.

Restores the pre-#96 semantic exactly: the heading line is never a finding of its own, but it supplies the blocker severity for the content lines under it, so a line there that states a defect without a `blocker` token is a finding again (review-measured shape: `## Blockers` + `- the census crashes when the key file is unreadable`). Guards: negation/documentation/narrative rules unchanged; a heading or horizontal rule ends the section; a whole-line determination of zero (`None.`, `None found.`, `Nothing outstanding.`, `0 remaining.`) reports nothing; a line opening with a resolution word is the reviewer's closure note; a labelled bullet (`- B2 — ...`, `1. ...`) keys as its own id so `resolved B2` cites it, and `blockerIdsIn` reads it the same way. Tests — `forgejo/tests/landing-blocker-heading-context.test.ts` (11 cases; **5 fail on 299a0c5**, 6 pass there): - regression shapes (fail before this fix): defect bullet under `## Blockers` → finding; plain defect line under it → finding; labelled bullet `B2` keys as `B2` and `resolved B2` closes it; the lock refuses the merge on a defect under the heading; the section does not leak (`## Summary`, `---`, a following heading). - #96 pins (green before and after): heading alone, `## Open blockers:`, `#### Blockers` → no finding; empty-heading verdict still lands; `## Blockers: <defect>` still a finding and `## Blockers: none` still zero; a real `**Blocker B1 — ...**` under the heading still counts exactly once. Full suite 24 files / 680 tests green; `tsc --noEmit` clean. Not merged.
HOMELAB-2682: fix(landing): blocker heading keeps context for following lines
Some checks failed
CI / pr-title (pull_request) Successful in 0s
CI / forgejo-ci (pull_request) Successful in 50s
CI / forgejo (pull_request) Failing after 2s
CI / pi-otel-telemetry (pull_request) Successful in 55s
CI / plane (pull_request) Successful in 1m23s
CI / mcp-bridge (pull_request) Successful in 29s
CI / nats-intercom (pull_request) Successful in 36s
CI / ask-user-question (pull_request) Successful in 43s
tests / python (pull_request) Successful in 14s
CI / pi-friction (pull_request) Successful in 47s
CI / refocus (pull_request) Successful in 51s
CI / skill-args (pull_request) Successful in 1m0s
CI / todo (pull_request) Successful in 1m4s
CI / redact (pull_request) Successful in 34s
CI / pi-swarm-suite (pull_request) Successful in 4m3s
1e65400138
#96 stopped a heading-only "## Blockers" line from being an id-less finding
(pi-extensions#180, unclosable), but that also dropped the severity the heading
confers on its own list: "## Blockers" + "- the census crashes when the key file
is unreadable" refused before #96 and passed after it, because the bullet states
a defect without carrying a "blocker" token.

The heading stays a section TITLE and never a finding; it now supplies the
blocker context for the content lines under it again, so a line there that
states a defect is a finding. Guards: negation, documentation and narrative
rules still apply, a nested heading or a horizontal rule ends the section, a
whole-line determination of zero (None. / None found. / Nothing outstanding. /
0 remaining.) reports nothing, and a line OPENING with a resolution word is the
reviewer's closure note, not a defect. A labelled bullet under the heading
("- B2 — …", "1. …") keys as its own id so "resolved B2" can cite it, and
blockerIdsIn reads it the same way. "## Blockers: <defect>" still counts and
"## Blockers: none" still counts as zero.
Collaborator

Verdict of record — ld-reviewer — head 1e65400138244b9650d785c2763ac0dd0094f31b: APPROVE
model: deepseek-flash
session: bb69e5db-864a-45dd-bcec-57de9b21eeb8

Reviewed at the pinned head in a throwaway clone, read-only: no edits, no commits, no pushes, this one comment.

  • Every claim checked by RUNNING the code, not by reading the PR text. ## Blockers alone, ## Open blockers:, #### Blockers, ## Blockers: none give zero findings; ## Blockers: <defect> gives one; the heading plus a defect bullet, or a plain defect line, gives one id-less finding again (the regression this closes); the heading plus resolved B2 gives zero; ## Summary and ## Verdict sections confer nothing, and a nested heading or a thematic break ends the section.
  • The 299a0c5d claim is exact: 5 of the 11 new cases fail there, 6 pass, and the 5 are the ones the description lists. Full suite 24 files / 680 tests green on the head; tsc --noEmit clean.
  • No new loosening in the finding scan: across a 40-shape corpus the head never reports fewer findings than 299a0c5d. The delta only ADDS findings, so every difference against 299a0c5d is a tightening, and the shapes that pass now but refused pre-#96 are exactly the #96 heading pins this PR re-pins in tests.
  • Mechanism bound: the section ends at the next heading or a thematic break — blank lines do NOT end it. A distant paragraph under the heading, even several blocks later, inherits the severity and becomes an id-less finding. That is over-refusal (a reviewer's resolution phrase closes it) and the same body refused pre-#96 too, but the inheritance is unbounded; worth a follow-up if it costs a round.
  • Two guards under-cover their stated intent: a BULLETED zero (- None., - 0 remaining) is not matched because the empty-statement test runs on plainText(line), which keeps the bullet mark — so the most common "nothing here" form registers as a finding, and - 0 remaining keys as id 0.
  • Watch item, not a blocker: blockerIdsIn now sees section-labelled ids, so a hold body that names a lead-labelled id and later says the earlier fix was premature clears ITSELF (hold open on 299a0c5d, cleared on this head; reproduced through evaluateLandingLock). Identity rules still hold — reviewer bodies only — and the token spelling of the same body already clears on 299a0c5d, so the root cause is the self-citing resolution arm, not this delta. No new test pins the changed blockerIdsIn section path.
  • Scope is clean: forgejo/landing.ts and the one new test file, +266/-7, nothing else.

Approving on that basis; the last three items are follow-up material, none of them a regression of a property the lock defends today.

**Verdict of record — ld-reviewer — head `1e65400138244b9650d785c2763ac0dd0094f31b`: APPROVE** model: deepseek-flash session: bb69e5db-864a-45dd-bcec-57de9b21eeb8 Reviewed at the pinned head in a throwaway clone, read-only: no edits, no commits, no pushes, this one comment. - Every claim checked by RUNNING the code, not by reading the PR text. `## Blockers` alone, `## Open blockers:`, `#### Blockers`, `## Blockers: none` give zero findings; `## Blockers: <defect>` gives one; the heading plus a defect bullet, or a plain defect line, gives one id-less finding again (the regression this closes); the heading plus `resolved B2` gives zero; `## Summary` and `## Verdict` sections confer nothing, and a nested heading or a thematic break ends the section. - The 299a0c5d claim is exact: 5 of the 11 new cases fail there, 6 pass, and the 5 are the ones the description lists. Full suite 24 files / 680 tests green on the head; `tsc --noEmit` clean. - No new loosening in the finding scan: across a 40-shape corpus the head never reports fewer findings than 299a0c5d. The delta only ADDS findings, so every difference against 299a0c5d is a tightening, and the shapes that pass now but refused pre-#96 are exactly the #96 heading pins this PR re-pins in tests. - Mechanism bound: the section ends at the next heading or a thematic break — blank lines do NOT end it. A distant paragraph under the heading, even several blocks later, inherits the severity and becomes an id-less finding. That is over-refusal (a reviewer's resolution phrase closes it) and the same body refused pre-#96 too, but the inheritance is unbounded; worth a follow-up if it costs a round. - Two guards under-cover their stated intent: a BULLETED zero (`- None.`, `- 0 remaining`) is not matched because the empty-statement test runs on `plainText(line)`, which keeps the bullet mark — so the most common "nothing here" form registers as a finding, and `- 0 remaining` keys as id `0`. - Watch item, not a blocker: `blockerIdsIn` now sees section-labelled ids, so a hold body that names a lead-labelled id and later says the earlier fix was premature clears ITSELF (hold open on 299a0c5d, cleared on this head; reproduced through `evaluateLandingLock`). Identity rules still hold — reviewer bodies only — and the token spelling of the same body already clears on 299a0c5d, so the root cause is the self-citing resolution arm, not this delta. No new test pins the changed `blockerIdsIn` section path. - Scope is clean: `forgejo/landing.ts` and the one new test file, +266/-7, nothing else. Approving on that basis; the last three items are follow-up material, none of them a regression of a property the lock defends today.
Collaborator

Verdict of record — ld-reviewer — head 1e65400138244b9650d785c2763ac0dd0094f31b: APPROVE
model: deepseek-flash
session: 4f1c9a02-7d63-4b1e-9c05-2a8b0e6f31c7

Independent seat 2. Every claim checked by RUNNING the code at the pinned head against two baselines (299a0c5, and pre-#96 f3927e7) over a 60-shape differential. This head never reports fewer findings than 299a0c5, so the delta only tightens.

  • Flag 1 (heading leak) CONFIRMED, and stickier than plain over-refusal. The section ends only at the next heading or a thematic break, so prose several blocks under ## Blockers becomes an id-less finding — and a later clean APPROVE from the same reviewer does NOT clear it (measured: two verdicts, still refused; only a "blockers resolved" phrase closes it). Not a loosening: that body refused on pre-#96 too. The cost is liveness, not safety.
  • Flag 2 (bulleted - None.) CONFIRMED and UNCHANGED from main. - None., * None found., - Nothing outstanding. refuse on pre-#96 main as well — there the heading carried the finding, here the bullet does. Same outcome, different carrier line. - 0 remaining keys as id 0.
  • Flag 3 CONFIRMED as a mechanism, and pre-existing. blockerIdsIn now sees section lead ids, so a hold whose own body carries a resolution line can self-clear (measured: ids ["B2"] here, [] on 299a0c5). The token spelling of the same body already clears on 299a0c5, so the self-citing resolution arm, not this delta, is the cause. Net direction is mostly safer: a hold naming a section-labelled id now binds (passes on 299a0c5, refuses here).
  • My own residual holes — real defects that still PASS, and not introduced here (they pass on 299a0c5 too): a nested heading between the title and the defect (## Blockers / ### Details / - the census crashes…) resets the section; and a defect that OPENS with a resolution word (resolved B2 does not actually fix the crash, Fixed path still crashes on a nil key) is read as a closure, because the section's resolution-lead guard carries no negation test. "Restores the pre-#96 semantic exactly" overstates the delta for those two families — follow-up material, not a regression against what is merged.
  • Suite 680/680 green; tsc --noEmit clean; 5 of the 11 new cases fail on 299a0c5 and they are exactly the five the description names.

Approving: no input loosens the merge condition against the merged head, and the flags are either unchanged-from-pre-#96 or pre-existing. The - None. stickiness is the one I would fix first.

**Verdict of record — ld-reviewer — head `1e65400138244b9650d785c2763ac0dd0094f31b`: APPROVE** model: deepseek-flash session: 4f1c9a02-7d63-4b1e-9c05-2a8b0e6f31c7 Independent seat 2. Every claim checked by RUNNING the code at the pinned head against two baselines (`299a0c5`, and pre-#96 `f3927e7`) over a 60-shape differential. This head never reports fewer findings than `299a0c5`, so the delta only tightens. - **Flag 1 (heading leak) CONFIRMED, and stickier than plain over-refusal.** The section ends only at the next heading or a thematic break, so prose several blocks under `## Blockers` becomes an id-less finding — and a later clean APPROVE from the same reviewer does NOT clear it (measured: two verdicts, still refused; only a "blockers resolved" phrase closes it). Not a loosening: that body refused on pre-#96 too. The cost is liveness, not safety. - **Flag 2 (bulleted `- None.`) CONFIRMED and UNCHANGED from main.** `- None.`, `* None found.`, `- Nothing outstanding.` refuse on pre-#96 main as well — there the heading carried the finding, here the bullet does. Same outcome, different carrier line. `- 0 remaining` keys as id `0`. - **Flag 3 CONFIRMED as a mechanism, and pre-existing.** `blockerIdsIn` now sees section lead ids, so a hold whose own body carries a resolution line can self-clear (measured: ids `["B2"]` here, `[]` on `299a0c5`). The token spelling of the same body already clears on `299a0c5`, so the self-citing resolution arm, not this delta, is the cause. Net direction is mostly safer: a hold naming a section-labelled id now binds (passes on `299a0c5`, refuses here). - **My own residual holes — real defects that still PASS, and not introduced here** (they pass on `299a0c5` too): a nested heading between the title and the defect (`## Blockers` / `### Details` / `- the census crashes…`) resets the section; and a defect that OPENS with a resolution word (`resolved B2 does not actually fix the crash`, `Fixed path still crashes on a nil key`) is read as a closure, because the section's resolution-lead guard carries no negation test. "Restores the pre-#96 semantic exactly" overstates the delta for those two families — follow-up material, not a regression against what is merged. - Suite 680/680 green; `tsc --noEmit` clean; 5 of the 11 new cases fail on `299a0c5` and they are exactly the five the description names. Approving: no input loosens the merge condition against the merged head, and the flags are either unchanged-from-pre-#96 or pre-existing. The `- None.` stickiness is the one I would fix first.
chore: re-trigger CI (transient checkout 502 on the previous run; no content change)
All checks were successful
CI / pr-title (pull_request) Successful in 0s
CI / forgejo-ci (pull_request) Successful in 46s
CI / skill-args (pull_request) Successful in 46s
CI / ask-user-question (pull_request) Successful in 44s
CI / todo (pull_request) Successful in 49s
CI / pi-otel-telemetry (pull_request) Successful in 1m1s
tests / python (pull_request) Successful in 9s
CI / mcp-bridge (pull_request) Successful in 26s
CI / nats-intercom (pull_request) Successful in 27s
CI / plane (pull_request) Successful in 1m22s
CI / redact (pull_request) Successful in 32s
CI / pi-friction (pull_request) Successful in 38s
CI / refocus (pull_request) Successful in 45s
CI / forgejo (pull_request) Successful in 1m43s
CI / pi-swarm-suite (pull_request) Successful in 3m43s
02ef268b84
Collaborator

Verdict of record — ld-reviewer — head 02ef268b84: APPROVE

model: deepseek-flash / session: 01a1aa98-f6e8-71f2-9feb-fb606f203bcb

Empty-commit re-trigger verified (diff vs 1e65400 empty); the heading-context restoration and its tests stand as reviewed (21675).

Evidence: 02ef268b's single parent is 1e65400138244b9650d785c2763ac0dd0094f31b; git diff --name-status 1e65400 02ef268b returns 0 entries and both commits share tree 54a03e4f3971815521e19aa9279bdf866a1cbd3a; git show 02ef268b --stat lists 0 files. The reviewed delta (forgejo/landing.ts +126, forgejo/tests/landing-blocker-heading-context.test.ts +147) is therefore byte-identical to the head reviewed at 21675, so no new content reached the review.

**Verdict of record — ld-reviewer — head 02ef268b841d9255ebbc59be5f96693081ede323: APPROVE** model: deepseek-flash / session: 01a1aa98-f6e8-71f2-9feb-fb606f203bcb Empty-commit re-trigger verified (diff vs 1e65400 empty); the heading-context restoration and its tests stand as reviewed (21675). Evidence: `02ef268b`'s single parent is `1e65400138244b9650d785c2763ac0dd0094f31b`; `git diff --name-status 1e65400 02ef268b` returns 0 entries and both commits share tree `54a03e4f3971815521e19aa9279bdf866a1cbd3a`; `git show 02ef268b --stat` lists 0 files. The reviewed delta (`forgejo/landing.ts` +126, `forgejo/tests/landing-blocker-heading-context.test.ts` +147) is therefore byte-identical to the head reviewed at 21675, so no new content reached the review.
Collaborator

Verdict of record — ld-reviewer — head 02ef268b84: APPROVE

model: deepseek-flash
session: 01a1aaaa-e7dc-71f2-9feb-fb64c7f2ea2a

Empty-commit re-trigger independently verified (0-file diff, shared tree); the heading-context delta stands as reviewed in 21690.

**Verdict of record — ld-reviewer — head 02ef268b841d9255ebbc59be5f96693081ede323: APPROVE** model: deepseek-flash session: 01a1aaaa-e7dc-71f2-9feb-fb64c7f2ea2a Empty-commit re-trigger independently verified (0-file diff, shared tree); the heading-context delta stands as reviewed in 21690.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Homelab/pi-extensions!97
No description provided.