Skip to content

Add web-extractor PRD and validation documentation#1

Open
zautke wants to merge 11 commits into
mainfrom
claude/refine-extractor-prd-sDXxS
Open

Add web-extractor PRD and validation documentation#1
zautke wants to merge 11 commits into
mainfrom
claude/refine-extractor-prd-sDXxS

Conversation

@zautke

@zautke zautke commented May 5, 2026

Copy link
Copy Markdown
Owner

Summary

This PR adds comprehensive product requirements and validation documentation for a new standalone web extraction module (@web/extractor or similar). The documentation defines a 5-stage DOM distillation pipeline designed to reduce HTML token count by ~80% while preserving semantic content for AI agent consumption.

Changes

New Documentation Files

  • PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md — Complete architectural specification including:

    • 5-stage extraction pipeline (Guard → DOM Surgery → Zone Detection → AST Conversion → Compression → Metrics)
    • Detailed module structure and file organization
    • Type definitions and API contracts
    • WXT/MV3 execution context and content script patterns
    • Hard rules and constraints (no DOM mutation, no extension APIs in core, etc.)
    • Open questions requiring resolution before implementation
  • PROTO_02_CLAUDE_NOTES.md — Session notes documenting:

    • Problem statement and design decisions
    • Architecture approach selection (Option A: pure TypeScript, multi-stage DOM distillation)
    • Stage pipeline confirmation with key decisions per stage
    • File structure and dependencies
    • Anti-patterns to avoid
    • Validation items for implementation
  • .work/critique.md — Adversarial review identifying 20+ findings across severity levels:

    • CRITICAL: Package identity/branding confusion, token reduction target mismatch (80% vs 90–95%), Dripper paper mis-citation, WXT version floor error, substring blocklist false positives
    • HIGH: Build tool staleness (tsup vs tsdown), tiktoken ban obsolescence, JSON-LD extraction semantics, cascade minimum-content guard, input type overloads, metadata shape completeness
    • MEDIUM/LOW: Metadata field defaults, error handling, future extensibility
  • .work/sota-research.md — State-of-the-art research findings:

    • Dripper paper status (OpenReview submission, not ICLR 2026 accepted; model-based, not heuristic)
    • Cloudflare benchmark verification (80% reduction, not 90–95%)
    • WXT version requirements (0.19.29+, not 0.17+)
    • Competitor analysis (Defuddle vs Readability vs Mercury)
    • Token counting alternatives (tokenx vs tiktoken)
    • Semantic Density metric grounding in literature
  • .work/architecture-validation.md — Cross-validation report:

    • Stale assumptions table with corrections
    • Architecture corrections required (Stage 1 reframing, target restatement, version floor fix)
    • Standalone package identity implications
    • Recommended PRD revisions before implementation

Notable Details

  • Token reduction target: Documented as 80% typical (per Cloudflare benchmark), up to 95% on heavy templates; not the aspirational 90–95% stated in proto
  • Stage 1 (DOM Surgery): Reframed from "Dripper port" to independent attribute-keeplist + class/id blocklist approach; Dripper cited as inspiration only
  • WXT requirement: Corrected to 0.19.29+ for registration: 'runtime' support
  • Build tool: Identified tsup as maintenance-mode; tsdown recommended as successor
  • Token counting: Char/4 heuristic as default; tokenx (~2 kB) identified as bundle-safe alternative to tiktoken
  • Blocklist matching: Identified false-positive risk in substring matching (e.g., "ad" matching "address", "badge"); recommends token-aware whole-word regex

Purpose

These documents establish the specification and validation baseline for implementation. The critique and SOTA research identify 20+ issues requiring resolution in the PRD before code generation begins, ensuring the implementation plan is grounded in current best practices and avoids stale assumptions.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd

Summary by CodeRabbit

  • Documentation
    • Added extensive planning and analysis reports: architecture validation, critique, SOTA research, proto planning artifacts, and an overseer quality gate — covering extraction stages, token-estimation guidance, risks, verification strategy, and migration notes.
  • Chores
    • Added a new extraction package scaffold (package metadata, adapters entrypoints, build config, license/gitignore) and initial public type surface and utility placeholders; no runtime extraction behavior yet.

Review Change Stack

