CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/nanoclaw-orders

Order-email triage for NanoClaw: fetches order-related Gmail, maintains the orders SQLite table, and flags recent anomalies (cancellations/refunds, large purchases until delivered, overdue deliveries), with a cadence companion that runs it on a schedule. NanoClaw per-chat overlay tile.

72

Quality

90%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

promote-stale-shipped.pyskills/check-orders/scripts/

#!/usr/bin/env python3
"""Auto-promote stale shipped/ordered rows to assumed_delivered.

Step 7 of check-orders SKILL.md: some senders (e.g. Chewy Autoship)
never send a delivered email — the order arrives but the status in
the orders table stays `shipped` forever and Step 8's "Overdue
delivery" rule keeps firing for the full 30-day cutoff.

Eligibility (all three must hold):
  - status IN ('shipped', 'ordered') — only non-terminal statuses
  - expected_delivery NOT NULL AND parses as ISO date AND >= 10 days
    before today (real overdue, not slow shipping)
  - last_updated >= 10 days before now (no fresh email about this
    order in >= 10 days)

`expected_delivery` is sometimes a free-text string like "overnight"
rather than an ISO date. Per the original within-days.py contract, a
malformed date counts as eligible (it's been long enough that no
specific date is meaningful). Implemented here in Python rather than
a single SQL UPDATE because the malformed-date branch can't be
expressed with the SQLite `date()` builtin without a complex
fallback.

Stdout on success: a single JSON object summarising the pass:
    {"promoted": <int>, "ids": ["amazon-...", ...]}

Stderr on per-row issues (e.g. unparseable last_updated): warn line
prefixed with the order id, and the row is skipped.

Exit codes: 0 success, 1 IO/schema error.
"""

from __future__ import annotations

import json
import os
import sqlite3
import sys
from datetime import date, datetime, timezone

DB_PATH = os.environ.get("ORDERS_DB_PATH", "/workspace/store/messages.db")
DAYS_THRESHOLD = 10


def _expected_delivery_eligible(value) -> bool:
    """True when the value should count as overdue per Step 7.

    Defensive against non-string types (SQLite is type-permissive — a
    hand-edited row with an integer/numeric value would otherwise crash
    `.strip()` with AttributeError and abort the whole pass). NULL,
    empty strings, and non-string values all return False (ineligible)
    so the caller treats them like a missing expected_delivery.
    """
    if not isinstance(value, str):
        return False
    stripped = value.strip()
    if not stripped:
        return False
    # Try parsing as ISO date (first 10 chars). Mirrors within-days.py.
    try:
        parsed = date.fromisoformat(stripped[:10])
    except ValueError:
        # Malformed date string ("overnight", "soon", etc.) — per the
        # original SKILL.md Step 7, these count as eligible because
        # it's been long enough that no specific date is meaningful.
        # The other two conditions (status + last_updated) still
        # gate, so this isn't a blanket promotion.
        return True
    return (date.today() - parsed).days >= DAYS_THRESHOLD


def _last_updated_eligible(value, order_id: str) -> bool:
    """True when last_updated is >= DAYS_THRESHOLD days ago.

    Defensive against NULL/non-string values: legacy rows pre-dating the
    NOT NULL constraint could still surface here if the schema is ever
    relaxed, and a hand-edited row with an integer/numeric column would
    otherwise crash `value.replace(...)` with AttributeError before the
    ValueError handler could catch it. Treat anything other than a
    non-empty string as "ineligible" — same outcome as a malformed
    ISO date — and skip with a diagnostic.
    """
    if not isinstance(value, str) or not value.strip():
        sys.stderr.write(
            f"promote-stale-shipped: order {order_id} has missing or "
            f"non-string last_updated={value!r}; skipping. To make this "
            f"row eligible for auto-promotion, set its last_updated to "
            f"an ISO-8601 timestamp at least 10 days in the past — "
            f"promotion requires the row to be stale, not fresh.\n"
        )
        return False
    try:
        # last_updated is ISO-8601 with timezone (e.g. "2026-04-02T00:00:00.000Z"
        # or "2026-04-02T00:00:00.000+00:00"). datetime.fromisoformat in 3.11+
        # accepts "Z" directly; for older, normalise.
        normalized = value.replace("Z", "+00:00")
        parsed = datetime.fromisoformat(normalized)
    except ValueError:
        sys.stderr.write(
            f"promote-stale-shipped: order {order_id} has unparseable "
            f"last_updated={value!r}; skipping\n"
        )
        return False
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)
    delta = datetime.now(timezone.utc) - parsed
    return delta.days >= DAYS_THRESHOLD


def main() -> int:
    now_iso = datetime.now(timezone.utc).isoformat()
    promoted_ids: list[str] = []
    conn = None
    try:
        conn = sqlite3.connect(DB_PATH)
        cur = conn.execute(
            """
            SELECT id, expected_delivery, last_updated
              FROM orders
             WHERE status IN ('shipped', 'ordered')
            """,
        )
        candidates = cur.fetchall()
        for order_id, expected_delivery, last_updated in candidates:
            if not _expected_delivery_eligible(expected_delivery):
                continue
            if not _last_updated_eligible(last_updated, order_id):
                continue
            conn.execute(
                """
                UPDATE orders
                   SET status = 'assumed_delivered',
                       flagged = 0,
                       flag_reason = NULL,
                       last_updated = ?
                 WHERE id = ?
                """,
                (now_iso, order_id),
            )
            promoted_ids.append(order_id)
        conn.commit()
        json.dump({"promoted": len(promoted_ids), "ids": promoted_ids}, sys.stdout)
        sys.stdout.write("\n")
        return 0
    except sqlite3.Error as exc:
        sys.stderr.write(
            f"promote-stale-shipped: SQLite error against {DB_PATH}: "
            f"{exc}. Verify the database file exists, is writable, and "
            f"the orders table is present (created by the orchestrator's "
            f"state-001 migration).\n"
        )
        return 1
    finally:
        if conn is not None:
            conn.close()


if __name__ == "__main__":
    sys.exit(main())

skills

check-orders

SKILL.md

state-schema.md

CHANGELOG.md

README.md

tile.json