Skip to content

Give pages a lead (H0) section so section coverage is total (B-0023)#105

Merged
mobileskyfi merged 3 commits into
mainfrom
lead-section-fragment
Jul 17, 2026
Merged

Give pages a lead (H0) section so section coverage is total (B-0023)#105
mobileskyfi merged 3 commits into
mainfrom
lead-section-fragment

Conversation

@mobileskyfi

@mobileskyfi mobileskyfi commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Makes a page's sections a total overlay of the page instead of a partial one, by minting a synthetic lead ("H0") fragment for the prose before the first h1–h3 heading. Homework, options, and the maintainer-reviewed decisions are in briefings/B-0023-page-section-normalization.md.

Why

parseSections() split the body only on h1–h3, so the lead-in prose — the AI-summary blockquote, the intro paragraph, and the entire body of pages whose only heading is the title — belonged to no sections row. On rc.99 that was 11.7% of the corpus by word (76,815 words) and 40 whole pages with no section at all, none of it rolling up to the page total or carrying a resolvable section_id. Worst case hotspot-customisation: one late h3 section covered 1,170 of 5,256 words (78% orphaned).

This is the pivot-table-friendliness gap the B-0022 export surfaced (pages.tsv sections didn't sum to the page), and the precondition for any future #27 section-level retrieval ("select sections X, Y, Z" instead of length=).

What changed

  • parseSections() emits a lead fragment for each page with non-empty lead prose: level = 0, reserved anchor_id = "_lead", heading = page title, sort_order = 0 (real sections shift to 1..n). A title-only lead mints nothing — no empty fragments (the Empty sections (129 / 4.4%): heading with no body wastes a get_page section= tool call #93 empty rule).
  • _lead is provably collision-free, not merely unlikely: slugify() deletes every character outside [a-z0-9\s-], so no heading — and no foo/foo-1 suffix — can produce an anchor_id containing _.
  • attributeSection() resolves pre-first-heading rows to the lead anchor instead of NULL (only genuinely lead-less pages stay NULL).
  • Extractor completion summary now reports lead count + % of page words covered; export's section_id disclosure updated.

Verification (rebuilt from the local page cache)

  • Section words cover 98.2% of page words (was 88.3%; the ~1.8% residual is heading-text lines, which belong to no fragment by construction).
  • 365 lead fragments; hotspot-customisation 1,170 → 5,250 covered, software-specifications 47 → 1,294, address-lists (a no-section page) 0 → 187.
  • Tables / properties / callouts with no section context: 16 / 12 / 104 → 0 / 0 / 0.
  • rosetta export regenerated: pages.tsv carries the _lead rows and rolls up.

Scope / non-goals

  • No schema DDL changesections already stores level/anchor_id, so no migration and no SCHEMA_VERSION bump; a re-extract simply produces the fuller rows.
  • No MCP tool-surface change (0.11.0 stance: stable surfaces, better underlying data). This ships the data a future Umbrella: MCP/TUI surface alignment #27 change would consume.
  • h4–h6 splitting (Option B) deliberately out of scope — grounding count: only 23 pages are h4-dominated, so the lead fragment alone closes the bulk; h4 is exception-driven and left for Umbrella: MCP/TUI surface alignment #27 if ever wanted.

Tests

Full suite green (877 pass / 0 fail). Anchor tests that encoded the old "preamble → null / zero sections" behavior were intentionally flipped with rationale; added lead-fragment / title-only-lead / lead-attribution unit tests and a fixture-built-DB coverage-invariant assertion (section_words / page_words > 0.9, lead present, level 0 reserved for the lead).

Briefing: briefings/B-0023-page-section-normalization.md. Part of #95; folds in #93's empty-section rule.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved section coverage for page content appearing before the first heading.
    • Tables, callouts, and properties in introductory content now receive accurate section attribution instead of being left unassigned.
    • Pages containing only a title or limited headings are handled more consistently.
  • Exports

    • Updated page export rollups to reflect fuller section coverage.
    • Added clearer disclosure details explaining when section attribution may be unavailable.

parseSections split the page body only on h1–h3, so pre-first-heading prose
(the AI-summary blockquote, the intro paragraph, and the entire body of pages
whose only heading is the title) belonged to no `sections` row — 11.7% of the
corpus by word and 40 whole pages on rc.99, none of it rolling up to the page
or carrying a resolvable section_id.

Mint a synthetic lead fragment per page (level 0, reserved anchor_id "_lead",
heading = page title) holding that content. `_lead` is provably collision-free:
slugify() strips every character outside [a-z0-9\s-], so no heading can produce
an underscore. attributeSection() now resolves pre-heading rows to the lead
instead of NULL. A title-only lead mints nothing (no empty fragments; #93 rule).

Rebuilt from the local cache: section words cover 98.2% of page words (was
88.3%; residual is heading-text lines), 365 lead fragments, and tables/
properties/callouts with no section context all drop to 0 (from 16/12/104).

No schema DDL change (sections already stores level/anchor_id) and no MCP
tool-surface change — this ships the data a future #27 retrieval-unit change
would need. Export pages.tsv/pages_summary.tsv now roll up; the section_id
disclosure and a fixture coverage-invariant test reflect the fuller coverage.

Briefing: B-0023-page-section-normalization. Part of #95; folds in #93's
empty-section rule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 01:09
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mobileskyfi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80151b25-ea22-4786-9f15-79c4a4396272

📥 Commits

Reviewing files that changed from the base of the PR and between 2c3a045 and 8f34216.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • DESIGN.md
  • MANUAL.md
  • briefings/B-0023-page-section-normalization.md
  • project-words.txt
  • src/export.ts
  • src/extract-docusaurus.test.ts
  • src/extract-docusaurus.ts
📝 Walkthrough

Walkthrough

The Docusaurus extractor now creates a synthetic _lead section for non-empty content before the first h1–h3 heading. Tables and callouts resolve to that section, storage tests validate coverage, and export disclosures document the updated attribution semantics.

Changes

Lead Section Normalization

Layer / File(s) Summary
Lead fragment parsing and attribution
src/extract-docusaurus.ts, src/extract-docusaurus.test.ts, briefings/B-0023-page-section-normalization.md
Adds the level-0 _lead fragment, updates pre-heading section attribution, and expands parser and end-to-end tests.
Coverage reporting and integrity validation
src/extract-docusaurus.ts, src/extract-docusaurus-storage.test.ts
Reports lead-section counts and section word coverage, with assertions for valid level-0 sections and at least 90% coverage.
Export disclosures and ETL decision records
src/export.ts, briefings/B-0023-page-section-normalization.md, CHANGELOG.md
Documents lead-anchor behavior, NULL semantics, reconciliation, and updated export rollups.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DocusaurusMarkdown
  participant parseSections
  participant parsePage
  participant Storage
  participant rosettaExport
  DocusaurusMarkdown->>parseSections: page markdown
  parseSections->>parsePage: sections including optional _lead
  parsePage->>Storage: pages, sections, tables, callouts
  Storage->>rosettaExport: section and page rollups
  rosettaExport-->>Storage: updated export disclosures
Loading

Possibly related PRs

Suggested labels: area:docusaurus

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a synthetic lead (H0) section to make section coverage total.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lead-section-fragment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Pull request overview

Normalizes Docusaurus page sectioning so sections becomes a near-total overlay of each page by introducing a synthetic lead (“H0”) fragment for all prose before the first real h1–h3 heading. This improves rollups/exports and makes pre-heading tables/properties/callouts reliably attributable to a resolvable section_id, aligned with the B-0023 briefing and #95’s schema/ETL audit track.

Changes:

  • Add a reserved lead section anchor (_lead) and update parseSections() / attributeSection() so pre-first-heading content resolves to that lead fragment when present.
  • Expand unit + storage/integration tests to cover lead minting, ordering, attribution behavior, and coverage invariants.
  • Update export disclosures and add a user-visible CHANGELOG.md entry documenting the behavior change.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/extract-docusaurus.ts Introduces LEAD_ANCHOR, emits lead (H0) fragments, updates section attribution rules, and reports lead/coverage metrics in the extractor summary.
src/extract-docusaurus.test.ts Updates/extends parsing + attribution tests to validate the new lead-fragment behavior and adjusted expectations.
src/extract-docusaurus-storage.test.ts Adds a DB-level coverage invariant and lead/level-0 assertions to catch regressions after extraction.
src/export.ts Updates disclosure text to reflect that pre-heading content now typically resolves to _lead.
CHANGELOG.md Adds an [Unreleased] entry describing the new lead fragment and its effects on attribution/exports.
briefings/B-0023-page-section-normalization.md Records the finalized, maintainer-reviewed decision and resolved questions for the lead-fragment approach.

Comment thread src/extract-docusaurus.ts
Comment thread src/export.ts
@mobileskyfi

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mobileskyfi

Copy link
Copy Markdown
Contributor Author

Codex Review/Fixes

• PR #105 (#105) is reviewed, fixed, and ready to merge.

Pushed commit 8f34216, which:

  • Addressed both Copilot comments and resolved both threads.
  • Replaced the correlated coverage query with global aggregates.
  • Corrected the NULL-section disclosure.
  • Fixed an additional parser bug where repeated # Title headings leaked into _lead text.
  • Added regression coverage matching real Docusaurus fixtures.
  • Updated DESIGN, MANUAL, CHANGELOG, briefing, and PR description wording.

Validation:

  • 97 targeted tests passed.
  • Full suite: 877 passed, 0 failed.
  • Typecheck, Biome, Markdown, spelling, and MCP contract checks passed.
  • Local retrieval eval failed only because ros-help.db is the legacy Confluence DB (321 pages, zero Docusaurus IDs); I left it untouched.
  • GitHub checks are green.
  • Open review threads: 0.
  • Final mergeStateStatus: CLEAN.

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