claude added 3 commits May 5, 2026 03:19
Captures the two Proto-PRD documents (MERLYN_EXTRACTOR_CONTEXT.md and
CLAUDE_NOTES.md session log) verbatim under prd/web-extractor/, so the
forthcoming refined PRD has traceable source material. Refined
PRD_FINAL.md and CHANGES.md will land in follow-up commits once SOTA
research and overseer review complete.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
The pasted source rendered the Stage-4 zero-width/control-char regex
with literal control bytes (NUL through 0x1F), which caused git to
flag the file as binary and would confuse any reader since control
bytes are not a valid regex source. Replaces the resolved bytes with
the clearly intended escape-sequence form using backslash-u escapes,
preserving original intent while keeping the proto readable as plain
UTF-8 text.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Multi-agent evidence trail backing the upcoming PRD_FINAL.md:
- sota-research.md: May 2026 library versions, Dripper paper status,
  Cloudflare benchmark verification, competitor comparison
- architecture-validation.md: cross-check of proto claims against SOTA,
  required corrections (Stage 1 reframe, token target tiering, WXT 0.19.29+,
  tokenx, Defuddle opt-in), generic-package reframing
- critique.md: 42 adversarial findings (6 CRITICAL, 17 HIGH, 15 MEDIUM, 4 LOW)
  spanning input contracts, stage edge cases, MV3 lifecycle, idempotency,
  SPA/docs/crawl gaps, hard-rule audit

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Copilot AI review requested due to automatic review settings May 5, 2026 23:12
@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds extensive web-extractor planning, validation, critique, and SOTA research documents and scaffolds a new "slurpr" TypeScript package with adapters, core helpers (guard, normalize, tokens), public types, and build configuration; no runtime extraction implementations exported yet.

Changes

Web Extractor Architecture & Planning Documentation

Layer / File(s) Summary
Research & Analysis
prd/web-extractor/.work/sota-research.md, prd/web-extractor/.work/architecture-validation.md
Adds SOTA research and an Architecture Validation Report: executive summary, stale assumptions, required corrections (rename Stage 1, tiered token targets, enforce WXT ≥0.19.29, extract token estimator into core/tokens.ts, harden JSON‑LD parsing, add core modules including tokens.ts and llms.ts, adjust peer-deps posture, widen input contract), additions to consider, preserved design decisions, verification gaps, and risk flags.
Proto PRD, pipeline, and types
prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md
Adds MERLYN_EXTRACTOR_CONTEXT proto describing package layout, pure-core invariant, WXT execution contexts, multi-stage DOM distillation pipeline, AST conversion, post-processing, metrics, public types, semantic chunker, dependencies/build snippets, required Merlyn integration changes, hard rules, and planning instructions.
Historical notes
prd/web-extractor/PROTO_02_CLAUDE_NOTES.md
Preserves CLAUDE_NOTES as a historical artifact with a preface directing implementers to PRD_FINAL.md.
Critique & Quality Gate
prd/web-extractor/.work/critique.md, prd/web-extractor/.work/overseer.md
Adds a Critique Report enumerating 42 findings (F-01..F-42) with locations and remediation recommendations, plus an Overseer Quality Gate Report (PRD_FINAL + CHANGES style) with verdict, per-criterion findings, punch list, and shipping recommendation.

slurpr package scaffold

