# Plotter Plan — ink-signature station for no-login CMS filings

Goal: turn the (already-built) ink-signature pipeline into a reliable physical
station that draws an **original wet-ink signature** on each printed CMS form so
the Standard (no-login) filing path can be mailed and accepted. The CR-10 V2 is
the motion system; this plan covers the retrofit, calibration, the
software/hardware integration that remains, validation, and the operational
runbook.

Status legend: ✅ done · 🔲 to do · ⏳ blocked on hardware

---

## 0. What already exists (software, hardware-independent)

- ✅ `signature_vector` capture end-to-end (migration 090, signing page, API).
- ✅ `ink_signature_plotter.py` — fit-to-box, PDF-pt → bed-mm, G-code (Z pen or
  M280 servo/BLTouch), SVG preview, `render_signature_on_pdf` digital twin,
  gated serial sender.
- ✅ `ink_signature_cli.py` — load record → gcode + preview, `--test-box`,
  `--plot`.
- ✅ `test_ink_signature.py` 30/30; signature anchor verified inside the
  CMS-10114 Section 4A cell.
- ✅ `docs/ink-signature-plotter.md` — retrofit + interpretive-risk writeup.

The remaining work is hardware + a few integration/safety pieces.

---

## 1. Hardware retrofit (CR-10 V2)

1. 🔲 **Pen holder.** Print a spring-loaded CR-10/Ender X-carriage pen holder
   (Printables/Thingiverse). Spring = even contact pressure. Print it before
   converting (you still have the hotend).
   - BOM: printed bracket, 1 compression spring (~8–12mm), 2–3× M3 screws, a
     smooth **gel/rollerball** pen (wet ink, hand-written look).
2. 🔲 **Mount + cable management.** Attach beside the hotend; make sure the pen
   tip is the lowest point and the bed can still home.
3. 🔲 **Pen-up/down method — pick one:**
   - *Z mode (default, simplest):* pen fixed; G-code lifts/drops Z. Spring
     forgives over-press. No firmware change.
   - *Servo/BLTouch mode (optional):* actuate pen with a servo or repurpose the
     BLTouch; `--servo-pen` emits `M280`. Faster, no Z lash, more build effort.
   - **Decision:** start Z mode; revisit servo only if Z lift is too slow.
4. 🔲 **Paper jig.** Tape an L-shaped corner stop to the bed (front-left) so a
   US-Letter sheet always sits at the same `(jig_x_mm, jig_y_mm)`. This is the
   single most important accuracy factor.
5. 🔲 **Pen-touch Z reference.** Either set `pen_down_mm` by hand, or use the
   BLTouch to probe a fixed point on the jig for a repeatable touch height.

Reversibility: nothing here is permanent; swap the hotend back to print anytime.

---

## 2. First-light calibration (uses --test-box)

1. 🔲 Connect: CR-10 enumerates as CH340 `/dev/ttyUSB0` @ 115200. Confirm with
   `ls -l /dev/ttyUSB*` and a manual `G28` via pronterface/printrun.
2. 🔲 Dry-run inspect: `ink_signature_cli.py --order <X> --doc cms10114
   --test-box` → review the `_testbox.gcode` + `_preview.svg`.
3. 🔲 Plot the box on a **blank** sheet in the jig:
   `--test-box --plot --home --port /dev/ttyUSB0`.
4. 🔲 Print a real (unsigned) CMS-10114 cert page, lay it in the jig, plot the
   test box again, and confirm the rectangle frames the Section 4A signing cell.
5. 🔲 Tune `--jig-x/--jig-y` (position) and `--pen-down` (ink without digging in)
   until the box lands on the line. **Record the final values** as the station
   defaults.
6. 🔲 Bake the calibrated defaults into `PlotterConfig` (or a small
   `plotter_config.json` the CLI/worker reads), so operators don't pass flags.

Acceptance: the plotted rectangle sits within ~1mm of the cell, repeatably,
across 3 reloads of the sheet.

---

## 3. Software/integration still to build

1. 🔲 **Persisted station config.** `scripts/workers/services/plotter_config.json`
   (jig offset, pen heights, feeds, port) loaded by `PlotterConfig.from_file()`.
   Keeps calibration out of code + flags.
2. 🔲 **Anchor scaling parity test.** A test that plots/previews against a page
   whose `page_w/page_h` differ from the authored size, asserting the strokes
   still land in the cell (the stamper already scales; add the matching ink
   test).
3. 🔲 **Signature-quality guard.** Reject/flag vectors that are too few points,
   near-empty bbox, or a single dot (someone tapped instead of signing) before
   they reach the pen. Add to `ink_signature_plotter` + a unit test.
