# Session: 2026-04-10 19:08:14 UTC

- **Session Key**: agent:main:main
- **Session ID**: 4cdec6ae-c104-4624-8018-913113e14cd4
- **Source**: telegram

## Conversation Summary

user: Conversation info (untrusted metadata):
```json
{
  "message_id": "4992",
  "sender_id": "8032472383",
  "sender": "Braden McLeish",
  "timestamp": "Fri 2026-04-10 06:30 PDT"
}
```

Sender (untrusted metadata):
```json
{
  "label": "Braden McLeish (8032472383)",
  "id": "8032472383",
  "name": "Braden McLeish"
}
```

How should we address the partial sync items after they are skipped?
assistant: [[reply_to_current]] They should go into an explicit **exception lane**, not disappear and not block the whole merchant.

## Final Answer

Use a **partial-sync review queue** with enough captured evidence to retry later.

## Structured Deliverable

### Recommended handling model

When a Target order fails the strict 5% reconciliation gate:

1. **Do not write the order into `purchase_orders` / `purchase_items`**
2. **Do write an exception record** to a separate tracking surface
3. Capture enough context to support deterministic retry:
   - merchant
   - order_id
   - route family
   - order_date
   - visible order total
   - extracted item subtotal
   - reconciliation delta
   - failure reason
   - screenshot paths
   - detail-page URL
   - first-seen timestamp
   - last-attempt timestamp
   - retry_count
4. Retry later using the same merchant skill after parser improvements or site-state changes
5. Only mark resolved when:
   - the order later passes reconciliation and is inserted cleanly, or
   - it is manually dispositioned as permanently unsupported / non-itemizable

### Best place to store it

Use one of these:

#### Option A — SQLite exception table inside the purchase tracker DB
Best default.

Add a table like:

- `purchase_sync_exceptions`
  - `merchant`
  - `order_id`
  - `route_family`
  - `order_date`
  - `order_total`
  - `item_subtotal_seen`
  - `reconciliation_delta`
  - `failure_reason`
  - `detail_url`
  - `artifact_paths_json`
  - `status` (`open`, `retrying`, `resolved`, `ignored`)
  - `retry_count`
  - `first_seen_at`
  - `last_seen_at`
  - `resolved_at`
  - `resolution_note`

Why this is best:
- keeps operational exceptions next to the canonical tracker
- easy to query by merchant/order/status
- easy to build retry jobs and dashboards later

#### Option B — JSON staging folder
Good as a lightweight fallback, but weaker long term.

Example:
- `/Users/openclaw/outputs/purchase-tracker/staging/exceptions/target/{order_id}.json`

Use this only if you want zero schema work right now.

### Retry policy

Recommended default:

- **Immediate run:** skip bad order, log exception
- **Nightly / next sync:** retry open exceptions for that merchant
- **After parser hardening:** bulk retry all open exceptions for the affected branch
- **After 3 failed retries:** escalate for review in Notion / ops thread

### What should appear in the sync log

For every skipped partial-sync order, log something like:

- merchant
- order_id
- status = `partial_sync`
- reason = `reconciliation_gt_5pct`
- delta
- artifact reference

This makes failures auditable without polluting canonical purchases.

### Production rule

The right principle is:

- **Canonical tables contain only trusted, reconciled orders**
- **Exception lane contains everything that was seen but not trusted enough to ingest**

That preserves data quality without losing visibility.

## Risks and Unknowns

- If you only log to plain text, exception retry management gets messy fast.
- If Target changes its detail layout again, exception volume may spike until parser rules are updated.
- Some orders may genuinely lack enough visible itemization to ever reconcile cleanly from the page alone.

## Next Actions

Best next step:
1. add a `purchase_sync_exceptions` table
2. wire Target partial-sync skips into it
3. store screenshot/detail URL references with each exception
4. add a retry mode for open exceptions

If you want, I can implement that exception lane next.
assistant: Session agent:main:main (main) at 46.2% — summarize + prep rollover.
assistant: Session agent:main:main (main) at 46.2% — summarize + prep rollover.
user: Conversation info (untrusted metadata):
```json
{
  "message_id": "4996",
  "sender_id": "8032472383",
  "sender": "Braden McLeish",
  "timestamp": "Fri 2026-04-10 10:11 PDT"
}
```

Sender (untrusted metadata):
```json
{
  "label": "Braden McLeish (8032472383)",
  "id": "8032472383",
  "name": "Braden McLeish"
}
```