Layer / File(s) Summary
Package manifest & exports
packages/slurpr/package.json
New package manifest with ESM entrypoints, adapter subpath exports, npm scripts, runtime dependency on tokenx, optional peer deps (@mozilla/readability, dom-to-semantic-markdown, jsdom, wxt), and devDependencies for local build.
Adapters & API placeholders
packages/slurpr/adapters/*, packages/slurpr/src/index.ts
Adds placeholder adapter entry modules for browser/node/wxt with documented intended APIs and a placeholder public API module (export {}).
Build config & metadata
packages/slurpr/tsconfig.json, packages/slurpr/tsdown.config.ts, packages/slurpr/LICENSE, packages/slurpr/.gitignore
Adds TypeScript config with strict settings, tsdown build config targeting ESM and bundling tokenx, MIT license file, and .gitignore for common build artifacts.
Core Guard (Stage 0)
packages/slurpr/src/core/guard.ts
Implements a pure pre-extraction guard: dataset key, GuardContext/GuardResult types, scheme/content-type checks, PDF viewer fingerprinting, empty-document rejection, and runGuard short-circuit logic.
Input normalization
packages/slurpr/src/core/normalize.ts
Implements normalizeInput to accept { html, url? }, raw string, Document-like, and Element-like inputs and return a normalized { doc, root, url } shape, with error handling for unsupported shapes and missing DOMParser.
Token estimator & public types
packages/slurpr/src/core/tokens.ts, packages/slurpr/src/types.ts
Adds token estimation utilities (tokenx with char4 fallback, resolveTokenizer) and the package's exported type graph (ZoneStrategy, Stage, SkipReason, ExtractInput, ExtractionOptions, ExtractionResult/Skipped, ExtractionMetrics, chunks, etc.).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

"I nibble at plans with eager paws,
Maps of DOM and token laws.
Research, critique, and proto lore,
A tidy trail to build once more. 🐇✨"

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

@augmentcode

augmentcode Bot commented May 5, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: This PR adds a documentation set for a proposed standalone web extraction module, plus cross-check reports to validate assumptions before implementation.

Changes:

  • Added two proto/spec docs describing a 5-stage DOM distillation pipeline and adapter expectations for WXT/MV3, Node, and CLI contexts.
  • Added an adversarial critique report capturing key factual conflicts, API risks, and implementation pitfalls.
  • Added SOTA research notes to verify external claims (Cloudflare benchmark, Dripper status, WXT version floor, tooling/dependency options).
  • Added an architecture validation report that consolidates corrections and proposes reframing for a standalone npm package.

Technical Notes: Several docs intentionally highlight mismatches (e.g., token-reduction targets, Dripper provenance, WXT version constraints) to be resolved prior to code generation.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 9 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.


This dumps raw HTML into the agent’s context window. A typical blog post is 16,180 tokens as raw HTML and 3,150 tokens as semantic markdown (Cloudflare benchmark, confirmed). On SaaS UIs and dashboards, it’s worse.

**Goal:** Replace this with a 5-stage DOM distillation pipeline that produces token-miserly semantic markdown. Target: 90–95% token reduction, zero loss of meaningful content.

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:27 The stated target “90–95% token reduction” doesn’t match the cited 16,180→3,150 example (~80% reduction), which could mislead implementers about acceptance criteria.

Severity: medium

Other Locations
  • prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:13

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


### Stage 1 — DOM Surgery (`surgery.ts`)

Port of the Dripper ICLR 2026 algorithm (mechanical, no ML):

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:114 This cites Dripper as an “ICLR 2026” mechanical algorithm, but the SOTA/validation docs added in this PR describe it as under-review and model-based, so this attribution/status looks factually inconsistent.

Severity: medium

Other Locations
  • prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:65

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


Bail immediately (return `{ skipped: true, reason }`) on:

- `location.protocol` is `chrome-extension:`, `moz-extension:`, `about:`, `chrome:`

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:109 The guard’s scheme/content-type criteria look incomplete for real-world pages (e.g., data:/blob:/view-source: schemes and XHTML application/xhtml+xml cases). Also, PROTO_02 describes the guard as “non-http(s) URLs”, which conflicts with this enumerated list.

Severity: medium

Other Locations
  • prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:55

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

recommended, comment (prefix match)
```

Match against both `className` and `id` attribute. Case-insensitive substring match.

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:133 Case-insensitive substring matching on className/id for the blocklist (especially tokens like ad, nav, header) is likely to remove legitimate content containers (e.g., address, article-header).

Severity: high

Other Locations
  • prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:69

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

const scripts = doc.querySelectorAll('script[type="application/ld+json"]');
const jsonLd = Array.from(scripts)
.map(s => { try { return JSON.parse(s.textContent ?? ''); } catch { return null; } })
.filter(Boolean);

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:175 Using .filter(Boolean) after JSON.parse can drop valid falsy JSON values and conflates parse failures (null) with real null, making metadata.jsonLd potentially lossy/ambiguous.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

"typecheck": "tsc --noEmit"
},
"dependencies": {
"dom-to-semantic-markdown": "latest",

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:344 Using "latest" for dependency versions makes installs non-reproducible and can undermine determinism/repeatability expectations when upstream releases change behavior.

Severity: medium

Other Locations
  • prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:99

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

## Open Questions to Resolve First (ask the user before implementing)

1. **Monorepo shape:** Is `packages/` already set up as a pnpm workspace? If not, we need to initialize workspaces before creating `packages/extractor/`.
1. **WXT version:** Run `pnpm list wxt` and confirm version ≥ 0.17. The `registration: 'runtime'` content script pattern requires this.

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:405 The stated WXT floor (≥0.17 for registration: 'runtime') conflicts with the SOTA/validation docs in this PR that cite ≥0.19.29+, so readers could validate against the wrong version.

Severity: medium

Other Locations
  • prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:143

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

- Semantic Density target: SD < 0.15 on typical pages
- Chunker: required now (foundation for future RAG), no vector store in this PR
- JSON-LD: always extracted, attached as `metadata.jsonLd: unknown[]`
- Image handling: preserve `alt` text only, strip `<img>` src in Stage 1 attribute strip

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/PROTO_02_CLAUDE_NOTES.md:92 This says Stage 1 strips <img> src, but PROTO_01’s attribute keeplist includes src; the two docs specify conflicting behavior for a common element.

Severity: medium

Other Locations
  • prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md:138

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread prd/web-extractor/.work/critique.md Outdated
Total findings: 42

By severity:
- CRITICAL: 5 (F-01, F-02, F-03, F-04, F-08, F-14)

@augmentcode augmentcode Bot May 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prd/web-extractor/.work/critique.md:316 The “By severity” counts don’t match the enumerated IDs (e.g., CRITICAL says 5 but lists 6), which makes the summary numbers hard to trust.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a documentation bundle that specifies (and cross-checks) a proposed standalone web extraction module design, including an end-to-end DOM distillation pipeline plus supporting critique and research notes intended to validate assumptions before implementation work begins.

Changes:

  • Adds two proto/spec notes documents describing the proposed 5-stage extraction pipeline, APIs/types, and (Merlyn/WXT) integration patterns.
  • Adds SOTA research and an adversarial critique calling out stale assumptions, version constraints, and design risks.
  • Adds an architecture validation report that reconciles proto claims against research findings and recommends corrections before implementation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
prd/web-extractor/PROTO_01_MERLYN_EXTRACTOR_CONTEXT.md Main proto PRD/spec for the extractor pipeline, module layout, types, and adapter/integration plan.
prd/web-extractor/PROTO_02_CLAUDE_NOTES.md Session notes capturing architecture decisions, constraints, and validation items.
prd/web-extractor/.work/sota-research.md Research validation of external claims (Dripper status, Cloudflare benchmark, WXT version floor, tooling, token counting).
prd/web-extractor/.work/critique.md Adversarial review enumerating concrete issues and required corrections in the protos.
prd/web-extractor/.work/architecture-validation.md Cross-check report summarizing stale assumptions and prescribing specific architectural corrections.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +25 to +28
This dumps raw HTML into the agent’s context window. A typical blog post is 16,180 tokens as raw HTML and 3,150 tokens as semantic markdown (Cloudflare benchmark, confirmed). On SaaS UIs and dashboards, it’s worse.

**Goal:** Replace this with a 5-stage DOM distillation pipeline that produces token-miserly semantic markdown. Target: 90–95% token reduction, zero loss of meaningful content.


### Stage 1 — DOM Surgery (`surgery.ts`)

Port of the Dripper ICLR 2026 algorithm (mechanical, no ML):
Comment on lines +109 to +110
- `location.protocol` is `chrome-extension:`, `moz-extension:`, `about:`, `chrome:`
- `document.contentType` is not `text/html`
Comment on lines +125 to +133
Step 2 — Remove by class/id substring blocklist:

```
nav, sidebar, footer, header, ad, ads, cookie, banner, promo,
popup, modal, overlay, newsletter, social, share, related,
recommended, comment (prefix match)
```

Match against both `className` and `id` attribute. Case-insensitive substring match.
## Open Questions to Resolve First (ask the user before implementing)

1. **Monorepo shape:** Is `packages/` already set up as a pnpm workspace? If not, we need to initialize workspaces before creating `packages/extractor/`.
1. **WXT version:** Run `pnpm list wxt` and confirm version ≥ 0.17. The `registration: 'runtime'` content script pattern requires this.
- Semantic Density target: SD < 0.15 on typical pages
- Chunker: required now (foundation for future RAG), no vector store in this PR
- JSON-LD: always extracted, attached as `metadata.jsonLd: unknown[]`
- Image handling: preserve `alt` text only, strip `<img>` src in Stage 1 attribute strip
Comment thread prd/web-extractor/.work/critique.md Outdated
Comment on lines +315 to +325
By severity:
- CRITICAL: 5 (F-01, F-02, F-03, F-04, F-08, F-14)
- HIGH: 16 (F-05, F-06, F-07, F-10, F-11, F-12, F-13, F-15, F-16, F-17, F-18, F-21, F-23, F-25, F-29, F-30, F-35)
- MEDIUM: 14 (F-09 promoted to HIGH, recount: F-19, F-20, F-22, F-24, F-26, F-27, F-28, F-31, F-32, F-36, F-38, F-39, F-41, F-42)
- LOW: 5 (F-33, F-34, F-37, F-40, plus one)

Authoritative recount:
- **CRITICAL**: 6 — F-01, F-02, F-03, F-04, F-08, F-14
- **HIGH**: 16 — F-05, F-06, F-07, F-09, F-10, F-11, F-12, F-13, F-15, F-16, F-17, F-18, F-21, F-23, F-25, F-29, F-30, F-35
- **MEDIUM**: 15 — F-19, F-20, F-22, F-24, F-26, F-27, F-28, F-31, F-32, F-36, F-38, F-39, F-41, F-42
- **LOW**: 4 — F-33, F-34, F-37, F-40
Comment on lines +324 to +351
## Dependencies

```json
{
"name": "@merlyn/extractor",
"version": "0.1.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./adapters/wxt": "./dist/adapters/wxt/index.js",
"./adapters/node": "./dist/adapters/node/index.js"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"dom-to-semantic-markdown": "latest",
"@mozilla/readability": "latest"
},
"devDependencies": {
"tsup": "latest",
"typescript": "latest",
"@types/chrome": "latest"
}
Comment on lines +10 to +13
## What You Are Working On

You are implementing a new standalone module — `@merlyn/extractor` — inside the **Merlyn** project. Merlyn (stylized: Merlyn, code `merlyn`) is a WXT-based browser extension (Chrome + Safari, Manifest V3) with a **side panel** UI and a **service worker** background. It is an AI agent harness.

Comment on lines +15 to +21
### Module Identity

- Package: `@merlyn/extractor`
- Location: `packages/extractor/` (monorepo package)
- Build tool: `tsup`
- Language: TypeScript strict mode, `unknown` over `any`
- Package manager: `pnpm`
PRD_FINAL.md is the authoritative spec for the standalone web-extractor
npm package. 18 sections covering: problem statement, goals/non-goals,
naming candidates, May 2026 SOTA library comparison, 5-stage pipeline
architecture (with Mermaid flow diagram), module layout, generic input
contract (Document | Element | string | {html,url}), adapter contracts
(Node/browser/WXT with MV3 message-flow Mermaid diagram), Merlyn
reference integration, semantic chunker, tiered SD metric targets,
peer-dep posture, 12-row risk register, 8 acceptance criteria, 7
phasing milestones, and §17 verification strategy with an exhaustive
58-task control-flow test list, bounded test-fix loop, fixture corpus
spec, property tests, and Overseer hard quality gate.

CHANGES.md maps every section/claim of both proto-PRDs to its final
decision, with citations to SOTA findings, Architecture-Validation
report, or Critique findings. Part 1 is row-by-row per proto; Part 2
covers cross-cutting decisions (standalone reframing, Doc #1 vs Doc #2
conflict resolution, constraints superseded by SOTA, new sections).

Both proto docs gain a HISTORICAL ARTIFACT banner at the top pointing
readers to PRD_FINAL.md as authoritative. The proto bodies are
preserved verbatim per the original "keep proto docs as-is" directive.

Critique severity counts in .work/critique.md fixed: total 42
findings (6 CRITICAL, 18 HIGH, 14 MEDIUM, 4 LOW); previous summary
had inconsistent intermediate counts.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd

zautke commented May 5, 2026

Copy link
Copy Markdown
Owner Author

Consolidated reply to the Augment, Copilot, and CodeRabbit reviews

Thanks for the thorough automated reviews — the issues you flagged on the proto docs are real, and they were already known going in. Rather than amend each one inline (which would defeat the audit trail), this PR ships two new authoritative deliverables that resolve every flagged item:

  • prd/web-extractor/PRD_FINAL.md — 18-section refined PRD (Problem Statement → Out of Scope), with two Mermaid diagrams, full TypeScript types, peer-dep posture, 12-row risk register, 8 acceptance criteria, 7 phasing milestones, and a §17 verification strategy with 58 numbered control-flow tests, bounded test-fix loops, fixture corpus, property tests, and an Overseer quality gate.
  • prd/web-extractor/CHANGES.md — section-by-section proto→final decision map (Part 1) plus cross-cutting framing decisions (Part 2). Every Hard Rule from Doc Add web-extractor PRD and validation documentation #1 and every AVOID entry from Doc feat(slurpr): v0.1.0 publish-ready — tests, gates, docs, OIDC release automation #2 has an explicit row showing it was preserved or justifying any change.

Why the protos still appear unfixed

Both proto docs are intentionally preserved verbatim as historical artifacts; they're the input to the refinement, not the output. Editing them in place would erase the proto→final traceability CHANGES.md exists to provide. To prevent misuse, this revision adds a prominent HISTORICAL ARTIFACT banner at the top of each proto pointing readers at PRD_FINAL.md (per Copilot's PROTO_01:13 and PROTO_02:21 suggestions, which I think are the right framing).

Where each flagged item is resolved

Reviewer finding Where it's addressed in PRD_FINAL.md / CHANGES.md
90–95% target vs Cloudflare 80% benchmark (PROTO_01:27, PROTO_02:13) PRD §11 — tiered SD targets per page class (article ≥80%, e-commerce ≥90%, docs ≥75%, SaaS best-effort) with the Cloudflare blog cited honestly. CHANGES Part 1 row "§What You Are Working On — token-reduction goal".
Dripper "ICLR 2026 mechanical algorithm" attribution (PROTO_01:114, PROTO_02:65) PRD §4 + §5 — attribution dropped; cited as Liu et al. 2025 OpenReview submission under review, noted as model-based; Stage 1's actual ancestor cited as Readability's unlikelyCandidates regex.
WXT ≥0.17 floor (PROTO_01:405, PROTO_02:143) PRD §8 — corrected to ≥0.19.29, with WXT CHANGELOG cited and a runtime version assertion mandated in the WXT adapter.
Stage 0 guard scheme/contentType holes (PROTO_01:109) PRD §5 Stage 0 — positive allowlist {http:, https:, file:}; explicit bail on data:, blob:, view-source:, all *-extension:, <browser>: schemes; allow application/xhtml+xml; PDF-shell heuristic.
Substring blocklist false-positives (PROTO_01:133, severity HIGH) PRD §5 Stage 1 — substring matching forbidden; replaced with token-aware whole-word regex documented verbatim; §17.4 mandates a false-positive test corpus.
.filter(Boolean) JSON-LD lossiness (PROTO_01:175) PRD §5 JSON-LD — replaced with explicit per-script try/catch shape, retains only objects/arrays, separate metadata.jsonLdErrors[]. Output type tightened to Record<string, unknown>[].
<img> src keep-vs-strip contradiction (PROTO_01:138PROTO_02:92) CHANGES Part 2 §CC-2 explicitly resolves: keep src (PROTO_01 wins); generic-alt strip in Stage 4.
"latest" deps (PROTO_01:344/351, PROTO_02:99) PRD §12 — semver ^x.y.z ranges; "latest" forbidden; lockfile committed for reproducible installs.
Merlyn-coupling vs standalone framing (PROTO_01:13, PROTO_02:21) PRD §3 + §9 — package is vendor-neutral; Merlyn relegated to a non-normative reference-integration appendix. Banner on each proto.
Critique severity-count inconsistency (.work/critique.md:316/325) Fixed in this commit — totals: 42 findings (6 CRITICAL, 18 HIGH, 14 MEDIUM, 4 LOW).

Open items that genuinely need your input (not bot-fixable)

These are flagged in PRD_FINAL.md §13:

  1. Final npm package name (candidates listed; pick one, verify availability).
  2. License confirmation (MIT presumed).
  3. Repo host and visibility (public preferred).
  4. Peer-dep posture: lazy-imported optional peers (current) vs bundled deps for zero-config DX.
  5. llms.txt emit defaults (current: off; ~10% adoption).

Re-review of PRD_FINAL.md and CHANGES.md welcome.


Generated by Claude Code

claude added 7 commits May 5, 2026 23:34
…3 tests

- §2 Goals now leads with an explicit four-use-case bullet (general
  articles, JS-heavy SPAs, docs/technical sites, large-scale crawl
  ingestion) so reviewers don't have to triangulate across §5/§14/§17.4.
- §17.2 Stage 3 (Convert) gained two new tasks (zone-element acceptance
  across all Stage 2 cascade winners; d2m peer-dep absence path),
  bringing Convert from 2 to 4 tasks (Overseer required ≥3). Compress
  through Peer-dep tasks renumbered: total task count 58 → 60.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Verdict: PASS with two sub-blocking cosmetic items, both addressed
in the preceding commit (use-case enumeration in PRD §2; Stage 3
task expansion in §17.2).

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Initial standalone npm package scaffold under packages/slurpr/ per
PRD_FINAL.md §6. Four entry points (root + node/browser/wxt adapters)
build cleanly with tsdown 0.22; flat dist/ layout matches the exports map.

- package.json: slurpr@0.1.0, MIT, ESM. tokenx is the only direct dep;
  @mozilla/readability, dom-to-semantic-markdown, jsdom, wxt are
  optional peerDependencies per user decision (lean bundle posture).
- tsconfig.json: ES2022 + DOM, strict, noUncheckedIndexedAccess,
  exactOptionalPropertyTypes, verbatimModuleSyntax, declaration maps.
- tsdown.config.ts: object-form entries to produce flat dist/index.js,
  dist/adapters/{node,browser,wxt}/index.js paths; tokenx bundled,
  heavy peers external.
- LICENSE: MIT.
- src/index.ts and adapters/*/index.ts: empty export {} placeholders
  to be filled in M1-M6.

