7.8 KiB
Tasks: PDF annotations (roadmap 32)
Spec: spec.md. Rules: AGENTS.md, docs/CODE-MAP.md.
One task = one agent; execute in order, dependencies are preceding tasks. Each task has its own test boundary; backend tests use fakes until approval. Existing paths verified with rg --files and source reads on 2026-10-21; paths marked new do not exist yet. No new crate; no workspace/layer registration needed. If owner instead approves a new crate, first revise tasks to own its manifest, root Cargo.toml and xtask/src/layers.rs, package/layer/dependency registration and crate smoke test before use.
First tool call rule: first tool call reads at most three listed inputs; write FIRST file in OWNS in SECOND tool call, with compiling stub and one failing test, then make it pass. Do not spend second call exploring. For document-only tasks write initial acceptance checklist in second call and review/complete it instead of a code stub.
Standing VERIFY (future implementation only): export PATH="$HOME/.cargo/bin:$PATH"; cargo test -p <owned-package>; cargo clippy -p <owned-package> --all-targets -- -D warnings; rustfmt --edition 2024 <owned Rust files>. UI tasks add cargo xtask i18n and render/read snapshot PNG. Last implementation task adds cargo xtask layers, cargo xtask tree, cargo xtask ci. These commands are instructions, not commands run during this spec-writing request.
Standing DO NOT: touch outside OWNS; change git state; run cargo fmt --all; add dependency without owner response; unsafe/panics/unchecked slicing; rewrite source attachments; hardcode UI strings; bypass engine commands. No production claim until platform acceptance passes.
Phase 1 — pure model
- 1. Annotation geometry, sidecar and fragment model.
OWNS:
crates/pdf/src/annotation.rs(new, FIRST),crates/pdf/src/annotation/fragment.rs(new),crates/pdf/src/lib.rs(declare/re-export),crates/pdf/src/error.rs(specific validation variant only). FILES TO OPEN:specs/plugins-parity/pdf-annotations/spec.mdD1–D2;crates/pdf/src/lib.rs;crates/pdf/src/error.rs. GOAL:Rect::validate,Annotations::parse/to_json,PdfTarget::parse/to_fragment, named caps and typed errors, pure no-IO APIs. Canonical normalized rect and stable annotation ID links, no PDF parser. Use existing serde/serde_json dependencies verified incrates/pdf/Cargo.toml; no manifest edit. TESTS: geometry round trip, rotated geometry mapping fixture, NaN/infinity, zero/negative/outside bounds, page 0/10001, duplicate ID, caps, unknown version, duplicate fragment keys, existingpage=3, Unicode target and unknown fragment refusal. VERIFY: standing-p notescraft-pdf; tests namedannotationexecute, exporter tests unchanged. DO NOT: IO, engine registration, parser dependency, modify PDF export pipeline.
Phase 2 — engine and backend
- 2. Sidecar commands, revisions and crop worker.
OWNS:
crates/engine/src/viewers/pdf_annotations.rs(new, FIRST),crates/engine/src/viewers/pdf_crop.rs(new),crates/engine/src/viewers/mod.rs,crates/engine/src/ai/assistant_gate.rs,crates/engine/tests/core_commands.rs,crates/engine/tests/pdf_annotations.rs(new),crates/engine/src/vault/notes.rs(file rename association hook only),crates/engine/src/vault/folders.rs(folder rename association hook only),crates/engine/src/vault/external.rs(external move association only). FILES TO OPEN: spec D1–D3;crates/engine/src/viewers/annotations.rs;crates/engine/src/viewers/info.rs; latercrates/engine/src/vault/attachments.rs,crates/engine/tests/common/mod.rs. GOAL: register list/upsert/delete/excerpt commands; excerpt defaults save=false but entire assistant command WRITE because save=true persists; read-only refuses save=true only; source-hash stale detection, optimistic revision checks, atomic private persistence, bounded crop work using existing PNG tooling; save crop via attachment helper. Propagate persistence errors; do not copy image-sidecar.badrewrite behavior because PDF spec preserves corrupt input. TESTS: TempVault round trip; byte-identical PDF; corrupt sidecar unchanged; concurrent revision rejected; traversal/symlink; rename/replacement; read-only; crop pixel boundaries and cap; fake worker cancellation. VERIFY: standing engine;cargo test -p notescraft-engine --test pdf_annotations --test core_commands. DO NOT: native backend dependency or silent source remap; call engine during paint. - 3. PDF geometry/text capability adapter (blocked on Q1).
OWNS:
crates/engine/src/viewers/pdf_backend.rs(new, FIRST),crates/engine/src/viewers/pdf_annotations.rs,crates/engine/src/viewers/mod.rs,crates/engine/src/vault/pdf_preview.rs(shared bounded worker boundary only),crates/engine/tests/pdf_backend.rs(new),specs/plugins-parity/pdf-annotations/backend-decision.md(new). FILES TO OPEN: spec D3/Q1;crates/engine/src/vault/pdf_preview.rs;crates/engine/src/viewers/mod.rs. GOAL: record approved native-tool scope first; register page_info; rotated crop geometry/text runs through bounded worker. Unsupported backend explicitly lacks text selection. If approved approach needs dependency, stop and amend owned files/approval before edits; no mock masquerading as implementation. TESTS: synthetic adapter output limits/errors/timeouts; native fixtures selectable text, scanned page, rotated/cropped page, missing executable and page range. Confirm actual macOS and supported Poppler behavior. VERIFY: standing engine, backend fixture suite on each supported platform; record unsupported targets. DO NOT: invent text from raster; unsafe, unapproved dependency, hidden fallback.
Phase 3 — PDF tab and linked embeds
- 4. Annotation tools, embed crops and shared navigation.
OWNS:
crates/ui-egui/src/views/media_view/pdf_annotations.rs(new, FIRST),crates/ui-egui/src/views/media_view/pdf.rs,crates/ui-egui/src/views/media_view/mod.rs,crates/ui-egui/src/views/note_view/blocks/embed.rs,crates/ui-egui/src/views/note_view/blocks/embed_data.rs,crates/ui-egui/src/shell/app/window/views.rs,crates/ui-egui/src/text/views/media_view/mod.rs,crates/i18n/locales/en.toml(append keys),crates/ui-egui/tests/pdf_annotations.rs(new). FILES TO OPEN: spec D2/D4;crates/ui-egui/src/views/media_view/pdf.rs;crates/ui-egui/src/views/note_view/blocks/embed.rs; later open remaining owned dispatch files before edit. GOAL: keyboard-accessible region/text selection, comments, copy quote/excerpt, delete confirmation, stale/unsupported messages; route PDF fragment actions to tab state; use viewport-to-normalized mapping independent of zoom. Queue engine work after frame. TESTS: kittest select/comment/reopen; crop click page jump; rotated coordinates; unsupported text; failed save retains input; frame counter prevents repeated jobs; old page links unchanged. VERIFY: standing ui-egui + PNG snapshots Mocha/Latte/scale 2 read visually. DO NOT: raw UI patterns contrary to existing shared controls, IO in paint, claim Obsidian rect compatibility. - 5. Acceptance evidence and status.
OWNS:
specs/plugins-parity/pdf-annotations/acceptance.md(new, FIRST),specs/plugins-parity/roadmap.md(rank 32 status only),ROADMAP.md,docs/ARCHITECTURE.md,docs/CODE-MAP.md(PDF status/API only). FILES TO OPEN: spec acceptance; completed test reports; existing docs relevant PDF sections. GOAL: record criterion-to-test/platform evidence, unresolved owner decisions and accurate capability table; mark complete only when mandatory text backend and UI tests pass. TESTS: trace every acceptance item to passing assertion/visual evidence, no unsupported-platform claim. VERIFY: final standing gates. Documentation-only spec-writing request must NOT execute this future task or edit outsidespecs/plugins-parity/. DO NOT: broaden feature, mark approval-gated backend done.