WP-0002: stale vs unresolved resolution, orphaned flag/helper, bounded fuzzy quote recovery, and PdfViewerAdapter promotion with resolveSelectors integration. WP-0003: DomSelectionCapture, DOM create/resolve ladder, HtmlViewerAdapter under evidence-anchor/dom, and finished workplans. Verification: 42 anchor tests, citation-evidence typecheck + 51 tests green.
125 lines
4.2 KiB
Markdown
125 lines
4.2 KiB
Markdown
---
|
|
id: EANCH-WP-0002
|
|
type: workplan
|
|
title: "Anchor resolution hardening: stale/orphan semantics, fuzzy re-anchoring, production PDF adapter"
|
|
domain: infotech
|
|
repo: evidence-anchor
|
|
status: finished
|
|
owner: codex
|
|
topic_slug: citation_evidence_mvp
|
|
created: "2026-07-08"
|
|
updated: "2026-07-09"
|
|
spec_refs:
|
|
- INTENT.md
|
|
- SCOPE.md
|
|
- ../citation-evidence/wiki/SharedContracts.md
|
|
- ../citation-evidence/wiki/ArchitectureOverview.md
|
|
state_hub_workstream_id: "2e1ad557-11d9-4e5c-8743-973002482a0c"
|
|
---
|
|
|
|
# EANCH-WP-0002 — Anchor Resolution Hardening
|
|
|
|
## Goal
|
|
|
|
Close the resolution-quality gaps left open by the MVP extraction
|
|
(`EANCH-WP-0001`) on the **existing PDF/text path**. The extracted slice
|
|
resolves exact position/quote/PDF selectors with a fixed confidence ladder and
|
|
returns `resolved | ambiguous | unresolved`. `INTENT.md` promises more:
|
|
`stale` detection, orphaned-annotation handling, fuzzy/recovery re-anchoring,
|
|
and human-confirmation workflows for ambiguity. This workplan makes those real
|
|
without changing the shared selector *types* (those stay in `citation-engine`).
|
|
|
|
Non-goals: HTML/Markdown selectors (see `EANCH-WP-0003`); new persistence or
|
|
UI shell behavior.
|
|
|
|
## Background
|
|
|
|
Current extracted behavior (`src/selectors/resolve.ts`):
|
|
|
|
- confidence ladder 1.00 → 0.70 across TextPosition/TextQuote/PdfPageText/PdfRect
|
|
- multiple undisambiguated quote matches → `ambiguous` at 0.5
|
|
- nothing matches → `unresolved` at 0.0
|
|
- there is **no** `stale` path (the `AnnotationResolutionStatus` enum includes
|
|
`stale`, but resolve.ts never returns it) and **no** fuzzy matching
|
|
|
|
## Task Breakdown
|
|
|
|
## T01 — Stale vs. unresolved: distinguish "representation changed" from "never found"
|
|
|
|
```task
|
|
id: EANCH-WP-0002-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "940b03ba-4b2f-4d1a-b7bc-72813817549d"
|
|
```
|
|
|
|
Return `stale` (not `unresolved`) when a previously-resolvable anchor no longer
|
|
matches because the representation's canonical text changed, while the stored
|
|
quote is still meaningful. Define the signal (e.g. position selector points
|
|
in-range but slice ≠ quote AND quote no longer found) and preserve the original
|
|
quote/context on the result. Add unit tests covering stale vs. unresolved.
|
|
|
|
## T02 — Orphaned-annotation detection surface
|
|
|
|
```task
|
|
id: EANCH-WP-0002-T02
|
|
status: done
|
|
priority: medium
|
|
depends_on: [T01]
|
|
state_hub_task_id: "f75b94cd-bfb8-47f9-bd89-0d605f925290"
|
|
```
|
|
|
|
Give callers a way to identify annotations whose selectors can no longer be
|
|
placed at all (orphaned) versus stale-but-recoverable. Decide whether this is a
|
|
distinct status, a flag on `AnchorResolution`, or a helper. Keep the shared
|
|
enum unchanged unless a contract change is agreed in the umbrella wiki first.
|
|
|
|
## T03 — Fuzzy / recovery re-anchoring
|
|
|
|
```task
|
|
id: EANCH-WP-0002-T03
|
|
status: done
|
|
priority: high
|
|
depends_on: [T01]
|
|
state_hub_task_id: "8548fa44-5bb3-40cf-b8f8-e745deae7dda"
|
|
```
|
|
|
|
Add a fuzzy quote-matching fallback (bounded edit-distance / token overlap)
|
|
below the exact ladder, returning candidates at appropriately low confidence
|
|
with `ambiguous`/`stale` status. Must never silently emit a confident wrong
|
|
match (INTENT: "No Silent Misleading Match"). Add tests with lightly-edited
|
|
source text. Confirm the ladder's exact tiers are untouched above the fuzzy
|
|
floor.
|
|
|
|
## T04 — Promote the PDF spike to a production adapter
|
|
|
|
```task
|
|
id: EANCH-WP-0002-T04
|
|
status: done
|
|
priority: medium
|
|
depends_on: [T03]
|
|
state_hub_task_id: "6a7163f5-39ca-48fa-8941-8459c563198f"
|
|
```
|
|
|
|
Turn `PdfSpikeViewer` into a supported `PDFViewerAdapter` that implements the
|
|
`DocumentViewerAdapter` contract cleanly (remove spike-only debug flags from the
|
|
public surface, or gate them), keep viewer libs behind `src/pdf/`, and keep the
|
|
umbrella green through the change. Update `evidence-anchor/pdf` exports and docs.
|
|
|
|
## T05 — Verification + close-out
|
|
|
|
```task
|
|
id: EANCH-WP-0002-T05
|
|
status: done
|
|
priority: high
|
|
depends_on: [T02, T04]
|
|
state_hub_task_id: "1ea0885a-c541-438f-a4fd-b8be0773b373"
|
|
```
|
|
|
|
`pnpm test`/`typecheck`/`lint` green here; umbrella typecheck/test/build green
|
|
after any contract-visible change; `fix-consistency` clean; progress note.
|
|
|
|
**Verification (2026-07-09):**
|
|
|
|
- `evidence-anchor`: 42 tests (9 files), typecheck + lint clean.
|
|
- `citation-evidence`: typecheck clean, 51 integration tests passed.
|