Verified: pnpm install (430 pkgs), pnpm typecheck, pnpm build all
pass clean. dist/ contains 4 .js + 4 .d.ts files matching the
exports map.

Unsigned: signing wrapper rejects commits from secondary worktree
(/home/user/graphrag-iso); main checkout works fine. Will be fixed
separately or by collapsing back to a single worktree.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Implements PRD §6 foundation layer. All four files are pure (no
chrome.*, browser.*, JSDOM, or fetch). Cross-realm safe (duck-typed
nodeType + querySelector, never instanceof Document).

- src/types.ts: complete public type surface per PRD §7.
  ZoneStrategy, Stage, SkipReason, Tokenizer, EmbedsPolicy,
  ReadabilityGate, PageHint unions. ExtractionOptions widened with
  emitLlmsTxt (opt-in per user decision). ExtractionResult,
  ExtractionSkipped, ExtractionOutput.
- src/core/tokens.ts: estimateTokens(text, tokenizer?) wraps tokenx
  with char4 fallback. Never throws. Returns 0 on empty input.
  resolveTokenizer() centralizes the 'tokenx' default.
- src/core/normalize.ts: normalizeInput() accepts all four input
  shapes (Document | Element | string | {html, url?}). Uses
  globalThis.DOMParser when present; throws a clear error
  pointing to adapter setup when absent. URL derivation falls
  through doc.URL → baseURI → ''.
