Repository navigation
Add promotional mail classification and volume-backed OCR image staging - #119
Merged
Merged
Conversation
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
force-pushed
the
feat/promo-mail-classification
branch
from
April 26, 2026 21:29
b192daf to
b2d9c5f
Compare
…mo-mail-classification
5 tasks
gekiclaws
self-requested a review
April 27, 2026 02:55
gekiclaws
requested changes
Apr 27, 2026
gekiclaws
left a comment
Contributor
There was a problem hiding this comment.
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
isPromotionalbeing ignored whenFEATURE_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: falsewhile 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
self-requested a review
April 27, 2026 20:24
gekiclaws
approved these changes
Apr 27, 2026
gekiclaws
self-requested a review
April 27, 2026 20:25
gekiclaws
self-requested a review
April 27, 2026 21:01
gekiclaws
approved these changes
Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
FEATURE_PROMO_CLASSIFICATIONflag and anisPromotionalfield onMAILso admins can mark mail as promotional during single-entry capture and bulk OCR review.Changes
isPromotionalplumbed throughmodels/builders.py,ocr_queue_repository.py, andocr_queue_controller.py. NewFEATURE_PROMO_CLASSIFICATIONflag inconfig.py,.env.sample, and both compose files.OcrQueue.tsxandRecordEntry.tsx, wired throughmail.ts/ocrQueue.ts, and added to the feature-flags hydration inApp.tsx(also fixes a bug where the full flags payload wasn't being copied into the store).docs/02-plans/plan-promotional-mail-classification-toggle.md.test_mail_service_create.py,test_models.py,mail-api.test.tscover the new field. NewPromoClassificationGatingTestsintest_ocr_queue_controller.pyconfirmsisPromotionalis dropped from PATCH writes and from the MAIL payload at confirm time when the flag is off.Intentional design notes
isPromotional: Falseexplicitly; MAIL docs omitFalse. 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_createonly writesisPromotionalwhen it'sTrue. There's now a comment oncreate_ocr_queue_itemscalling this out.get_ocr_queue_items_for_jobalready filtersstatus: "deleted"rows out via{"status": {"$ne": "deleted"}}, so the admin queue view never surfaces pruned items. That code lives onmain(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_controllercd frontend && npm test -- mail-apiFEATURE_PROMO_CLASSIFICATION=true, mark a piece as promotional in bothRecordEntryand the OCR review screen, confirm it persists on the resultingMAILdoc.isPromotionalvalue, and confirm() does not writeisPromotionalto MAIL even if a stale queue row carries it.Closes #90