https://github.com/ThriveMarket/autoship-service/pull/14 Review those specs and consider how autoship works in this repository. What seems to be missing?
⏺ mcp__github__pull_request_read(...) ⏺ mcp__github__pull_request_read(...) ⏺ Glob(dags/autoship/**/*.py) ⎿ /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_single_email_process.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_prefill_recs_master_cart.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_slow_queries.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_schedule_single_cart_process_items.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_anomaly_detection.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_daily_snapshot.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_newaddress.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_process_schedule.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_schedule.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_schedule_prefill.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_single_cart.py /Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_schedule_process_trigger.py
⎿ [{'type': 'text', 'text': '[{"sha":"c454a8188d53935d3c2bad0f17713413bc90ef50","filename":"openspec/project.md","additions":76,"deletions":0,"changes":76,"status":"added","patch":"@@ -0,0 +1,76 @@\n+# Project Context\n+\n+## Purpose\n+\n+Autoship Service manages Thrive Market's subscription-based recurring order\n+system. It enables members to automatically receive products at configurable\n+intervals with per-item frequency control.\n+\n+## Tech Stack\n+\n+- Python (primary language)\n+- Temporal (workflow orchestration)\n+- PostgreSQL (data persistence)\n+- Integration with Magento 1 (Thrive_Autoship module) during migration\n+\n+## Project Conventions\n+\n+### Code Style\n+\n+- Follow PEP 8 for Python code\n+- Use type hints throughout\n+- Prefer explicit over implicit\n+\n+### Architecture Patterns\n+\n+- Domain-driven design with clear bounded contexts\n+- Temporal workflows for long-running processes\n+- Event-driven integration with external systems\n+\n+### Testing Strategy\n+\n+- Unit tests for business logic\n+- Integration tests for Temporal workflows\n+- Contract tests for API boundaries\n+\n+### Git Workflow\n+\n+- Feature branches from main\n+- PR required for all changes\n+- Squash merge to main\n+\n+## Domain Context\n+\n+### Key Concepts\n+\n+- Subscription: A member's autoship enrollment (active, paused, or canceled)\n+- Item: A product within a subscription with quantity and delivery frequency\n+- Schedule: An upcoming delivery with items, dates, and totals\n+- AROPE : Asynchronous Recurring Order Processing Engine - converts\n+ schedules to orders\n+\n+### Business Rules\n+\n+- Per-item frequency: Items can have different delivery cadences (2, 4, 6, 8\n+ weeks)\n+- Cutoff time: Schedules lock for modification 24 hours before processing\n+- Payment retry: 3 attempts before pausing subscription\n+- Membership required: Active Thrive Market membership required for autoship\n+\n+## Important Constraints\n+\n+- Must maintain compatibility with existing Magento module during migration\n+- Payment processing via Braintree vault\n+- Notifications via Sailthru (email) and Attentive (SMS)\n+\n+## External Dependencies\n+\n+- Magento/MOA API: Current autoship implementation\n+- Braintree: Payment processing and vault\n+- Sailthru: Email notifications\n+- Attentive: SMS notifications\n+- Customer Score Service: ML-powered product recommendations\n+\n+## Pod Ownership\n+\n+Habit Formation - Autoship / recurring orders","blob_url":"https://github.com/ThriveMarket/autoship-service/blob/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fproject.md","raw_url":"https://github.com/ThriveMarket/autoship-service/raw/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fproject.md","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/openspec%2Fproject.md?ref=f6e462e8e297f75b00370bef1e612b6a9331fb27"},{"sha":"1274338098209f447d204a264eb30aae392cd507","filename":"openspec/specs/item-management/spec.md","additions":161,"deletions":0,"changes":161,"status":"added","patch":"@@ -0,0 +1,161 @@\n+# Item Management\n+\n+## Purpose\n+\n+Management of products within an autoship subscription. Handles adding,\n+updating, and removing items with per-item frequency control and price locking.\n+\n+## Requirements\n+\n+### Requirement: Add Item to Subscription\n+\n+The system SHALL allow members to add products to their autoship subscription.\n+\n+#### Scenario: Add single item\n+\n+- WHEN a member adds a product to their autoship\n+- THEN the item is created with ACTIVE status\n+- AND the item is assigned to the next eligible schedule\n+- AND if in post-prefill state, the item is added to the member's cart\n+\n+#### Scenario: Add item with custom frequency\n+\n+- WHEN a member adds a product with a specific delivery frequency\n+- THEN the item uses the specified frequency (1, 2, 4, 8, or 12 weeks)\n+- AND the item may appear on different schedules than other items\n+\n+#### Scenario: Duplicate product prevention\n+\n+- WHEN a member attempts to add a product already in their subscription\n+- THEN the system rejects the duplicate addition\n+\n+### Requirement: Default Product Frequency\n+\n+The system SHALL use Data Science-provided default frequencies for products.\n+\n+#### Scenario: Default frequency from Data Science\n+\n+- WHEN a product is added without a specified frequency\n+- THEN the system uses the default frequency provided by Data Science\n+- AND the default is based on optimal replenishment cadence\n+\n+#### Scenario: Goal of default frequency\n+\n+- WHEN the default frequency is applied\n+- THEN the frequency is set so the member \"never runs out\" of the product\n+\n+### Requirement: Update Item\n+\n+The system SHALL allow members to update item quantity and frequency.\n+\n+#### Scenario: Update quantity\n+\n+- WHEN a member updates an item's quantity\n+- THEN the new quantity applies to the next schedule\n+\n+#### Scenario: Update delivery frequency\n+\n+- WHEN a member changes an item's delivery frequency\n+- THEN the item's schedule assignment is recalculated\n+\n+### Requirement: Remove Item\n+\n+The system SHALL allow members to remove items from their subscription.\n+\n+#### Scenario: Member removes item\n+\n+- WHEN a member removes an item from their autoship\n+- THEN the item status changes to CANCELED\n+- AND the item is removed from upcoming schedules\n+\n+### Requirement: Item Status Management\n+\n+The system SHALL maintain item status to track lifecycle.\n+\n+#### Scenario: Active items\n+\n+- WHEN an item is ACTIVE\n+- THEN it is included in schedule generation\n+\n+#### Scenario: Discontinued items\n+\n+- WHEN a product is discontinued\n+- THEN the item status changes to DISCONTINUED\n+- AND the member is notified\n+- AND alternative products are recommended\n+\n+#### Scenario: Discontinued item reactivation\n+\n+- WHEN a discontinued product becomes available again\n+- THEN the item can be reactivated to DISCONTINUED_ACTIVE_AGAIN status\n+\n+### Requirement: Per-Item Frequency Control\n+\n+The system SHALL support different delivery frequencies per item within a single\n+subscription.\n+\n+#### Scenario: Mixed frequencies\n+\n+- WHEN a subscription contains items with different frequencies\n+- THEN each item appears on schedules according to its own frequency\n+- AND a single schedule may contain items with different frequencies\n+\n+#### Scenario: Per-item frequency within Account Delivery Frequency\n+\n+- WHEN a cart prefill event occurs based on Account Delivery Frequency\n+- THEN per-item frequency determines which subscribed items are included\n+- AND items with longer per-item frequencies may skip some prefill events\n+\n+#### Scenario: Valid frequencies\n+\n+- WHEN a member sets an item's delivery frequency\n+- THEN the frequency must be one of: 1, 2, 4, 8, or 12 weeks\n+\n+### Requirement: Price Locking\n+\n+The system SHALL lock item prices at the time the \"Order Processing Soon\"\n+notification is sent.\n+\n+#### Scenario: Price locked on notification\n+\n+- WHEN the \"Order Processing Soon\" email is sent for a schedule\n+- THEN the current catalog price for each schedule item is locked\n+- AND the locked price is stored in the schedule item record\n+\n+#### Scenario: Locked price used in order\n+\n+- WHEN AROPE processes the schedule\n+- THEN items use their locked_price for the order\n+- AND this price may differ from the current catalog price\n+\n+#### Scenario: Locked price displayed to member\n+\n+- WHEN a member views their upcoming schedule after price lock\n+- THEN the locked prices are displayed\n+- AND these prices are what was promised in the notification email\n+\n+### Requirement: Weather-Dependent Item Handling\n+\n+The system SHALL handle items that cannot be shipped due to weather conditions.\n+\n+#### Scenario: Weather restriction detected\n+\n+- WHEN an item cannot be shipped due to weather\n+- THEN the member is notified\n+- AND non-weather-dependent alternatives are suggested\n+\n+#### Scenario: Weather restriction lifted\n+\n+- WHEN weather conditions improve\n+- THEN the item is automatically restored to future shipments\n+\n+### Requirement: Non-Autoshippable Items\n+\n+The system SHALL allow non-autoshippable items in subscription orders as\n+one-time purchases.\n+\n+#### Scenario: One-time item in subscription order\n+\n+- WHEN a non-autoshippable item is added to a subscription order\n+- THEN the item ships once\n+- AND the item is NOT added to the recurring subscription","blob_url":"https://github.com/ThriveMarket/autoship-service/blob/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Fitem-management%2Fspec.md","raw_url":"https://github.com/ThriveMarket/autoship-service/raw/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Fitem-management%2Fspec.md","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/openspec%2Fspecs%2Fitem-management%2Fspec.md?ref=f6e462e8e297f75b00370bef1e612b6a9331fb27"},{"sha":"7f76a4c5fec44984b649332529755b925534cbb7","filename":"openspec/specs/order-processing/spec.md","additions":219,"deletions":0,"changes":219,"status":"added","patch":"@@ -0,0 +1,219 @@\n+# Order Processing\n+\n+## Purpose\n+\n+AROPE (Asynchronous Recurring Order Processing Engine) - the system that\n+converts scheduled deliveries into orders. Handles validation, payment\n+processing, and order creation.\n+\n+## Requirements\n+\n+### Requirement: Scheduled Processing\n+\n+The system SHALL automatically process eligible schedules via cron job.\n+\n+#### Scenario: Cron-based processing\n+\n+- WHEN the AROPE cron runs (every 2 hours at the 30th minute)\n+- THEN all schedules where scheduled_at \u003c= NOW and status = ACTIVE are\n+ processed\n+\n+#### Scenario: Processing limit per run\n+\n+- WHEN processing runs\n+- THEN up to 1000 schedules are processed per run\n+- AND the system can handle approximately 12,000 orders per day\n+\n+#### Scenario: Isolated schedule processing\n+\n+- WHEN a schedule is processed\n+- THEN it executes in an isolated sub-process\n+- AND its failure does not affect the next schedule\n+\n+### Requirement: Processing Stop Conditions\n+\n+The system SHALL stop processing when error thresholds are exceeded.\n+\n+#### Scenario: Consecutive failure limit\n+\n+- WHEN 15 consecutive schedules fail to process\n+- THEN further processing is stopped\n+- AND an alert email is sent to system administrators\n+\n+#### Scenario: Percentage failure limit\n+\n+- WHEN more than 10% of planned schedules fail\n+- THEN further processing is stopped\n+- AND an alert email is sent to system administrators\n+\n+### Requirement: Order Validation Pipeline\n+\n+The system SHALL validate schedules before creating orders.\n+\n+#### Scenario: Customer validation\n+\n+- WHEN a schedule is processed\n+- THEN the system verifies:\n+ - Valid billing address\n+ - Valid shipping address\n+ - Valid default credit card\n+\n+#### Scenario: Item validation\n+\n+- WHEN a schedule is processed\n+- THEN the system verifies each item:\n+ - Product is enabled\n+ - Product is in stock\n+ - Product is simple type\n+ - Product is not an IcePack\n+ - Product is not a GWP\n+ - Product is not a Membership\n+\n+#### Scenario: Failed item handling\n+\n+- WHEN an item fails validation\n+- THEN the item is skipped (status = SKIPPED)\n+- AND schedule processing continues with remaining items\n+\n+#### Scenario: Schedule validation\n+\n+- WHEN all items have been validated\n+- THEN the system verifies the schedule still has active items\n+- AND if no active items remain, the schedule is canceled\n+\n+#### Scenario: Customer validation failure\n+\n+- WHEN customer validation fails\n+- THEN the schedule enters failed charge processing\n+- AND processing stops for this schedule\n+\n+### Requirement: Quote Building\n+\n+The system SHALL build a quote from the validated schedule.\n+\n+#### Scenario: Quote creation\n+\n+- WHEN validation passes\n+- THEN a quote is built with:\n+ - Customer billing and shipping address\n+ - Shipping method\n+ - Products list with locked prices\n+ - Credit card information\n+\n+#### Scenario: Locked price application\n+\n+- WHEN items are added to the quote\n+- THEN the quote item custom_price is set to the schedule item locked_price\n+\n+#### Scenario: Promotions applied\n+\n+- WHEN the quote is built\n+- THEN active shopping cart rules are applied\n+- AND active coupon codes are applied\n+- AND eligible GWP promotions are added\n+\n+### Requirement: Order Creation\n+\n+The system SHALL create orders from validated quotes.\n+\n+#### Scenario: Order from quote\n+\n+- WHEN a quote is valid\n+- THEN an order is created from the quote\n+\n+#### Scenario: Order linkage\n+\n+- WHEN an order is created\n+- THEN the schedule is updated with:\n+ - Order ID\n+ - Quote ID\n+ - Tax amount\n+ - Shipping amount\n+ - Subtotal\n+ - Grand total\n+\n+### Requirement: Payment Processing\n+\n+The system SHALL charge the member's stored payment method.\n+\n+#### Scenario: Successful payment\n+\n+- WHEN payment is successfully charged\n+- THEN the schedule status changes to PROCESSING\n+- AND charge timestamp is recorded\n+\n+#### Scenario: Payment failure with retry\n+\n+- WHEN payment fails on attempt 1\n+- THEN schedule status changes to FAILED_CHARGE_1\n+- AND next attempt is scheduled for +1 day\n+\n+#### Scenario: Second payment failure\n+\n+- WHEN payment fails on attempt 2\n+- THEN schedule status changes to FAILED_CHARGE_2\n+- AND next attempt is scheduled for +2 days\n+\n+#### Scenario: Third payment failure (final)\n+\n+- WHEN payment fails on attempt 3\n+- THEN schedule status changes to FAILED_CHARGE_3\n+- AND the schedule is canceled\n+- AND the subscription is paused with CC_FAILED reason\n+- AND the member is notified to update payment information\n+\n+### Requirement: Post-Order Actions\n+\n+The system SHALL perform follow-up actions after successful order creation.\n+\n+#### Scenario: Order confirmation\n+\n+- WHEN an order is successfully placed\n+- THEN the member receives an order confirmation email\n+\n+#### Scenario: Next schedule generation\n+\n+- WHEN an order is successfully placed\n+- THEN the next delivery schedule is generated\n+\n+#### Scenario: Schedule status update\n+\n+- WHEN an order is successfully placed\n+- THEN the schedule status changes to PROCESSING\n+\n+### Requirement: Event Logging\n+\n+The system SHALL log events throughout the processing pipeline.\n+\n+#### Scenario: Processing events logged\n+\n+- WHEN a schedule is processed\n+- THEN events are logged to:\n+ - File: autoship-schedule-events.log\n+ - Database: Admin Panel → Sales → Autoship → Schedule Events\n+ - Email: system administrators (for critical errors)\n+\n+#### Scenario: Error severity levels\n+\n+- WHEN errors occur\n+- THEN they are logged with appropriate severity:\n+ - INFO: General messages\n+ - WARNING: Skipped items, canceled schedules\n+ - ERROR: Payment failures, quote/order creation failures\n+ - CRITICAL: System errors requiring admin attention\n+\n+### Requirement: Event Observers\n+\n+The system SHALL dispatch events before and after schedule processing.\n+\n+#### Scenario: Before processing event\n+\n+- WHEN AROPE starts processing a schedule\n+- THEN the autoship_schedule_process_before event is dispatched\n+- AND external modules can hook into this event\n+\n+#### Scenario: After processing event\n+\n+- WHEN AROPE completes processing a schedule\n+- THEN the autoship_schedule_process_after event is dispatched\n+- AND the event includes schedule, customer, quote, and order objects","blob_url":"https://github.com/ThriveMarket/autoship-service/blob/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Forder-processing%2Fspec.md","raw_url":"https://github.com/ThriveMarket/autoship-service/raw/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Forder-processing%2Fspec.md","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/openspec%2Fspecs%2Forder-processing%2Fspec.md?ref=f6e462e8e297f75b00370bef1e612b6a9331fb27"},{"sha":"ed71284bb77c8b4c75b6cae3e423ff48ace71acb","filename":"openspec/specs/schedule-management/spec.md","additions":189,"deletions":0,"changes":189,"status":"added","patch":"@@ -0,0 +1,189 @@\n+# Schedule Management\n+\n+## Purpose\n+\n+Generation and management of delivery schedules. Handles schedule creation,\n+modification, notifications, and cart prefill for upcoming deliveries.\n+\n+Smart recommendations for new products come from the Repeat Order Quiz (ROQ),\n+where a model determines products a member may want, categories are presented,\n+and selected items are added to the cart. This replaced the deprecated Repeat\n+Order Prefill (ROP) program which was shuttered due to increased member\n+cancellations.\n+\n+## Requirements\n+\n+### Requirement: Schedule Generation\n+\n+The system SHALL automatically generate delivery schedules based on item\n+frequencies.\n+\n+#### Scenario: Initial schedule creation\n+\n+- WHEN a subscription is activated\n+- THEN the first schedule is generated for the configured initial delay\n+ (default 3-7 days)\n+\n+#### Scenario: Subsequent schedule generation\n+\n+- WHEN an order is successfully placed\n+- THEN the next schedule is generated based on item frequencies\n+\n+### Requirement: Schedule States\n+\n+The system SHALL maintain schedule status throughout the order lifecycle.\n+\n+#### Scenario: Active schedule\n+\n+- WHEN a schedule is ACTIVE\n+- THEN it can be modified by the member\n+\n+#### Scenario: Processing schedule\n+\n+- WHEN a schedule enters PROCESSING status\n+- THEN it is being processed by AROPE and cannot be modified\n+\n+#### Scenario: Shipping schedule\n+\n+- WHEN a schedule is in SHIPPING status\n+- THEN the order has been placed and is awaiting shipment\n+\n+#### Scenario: Completed schedule\n+\n+- WHEN a schedule is COMPLETED\n+- THEN the order has been fulfilled\n+\n+#### Scenario: Failed charge states\n+\n+- WHEN payment fails\n+- THEN the schedule status reflects the failure attempt count\n+ (FAILED_CHARGE_1, FAILED_CHARGE_2, FAILED_CHARGE_3)\n+\n+### Requirement: Schedule Modification\n+\n+The system SHALL allow members to modify active schedules before the cutoff\n+time.\n+\n+#### Scenario: Move delivery date\n+\n+- WHEN a member requests to change the delivery date\n+- AND the schedule is still modifiable\n+- THEN the scheduled_at date is updated\n+\n+#### Scenario: Skip schedule\n+\n+- WHEN a member skips an upcoming schedule\n+- THEN the schedule is canceled\n+- AND the next schedule is generated\n+\n+#### Scenario: Cutoff enforcement\n+\n+- WHEN a member attempts to modify a schedule past the cutoff time\n+- THEN the modification is rejected\n+\n+### Requirement: Schedule Item Management\n+\n+The system SHALL allow members to modify items within an active schedule.\n+\n+#### Scenario: Add item to schedule\n+\n+- WHEN a member adds an item to an active schedule\n+- THEN the item is included in that schedule's order\n+\n+#### Scenario: Remove item from schedule\n+\n+- WHEN a member removes an item from an active schedule\n+- THEN the item is excluded from that schedule only (not removed from\n+ subscription)\n+\n+#### Scenario: Update item quantity in schedule\n+\n+- WHEN a member updates quantity for an item in a schedule\n+- THEN the quantity change applies to that schedule only\n+\n+#### Scenario: Skip item in schedule\n+\n+- WHEN a member skips an item in an upcoming schedule\n+- THEN the item is excluded from that schedule\n+- AND the item is rescheduled for the following order\n+\n+### Requirement: Schedule Notifications\n+\n+The system SHALL send notifications before schedules are processed.\n+\n+#### Scenario: Three-day review notification\n+\n+- WHEN a schedule is 3 days before processing\n+- THEN the member receives a \"Review Upcoming Order\" notification\n+\n+#### Scenario: One-day reminder\n+\n+- WHEN a schedule is 1 day (24 hours) before processing\n+- THEN the member receives a final reminder notification\n+\n+#### Scenario: Notification resend on date change\n+\n+- WHEN the schedule date is changed after the 3-day email\n+- AND the new date is 7+ days from the original\n+- THEN the 3-day review email is resent\n+\n+#### Scenario: Skip notifications for immediate ship\n+\n+- WHEN a member chooses to ship immediately\n+- THEN pending review notifications are skipped\n+\n+### Requirement: Cart Prefill\n+\n+The system SHALL prefill the member's cart with scheduled items 4 days before\n+processing, based on the member's Account Delivery Frequency.\n+\n+#### Scenario: Automatic prefill driven by Account Delivery Frequency\n+\n+- WHEN a prefill event occurs according to Account Delivery Frequency\n+- THEN only items whose per-item frequency makes them due are included\n+- AND the member's cart is prefilled 4 days before the processing date\n+\n+#### Scenario: Automatic prefill at 4 days\n+\n+- WHEN a schedule is 4 days before the processing date\n+- THEN the member's cart is prefilled with schedule items\n+- AND the member can add/remove items for the upcoming schedule\n+\n+#### Scenario: Manual prefill\n+\n+- WHEN a member requests to prefill their cart\n+- THEN the next schedule's items are loaded into the cart\n+\n+#### Scenario: Prefill enables \"Box\" management\n+\n+- WHEN the cart is prefilled\n+- THEN the member can manage their upcoming \"Box\" using the cart interface\n+\n+### Requirement: Schedule Totals Calculation\n+\n+The system SHALL calculate and display accurate totals for schedules.\n+\n+#### Scenario: Totals include all components\n+\n+- WHEN schedule totals are calculated\n+- THEN the totals include subtotal, shipping, tax, and grand total\n+\n+#### Scenario: Totals recalculation\n+\n+- WHEN a schedule is modified\n+- THEN totals are recalculated to reflect changes\n+\n+### Requirement: GWP and Sample Selection\n+\n+The system SHALL allow members to select GWPs and samples for subscription\n+orders.\n+\n+#### Scenario: GWP selection on review\n+\n+- WHEN a member receives the \"Review Upcoming Order\" notification\n+- THEN the member can select their GWP or sample\n+\n+#### Scenario: GWP added to schedule\n+\n+- WHEN a GWP or sample is selected\n+- THEN it is added only to the upcoming subscription order","blob_url":"https://github.com/ThriveMarket/autoship-service/blob/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Fschedule-management%2Fspec.md","raw_url":"https://github.com/ThriveMarket/autoship-service/raw/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Fschedule-management%2Fspec.md","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/openspec%2Fspecs%2Fschedule-management%2Fspec.md?ref=f6e462e8e297f75b00370bef1e612b6a9331fb27"},{"sha":"fec9dae03e990b1dfca66e5d018830894a8ca231","filename":"openspec/specs/subscription-management/spec.md","additions":202,"deletions":0,"changes":202,"status":"added","patch":"@@ -0,0 +1,202 @@\n+# Subscription Management\n+\n+## Purpose\n+\n+Core lifecycle management for member autoship subscriptions. Handles activation,\n+pause, resume, and cancellation of recurring delivery subscriptions.\n+\n+## Requirements\n+\n+### Requirement: Automatic Enrollment\n+\n+The system SHALL automatically enroll members in autoship when they join Thrive\n+Market.\n+\n+#### Scenario: New member enrollment\n+\n+- WHEN a member joins Thrive Market\n+- THEN the member is automatically enrolled in autoship\n+- AND the subscription status is set to INACTIVE until first order\n+\n+#### Scenario: First order with autoshippable items\n+\n+- WHEN an enrolled member places their first order with autoshippable items\n+- THEN the subscription status changes to ACTIVE\n+- AND all autoshippable items from the first order are subscribed\n+ automatically\n+\n+### Requirement: Account Delivery Frequency\n+\n+The system SHALL maintain an account-level delivery frequency that dictates how\n+frequently a cart will be prefilled with subscribed items.\n+\n+#### Scenario: Account frequency drives prefill timing\n+\n+- WHEN a member has an Account Delivery Frequency set\n+- THEN the member's cart is prefilled at intervals matching that frequency\n+- AND the cart prefill event occurs independent of per-item frequency\n+\n+#### Scenario: Per-item frequency within account frequency\n+\n+- WHEN a cart prefill event occurs based on Account Delivery Frequency\n+- THEN only items whose per-item frequency makes them due are included\n+- AND items with longer per-item frequencies may skip some prefill events\n+\n+### Requirement: Autoship Operating Modes\n+\n+The system SHALL support two distinct operating modes based on whether a member\n+has active item subscriptions.\n+\n+#### Scenario: Autoship+Autofill mode\n+\n+- WHEN a member has at least one active subscribed item\n+- THEN the member's cart is automatically prefilled at scheduled intervals\n+- AND upcoming orders are processed automatically with prefilled items\n+\n+#### Scenario: Autoship-only mode\n+\n+- WHEN a member has zero active subscribed items\n+- THEN the member's cart is NOT prefilled automatically\n+- AND if the member manually adds items to their cart before a scheduled\n+ processing date, that cart will be processed automatically\n+\n+#### Scenario: Autoship-only edge case behavior\n+\n+- WHEN a member is in Autoship-only mode\n+- AND the member has items in their cart at a scheduled processing time\n+- THEN the system processes the order using whatever is in the cart\n+- AND this allows members to maintain autoship without recurring items\n+\n+### Requirement: Subscription States\n+\n+The system SHALL maintain subscription state as one of: INACTIVE, ACTIVE,\n+PAUSED, or CANCELED.\n+\n+#### Scenario: Inactive state meaning\n+\n+- WHEN a member has subscription status INACTIVE\n+- THEN the member is enrolled but has not yet activated autoship\n+- AND the member has not placed their first order with autoshippable items\n+\n+#### Scenario: Active state\n+\n+- WHEN a subscription is ACTIVE\n+- THEN the member has at least one active item or schedule\n+- AND schedules are being generated\n+\n+#### Scenario: State transitions from Inactive\n+\n+- WHEN a subscription is INACTIVE\n+- THEN it can transition to ACTIVE (on opt-in or first order)\n+\n+#### Scenario: State transitions from Active\n+\n+- WHEN a subscription is ACTIVE\n+- THEN it can transition to PAUSED or CANCELED\n+\n+#### Scenario: Paused state transitions\n+\n+- WHEN a subscription is PAUSED\n+- THEN it can transition to ACTIVE (resume) or CANCELED\n+\n+#### Scenario: Canceled is terminal\n+\n+- WHEN a subscription is CANCELED\n+- THEN the member sees reactivation messaging\n+- AND reactivation creates a new active subscription\n+\n+### Requirement: Subscription Activation\n+\n+The system SHALL activate subscriptions when a member places their first order\n+with autoshippable items.\n+\n+#### Scenario: First order activation\n+\n+- WHEN a member places their first order\n+- AND the order contains at least one autoshippable item\n+- THEN the subscription status changes from INACTIVE to ACTIVE\n+- AND all autoshippable items from the first order are subscribed\n+ automatically\n+\n+#### Scenario: First order with no autoshippable items\n+\n+- WHEN a member places an order with zero autoshippable items\n+- THEN the subscription status remains INACTIVE\n+\n+### Requirement: Subscription Pause\n+\n+The system SHALL allow autoship subscriptions to be paused with a reason code.\n+\n+#### Scenario: Manual pause by member\n+\n+- WHEN a member requests to pause their autoship\n+- THEN the subscription status changes to PAUSED\n+- AND upcoming schedules are paused\n+\n+#### Scenario: Automatic pause on payment failure\n+\n+- WHEN payment fails 3 times for a scheduled order\n+- THEN the subscription is paused with reason CC_FAILED\n+\n+#### Scenario: Automatic pause on membership cancellation\n+\n+- WHEN a member cancels their Thrive Market membership\n+- THEN the autoship subscription is paused with reason UNSUBSCRIPTION\n+\n+### Requirement: Subscription Resume\n+\n+The system SHALL allow paused subscriptions to be resumed.\n+\n+#### Scenario: Resume after payment update\n+\n+- WHEN a member updates their payment method\n+- AND the subscription was paused due to CC_FAILED\n+- THEN the subscription automatically resumes\n+\n+#### Scenario: Resume after membership renewal\n+\n+- WHEN a member renews their Thrive Market membership\n+- AND the subscription was paused due to UNSUBSCRIPTION\n+- THEN the subscription automatically resumes\n+\n+### Requirement: Subscription Cancellation\n+\n+The system SHALL allow members to permanently cancel their autoship\n+subscription.\n+\n+#### Scenario: Member cancels subscription\n+\n+- WHEN a member requests to cancel their autoship\n+- THEN the subscription status changes to CANCELED\n+- AND all upcoming schedules are canceled\n+\n+### Requirement: Pause Reason Tracking\n+\n+The system SHALL track the reason for subscription pauses.\n+\n+#### Scenario: Pause reasons recorded\n+\n+- WHEN a subscription is paused\n+- THEN the pause reason is recorded as one of:\n+ - CC_FAILED (payment method declined)\n+ - UNSUBSCRIPTION (membership canceled)\n+ - SHIPPING_ADDRESS (invalid shipping address)\n+ - BACKOFFICE (admin action)\n+ - MIGRATION (system migration)\n+\n+### Requirement: Autoship TPR Pricing\n+\n+The system SHALL apply autoship-specific pricing based on subscription status\n+and item enrollment.\n+\n+#### Scenario: TPR applied to subscribed items\n+\n+- WHEN a member is subscribed to an item\n+- AND an Autoship TPR exists for that item\n+- THEN the Autoship TPR discount is applied regardless of order type\n+\n+#### Scenario: TPR not applied to non-subscribed items\n+\n+- WHEN a member is NOT subscribed to an item\n+- THEN Autoship TPR discounts are NOT applied\n+- AND only regular TPRs apply","blob_url":"https://github.com/ThriveMarket/autoship-service/blob/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Fsubscription-management%2Fspec.md","raw_url":"https://github.com/ThriveMarket/autoship-service/raw/f6e462e8e297f75b00370bef1e612b6a9331fb27/openspec%2Fspecs%2Fsubscription-management%2Fspec.md","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/openspec%2Fspecs%2Fsubscription-management%2Fspec.md?ref=f6e462e8e297f75b00370bef1e612b6a9331fb27"}]'}]
⎿ [{'type': 'text', 'text': '{"id":3192511959,"number":14,"state":"open","locked":false,"title":"Add autoship spec","created_at":"2026-01-20T19:30:53Z","updated_at":"2026-01-29T18:08:28Z","user":{"login":"tuyenlethrive","id":218554606,"node_id":"U_kgDODQbg7g","avatar_url":"https://avatars.githubusercontent.com/u/218554606?v=4","html_url":"https://github.com/tuyenlethrive","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/tuyenlethrive","events_url":"https://api.github.com/users/tuyenlethrive/events{/privacy}","following_url":"https://api.github.com/users/tuyenlethrive/following{/other_user}","followers_url":"https://api.github.com/users/tuyenlethrive/followers","gists_url":"https://api.github.com/users/tuyenlethrive/gists{/gist_id}","organizations_url":"https://api.github.com/users/tuyenlethrive/orgs","received_events_url":"https://api.github.com/users/tuyenlethrive/received_events","repos_url":"https://api.github.com/users/tuyenlethrive/repos","starred_url":"https://api.github.com/users/tuyenlethrive/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tuyenlethrive/subscriptions"},"draft":false,"url":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/14","html_url":"https://github.com/ThriveMarket/autoship-service/pull/14","issue_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/14","statuses_url":"https://api.github.com/repos/ThriveMarket/autoship-service/statuses/f6e462e8e297f75b00370bef1e612b6a9331fb27","diff_url":"https://github.com/ThriveMarket/autoship-service/pull/14.diff","patch_url":"https://github.com/ThriveMarket/autoship-service/pull/14.patch","commits_url":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/14/commits","comments_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/14/comments","review_comments_url":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/14/comments","review_comment_url":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/comments{/number}","author_association":"MEMBER","node_id":"PR_kwDOQufPCM6-Sd3X","auto_merge":{"enabled_by":{"login":"justinabrahms","id":3853,"node_id":"MDQ6VXNlcjM4NTM=","avatar_url":"https://avatars.githubusercontent.com/u/3853?v=4","html_url":"https://github.com/justinabrahms","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/justinabrahms","events_url":"https://api.github.com/users/justinabrahms/events{/privacy}","following_url":"https://api.github.com/users/justinabrahms/following{/other_user}","followers_url":"https://api.github.com/users/justinabrahms/followers","gists_url":"https://api.github.com/users/justinabrahms/gists{/gist_id}","organizations_url":"https://api.github.com/users/justinabrahms/orgs","received_events_url":"https://api.github.com/users/justinabrahms/received_events","repos_url":"https://api.github.com/users/justinabrahms/repos","starred_url":"https://api.github.com/users/justinabrahms/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/justinabrahms/subscriptions"},"merge_method":"merge","commit_title":"Merge pull request #14 from ThriveMarket/add-autoship-spec","commit_message":"Add autoship spec"},"merged":false,"mergeable":true,"mergeable_state":"blocked","rebaseable":true,"merge_commit_sha":"6f31c7c6e364124b0af74e0eca2f2c0dcc2613e0","comments":0,"commits":6,"additions":847,"deletions":0,"changed_files":5,"maintainer_can_modify":false,"review_comments":4,"_links":{"self":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/14"},"html":{"href":"https://github.com/ThriveMarket/autoship-service/pull/14"},"issue":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/14"},"comments":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/14/comments"},"review_comments":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/14/comments"},"review_comment":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls/14/commits"},"statuses":{"href":"https://api.github.com/repos/ThriveMarket/autoship-service/statuses/f6e462e8e297f75b00370bef1e612b6a9331fb27"}},"head":{"label":"ThriveMarket:add-autoship-spec","ref":"add-autoship-spec","sha":"f6e462e8e297f75b00370bef1e612b6a9331fb27","repo":{"id":1122488072,"node_id":"R_kgDOQufPCA","owner":{"login":"ThriveMarket","id":8164832,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxNjQ4MzI=","avatar_url":"https://avatars.githubusercontent.com/u/8164832?v=4","html_url":"https://github.com/ThriveMarket","gravatar_id":"","type":"Organization","site_admin":false,"url":"https://api.github.com/users/ThriveMarket","events_url":"https://api.github.com/users/ThriveMarket/events{/privacy}","following_url":"https://api.github.com/users/ThriveMarket/following{/other_user}","followers_url":"https://api.github.com/users/ThriveMarket/followers","gists_url":"https://api.github.com/users/ThriveMarket/gists{/gist_id}","organizations_url":"https://api.github.com/users/ThriveMarket/orgs","received_events_url":"https://api.github.com/users/ThriveMarket/received_events","repos_url":"https://api.github.com/users/ThriveMarket/repos","starred_url":"https://api.github.com/users/ThriveMarket/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThriveMarket/subscriptions"},"name":"autoship-service","full_name":"ThriveMarket/autoship-service","description":"This is autoship-service","default_branch":"main","created_at":"2025-12-24T21:02:06Z","pushed_at":"2026-01-29T18:08:27Z","updated_at":"2026-01-28T13:24:19Z","html_url":"https://github.com/ThriveMarket/autoship-service","clone_url":"https://github.com/ThriveMarket/autoship-service.git","git_url":"git://github.com/ThriveMarket/autoship-service.git","ssh_url":"[email protected]:ThriveMarket/autoship-service.git","svn_url":"https://github.com/ThriveMarket/autoship-service","language":"Python","fork":false,"forks_count":0,"open_issues_count":5,"open_issues":5,"stargazers_count":0,"watchers_count":0,"watchers":0,"size":145,"allow_forking":false,"web_commit_signoff_required":false,"archived":false,"disabled":false,"private":true,"has_issues":true,"has_wiki":true,"has_pages":false,"has_projects":false,"has_downloads":true,"has_discussions":false,"is_template":false,"url":"https://api.github.com/repos/ThriveMarket/autoship-service","archive_url":"https://api.github.com/repos/ThriveMarket/autoship-service/{archive_format}{/ref}","assignees_url":"https://api.github.com/repos/ThriveMarket/autoship-service/assignees{/user}","blobs_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/blobs{/sha}","branches_url":"https://api.github.com/repos/ThriveMarket/autoship-service/branches{/branch}","collaborators_url":"https://api.github.com/repos/ThriveMarket/autoship-service/collaborators{/collaborator}","comments_url":"https://api.github.com/repos/ThriveMarket/autoship-service/comments{/number}","commits_url":"https://api.github.com/repos/ThriveMarket/autoship-service/commits{/sha}","compare_url":"https://api.github.com/repos/ThriveMarket/autoship-service/compare/{base}...{head}","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/{+path}","contributors_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contributors","deployments_url":"https://api.github.com/repos/ThriveMarket/autoship-service/deployments","downloads_url":"https://api.github.com/repos/ThriveMarket/autoship-service/downloads","events_url":"https://api.github.com/repos/ThriveMarket/autoship-service/events","forks_url":"https://api.github.com/repos/ThriveMarket/autoship-service/forks","git_commits_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/commits{/sha}","git_refs_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/refs{/sha}","git_tags_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/tags{/sha}","hooks_url":"https://api.github.com/repos/ThriveMarket/autoship-service/hooks","issue_comment_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/comments{/number}","issue_events_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/events{/number}","issues_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues{/number}","keys_url":"https://api.github.com/repos/ThriveMarket/autoship-service/keys{/key_id}","labels_url":"https://api.github.com/repos/ThriveMarket/autoship-service/labels{/name}","languages_url":"https://api.github.com/repos/ThriveMarket/autoship-service/languages","merges_url":"https://api.github.com/repos/ThriveMarket/autoship-service/merges","milestones_url":"https://api.github.com/repos/ThriveMarket/autoship-service/milestones{/number}","notifications_url":"https://api.github.com/repos/ThriveMarket/autoship-service/notifications{?since,all,participating}","pulls_url":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls{/number}","releases_url":"https://api.github.com/repos/ThriveMarket/autoship-service/releases{/id}","stargazers_url":"https://api.github.com/repos/ThriveMarket/autoship-service/stargazers","statuses_url":"https://api.github.com/repos/ThriveMarket/autoship-service/statuses/{sha}","subscribers_url":"https://api.github.com/repos/ThriveMarket/autoship-service/subscribers","subscription_url":"https://api.github.com/repos/ThriveMarket/autoship-service/subscription","tags_url":"https://api.github.com/repos/ThriveMarket/autoship-service/tags","trees_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/trees{/sha}","teams_url":"https://api.github.com/repos/ThriveMarket/autoship-service/teams","visibility":"internal"},"user":{"login":"ThriveMarket","id":8164832,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxNjQ4MzI=","avatar_url":"https://avatars.githubusercontent.com/u/8164832?v=4","html_url":"https://github.com/ThriveMarket","gravatar_id":"","type":"Organization","site_admin":false,"url":"https://api.github.com/users/ThriveMarket","events_url":"https://api.github.com/users/ThriveMarket/events{/privacy}","following_url":"https://api.github.com/users/ThriveMarket/following{/other_user}","followers_url":"https://api.github.com/users/ThriveMarket/followers","gists_url":"https://api.github.com/users/ThriveMarket/gists{/gist_id}","organizations_url":"https://api.github.com/users/ThriveMarket/orgs","received_events_url":"https://api.github.com/users/ThriveMarket/received_events","repos_url":"https://api.github.com/users/ThriveMarket/repos","starred_url":"https://api.github.com/users/ThriveMarket/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThriveMarket/subscriptions"}},"base":{"label":"ThriveMarket:main","ref":"main","sha":"e9c22adeb225edb986823bacc3037b92687920a8","repo":{"id":1122488072,"node_id":"R_kgDOQufPCA","owner":{"login":"ThriveMarket","id":8164832,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxNjQ4MzI=","avatar_url":"https://avatars.githubusercontent.com/u/8164832?v=4","html_url":"https://github.com/ThriveMarket","gravatar_id":"","type":"Organization","site_admin":false,"url":"https://api.github.com/users/ThriveMarket","events_url":"https://api.github.com/users/ThriveMarket/events{/privacy}","following_url":"https://api.github.com/users/ThriveMarket/following{/other_user}","followers_url":"https://api.github.com/users/ThriveMarket/followers","gists_url":"https://api.github.com/users/ThriveMarket/gists{/gist_id}","organizations_url":"https://api.github.com/users/ThriveMarket/orgs","received_events_url":"https://api.github.com/users/ThriveMarket/received_events","repos_url":"https://api.github.com/users/ThriveMarket/repos","starred_url":"https://api.github.com/users/ThriveMarket/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThriveMarket/subscriptions"},"name":"autoship-service","full_name":"ThriveMarket/autoship-service","description":"This is autoship-service","default_branch":"main","created_at":"2025-12-24T21:02:06Z","pushed_at":"2026-01-29T18:08:27Z","updated_at":"2026-01-28T13:24:19Z","html_url":"https://github.com/ThriveMarket/autoship-service","clone_url":"https://github.com/ThriveMarket/autoship-service.git","git_url":"git://github.com/ThriveMarket/autoship-service.git","ssh_url":"[email protected]:ThriveMarket/autoship-service.git","svn_url":"https://github.com/ThriveMarket/autoship-service","language":"Python","fork":false,"forks_count":0,"open_issues_count":5,"open_issues":5,"stargazers_count":0,"watchers_count":0,"watchers":0,"size":145,"allow_forking":false,"web_commit_signoff_required":false,"archived":false,"disabled":false,"private":true,"has_issues":true,"has_wiki":true,"has_pages":false,"has_projects":false,"has_downloads":true,"has_discussions":false,"is_template":false,"url":"https://api.github.com/repos/ThriveMarket/autoship-service","archive_url":"https://api.github.com/repos/ThriveMarket/autoship-service/{archive_format}{/ref}","assignees_url":"https://api.github.com/repos/ThriveMarket/autoship-service/assignees{/user}","blobs_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/blobs{/sha}","branches_url":"https://api.github.com/repos/ThriveMarket/autoship-service/branches{/branch}","collaborators_url":"https://api.github.com/repos/ThriveMarket/autoship-service/collaborators{/collaborator}","comments_url":"https://api.github.com/repos/ThriveMarket/autoship-service/comments{/number}","commits_url":"https://api.github.com/repos/ThriveMarket/autoship-service/commits{/sha}","compare_url":"https://api.github.com/repos/ThriveMarket/autoship-service/compare/{base}...{head}","contents_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contents/{+path}","contributors_url":"https://api.github.com/repos/ThriveMarket/autoship-service/contributors","deployments_url":"https://api.github.com/repos/ThriveMarket/autoship-service/deployments","downloads_url":"https://api.github.com/repos/ThriveMarket/autoship-service/downloads","events_url":"https://api.github.com/repos/ThriveMarket/autoship-service/events","forks_url":"https://api.github.com/repos/ThriveMarket/autoship-service/forks","git_commits_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/commits{/sha}","git_refs_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/refs{/sha}","git_tags_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/tags{/sha}","hooks_url":"https://api.github.com/repos/ThriveMarket/autoship-service/hooks","issue_comment_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/comments{/number}","issue_events_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues/events{/number}","issues_url":"https://api.github.com/repos/ThriveMarket/autoship-service/issues{/number}","keys_url":"https://api.github.com/repos/ThriveMarket/autoship-service/keys{/key_id}","labels_url":"https://api.github.com/repos/ThriveMarket/autoship-service/labels{/name}","languages_url":"https://api.github.com/repos/ThriveMarket/autoship-service/languages","merges_url":"https://api.github.com/repos/ThriveMarket/autoship-service/merges","milestones_url":"https://api.github.com/repos/ThriveMarket/autoship-service/milestones{/number}","notifications_url":"https://api.github.com/repos/ThriveMarket/autoship-service/notifications{?since,all,participating}","pulls_url":"https://api.github.com/repos/ThriveMarket/autoship-service/pulls{/number}","releases_url":"https://api.github.com/repos/ThriveMarket/autoship-service/releases{/id}","stargazers_url":"https://api.github.com/repos/ThriveMarket/autoship-service/stargazers","statuses_url":"https://api.github.com/repos/ThriveMarket/autoship-service/statuses/{sha}","subscribers_url":"https://api.github.com/repos/ThriveMarket/autoship-service/subscribers","subscription_url":"https://api.github.com/repos/ThriveMarket/autoship-service/subscription","tags_url":"https://api.github.com/repos/ThriveMarket/autoship-service/tags","trees_url":"https://api.github.com/repos/ThriveMarket/autoship-service/git/trees{/sha}","teams_url":"https://api.github.com/repos/ThriveMarket/autoship-service/teams","visibility":"internal"},"user":{"login":"ThriveMarket","id":8164832,"node_id":"MDEyOk9yZ2FuaXphdGlvbjgxNjQ4MzI=","avatar_url":"https://avatars.githubusercontent.com/u/8164832?v=4","html_url":"https://github.com/ThriveMarket","gravatar_id":"","type":"Organization","site_admin":false,"url":"https://api.github.com/users/ThriveMarket","events_url":"https://api.github.com/users/ThriveMarket/events{/privacy}","following_url":"https://api.github.com/users/ThriveMarket/following{/other_user}","followers_url":"https://api.github.com/users/ThriveMarket/followers","gists_url":"https://api.github.com/users/ThriveMarket/gists{/gist_id}","organizations_url":"https://api.github.com/users/ThriveMarket/orgs","received_events_url":"https://api.github.com/users/ThriveMarket/received_events","repos_url":"https://api.github.com/users/ThriveMarket/repos","starred_url":"https://api.github.com/users/ThriveMarket/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThriveMarket/subscriptions"}}}'}]
⏺ Let me read the autoship DAGs in this repository to understand the current implementation.
⏺ Read(/Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_schedule_process_trigger.py) ⏺ Read(/Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_process_schedule.py) ⏺ Read(/Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_single_email_process.py) ⏺ Read(/Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/alert_autoship_schedule_prefill.py) ⎿ 1→"""DAG to trigger cms-api endpoint to process schedules for given customer_ids. 2→ 3→Migrated from airflow-magento for Airflow 3 compatibility. 4→""" 5→ 6→from future import annotations 7→ 8→import json 9→import numpy as np 10→import os 11→import requests 12→ 13→from collections import Counter 14→from threading import Thread 15→from datetime import datetime 16→from airflow import DAG 17→from airflow.models import Variable 18→from airflow.providers.mysql.hooks.mysql import MySqlHook 19→from airflow.providers.standard.operators.python import PythonOperator 20→ [..snip.. 635 more lines]
⎿ 1→"""Alert to see if autoship schedule process job is failing. 2→ 3→Migrated from airflow-magento for Airflow 3 compatibility. 4→""" 5→ 6→from future import annotations 7→ 8→import json 9→import pandas as pd 10→ 11→from airflow import DAG 12→from airflow.models import Variable 13→from airflow.providers.mysql.hooks.mysql import MySqlHook 14→from airflow.providers.standard.operators.python import PythonOperator 15→from contextlib import closing 16→from datetime import datetime, timedelta 17→ 18→from include.shared_utils.notifications.slack import SlackAlertsNotifier 19→from include.shared_utils.notifications import email 20→from include.shared_utils.notifications.alert_util import DEFAULT_SLACK_CHANNEL [..snip.. 361 more lines]
⎿ 1→"""DAG to process autoship schedules for a single email address. 2→ 3→Moves schedule dates and runs AROPE for the specified email. 4→Migrated from airflow-magento for Airflow 3 compatibility. 5→""" 6→ 7→from future import annotations 8→ 9→import json 10→import os 11→import requests 12→ 13→from datetime import datetime 14→from airflow.models import Variable 15→from airflow import DAG 16→from airflow.providers.standard.operators.python import PythonOperator 17→ 18→from include.shared_utils.notifications.slack import SlackAlertsNotifier 19→from include.shared_utils.dag_helpers import get_env 20→ [..snip.. 216 more lines]
⎿ 1→""" 2→Alert when autoship schedules are not prefilled at end of day. 3→""" 4→ 5→from future import annotations 6→ 7→import json 8→from contextlib import closing 9→from datetime import datetime 10→ 11→import pandas as pd 12→from airflow import DAG 13→from airflow.models import Variable 14→from airflow.providers.mysql.hooks.mysql import MySqlHook 15→from airflow.providers.standard.operators.python import PythonOperator 16→ 17→from include.shared_utils.dag_helpers import get_env 18→from include.shared_utils.notifications import email 19→from include.shared_utils.notifications.slack import SlackAlertsNotifier 20→from include.shared_utils.teams import get_oncall_mention [..snip.. 233 more lines]
⏺ Let me also check the other autoship-related DAGs to get a fuller picture.
⏺ Read(/Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_schedule_single_cart_process_items.py) ⏺ Read(/Users/justinabrahms/src/github.com/thrivemarket/be-airflow/dags/autoship/autoship_prefill_recs_master_cart.py) ⎿ 1→"""DAG to trigger cms-api endpoint to move items to master cart for given schedules. 2→ 3→Migrated from airflow-magento for Airflow 3 compatibility. 4→""" 5→ 6→from future import annotations 7→ 8→import json 9→import numpy as np 10→import requests 11→ 12→from threading import Thread 13→from datetime import datetime 14→from airflow import DAG 15→from airflow.models import Variable 16→from airflow.providers.standard.operators.python import PythonOperator 17→ 18→from include.shared_utils.notifications.slack import SlackAlertsNotifier 19→from include.shared_utils.notifications import email 20→from include.shared_utils.notifications.alert_util import DEFAULT_SLACK_CHANNEL [..snip.. 389 more lines]
⎿ 1→"""DAG sends schedules list to /autoship/assignPrefillRecsToMasterCart CMS endpoint. 2→ 3→Previously schedules saved in another DAG. 4→Failed schedules from response will be alerted to slack and email. 5→ 6→CONFIG = { 7→ 'assign_prefill_recs_master_cart_url': 'https://cms-api.thrivemarket.loc/autoship/assignPrefillRecsToMasterCart', 8→ 'parent_dag_id': 'autoship_schedule_single_cart_process_items', 9→ 'parent_task_id': 'autoship_trigger_cmsapi', 10→ 'xcom_key': 'schedule_ids', 11→ 'email_on_failure': '[email protected]', 12→ 'threads': 3, 13→ 'timeout': 90 14→} 15→ 16→Migrated from airflow-magento for Airflow 3 compatibility. 17→""" 18→ 19→from future import annotations 20→ [..snip.. 255 more lines]
⏺ Based on comparing the PR specs with this Airflow repository, here's what seems to be missing from the specs:
The DAGs reveal a two-phase cart process not mentioned in specs:
autoship_schedule_single_cart_process_items- moves items to "master cart" (runs every 5 min)autoship_prefill_recs_master_cart- assigns prefill recommendations to master cart (runs 14,34,54 min)
The specs mention "Cart Prefill" but don't explain this master cart abstraction.
All DAGs integrate with CMS-API but the specs don't document these interfaces:
/autoship/singleCartCustomersToProcess/autoship/runarope/{customer_id}/autoship/assignItemsToMasterCart/autoship/assignPrefillRecsToMasterCart/autoship/moveScheduleDates/{email}
The spec says AROPE runs "every 2 hours at the 30th minute" but the DAG runs every 20 minutes. Either the spec is outdated or describes different behavior.
The specs have no coverage of alerting/monitoring despite extensive DAGs:
- Backlog monitoring - alerts when processed count drops below threshold
- Prefill alerts -
alert_autoship_schedule_prefillfor end-of-day failures - Anomaly detection - ML-based detection
- Slow query monitoring
The DAGs show sophisticated patterns not in specs:
- Soft vs hard failure categorization - different alert severities
- Duplicate schedule detection - dedupe before processing
- Schedule event logging - querying
thrive_autoship_schedule_eventfor diagnostics
The specs list notification requirements (3-day review, 1-day reminder) but don't cover:
- How/when notifications are triggered (no notification DAG visible)
- Sailthru/Attentive integration details
- Error handling for failed notifications
The DAGs reference tables (thrive_autoship_schedule, thrive_autoship, customer_entity_int) that aren't documented in the specs.
Bottom line: The specs capture the domain model well but are missing the operational layer (monitoring, alerting, processing cadence) and integration details (CMS-API contracts, database schema, master cart workflow). This gap could cause confusion when building the new service since the specs don't tell developers how to replicate the current operational behavior.
Great. Draft the updated specs. Make a PR into the original PR I linked you to update.
⏺ I'll create the updated specs and submit a PR to the original branch. Let me first clone the repo and create the additional spec files.
⏺ Bash(cd /tmp && rm -rf autoship-service && gh repo clone ThriveMarket/autoship-ser...) ⎿ Exit code 137 [Request interrupted by user for tool use] Cloning into 'autoship-service'... warning: templates not found in /home/abrahms/.git_template/
choose a different name for that folder so that other things can work in parallel.
⏺ Bash(cd /tmp && rm -rf autoship-service-specs-update && gh repo clone ThriveMarket...) ⎿ branch 'add-autoship-spec' set up to track 'origin/add-autoship-spec'. Already up to date. Cloning into 'autoship-service-specs-update'... warning: templates not found in /home/abrahms/.git_template/ Switched to a new branch 'add-autoship-spec' From https://github.com/ThriveMarket/autoship-service * branch add-autoship-spec -> FETCH_HEAD Shell cwd was reset to /Users/justinabrahms/src/github.com/thrivemarket/be-airflow
⏺ Good. Now let me create a new branch and add the missing specs.
⏺ Bash(cd /tmp/autoship-service-specs-update && git checkout -b add-operational-spec...) ⎿ drwxr-xr-x justinabrahms wheel 192 B Thu Jan 29 10:16:16 2026 . drwxr-xr-x justinabrahms wheel 128 B Thu Jan 29 10:16:16 2026 .. drwxr-xr-x justinabrahms wheel 96 B Thu Jan 29 10:16:16 2026 item-management drwxr-xr-x justinabrahms wheel 96 B Thu Jan 29 10:16:16 2026 order-processing drwxr-xr-x justinabrahms wheel 96 B Thu Jan 29 10:16:16 2026 schedule-management drwxr-xr-x justinabrahms wheel 96 B Thu Jan 29 10:16:16 2026 subscription-management Switched to a new branch 'add-operational-specs' Shell cwd was reset to /Users/justinabrahms/src/github.com/thrivemarket/be-airflow
⏺ Now let me create the new spec files. I'll add an operations/monitoring spec and a CMS-API integration spec.
⏺ Bash(mkdir -p /tmp/autoship-service-specs-update/openspec/specs/operations-monitor...) ⏺ mcp__filesystem__write_file(...) ⎿
⎿ Error: Access denied - path outside allowed directories: /tmp/autoship-service-specs-update/openspec/specs/operations-monitoring/spec.md not in /projects
⏺ Let me use the Write tool instead.
⏺ Write(/tmp/autoship-service-specs-update/openspec/specs/operations-monitoring/spec.md) ⏺ Write(/tmp/autoship-service-specs-update/openspec/specs/cms-api-integration/spec.md) ⎿ File created successfully at: /tmp/autoship-service-specs-update/openspec/specs/operations-monitoring/spec.md
⎿ File created successfully at: /tmp/autoship-service-specs-update/openspec/specs/cms-api-integration/spec.md
⏺ Now let me read and update the order-processing spec to fix the processing cadence.
⏺ Read(/tmp/autoship-service-specs-update/openspec/specs/order-processing/spec.md) ⎿ 1→# Order Processing 2→ 3→## Purpose 4→ 5→AROPE (Asynchronous Recurring Order Processing Engine) - the system that 6→converts scheduled deliveries into orders. Handles validation, payment 7→processing, and order creation. 8→ 9→## Requirements 10→ 11→### Requirement: Scheduled Processing 12→ 13→The system SHALL automatically process eligible schedules via cron job. 14→ 15→#### Scenario: Cron-based processing 16→ 17→- WHEN the AROPE cron runs (every 2 hours at the 30th minute) 18→- THEN all schedules where scheduled_at <= NOW and status = ACTIVE are 19→ processed 20→ [..snip.. 205 more lines]
⏺ Edit(/tmp/autoship-service-specs-update/openspec/specs/order-processing/spec.md) ⎿ The file /tmp/autoship-service-specs-update/openspec/specs/order-processing/spec.md has been updated successfully.
⏺ Now let me update the schedule-management spec to add master cart details.
⏺ Read(/tmp/autoship-service-specs-update/openspec/specs/schedule-management/spec.md) ⎿ 1→# Schedule Management 2→ 3→## Purpose 4→ 5→Generation and management of delivery schedules. Handles schedule creation, 6→modification, notifications, and cart prefill for upcoming deliveries. 7→ 8→Smart recommendations for new products come from the Repeat Order Quiz (ROQ), 9→where a model determines products a member may want, categories are presented, 10→and selected items are added to the cart. This replaced the deprecated Repeat 11→Order Prefill (ROP) program which was shuttered due to increased member 12→cancellations. 13→ 14→## Requirements 15→ 16→### Requirement: Schedule Generation 17→ 18→The system SHALL automatically generate delivery schedules based on item 19→frequencies. 20→ [..snip.. 175 more lines]
⏺ Edit(/tmp/autoship-service-specs-update/openspec/specs/schedule-management/spec.md) ⎿ The file /tmp/autoship-service-specs-update/openspec/specs/schedule-management/spec.md has been updated successfully.
⏺ Let me also add a notifications spec since that was identified as missing.
⏺ Bash(mkdir -p /tmp/autoship-service-specs-update/openspec/specs/notifications) ⏺ Write(/tmp/autoship-service-specs-update/openspec/specs/notifications/spec.md) ⎿
⎿ File created successfully at: /tmp/autoship-service-specs-update/openspec/specs/notifications/spec.md
⏺ Now let me commit and push to create a PR.
⏺ Bash(cd /tmp/autoship-service-specs-update && git status) ⎿ On branch add-operational-specs Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git restore ..." to discard changes in working directory) modified: openspec/specs/order-processing/spec.md modified: openspec/specs/schedule-management/spec.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
openspec/specs/cms-api-integration/
openspec/specs/notifications/
openspec/specs/operations-monitoring/
no changes added to commit (use "git add" and/or "git commit -a")
Shell cwd was reset to /Users/justinabrahms/src/github.com/thrivemarket/be-airflow
⏺ Bash(cd /tmp/autoship-service-specs-update && git add openspec/specs/order-process...) ⎿ [add-operational-specs 6941d37] Add operational specs for autoship service 5 files changed, 683 insertions(+), 3 deletions(-) create mode 100644 openspec/specs/cms-api-integration/spec.md create mode 100644 openspec/specs/notifications/spec.md create mode 100644 openspec/specs/operations-monitoring/spec.md │ No config files with names ["lefthook" ".lefthook" ".config/lefthook"] have been found in "/private/tmp/autoship-service-specs-update" │ No config files with names ["lefthook" ".lefthook" ".config/lefthook"] have been found in "/private/tmp/autoship-service-specs-update" Shell cwd was reset to /Users/justinabrahms/src/github.com/thrivemarket/be-airflow
⏺ Bash(cd /tmp/autoship-service-specs-update && git push -u origin add-operational-s...)
⎿ │ No config files with names ["lefthook" ".lefthook" ".config/lefthook"] have been found in "/private/tmp/autoship-service-specs-update"
branch 'add-operational-specs' set up to track 'origin/add-operational-specs'.
remote:
remote: Create a pull request for 'add-operational-specs' on GitHub by visiting:
remote: https://github.com/ThriveMarket/autoship-service/pull/new/add-operational-specs
remote:
remote: GitHub found 3 vulnerabilities on ThriveMarket/autoship-service's default branch (3 high). To find out more, visit:
remote: https://github.com/ThriveMarket/autoship-service/security/dependabot
remote:
To https://github.com/ThriveMarket/autoship-service.git
* [new branch] add-operational-specs -> add-operational-specs
Shell cwd was reset to /Users/justinabrahms/src/github.com/thrivemarket/be-airflow
⏺ Bash(cd /tmp/autoship-service-specs-update && gh pr create --base add-autoship-spe...) ⎿ https://github.com/ThriveMarket/autoship-service/pull/20 Shell cwd was reset to /Users/justinabrahms/src/github.com/thrivemarket/be-airflow
⏺ I've created the PR with the updated specs.
PR URL: https://github.com/ThriveMarket/autoship-service/pull/20
The PR adds the following new specs based on analysis of the be-airflow DAGs:
| Spec | Purpose |
|---|---|
operations-monitoring/spec.md |
Processing health monitoring, alerting, failure categorization, metrics |
cms-api-integration/spec.md |
API contracts, database schema reference, endpoint documentation |
notifications/spec.md |
Email/SMS notification requirements (Sailthru, Attentive) |
And updates existing specs:
- order-processing: Fixed processing cadence (20 min, not 2 hours), added two-phase workflow
- schedule-management: Added master cart processing workflow details
publish-session /publish-session
⏺ Bash(python3 /Users/justinabrahms/.claude/plugins/cache/session-share-marketplace/...) ⏺ Bash(python3 /Users/justinabrahms/.claude/plugins/cache/session-share-marketplace/...) ⎿ Session published: https://custardseed.com/g/f715a492070c2d6c12f73d88c0eef6ce
⎿
⏺ Session published: https://custardseed.com/g/f715a492070c2d6c12f73d88c0eef6ce
share /share
⏺ Bash(~/.claude/scripts/share-session.sh "$(pwd)")