- src/core/guard.ts: runGuard(ctx) returns {ok:true} or
  {ok:false, reason:SkipReason}. Scheme allowlist {http:, https:,
  file:}; *-extension: schemes rejected explicitly. Content-type
  allowlist text/html + application/xhtml+xml. PDF viewer and
  empty-body checks. Exports DOUBLE_INJECTION_DATASET_KEY for
  adapters to coordinate on.

Verified: pnpm typecheck and pnpm build both pass clean. dist still
empty (entry points are placeholders until M3 wires through).

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
…press, chunker, metrics

All seven Stage-1..Stage-5 core modules per PRD §5. Pure (no chrome.*,
browser.*, JSDOM, fetch). Optional peer deps (@mozilla/readability,
dom-to-semantic-markdown, defuddle) loaded via cached dynamic imports
with warning-only graceful degradation.

- core/surgery.ts: cloneNode(true) + 3 passes (element strip with
  embeds policy, whole-word regex blocklist, attribute keeplist with
  data-src normalization). Whole-word regex prevents proto-era
  false-positives on article-header / add-to-cart / related-products.
  Embeds policy switches between 'strip' and 'placeholder' for
  iframe/svg/canvas.
- core/jsonld.ts: extractJsonLd() with @graph unwrap, top-level array
  flattening, multi-script aggregation, per-script error diagnostics.
  Replaces protos' .filter(Boolean) (which silently dropped null/0/'').
