9.6 KiB
Tasks: web clipper (roadmap 33)
One task = one bounded implementation handoff. Spec: spec.md; rules: AGENTS.md, docs/CODE-MAP.md. Paths marked new do not exist yet. Tasks run in order; each leaves its own testable result. Later implementation owns code paths listed below; this documentation run writes only specs/plugins-parity/.
First tool call rule: batch at most three relevant reads in first tool call; write first OWNS file in SECOND tool call (compiling stub plus one failing test), then implement and pass that test. Do not spend second call exploring. For docs-only task, write first OWNS document in second call and validate its acceptance checklist.
Standing VERIFY (future implementation only): export PATH="$HOME/.cargo/bin:$PATH"; cargo test -p <package>; cargo clippy -p <package> --all-targets -- -D warnings; rustfmt --edition 2024 <owned Rust files>; UI tasks add cargo xtask i18n and read snapshot PNGs; final integration adds cargo xtask layers, cargo xtask tree, cargo xtask ci. Stop on failure; never bypass checks.
Standing DO NOT: edit outside OWNS, change Git state, commit, use cargo fmt --all, add unapproved dependency, modify craft-libs API, use unsafe/panics/input indexing, write disk outside atomic Vault/Session/settings APIs, hardcode UI strings. Parent module declarations/test harnesses must be owned explicitly.
Phase 1 — pure contract
- 1. Versioned envelope and validation (brief
briefs/20-web-capture-envelope.md). OWNS:crates/engine/src/capture/web_model.rs(new, first write),crates/engine/src/capture/mod.rs(module declaration only). FILES TO OPEN:specs/plugins-parity/web-clipper/spec.mdD1,crates/engine/src/capture/rules.rs,crates/engine/src/capture/mod.rs. GOAL:WebCapture::parse(&str) -> Result<Self, CommandError>, validated metadata/assets and normalized capture target; named caps from D1; decoded signatures/duplicate ids/placeholder validation; pure function, no IO. Keep result type crate-private and tests local. Use existing base64 decoder, no added dependency. TESTS: roundtrip minimal envelope; unknown version/fields; exact byte caps/+1; UTF-8 size; bad source/credentials; traversal/reserved paths; duplicate/missing asset ids; bad base64/MIME/signature; no replacement inside code; empty capture; valid PNG/JPEG fixtures. VERIFY: standing-p notescraft-engine,cargo test -p notescraft-engine capture::web_modelpasses all named cases. DO NOT: register commands, save assets, open listener, convert arbitrary HTML or add htmlmd dependency.
Phase 2 — engine save semantics
- 2. Preview/save, local assets and retry identity. Depends on 1 and owner Q3 decision.
OWNS:
crates/engine/src/capture/web.rs(new, first write),crates/engine/src/capture/mod.rs,crates/engine/src/capture/rules.rs(shared capture execution only),crates/engine/src/vault/session.rs(bounded replay state only),crates/engine/src/commands.rs,crates/engine/src/ai/assistant_gate.rs,crates/engine/tests/core_commands.rs,crates/engine/tests/web_capture.rs(new);crates/engine/Cargo.tomlONLY if owner Q3 approves existing workspace sha2 usage. FILES TO OPEN: spec D1-D2,crates/engine/src/capture/mod.rs,crates/engine/src/vault/attachments.rs,crates/engine/tests/capture.rs,crates/engine/tests/common/mod.rs. GOAL: two D2 commands, shared existing capture executor, attachment destination span rewrite; preflight before writes; replay ledger 128 ids/digests/results; explicit partial_capture path report. Do not promise transactional multi-file save. TESTS: TempVault preview zero writes; text+PNG emits local embed; Daily dirty buffer and undo; Inbox collisions; same-id replay once; changed-id payload conflict; ledger eviction at 129 entries; retry_only unknown/evicted id zero writes; bad asset no writes; injected later write failure lists only new assets; read-only; existing note.capture tests remain passing. VERIFY: standing engine plus--test web_capture --test capture --test core_commands. DO NOT: network fetch, overwrite existing files, delete orphaned assets automatically, change NDJSON limit, add a new crate.
Phase 3 — loopback transport
- 3. HTTP bridge owned by control lifecycle. Depends on 2; existing axum/tokio approved, no new dependency proposal.
OWNS:
crates/engine/src/control/capture_http.rs(new, first write),crates/engine/src/control/mod.rs,crates/engine/src/control/server.rs,crates/engine/src/control/pump.rs,crates/engine/src/control/protocol.rs,crates/engine/src/control/token.rs,crates/engine/Cargo.toml(existing workspace axum/tokio),Cargo.lock(resolved approved workspace dependencies only),crates/engine/tests/web_capture_http.rs(new). FILES TO OPEN: spec D3,crates/engine/src/control/server.rs,crates/engine/src/control/pump.rs,crates/engine/src/control/protocol.rs,crates/engine/tests/control.rs,apps/notescraft-sync-server/Cargo.toml(existing approved HTTP framework). GOAL: separate HTTP loopback listener, same control pump/registry dispatch, optional descriptor field; explicit local enable/token rotation API using fallible OS CSPRNG (no RandomState fallback); capture-only auth and exact origin/Host gates; fixed body/header/connection/inflight/deadline limits. Existing NDJSON payload cap/protocol untouched. Extend Job with expiry so skipped-before-execution jobs do not mutate after timeout. Reuse framework HTTP parsing. TESTS: raw HTTP/axum tests: no bind disabled; POST preview/save/replay with explicit envelope-to-command wrapping and retry-only header; arbitrary route/command refused; token separation, wrong Host, null/foreign origin, preflight, missing bearer, oversized body/header; queued expiry zero writes; drop/disable shuts listener; RNG failure leaves listener disabled; Unix token source and unsupported Windows status until Q5 approval; slow client/flood bounded; old NDJSON control tests pass. VERIFY: standing engine,--test web_capture_http --test control --test control_client;cargo xtask layers,cargo xtask tree. DO NOT: expose control token, log payload, permit wildcard CORS or LAN, allow page origins, implement handwritten HTTP parser, change workspace registrations (no new crate). Windows bridge is blocked on Q5; before any approved RNG dependency addition, amend OWNS to include root Cargo.toml and Cargo.lock plus engine manifest. Unix-only transport can proceed without it.
Phase 4 — import and pairing UI
- 4. Explicit settings, pairing and clipboard-envelope preview/save.
OWNS:
crates/ui-egui/src/views/web_capture/mod.rs(new, first write),crates/ui-egui/src/views/mod.rs,crates/ui-egui/src/shell/app.rs,crates/ui-egui/src/shell/app/window/commands.rs,crates/ui-egui/src/shell/app/window/frame.rs,crates/ui-egui/src/text/features/web_capture.rs(new),crates/ui-egui/src/text/features/mod.rs,crates/i18n/locales/en.toml(append ui.web_capture),crates/engine/src/prefs/app_settings.rs,crates/engine/src/prefs/settings.rs,crates/ui-egui/tests/web_capture.rs(new),apps/notescraft/src/main.rs(control startup/local bridge lifecycle hook only). FILES TO OPEN: spec D3-D5,crates/ui-egui/src/views/share_dialog/mod.rs,crates/engine/src/prefs/app_settings.rs,crates/engine/src/prefs/settings.rs,crates/ui-egui/src/shell/app/window/frame.rs. GOAL: off-by-default web enable setting and extension-origin list, local token display/revoke, listener reconciliation outside paint; Import web capture dialog takes copied JSON, previews, confirms save, reports remote images/partial capture/uncertain retry. Save stays registry-backed. Secrets absent from diagnostics/snapshot fixtures. TESTS: kittest disabled/enabled/revoke; invalid JSON and selection-only envelope; Save once; retry-only unknown result requires explicit fresh-submission confirmation; missing assets warning; read-only; queued failure keeps draft. Settings restart preserves enabled/origins but rotates token. TOML parses. VERIFY: standing engine + ui-egui;cargo xtask i18n; offscreen screenshots Mocha/Latte, normal/compact, read PNGs. DO NOT: write browser extension, silently change destination, execute HTML, read clipboard automatically, call engine per row/frame.
Phase 5 — external client contract and status
- 5. Browser adapter contract with executable transport fixtures; close acceptance gaps.
OWNS:
specs/plugins-parity/web-clipper/browser-contract.md(new, first write),crates/engine/tests/web_capture_http.rs,specs/plugins-parity/web-clipper/spec.md,specs/plugins-parity/web-clipper/tasks.md,specs/plugins-parity/roadmap.md(33 only),ROADMAP.md,docs/ARCHITECTURE.md,docs/CODE-MAP.md(feature entries only). FILES TO OPEN: spec D4 and AC1-8,crates/engine/tests/web_capture_http.rs,specs/plugins-parity/roadmap.md. GOAL: publish exact JSON/route/error/pairing contract, selection-only bookmarklet algorithm and clipboard fallback, extension background/host-permission and asset rules. Include request/response examples and manual browser matrix for restricted page/CSP/permission/stale token/offline app. Mark external adapter unimplemented until separate work exists; update status with actual scope. TESTS: example envelopes accepted by registry/HTTP fixtures; invalid examples rejected; manual browser checks only for actual adapters, record untested contract claims as such. Trace every AC to prior tests or explicit gated adapter check. VERIFY: fixture tests plus standing final gates; docs links resolve. No cargo is run while writing these plans. DO NOT: publish/install extension, add JS to native app, mark full adapter shipped without real browser test, infer owner approval from time elapsed.