Skip to content

Add promotional mail classification and volume-backed OCR image staging - #119

Merged
gekiclaws merged 6 commits into
mainfrom
feat/promo-mail-classification
Apr 27, 2026
Merged

gekiclaws merged 6 commits into
mainfrom
feat/promo-mail-classification

Conversation

@thejonathangu

@thejonathangu thejonathangu commented Apr 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds a FEATURE_PROMO_CLASSIFICATION flag and an isPromotional field on MAIL so admins can mark mail as promotional during single-entry capture and bulk OCR review.

Note for reviewers: an earlier draft of this PR was stacked on top of feat/ocr-image-staging, which has since been merged via #118. The diff against main is now scoped to the promo-classification work only (4 commits + a single new env line in docker-compose-prod.yml for the new flag).

Changes

  • Backend: isPromotional plumbed through models/builders.py, ocr_queue_repository.py, and ocr_queue_controller.py. New FEATURE_PROMO_CLASSIFICATION flag in config.py, .env.sample, and both compose files.
  • Frontend: Toggle exposed in OcrQueue.tsx and RecordEntry.tsx, wired through mail.ts / ocrQueue.ts, and added to the feature-flags hydration in App.tsx (also fixes a bug where the full flags payload wasn't being copied into the store).
  • Docs: Data-model diagram + architecture doc updated; design plan in docs/02-plans/plan-promotional-mail-classification-toggle.md.
  • Tests: test_mail_service_create.py, test_models.py, mail-api.test.ts cover the new field. New PromoClassificationGatingTests in test_ocr_queue_controller.py confirms isPromotional is dropped from PATCH writes and from the MAIL payload at confirm time when the flag is off.

Intentional design notes

  • Queue rows store isPromotional: False explicitly; MAIL docs omit False. The review UI reads queue rows directly and benefits from the field always being present (lets the toggle render without an extra null check). MAIL is the source of truth for downstream consumers and stays sparse — build_mail_create only writes isPromotional when it's True. There's now a comment on create_ocr_queue_items calling this out.
  • Soft-delete filtering (raised by reviewer): get_ocr_queue_items_for_job already filters status: "deleted" rows out via {"status": {"$ne": "deleted"}}, so the admin queue view never surfaces pruned items. That code lives on main (came in via Feat/ocr image staging #118).

Test plan

  • cd backend && python -m unittest tests.unit.test_mail_service_create tests.unit.test_models tests.unit.test_ocr_queue_controller
  • cd frontend && npm test -- mail-api
  • Toggle FEATURE_PROMO_CLASSIFICATION=true, mark a piece as promotional in both RecordEntry and the OCR review screen, confirm it persists on the resulting MAIL doc.
  • Toggle off, confirm the UI hides the control, the PATCH endpoint silently drops any isPromotional value, and confirm() does not write isPromotional to MAIL even if a stale queue row carries it.

Closes #90

Admin mail-entry form gains an optional "Promotional" checkbox, gated
behind a new FEATURE_PROMO_CLASSIFICATION flag (default off). Stored as
an optional isPromotional boolean on the MAIL document — present only
when true on create, explicit true|false on PATCH. No user-facing
effect today; foundation for future summary/notification filtering.
Extends the OCR queue so admins can tag an item as promotional during
the review step (same gate as the manual record-entry form:
FEATURE_PROMO_CLASSIFICATION / promoClassification).

- Queue items now persist `isPromotional` (default false); surfaced on
  GET and settable via PATCH /api/ocr/queue/:id.
- POST /api/ocr/queue/:id/confirm forwards `isPromotional: true` into
  the created mail record when set, matching the existing MAIL shape
  (omitted when false).
- OcrQueue.tsx renders a Promotional checkbox between Type and Mailbox
  when the flag is on, persisting immediately via PATCH like the Type
  selector so it survives navigation.

Made-with: Cursor
- .env.sample: rewrite the FEATURE_PROMO_CLASSIFICATION comment, which
  claimed no user-facing effect; the flag now gates the toggle on both
  the record-entry form and the OCR queue review screen.
- data-model.md / data-model.mmd: add `isPromotional` to OCR_QUEUE_ITEM
  and fill in the previously-undocumented `fileId` / `imagePath`
  fields alongside it.

Made-with: Cursor
App.tsx was copying only three of the five fields returned by
/api/feature-flags into the Zustand store, so `ocrAutoExtract` and
`promoClassification` were permanently stuck at their default `false`
regardless of backend configuration. Pass every field through.

Made-with: Cursor
@thejonathangu
thejonathangu force-pushed the feat/promo-mail-classification branch from b192daf to b2d9c5f Compare April 26, 2026 21:29
@gekiclaws
gekiclaws requested a review from AutumnQiu99 April 27, 2026 02:54
@gekiclaws gekiclaws added the enhancement New feature or request label Apr 27, 2026
@gekiclaws gekiclaws added this to the v1.0 milestone Apr 27, 2026
@gekiclaws
gekiclaws self-requested a review April 27, 2026 02:55
@gekiclaws gekiclaws changed the title Feat/promo mail classification Add promotional mail classification and volume-backed OCR image staging Apr 27, 2026
@gekiclaws gekiclaws changed the title Add promotional mail classification and volume-backed OCR image staging Feat/promo mail classification Apr 27, 2026

@gekiclaws gekiclaws left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! The promo classification flow itself looks clean, and feature-gating it makes sense.

Main concern is that this PR is doing more than the title suggests. It includes promo classification, OCR auto-extract gating, volume-backed image storage, image pruning, scheduler config, and a CI timeout change. I’d rename it to something like Add promotional mail classification and volume-backed OCR image staging, or split the image-staging/pruning pieces out if that is still easy.

A few things to check before merge:

  • Replace/remove the #<ocr-image-staging-pr> placeholder in the PR body.
  • Add/confirm tests for isPromotional being ignored when FEATURE_PROMO_CLASSIFICATION=false.
  • Confirm status: "deleted" OCR queue rows are filtered out in the admin queue after pruning.
  • Add the image upload/load/prune path to the test plan, since that is a substantial part of this diff.
  • Decide whether the queue should persist isPromotional: false while MAIL omits false values. That may be fine, but it should be intentional.

Adds controller-level tests confirming `isPromotional` is dropped from
PATCH writes and from the MAIL payload at confirm time when the flag is
off, and propagated when it's on.

Also documents the intentional asymmetry between queue rows (always
store `isPromotional: False`) and MAIL docs (omit `False`): the review
UI reads queue rows directly and benefits from the field always being
present, while MAIL is the source of truth and stays sparse.

Addresses review feedback on #119.

Made-with: Cursor
@gekiclaws gekiclaws changed the title Feat/promo mail classification Add promotional mail classification and volume-backed OCR image staging Apr 27, 2026
@gekiclaws
gekiclaws self-requested a review April 27, 2026 20:24
@gekiclaws
gekiclaws self-requested a review April 27, 2026 20:25

@gekiclaws gekiclaws left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REDACTED

@gekiclaws
gekiclaws self-requested a review April 27, 2026 21:01
@gekiclaws
gekiclaws merged commit 0bd341c into main Apr 27, 2026
7 checks passed
@gekiclaws
gekiclaws deleted the feat/promo-mail-classification branch April 27, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support promotional mail classification

2 participants