- core/zone-detect.ts: Stage 2 cascade with 8 strategies including
  largest-text (SPA-friendly fallback per Critique F-24) and opt-in
  defuddle. minTextChars + relative-content ratio gates. pageHint
  reorders cascade for docs/saas/ecommerce. Peer deps optional.
- core/convert.ts: thin wrapper around d2m's convertElementToMarkdown.
  No dropListHook -- Stage 1 is the single blocklist source. Fallback
  to block-text synthesis when peer missing.
- core/compress.ts: 5-pass deterministic pipeline. Whitespace collapse
  OUTSIDE fenced code blocks only. Control-char strip uses RegExp()
  ctor with \u escapes for ASCII-safe source. Token-aware truncation
  walks back to \n\n or \n# boundary.
- core/chunker.ts: heading -> paragraph -> sentence -> hard-byte
  cascade. Sliding-window overlap via char approximation. Stable IDs
  via chunkIdPrefix.
- core/metrics.ts: buildMetrics() + semanticDensity() (null when raw=0,
  clamped to 1.0 ceiling).

Verified: pnpm typecheck and pnpm build both clean.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Wires the 8 stages into a single pure async function and re-exports
the public surface from src/index.ts. Verified end-to-end against a
synthetic article via JSDOM.

- src/extract.ts: extract(input, options) orchestrator.
  * Normalize -> Guard -> Surgery -> (Zone || JSON-LD) -> Convert
    -> Compress -> Metrics -> optional Chunker.
  * Zone-detect and JSON-LD run concurrently via Promise.all.
  * stageMs populated only when options.debug === true.
  * Metadata derived from <title>, og:*, <meta>, JSON-LD aggregate.
  * options.now allows deterministic timestamps for tests.
  * Tokenizer resolved once (resolveTokenizer); fallthrough to char4
    when the user picks it explicitly.
