Review: Schedule image digestion: Claude Vision OCR to WorkQueueItems

review-1450-2026-06-13 Review

review needs-refinement

Verdict: NEEDS_REFINEMENT

Template Completeness

  • [x] Type -- Feature
  • [x] Lineage -- Standalone, extends Upload tab (#33)
  • [x] Repo -- ldraney/landscaping-assistant
  • [x] User Story -- crew lead schedule parsing
  • [x] Context -- thorough, covers column-overflow, handwritten additions, existing infra
  • [x] File Targets -- create 3, modify 5, explicit do-not-touch list
  • [x] Feature Flag -- none, justified (extension of existing upload flow)
  • [x] Acceptance Criteria -- 8 criteria
  • [x] Test Expectations -- 4 test expectations with run command
  • [x] Constraints -- Claude model, API key source, fuzzy matching approach
  • [x] Checklist -- standard 3-item
  • [x] Related -- references project and parent issue #33

Traceability

  • [x] story:schedule-upload label -- present on board item
  • [x] story note verified -- found in project-landscaping-assistant user-stories section: "Snap a photo of the paper schedule for later reference"
  • [ ] story note OUTDATED -- [SCOPE] The user story summary says "Snap a photo of the paper schedule for later reference" but this ticket extends the story to include AI-powered parsing into WorkQueueItems. The user story success metric should be updated to reflect schedule digestion, not just photo upload.
  • [x] arch:rails-app label -- present on board item
  • [ ] arch note MISSING -- [SCOPE] No architecture note found for arch-rails-app in pal-e-docs. Search returned zero results. Create architecture note arch-rails-app for the Rails application component.
  • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/landscaping-assistant/issues/204, state: open

File Targets

Files to create:
  • [x] app/services/schedule_digester.rb -- directory app/services/ does not exist yet (must be created). This is standard Rails convention, acceptable.
  • [x] app/views/uploads/digest.html.erb -- parent directory exists, confirmed
  • [x] db/migrate/TIMESTAMP_add_week_start_to_uploads.rb -- standard migration, OK
Files to modify:
  • [x] Gemfile -- verified exists. No anthropic gem present currently. Correct target.
  • [x] app/controllers/uploads_controller.rb -- verified exists. Currently has index/show/create/edit/update/destroy. Adding digest and confirm_digest actions is appropriate.
  • [ ] app/models/upload.rb -- ISSUE: ticket says "add has_many :work_queue_items through digest" but there is no digest join model defined anywhere in the issue. The Upload model currently has only has_one_attached :photo and no associations. The relationship between Upload and WorkQueueItems needs clarification -- is it a direct has_many or through a join table? [BODY]
  • [x] config/routes.rb -- verified exists. Currently resources :uploads, only: [:index, :show, :create, :edit, :update, :destroy]. Adding member routes for digest/confirm_digest is correct.
  • [x] app/views/uploads/show.html.erb -- verified exists. Adding "Digest Schedule" button is straightforward.
Do-not-touch files:
  • [x] app/models/property.rb -- confirmed, no schema changes needed. Property already has validates :client_name, presence: true.
  • [x] app/controllers/weeks_controller.rb -- confirmed, week view stays unchanged.

Repo Placement

OK -- issue filed on ldraney/landscaping-assistant, all file targets are in this repo. Single-repo change, no cross-repo concerns.

Dependencies

  • [x] Issue #33 (Upload tab) -- done, in done column on board. Upload model and controller exist and work.
  • [x] WorkQueueItem model -- exists with work_date, property_id (unique per date), position fields.
  • [x] Property model -- exists with client_name field for fuzzy matching.
  • [x] Anthropic API key -- confirmed at ~/secrets/anthropic/credentials.env, key present.
  • No blocking items found on the board. No items in in_progress that conflict.

Acceptance Criteria

8 acceptance criteria. All are testable by an agent:
  • AC1-3: Vision API call and response parsing -- testable via mocked API response in unit tests
  • AC4: Fuzzy matching -- testable with known property fixtures
  • AC5: Confirmation screen -- testable via system/request spec checking HTML response
  • AC6: WorkQueueItem creation -- testable via request spec with confirm action
  • AC7: No new Property records -- testable via assertion on Property.count
  • AC8: Duplicate detection -- testable via uniqueness constraint (already exists: validates :property_id, uniqueness: { scope: :work_date })
Missing criteria: No AC for error handling when Claude Vision API is unavailable or returns malformed JSON. Should add resilience AC.

Blast Radius

  • WorkQueueItem uniqueness constraint already exists (index_work_queue_items_on_work_date_and_property_id, unique: true) -- AC8 duplicate detection is already enforced at DB level. The service should handle ActiveRecord::RecordNotUnique gracefully.
  • Upload model gains a new field (week_start) and potentially new associations -- existing upload tests (if any) should still pass.
  • No existing fuzzy matching patterns in the codebase -- this is net-new functionality.
  • Adding the anthropic gem adds an external API dependency. No other external AI/ML gems exist in the project currently.

Decomposition Assessment

File count: 3 new + 5 modified = 8 files in 1 repo. AC count: 8 acceptance criteria + 4 test expectations. This is a substantial ticket but all within one repo and one logical feature. The service object pattern (ScheduleDigester) encapsulates the core complexity. Estimated agent work: ~8-10 minutes due to Vision API integration, fuzzy matching implementation, and confirmation UI.
Borderline on decomposition. The ticket could be split into: (1) ScheduleDigester service + tests, (2) controller actions + views + routes. However, the two halves are tightly coupled (the view renders ScheduleDigester output). Recommending single pass with careful AC ordering, but flagging for awareness.

Recommendations

  • [BODY] Clarify the Upload-to-WorkQueueItem relationship in File Targets. The issue says "has_many :work_queue_items through digest" but no digest join model is defined. Either: (a) define a direct association with an upload_id foreign key on work_queue_items, or (b) make the relationship implicit (ScheduleDigester creates WorkQueueItems but Upload has no ActiveRecord association to them). Option (b) is simpler and matches the "digestion" metaphor -- the upload is consumed, not permanently linked.
  • [BODY] Add an acceptance criterion for error handling: "When Claude Vision API returns an error or malformed response, the user sees a clear error message and no WorkQueueItems are created."
  • [SCOPE] Update the schedule-upload user story on project-landscaping-assistant to reflect AI-powered parsing. Current summary: "Snap a photo of the paper schedule for later reference." Proposed: "Photograph the paper schedule and parse it into daily work queues via AI vision."
  • [SCOPE] Create architecture note arch-rails-app for the Rails application component.