Skip to content

feat: promote beholder 4.0 main-content data into core schema#227

Merged
YusukeHirao merged 13 commits into
devfrom
feature/main-contents-promotion
Jul 22, 2026
Merged

feat: promote beholder 4.0 main-content data into core schema#227
YusukeHirao merged 13 commits into
devfrom
feature/main-contents-promotion

Conversation

@YusukeHirao

Copy link
Copy Markdown
Member

Summary

Bumps @d-zero/beholder to 4.0.0, which now extracts main-content region metrics
(word counts, headings, images, tables, buttons, iframes, videos, audios, canvases,
scroll heights) during crawling. This promotes that data into the core crawl-time
schema and removes the old post-hoc @nitpicker/analyze-main-contents plugin.

  • Removes @nitpicker/analyze-main-contents entirely — no compatibility shim,
    no deprecation warning. Its former --main-content-selector option moves from an
    analyze/pipeline flag to a top-level crawl-time flag (nitpicker crawl --main-content-selector), matching where beholder now applies it.
  • Extends the core DB schema: 17 new flat aggregate columns on page_meta
    (denormalised, same pattern as tag_count/jsonld_count) plus 8 new child tables
    (page_main_content_headings/images/tables/buttons/iframes/videos/audios/canvases).
    Existing archives get a lightweight additive migration — no
    REQUIRED_FORMAT_VERSION bump.
  • @nitpicker/query: new getPageMainContents(accessor, url) returning the full
    drill-down (identity + aggregates + all 8 child arrays); PageListItem/PageDetail
    gain the 17 scalar fields; viewer_pages read model gains 12 sortable numeric
    columns.
  • Viewer: 17 new columns on the Pages table (12 sortable), a new "main content"
    detail section with full child-entity drill-down, backed by a new
    /api/pages/main-contents route.
  • MCP server: new get_page_main_contents tool.
  • Google Sheets report: 13 new columns after tags_providers.

Review process

  • Independent code-review pass: no correctness bugs found.
  • QA review surfaced 8 test-coverage gaps (Page class getters/methods untested,
    6 of 8 new insert paths never exercised with real data, viewer read model's new
    columns unverified, reset/repromote only checked 2 of 8 child tables, report
    cell values unverified, MCP tool only tested the null path, no E2E exercising
    beholder's actual DOM detection, viewer frontend untested) — all fixed, including
    a new crawler-level E2E (main-contents.e2e.ts) that crawls a real fixture page
    through a real headless browser and verifies extraction against actual DOM
    heuristics rather than a hand-built fixture object.
  • PdM review found one doc/implementation mismatch in the MCP tool's description
    (null-vs-empty-object semantics) — fixed.
  • Manually verified against a real pre-4.0 production archive
    (www.d-zero.co.jp-*-0.13.nitpicker): migration runs cleanly, query/viewer
    read paths return the new fields as null with no runtime errors, and the
    original archive file is left untouched (migration only touches the ephemeral
    extraction).

Test plan

  • yarn build — all 13 packages
  • yarn test — 479 files / 3372 tests passing
  • yarn vitest run --config vitest.e2e.config.ts — 29 files / 136 tests passing
  • yarn workspace @nitpicker/viewer test:e2e — 48/48 Playwright tests passing
  • yarn lint — clean
  • Manual read-test against a real pre-4.0 production archive (see above)

🤖 Generated with Claude Code

BREAKING CHANGE: @nitpicker/analyze-main-contents is deleted with no
compatibility shim. Beholder 4.0.0 now extracts the same main-content
metrics during crawling, promoted into the core page schema instead of
a post-hoc analyze plugin.
BREAKING CHANGE: NitpickerConfig no longer accepts a
'@nitpicker/analyze-main-contents' override — the plugin is gone
(see the analyze-main-contents package removal).
Bump @d-zero/beholder to 4.0.0, which extracts main-content region
metrics (word counts, headings, images, tables, buttons, iframes,
videos, audios, canvases, scroll heights) during crawling instead of
requiring a post-hoc analyze pass.

- Add 17 flat aggregate columns to page_meta (denormalised, same
  pattern as tag_count/jsonld_count) plus 8 new child tables
  (page_main_content_headings/images/tables/buttons/iframes/videos/
  audios/canvases), each individually self-healing via hasTable guards
- Add a lightweight ALTER TABLE migration for page_meta's new columns
  and for info.mainContentSelector, so existing archives pick up the
  schema without a REQUIRED_FORMAT_VERSION bump
- Thread a new mainContentSelector crawl option from Config through
  CrawlerOptions into Scraper.scrapeStart, letting callers override
  beholder's automatic main-content-region detection
- Clear the new child tables (and denormalised counts) on
  --retry-failed / --append re-promotion, mirroring the existing
  page_tags/page_jsonld reset behaviour
- Add typed Page getters/methods for the new columns and child tables

BREAKING CHANGE: page_meta gains 17 new columns and 8 new adjunct
tables are created on next archive open; existing archives are
migrated additively (no REQUIRED_FORMAT_VERSION change).
… list views

- Add getPageMainContents(accessor, url), returning the detected
  main-content region's identity, aggregate counts, and all 8
  child-entity arrays (headings/images/tables/buttons/iframes/videos/
  audios/canvases) in one call
- Extend PageListItem/PageDetail with the 17 new scalar fields and
  ListPagesOptions.sortBy with the sortable ones
- Add 12 numeric main-content columns to the viewer_pages read model
  (NOT NULL DEFAULT 0, matching tag_count/jsonld_count's keyset-safe
  pattern) and wire them into the cursor sort-spec
Mirrors get_page_tags' shape (archiveId + url, jsonResult wrapper).
Also updates get_page_detail's description to mention the new
aggregate-only main-content fields it now returns.
Appends 13 columns after tags_providers (selector + 10 counts + 2
scroll heights). The old analyze-main-contents plugin's dynamic
pageData columns are gone along with the plugin itself.
- Add 17 new columns to the Pages table; register sort UI for the 12
  numeric ones and extend to-page-sort-by's allow-list (it gates both
  the viewer_pages fast path and the legacy listPages path)