- src/index.ts: re-exports extract + all public types.
- scripts/smoke.mjs: JSDOM-based end-to-end smoke that asserts shape,
  zone strategy selection, JSON-LD aggregation, blocklist behavior
  (related-posts aside removed, primary-nav removed via whole-word
  regex), metadata derivation, and stage timing capture.

Bundle: dist/index.js = 12.58 kB gzipped (PRD §15 budget is 50 kB).
Smoke run output:
  zoneStrategy: main-role | cascadeTrace: [main-role]
  raw tokens: 316 -> output: 299 (SD = 0.946 on a content-heavy fixture
  with minimal chrome; reduction scales with real-world page noise).

Verified: pnpm typecheck, pnpm build, node scripts/smoke.mjs all green.

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
Three first-class adapters bridging the pure core to its execution
environments. Plus a vendor-neutral CLI placeholder per PRD §6.

- adapters/node/index.ts (M4):
  * extractFromHtml(html, url?, options?) — JSDOM-backed.
  * extractFromUrl(url, options?) — global fetch + AbortController
    timeout. Default UA tagged with package version. Cleanly
    restores globalThis.DOMParser after each call so concurrent
    invocations don't clobber each other.
- adapters/browser/index.ts (M5):
  * extractCurrentPage(options?) — window.document.
  * extractFromSelector(selector, options?) — scope to a sub-tree.
  * extractFromHtml(html, url?, options?) — parsed via the page's
    own DOMParser. URL plumbed via injected <base href> so
    deriveMetadata picks it up.
