Every item the scanner produces — and everything board/index.html renders — is a JSON object with these fields.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Stable unique id, e.g. loop-2026-09-28-001. The scanner must reuse the same id for the same open loop across scans so items don't duplicate. |
title |
string | yes | Short, actionable title. Verb-first: "Reply to…", "Finish…", "Book…". |
priority |
string | yes | One of urgent, high, normal, low. See prompts/prioritization.md. |
status |
string | yes | One of open, waiting (blocked on someone/something else), snoozed (deliberately deferred), dropped (removed from the board by the user — not deleted, waits in the Dropped list until resurfaced), shipped (done — confirmed by the user). The board moves shipped items to the shipped log and dropped items to the dropped list instead of hiding them. |
dropped_at |
string | no | YYYY-MM-DD date the item was dropped. The scan must never resurrect a dropped item on its own — a drop stays a drop until the user says otherwise. |
source_chat |
string | yes | Human-readable name of the conversation the item came from, e.g. "Job search". Shown on the card so the user can jump back to context. |
source_fingerprint |
string | yes | Machine key for dedup, e.g. the chat's id + a topic hash. Same loop seen in a later scan must produce the same fingerprint. |
note |
string | no | One or two sentences of context: what's blocking, what's next, why it matters. |
detail |
string | no | Longer context for the Focus view: background, what was tried, what "done" looks like. |
why_urgent |
string | no | One sentence: why the scanner ranked it at this urgency. Shown in the Focus view so the user can agree or override. |
due |
string | no | YYYY-MM-DD deadline, when one exists. |
snooze_until |
string | no | YYYY-MM-DD comeback date. Set when the user snoozes; the scan should not surface the item before this date. |
shipped_at |
string | no | YYYY-MM-DD date the user confirmed shipped. |
shipped_note |
string | no | What the user actually did, in their words. Proof, not a checkbox. |
{
"id": "loop-2026-09-28-001",
"title": "Reply to landlord about lease renewal",
"priority": "high",
"status": "waiting",
"source_chat": "Personal",
"source_fingerprint": "chat-9f31::lease-renewal",
"note": "Waiting on their answer about the parking spot. Nudge if no reply by Thursday.",
"due": "2026-10-03"
}- Never invent items. Every item traces to a real, observed unfinished thread.
- Never duplicate. Match
source_fingerprintagainst existing items first; update the existing item instead of creating a new one. shippedis sacred. Only mark shipped when the user confirms they acted. "Produced" is not "shipped."- Keep titles actionable. If you can't phrase it as a next action, it's not an open loop — it's a note. Leave notes out.