- Add a "main content" section to the page detail view: aggregate
  summary plus full drill-down for all 8 child-entity arrays, backed
  by a new /api/pages/main-contents route and usePageMainContents hook
- Add en/ja i18n strings for the new section
--main-content-selector moves from an analyze/pipeline plugin flag to
a top-level crawl-time flag (alongside crawl/pipeline's other crawl
options), matching where beholder now applies it. Also drops the
@nitpicker/analyze-main-contents dependency and its analyze/pipeline
wiring.

BREAKING CHANGE: `nitpicker analyze --main-content-selector` and
`nitpicker pipeline --main-content-selector` (as an analyze option) no
longer exist. Use `nitpicker crawl --main-content-selector` (or
`pipeline`'s own crawl-time flag of the same name) instead.
…s fixture

- Swap analyze-pipeline.e2e.ts's use of analyze-main-contents (deleted)
  for analyze-search
- Add /main-content/ — a page with one of each element type beholder's
  main-content extraction recognizes — and a real headless-browser E2E
  crawling it, verifying the extraction and all 8 child tables against
  actual DOM detection rather than a hand-built fixture object
…ts references

Add the new page_meta columns and 8 page_main_content_* child tables
to ARCHITECTURE.md's DB schema section, and remove the deleted
analyze-main-contents package from README.md's package table, the
CONTRIBUTING.md concurrency example, and the git skill's package tier
tables.
Rebasing onto origin/dev's v0.13.0 release caused yarn.lock conflicts
(package version bumps colliding with local workspace edits) that were
resolved by taking the rebase's current side wholesale, which
inadvertently dropped the @d-zero/beholder 4.0.0 upgrade (and its
@d-zero/puppeteer-page-scan/puppeteer-scroll transitive bumps) from
the lockfile. Restore those entries and let yarn install prune the
now-deleted analyze-main-contents package's stale entries.
verify-shards caught the gap: the new E2E test file wasn't listed in
any shard, so CI would never actually run it.
This pre-existing test seeds 240 setPage() calls plus a full read-model
build in beforeAll — legitimately heavy, and apparently marginal
against vitest's 10s default hook timeout on CI hardware (passed
consistently locally). Pass an explicit 30s timeout, per vitest's own
suggested fix, rather than trimming the fixture's scale.
@YusukeHirao
YusukeHirao merged commit 5ce5eef into dev Jul 22, 2026
10 checks passed
@YusukeHirao
YusukeHirao deleted the feature/main-contents-promotion branch July 22, 2026 00:42
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.

1 participant