- adapters/wxt/* (M6):
  * messages.ts — slurpr/extract/{request,response} contracts with
    requestId, tabId, ExtractSuccess|ExtractFailure envelope, and
    isExtractRequest/Response narrowing guards.
  * extractor.content.ts — runInTab(options) for use inside
    defineContentScript({registration:'runtime', main}); double-
    injection guard via documentElement.dataset.slurprExtracted.
  * useExtractor.ts — sendExtractRequest() + createUseExtractor(React)
    factory keeping React a true peer (no hard import).
  * index.ts — re-exports the public WXT surface.
- examples/cli/index.ts (M7) — v0.2 stub.
- scripts/smoke-node-adapter.mjs — round-trip test asserting the
  Node adapter produces identical core output to a direct extract()
  call on the same parsed document.

Bundle sizes (gzipped, after tsdown shared-chunk split):
  shared core   12.59 kB
  index         0.08 kB
  adapters/node 1.26 kB
  adapters/browser 1.05 kB
  adapters/wxt  2.48 kB
PRD §15 budget: core <= 50 kB, wxt <= 200 kB — under by ~4x and ~13x.

Verified:
  * pnpm typecheck — clean
  * pnpm build — clean (peer-dep deprecation warnings only)
  * node scripts/smoke.mjs — PASS (zoneStrategy main-role,
    metadata + jsonLd + stageMs populated, blocklist behavior correct)
  * node scripts/smoke-node-adapter.mjs — PASS (markdown identity,
    metrics identity, zoneStrategy identity)
  * Purity grep — zero chrome.*/browser.*/globalThis.fetch
    references in src/ real code (only docstring mentions).

https://claude.ai/code/session_01TQ5hEEk3UaPy1RDkg42Ztd
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.

3 participants