Generated by the improve skill on 2026-07-17 (deep audit at commit a6293e1,
baseline: tsc clean, 41/41 tests passing, npm audit clean). Execute in
the order below unless dependencies say otherwise. Each executor: read the
plan fully before starting, honor its STOP conditions, and update your row
when done.
| Plan | Title | Priority | Effort | Depends on | Status |
|---|---|---|---|---|---|
| 001 | Characterization tests for the content-script protection flow | P1 | M | — | DONE |
| 002 | Ignore Enter during IME composition | P1 | S | 001 | DONE |
| 003 | Retry path honors the review contract | P1 | S | 001 | DONE |
| 004 | Timeouts on worker/protect requests | P1 | M | 001, 003 | DONE |
| 013 | Fix debugSettingsPromise TDZ crash on content-script init | P1 | S | 001, 002 | DONE |
| 005 | Serialize offscreen-document creation | P2 | S | — | DONE |
| 006 | CI workflow (verify, typecheck, test, build) | P2 | S | — | DONE |
| 007 | Vendor only the ORT files the runtime loads | P2 | M | 006 (soft) | DONE |
| 008 | Correct README rehydration claim | P3 | S | — | DONE |
| 009 | Validate custom-domain input | P2 | S | — | DONE |
| 010 | Dedupe worker protocol types + escapeHtml | P3 | S | — (after 004 preferred) | DONE |
| 011 | Debug logging off the send critical path | P3 | S | 001 (after 002–004 preferred) | DONE |
| 012 | Remove no-op WebGPU toggle | P3 | S | — | DONE |
| Status values: TODO | IN PROGRESS | DONE | BLOCKED (with one-line reason) | REJECTED (with one-line rationale) |
src/content.ts’s
orchestration; 001 freezes today’s behavior so their diffs are reviewable.handleProtectResponse extraction (timeout errors
route through it).jsep ORT files. If 012 is REJECTED (WebGPU gets
wired instead), 007’s keep-list must add the jsep pair.npm test exit 1 even when every
test passes. It must land before 006 (CI) — otherwise CI reports every
green run as failed. Execute after 001+002 (both touch the same file
region); ideally before 003/004 too, though it doesn’t strictly block them.@crxjs/vite-plugin pinned at 2.0.0-beta.32: builds fine, no
advisories; the project’s maintenance status is worth watching but a
migration has no current payoff. Monitor only.${tabId}: prefix matching in
src/rampart-worker.ts reset): verified correct — "123:..." does not
prefix-match "12:"; no fix needed.debugSettingsPromise
in src/content.ts): toggling raw diagnostics requires a tab reload to
affect open tabs. Diagnostics-only, trivial impact; revisit only if it
confuses a real debugging session.npm audit: 0 vulnerabilities at planning time; nothing to plan.src/content.ts:310, debugSettingsPromise ??= ... inside getDebugSettings
is reached (via the module-init void logDebug(...) call at line 14) before
the let debugSettingsPromise declaration at line 307 executes in module
evaluation order. Reproducible with a bare await import("../src/content");
surfaces as an unhandled ReferenceError on every import/page load (Vitest
reports it as a non-fatal “Errors: 1”, not a test failure). Not fixed by
plan 001 (tests-only scope). No plan currently owns this — worth a follow-up
fix (hoist the let debugSettingsPromise above its first use, or otherwise
ensure the init-log call can’t race the declaration) before or alongside
plan 002/003/004 since they touch the same file. Confirmed during plan
002’s review: this bug makes npm test/npm run vitest run exit 1 even
though every test passes (Vitest counts the unhandled rejection as an
“Errors: 1” and reflects it in the process exit code). This is a new
finding requiring plan 013 before plan 006 (CI) lands, since CI would
otherwise mark every green test run as a failure.git worktree
add) after the Agent tool’s built-in worktree isolation based its first
attempt off a stale pre-plan-001 snapshot (a6293e1) instead of current
master — that attempt correctly hit its own STOP condition (missing
tests/content-flow.test.ts) and was discarded without any changes.
Also surfaced a worktree-checkout artifact (not a plan defect): a fresh
git worktree add on this machine reconverts vendored public/ort/*.mjs
line endings, breaking verify:assets/npm run build checksum comparison
until the affected files are force-recheckout with
git -c core.autocrlf=false checkout -- public/. Apply that before running
npm run build in any future manually created worktree.ort-wasm-simd-threaded.{wasm,mjs}
pair as the keep-list, reasoning that device: "wasm" + numThreads: 1
loads only that pair. The first executor attempt correctly hit its own
STOP condition rather than guessing: the built worker bundle
(dist/assets/rampart-worker-*.js) contained a live, executing reference
to ort-wasm-simd-threaded.jsep-*.wasm. Root cause, confirmed
independently by the reviewer: @huggingface/transformers imports the bare
"onnxruntime-web" specifier, and onnxruntime-web’s package.json
exports map resolves that to dist/ort.bundle.min.mjs — the jsep-capable
bundle — regardless of the device option passed to the pipeline;
device: "wasm" only gates the WebGPU buffer-registration branch inside
that bundle’s init, not which WASM binary it instantiates. Grepping
ort.bundle.min.mjs confirms 4 references to the jsep pair and 0 to the
plain pair. Keep-list corrected to the jsep pair; re-verified end to end
(tsc, 64/64 tests, build ships only the jsep wasm with zero plain-variant
references anywhere in dist/, 23 MB zip vs the 39 MB baseline — smaller
savings than the plan’s original estimate since the jsep binary is larger,
but still a real reduction from the 93 MB/26-file original). Steps 1-4 are
merged into master. Step 5 (the mandatory manual Chrome runtime gate —
load dist/ as an unpacked extension, open the side panel, confirm the
model loads and redacts a test prompt) has NOT been performed — no
browser-automation tool in this session could reliably drive
chrome://extensions’s native “Load unpacked” file picker. This is
exactly the scenario the plan’s mandatory gate exists for: static/build
analysis got the right answer this time, but nothing in this repo’s
automated suite would have caught the original wrong keep-list at
runtime, and it shouldn’t be trusted without the manual check.
Action needed from you: load dist/ (or the packaged
packages/promptward-extension.zip, unzipped) as an unpacked extension,
open the side panel, confirm the model reaches “Ready” with no errors in
Diagnostics, and confirm a test prompt containing a fake SSN gets redacted
on a supported site. Once confirmed, flip this row to DONE.runtime-configured
prewarm-end events present). The redacts-a-test-SSN half could not be
tested initially because a separate, pre-existing content-script bug was
silently no-op’ing protection on ChatGPT/Perplexity/Claude: findEditorIn
resolved the wrong contenteditable (an earlier empty field) and read empty
text, so sends passed through as empty-editor-ignored without ever reaching
the model. That bug was unrelated to plan 007 — 007 only touches which
WASM files ship, and the model demonstrably loaded and initialized off them.
The SSN-redaction surface 007 gates was instead proven by the existing
tests/rampart-local-model.test.ts:51, which runs the exact string
123-45-6789 through guard.protect() and asserts redaction. The
editor-resolution bug was fixed in release 0.11.0 (see CHANGELOG), after
which the live SSN test on chatgpt.com succeeds. Row flipped to DONE.tsc --noEmit exited 0, but it had only run that check
immediately after editing src/background.ts, before writing
tests/background-offscreen.test.ts — the test file’s unused beforeEach
import (never called) failed noUnusedLocals. Caught on independent
reviewer re-verification, sent back with the literal failing output;
fixed in one round with the import removed and re-verified with actual
terminal output this time. General lesson: executor self-reports of
“exit 0” must be independently re-run by the reviewer, not trusted —
this is exactly why the closing-the-loop review step exists.Recorded from the deep audit for future consideration:
[TOKEN_n] in assistant
responses and calls PW_REVEAL_TEXT. The hard part is watching response
DOM per site — the same fragile surface as send interception. Effort: L
(coarse). Plan 008 documents reality until/unless this is built.optional_host_permissions: ["https://*/*",
"http://*/*"] which store review scrutinizes. Effort: M (coarse).jsep ORT builds
exist. Meaningful latency win on the 14.7 MB model. Effort: M (coarse).