Your Option A design is approved — proceed with the SQLite exception table approach. The schema and retry policy you outlined look right; use your judgment on any field-level details as you implement.
One addition: wire in a Telegram alert if a single sync run produces more than 2 partial_sync exceptions for any merchant. Keep it lightweight — just the merchant name, count, and a note to check the exception table. Adjust the threshold if you find a different number makes more sense in practice.
General principle to follow: canonical tables stay clean and reconciled, exception lane captures everything that was seen but couldn’t be trusted. As long as that holds, I’m fine with you adapting the specifics as you go.
assistant: I’m proceeding with the exception-lane implementation on the approved SQLite design, including the lightweight Telegram alert threshold for >2 partial-sync exceptions in a single run.
assistant: [[reply_to_current]] Proceeding. I’m implementing the SQLite exception lane now, wiring partial-sync skips into it, and adding the lightweight merchant-level Telegram alert for runs that exceed the partial-sync threshold.
assistant: [[reply_to_current]] Done.

I implemented the SQLite exception lane and wired partial-sync skips into it without contaminating the canonical tables.

What’s now in place:
- New exception table in the canonical DB:
  - `purchase_order_exceptions`
- It stores the skipped/untrusted order context needed for retry, including:
  - merchant
  - order_id
  - route family
  - order date
  - visible order total
  - extracted subtotal
  - reconciliation delta
  - failure reason/detail
  - detail URL
  - artifact references
  - status / retry state
  - timestamps
- Indexes were added so it stays queryable.

Write-path behavior:
- strict-gate failures are still **skipped** from canonical order/item tables
- but they are now **recorded** in `purchase_order_exceptions` instead of disappearing
- that preserves the principle:
  - canonical tables stay clean
  - exception lane captures what was seen but not trusted

Alerting:
- lightweight Telegram alerting is now wired in for a single sync run that produces **more than 2 partial-sync exceptions for a merchant**
- the alert includes:
  - merchant
  - count
  - note to check `purchase_order_exceptions`
- threshold is configurable, but currently defaults to **2**, as approved

What changed:
- Target writer was updated directly for exception recording + run-level alerting
- the shared schema bootstrap was updated across the merchant sync scripts so the exception table exists where needed
- the purchase-tracker README and DB were updated
- the Notion thread was updated with the implementation summary

Commit:
- `a61cdf9558d2211d7c716d316e3a245434342966`

Validation:
- the no-incomplete-write behavior still holds
- a strict-gate failing order was confirmed skipped from canonical tables and recorded in the exception lane instead

This is now in place.
user: Conversation info (untrusted metadata):
```json
{
  "message_id": "5000",
  "sender_id": "8032472383",
  "sender": "Braden McLeish",
  "timestamp": "Fri 2026-04-10 10:19 PDT"
}
```

Sender (untrusted metadata):
```json
{
  "label": "Braden McLeish (8032472383)",
  "id": "8032472383",
  "name": "Braden McLeish"
}
```

Can you write this up in the thread?
assistant: I’m writing the exception-lane implementation summary into the existing thread now so the record is explicit and reviewable.
assistant: [[reply_to_current]] Done. I wrote the exception-lane implementation summary into the thread, including:
- canonical tables stay clean
- `purchase_order_exceptions` is now the retry/review lane
- strict 5% failures are skipped from canonical writes but preserved in the exception table
- run-level grouping is in place
- lightweight Telegram alerting now fires if a merchant run exceeds the partial-sync threshold
assistant: Session agent:main:main (main) at 48.5% — summarize + prep rollover.
user: Conversation info (untrusted metadata):
```json
{
  "message_id": "5004",
  "reply_to_id": "5003",
  "sender_id": "8032472383",
  "sender": "Braden McLeish",
  "timestamp": "Fri 2026-04-10 12:06 PDT",
  "has_reply_context": true
}
```

Sender (untrusted metadata):
```json
{
  "label": "Braden McLeish (8032472383)",
  "id": "8032472383",
  "name": "Braden McLeish"
}
```

Replied message (untrusted, for context):
```json
{
  "sender_label": "Dorian",
  "body": "Session agent:main:main (main) at 48.5% — summarize + prep rollover."
}
```

Rollover, handoff session
user: Conversation info (untrusted metadata):
```json
{
  "message_id": "5006",
  "reply_to_id": "5005",
  "sender_id": "8032472383",
  "sender": "Braden McLeish",
  "timestamp": "Fri 2026-04-10 12:07 PDT",
  "has_reply_context": true
}
```

Sender (untrusted metadata):
```json
{
  "label": "Braden McLeish (8032472383)",
  "id": "8032472383",
  "name": "Braden McLeish"
}
```

Replied message (untrusted, for context):
```json
{
  "sender_label": "Dorian",
  "body": "Use /rollover here."
}
```

It hasn't been working
assistant: [[reply_to_current]] Then do this instead:

1. Send `/reset`
2. In the first message of the new session, paste:
   - `Read today's memory and continue the Target / purchase-tracker handoff from the latest rollover summary.`

If you want, I can also give you a one-message handoff block to paste into the new session.