4. 🔲 **855 anchors for ink.** `cms855_pdf_filler` only has the 855I signature
   rect today; add 855B/O/A signature rects so org filings can also be plotted
   (currently human-placed). Mirror the CMS-10114 anchor approach.
5. 🔲 **Batch integration.** Teach `daily_paper_batch.py` to, for each signed
   filing in a batch: (a) print the filled form, (b) call the plotter to ink the
   signature, (c) include it in the per-agency envelope. Sequencing + a
   per-sheet "plotted ✓" status column so a crash doesn't double-sign or skip.
6. 🔲 **Plot audit trail.** Store the emitted G-code hash + a post-plot photo (if
   a camera is added) on the esign/batch record for traceability.
7. 🔲 **Operator safety.** `--plot` already gated; add a hard env guard
   (`INK_PLOTTER_ENABLED=1`) so a stray `--plot` on a dev box can't move a
   machine, and a `--confirm` prompt for live runs.

---

## 4. Validation (build verifiable criteria)

- 🔲 **Geometry (done, extend):** keep `test_ink_signature.py` green; add the
  page-scale parity + quality-guard tests above.
- 🔲 **Physical accuracy:** a calibration card — plot a 10mm grid + the test box,
  measure with calipers, assert ≤1mm error. Document the measured numbers.
- 🔲 **Legibility:** plot 5 real captured signatures, scan, eyeball that each is
  recognizably the signer's mark (not mangled by resampling). Tune
  `min_segment_mm` / feeds if jagged.
- 🔲 **Ink originality sanity:** confirm under magnification the line is
  continuous wet ink (not dotty), since "deemed not original" is the failure mode
  we're guarding against.
- 🔲 **End-to-end dry run:** one fake order → form printed → signature plotted →
  batched → addressed envelope, with nothing actually mailed.

---

## 5. Go-live policy (interpretive risk)

CMS does not explicitly bless/ban autopen for the 855/10114. Therefore:

1. 🔲 **Two paths, choose per filing:**
   - *Plotter (fast):* default for low-stakes changes (e.g. address/contact
     NPPES updates) where a re-file is cheap.
   - *True wet-signature mail-out (safe):* a printed packet to the provider
     (cover sheet with a signing arrow + return envelope) for high-stakes
     filings (enrollment, reactivation) until acceptance is proven.
2. 🔲 **Pilot:** run a handful of real plotter-signed filings, track acceptance
   vs rejection at the MAC/Enumerator. If clean, widen the plotter default.
3. 🔲 **Kill switch:** if any rejection cites the signature, fall back to wet-sig
   mail-out for that service and log it.

Note: never expose any of this (plotter, paper, ink, MAC, Enumerator) in
client-facing copy — same rule as the rest of the no-login model.

---

## 6. Operational runbook (once calibrated)

```
# Daily, per signed Standard filing (or via daily_paper_batch once wired):
1. Print the filled, unsigned form (form printer, plain paper).
2. Lay the cert page in the jig on the CR-10.
3. Plot the signature:
   INK_PLOTTER_ENABLED=1 python scripts/workers/ink_signature_cli.py \
     --order CO-XXXX --doc cms10114 --plot --home --confirm
4. Verify the ink landed on the line; reassemble the form pages.
5. Hand to the daily batch -> per-agency Priority Mail envelope.
```

---

## 7. Sequence (what to do, in order)

1. Print + mount the spring pen holder; tape the jig. (§1)
2. Calibrate with `--test-box`; record jig/pen values. (§2)
3. Add persisted `plotter_config.json` + load it. (§3.1)
4. Add quality guard + page-scale parity tests. (§3.2–3.3)
5. Physical accuracy + legibility validation. (§4)
6. Wire into `daily_paper_batch` with per-sheet plotted status. (§3.5)
7. Add env/confirm safety guards. (§3.7)
8. Pilot live filings under the go-live policy; widen or fall back. (§5)
9. (Later) 855B/O/A anchors so org filings can be plotted too. (§3.4)

---

## Dependencies / risks

- ⏳ Everything in §1–2 is blocked on the pen holder + jig (cheap, fast).
- Risk: jig drift → signature off the line. Mitigate with a fixed, screwed-down
  jig and a per-session `--test-box` check.
- Risk: pen dry-out / skipping → illegible mark. Mitigate with gel/rollerball +
  legibility validation + a "prime the pen" pre-stroke.
- Risk: CMS deems plotted signature "not original." Mitigate with the two-path
  go-live policy + pilot.
