# ORDERS.md — the operator-order ledger

One row per order the operator gave. This file is **enforced**: every claim in it
is checked by `packages/shared/src/orders-ledger.test.ts`, which runs in a suite
that runs every session.

## Why this file exists

The 2026-07-28 audit found the mechanism by which orders go unbuilt while
reading as done:

1. **A ruling written into a document counted as done.** His comment labels were
   read, reasoned, and written into `docs/DECISIONS.md` on 2026-07-27. The seeds
   were never touched.
2. **Work-packages close without a gate**, so nobody re-reads the order list
   against the code (`agent-scope-14` has no gate verdict and no stage log).
3. **The status map was wrong in BOTH directions.** `scope-board.md:229-244` says
   IG delete has never executed (prod has successful deletes); `scope-board.md:475-486`
   says the flow button sender is unbuilt (it ships).
4. **Nothing in the repo failed when an order was not done.** Tests enforce code;
   nothing enforced intent.

The operator's own rule already covered it and was not enough on its own:
*"Never write a doc that nothing loads: reinforcement without enforcement decays."*
So this ledger ships with the check that makes lying about it red.

## The four statuses — and what each one costs you

| Status | What it means | What the test demands |
|---|---|---|
| `DONE` | Built, in the code, on this branch. | At least one citation that resolves — a `path:line` whose file exists and is long enough, or a commit sha that exists — **and at least one of them must be a non-doc artifact.** A ruling in `docs/**`, `plans/**` or any `*.md` closes nothing. |
| `OPEN` | Not built. | A non-empty Note saying concretely what is missing. |
| `BLOCKED` | Cannot proceed. | A non-empty Note naming the blocker AND what clears it: an operator action, a Meta product, or another work-package. |
| `UNVERIFIED` | Extracted from a document, never checked against the code. | Nothing. This is the honest placeholder and it is **always** better than a guessed `DONE`. |

There is deliberately **no `PARTIAL`**. Split the row: the built half is `DONE`
with its citation, the rest is `OPEN` with what is missing.

## How to use it

- Picking up work: read the `OPEN` and `BLOCKED` rows for your scope. The Note is
  the brief.
- Finishing work: flip the row to `DONE` **and put the `path:line` of the code in
  the Evidence cell**. If you cannot, it is not done.
- Auditing: a row that has been `UNVERIFIED` for a long time is not a safe row.
  It is an unchecked claim. Verify it against the code and give it a real status.
- Format: rows are markdown table lines starting with `| ORD-`, exactly six cells.
  A literal `|` inside a cell breaks the row and the test says so.

Seeded 2026-07-28 from a 15-agent audit (5 extraction seats over
`agent-scope-14-kickoff.md`, `HANDOFF.md` + `plans/rethink/README.md`,
`docs/DECISIONS.md`, `scope-board.md` and the other kickoffs; 10 verification
seats that checked orders against the code and prod). Rows the verification
seats reached carry a real status. **Every row they did not reach is
`UNVERIFIED`** — no evidence has been invented for any row.

---

## A. The 2026-07-27 Behaviour + Activity glance (agent-scope-14)

| ID | Order (his words where quoted) | Asked | Status | Evidence | Note |
|---|---|---|---|---|---|
| ORD-001 | Rename `Situations` to "Behaviour" everywhere he can see it - nav, heading, route - and delete the legacy unrouted `BehaviourTab.tsx` in the same commit so the app does not ship two things called Behaviour | `plans/rethink/agent-scope-14-kickoff.md:9-13`, `plans/rethink/scope-board.md:14-22` (2026-07-27) | DONE | `apps/web/src/routes/agent/Behaviour.tsx:58`, `apps/web/src/components/AppShell.tsx:110`, `apps/web/src/routes/agent/agentRoutes.tsx:71`, redirect at `apps/web/src/routes/agent/agentRoutes.tsx:79`, commit `868e0bb` | `Situations.tsx` and the legacy `BehaviourTab.tsx` are both gone in `868e0bb`. |
| ORD-002 | Delete the Global channel-chip banner strip ("Applies underneath every situation below, on every channel.") on ALL tabs, not just Global | `plans/rethink/agent-scope-14-kickoff.md:14-16` (2026-07-27) | DONE | `apps/web/src/routes/agent/Behaviour.test.tsx:344`, `apps/web/src/routes/agent/Behaviour.test.tsx:351`, commit `868e0bb` | The surviving `ChannelChip` is the per-field channel-addition marker, not the banner strip. |
| ORD-003 | Delete the Knowledge-search section - "why did situations create a whole section for knowledge search? no need" | `plans/rethink/agent-scope-14-kickoff.md:17-19` (2026-07-27) | DONE | `apps/web/src/routes/agent/Behaviour.tsx:49-54`, commits `868e0bb`, `3bda3bb` | `situations-knowledge.tsx` deleted in `868e0bb`; the standalone Knowledge screen retired in `3bda3bb` and `/agent/knowledge*` now redirects. |
| ORD-004 | Fields must take FULL markdown, not just `##` heading lines - lists, bold, links, code render and are accepted end to end | `plans/rethink/agent-scope-14-kickoff.md:20-23` (2026-07-27) | DONE | `apps/web/src/routes/agent/situations-ui.tsx:96`, `apps/web/src/routes/agent/situations-ui.tsx:124` | The kickoff's own sequencing note undersells this: it says items 4-5 were "not re-verified", but both are built. |
| ORD-005 | Move `See the exact prompt` and `Edit` to the LEFT - "where it is most expected" | `plans/rethink/agent-scope-14-kickoff.md:24-25` (2026-07-27) | DONE | `apps/web/src/routes/agent/situations.css:26` | CSS comment on the rule names the defect. |
| ORD-006 | Re-scope KB: common KB to the Global tab only, situation-specific KB to that situation's tab, channel-AND-situation-specific shown in the right tab and visibly marked as channel-specific | `plans/rethink/agent-scope-14-kickoff.md:29-33` (2026-07-27) | UNVERIFIED | | The 2026-07-28 audit found only the DB and API half built (`6528a1c`) with no web consumer and the migration not deployed to prod. Commit `3bda3bb` landed AFTER that check and claims "the knowledge library reads per situation". Re-verify the whole clause, including the channel-specific marking and `prisma migrate deploy` on the VPS, before flipping this to DONE. |
| ORD-007 | Delete EVERY existing KB document and rewrite from zero ("all KBs some unnecessary, generic, unspecific or maybe meant to be a prompt"). Keep a backup of the rows first. Not a bulk-generate - each doc earns its place and true prompt text moves into a Behaviour field | `plans/rethink/agent-scope-14-kickoff.md:34-39` (2026-07-27) | OPEN | | Nothing deleted or rewritten: prod `select count(*) from knowledge_documents` returned 9, exactly the rows the backup captured (audit read, 2026-07-28). Backup exists at `~/kb-backups/knowledge_documents-20260727.sql` on the VPS but has never been restore-tested. Destructive and operator-gated - confirm the restore first, then delete, then author replacements one at a time with the situation and channel scope set. |
| ORD-008 | Go through the labelled/marked activities and reason their labels - surface intent rather than inferring it | `plans/rethink/agent-scope-14-kickoff.md:43-46` (2026-07-27) | UNVERIFIED | | The reasoning was written into `docs/DECISIONS.md` ("Comments: the moderation bar, and how a DM actually starts"). That is a doc, not a build - see ORD-009 and ORD-010 for the prompt changes it ordered, which is where this order's failure mode originally lived. |
| ORD-009 | Prompt change: `skip` stops being the catch-all. Personal, unrelated, flirtatious, unprofessional or trash-talk comments must be MODERATED (hidden/deleted), not skipped and not answered - including "Influencer email if possible?", a personal approach, not a partnership lead | `plans/rethink/agent-scope-14-kickoff.md:47-52`, `docs/DECISIONS.md:103-117` (2026-07-27) | UNVERIFIED | | This is the order the audit named as failure mode 1: the ruling reached `docs/DECISIONS.md` on 2026-07-27 and the seeds were reported untouched. Check `packages/shared/src/agent/comment-behaviour.ts` and the moderation seed for the personal/unrelated/flirtatious categories routing to moderate, and confirm the seeded DB rows changed, not just source. |
| ORD-010 | Prompt change: the FIRST comment on a post carries extra weight - it "can affect the movement of the comments section", so junk in that slot is moderated, not tolerated | `plans/rethink/agent-scope-14-kickoff.md:53-55`, `docs/DECISIONS.md:103-117` (2026-07-27) | UNVERIFIED | | Also check whether the comment payload even carries an ordinal or position today. |
| ORD-011 | Delete the public DM-CTA - every Tier-2 reply currently ends by asking the customer to DM us | `plans/rethink/agent-scope-14-kickoff.md:56-61` (2026-07-27) | BLOCKED | | Blocked on ORD-012, the comment-to-DM private-reply path. Unblocks when the AI reply pass can open the DM itself. The CTA is still live and test-locked at `packages/shared/src/agent/comment-behaviour.ts:29` and `packages/shared/src/agent/moderation-bar.behaviour.test.ts:147`. The ordering constraint is the operator's own: do not strip the CTA before the private reply ships, or Tier-2 comments point at a DM nobody opens. |
| ORD-012 | Comment to DM, done properly - "WE open the thread": a private reply goes to the commenter and the public reply tells them to check their DMs. "Needs its own planning" | `docs/DECISIONS.md:119-129`, `plans/rethink/scope-board.md:582-593`, `plans/rethink/agent-scope-14-kickoff.md:56-61`, `HANDOFF.md:77-80` (2026-07-27) | OPEN | | Nothing built. The seed still says "STEER them into our DMs" (`packages/shared/src/agent/comment-behaviour.ts:29`) and a golden pins it. The private-reply transport is real (`sendPrivateReply` in `packages/shared/src/instagram/instagram-client.ts`) but wired only to the keyword funnel: `sendDmOpener` has one caller, inside `if (trig.action === "dm_flow")` in `apps/api/src/instagram/comment-pipeline.ts`. The AI reply pass has no private-reply leg, so flipping `instagram:comment_to_dm` today delivers a DM to keyword comments only. Needs: a plan file (none owns it), a decision on whether every replied comment gets a private reply, a generator for the opener text, the two Meta constraints enforced (one private reply per comment, 7-day window), gate semantics, and Facebook parity. |
| ORD-013 | Defect 9: the interpretation row's details header reads "no name" - make it honest | `plans/rethink/agent-scope-14-kickoff.md:62-86` (2026-07-27) | DONE | `apps/web/src/routes/agent/Activity.tsx:102-104`, commit `6ac1894` | |
| ORD-014 | Defect 10: a Needs-action row shows no source - it must carry the post it came from | `plans/rethink/agent-scope-14-kickoff.md:62-86` (2026-07-27) | DONE | `apps/api/src/review/review-items.service.ts:57`, `apps/api/src/review/review-items.service.ts:521`, commit `6ac1894` | |
| ORD-015 | Defect 11: rejecting a waiting draft did not change the row's outcome | `plans/rethink/agent-scope-14-kickoff.md:62-86` (2026-07-27) | DONE | `apps/api/src/feed/agent-feed.service.ts:310`, commits `6ac1894`, `124c4c4` | The review join is now status-agnostic; `124c4c4` pins the where-clause because the defect could regress test-clean. |
| ORD-016 | Defect 12: the label/mark tag moves to its own line inside the outcome cell, never a new column - "visually separate but subtle" | `plans/rethink/agent-scope-14-kickoff.md:83-84` (2026-07-27) | DONE | `apps/web/src/routes/agent/Activity.tsx:956`, `apps/web/src/routes/agent/activity.css:83`, commit `6ac1894` | Code is in; the LOOK has never been seen by the operator. Every test behind it is jsdom, so "subtle" is still glance-gated - see ORD-060. |
| ORD-017 | Defect 13: reversible decisions must be overridable, scoped to `approve` only (moderate deletes are irreversible Graph calls; `dm_opener` and `crm_action` stay terminal) | `plans/rethink/agent-scope-14-kickoff.md:84-86` (2026-07-27) | DONE | `apps/web/src/routes/agent/Activity.tsx:1382-1383`, commits `6ac1894`, `124c4c4` | |
| ORD-018 | Redesign the right-hand review detail panel completely - mockup rounds required, he asked for them explicitly | `plans/rethink/agent-scope-14-kickoff.md:87-88` (2026-07-27) | OPEN | | Zero work. No mockup exists: `design/` has had no commit since 2026-07-24 and `design/tiles/` has no review or detail or panel round. The live surface is still the inline `<aside className="cm-review__panel">` in `apps/web/src/routes/agent/Activity.tsx`, structurally untouched since agent-scope-12 - only defect-13 copy was inserted. Needs a mockup round as a standalone tile under `design/tiles/` (house pattern), grounded in the real `FeedRow` and `ReviewItemRow` fields, an operator reaction ledgered in `docs/DECISIONS.md`, then the rebuild, inside `design/tokens.md`. |
| ORD-019 | Rework the filtration UI to match the filtration system that now exists - "The filtration system changed but not the UI". Also a mockup item | `plans/rethink/agent-scope-14-kickoff.md:89-91` (2026-07-27) | OPEN | | Zero work. The filter bar is still three bare `<select className="cm-input cm-activity__filter">` controls in `apps/web/src/routes/agent/Activity.tsx` - the exact surface the glance rejected. The 2026-07-24 rounds under `design/tiles/activity-filters-round-1`, `-2` and `-3` are the ALREADY-SHIPPED design and cannot be reused as the answer; a round 4 is required. Keep the existing URL-param contract and the closed `OUTCOME_FILTER_OPTIONS` vocabulary intact through the rebuild. |

## B. The standing order: FINISH THE AGENT

| ID | Order (his words where quoted) | Asked | Status | Evidence | Note |
|---|---|---|---|---|---|
| ORD-020 | Item 1: get moderation running cleanly end to end and WITNESSED. "Built and deployed, never proven clean - this is the headline" | `plans/rethink/scope-board.md:84-98` (2026-07-21) | BLOCKED | | Blocked on the operator's own eyes. All five named defects have code (ORD-021 to ORD-025) and the audit read prod as clean since 2026-07-22, but the witness is the order and only he can give it: open `/agent/activity` and the moderation console post-fix and confirm the failed-action surfacing reads right. Every test behind it is jsdom. |
| ORD-021 | Named defect: a failed moderation action reads as success in the escalation email | `plans/rethink/scope-board.md:229-244` (2026-07-21) | DONE | `apps/api/src/alerts/escalation-notifier.service.ts:122`, `apps/api/src/alerts/escalation-notifier.service.ts:128`, commit `11b69a8` | The failed branch titles the mail "FAILED - comment still public"; only the success path says "comment flagged". Console half surfaces `lastActionOutcome` and `lastActionError` on the row. |
| ORD-022 | Named defect: no detector for a silent fail-open - the moderator failing open must raise something, not just be capped | `plans/rethink/scope-board.md:229-244` (2026-07-21) | DONE | `apps/api/src/alerts/red-rules.ts:163-164`, `apps/api/src/alerts/alert-evaluator.service.ts:143`, commit `03b6afb` | Rule 5 raises `moderation:failsafe` at a tunable threshold. Never yet fired in prod because no recurrence has happened since the 2026-07-20 cap fix, so the detector is unexercised in the wild. |
| ORD-023 | Named defect: real escalations are buried by known-dead-number noise | `plans/rethink/scope-board.md:229-244` (2026-07-21) | BLOCKED | | Blocked on one operator action: tick the WhatsApp channel in Settings then Alerts then "Silence alerts per channel" (`apps/web/src/routes/Settings.tsx:1545`), which writes `alerts.config.silence.mutedChannels`. The mechanism shipped 2026-07-21 (`03b6afb`, `febdf4b`) and the code deliberately seeds the list empty - `alerting-config.ts:82`, "code never pre-mutes a number" - so it cannot close itself. The audit read prod on 2026-07-28: `alerts.config` has no `silence` key and `red.silence` was still firing 6 times a day. The alternative, auto-muting a channel Meta reports as non-CONNECTED, is NEW code and is not what was built - see ORD-046. |
| ORD-024 | Named defect: Facebook hide and delete blocked on permissions | `plans/rethink/scope-board.md:229-244` (2026-07-21) | UNVERIFIED | | The 2026-07-28 audit read prod `moderation_actions` and found `facebook hide bot ok` at 07:42 and `facebook delete human ok` at 11:42 on 2026-07-28, with every failure predating 2026-07-22 12:02. That is a prod-database claim this ledger cannot cite as `path:line`, and it was not re-run by hand. Re-run the query before flipping to DONE - and note `scope-board.md:229-244` still asserts the opposite. |
| ORD-025 | Named defect: IG delete has never once executed | `plans/rethink/scope-board.md:229-244` (2026-07-21) | UNVERIFIED | | Same prod-only evidence problem as ORD-024. The audit counted 6 successful IG deletes (2026-07-24 x3, 07-26, and two human console deletes on 07-27) and zero IG failures ever. The board line claiming "never executed" is one of the two directions the status map was wrong in. Re-run `select requested_at, channel, action, outcome from moderation_actions` before flipping. |
| ORD-026 | Item 3: Facebook posts into the Posts section - mirror the IG sync-vs-comment-time question, needs a mini-kickoff and one grill question | `plans/rethink/scope-board.md:100-106` (2026-07-20) | UNVERIFIED | | Queued behind the interpretation work. Check for any FB page-post sync path and the platform enum on the post model. |
| ORD-027 | Item 1 of the interpretation pair: the interpretation REPLACES the caption in all agent-visible context | `plans/rethink/scope-board.md:100-103` (2026-07-20) | UNVERIFIED | | Board says "seat in flight". Check whether any agent-visible context builder still concatenates the caption alongside the interpretation. |
| ORD-028 | Item 2 of the interpretation pair: stored beside-caption interpretations get rewritten standalone by a Claude seat, with no LLM re-spend | `plans/rethink/scope-board.md:100-103` (2026-07-20) | UNVERIFIED | | Board says "follow-up seat". Check whether stored rows read as standalone text or as beside-caption addenda. |
| ORD-029 | Posts: show comment replies (anyone's), with the hard rule that the AI never replies to a comment-reply but moderation still runs on it | `plans/rethink/scope-board.md:283-285` (2026-07-24) | UNVERIFIED | | Moving under the audit. Ingestion was already done (`IgComment.parentId`, both IG and FB parsers). The 2026-07-28 audit found the Posts display half and the hard rule both absent; commits `3431e99` (row carries its parent) and the reply-guard work in `apps/api/src/instagram/comment-pipeline.ts` landed AFTER that check. Re-verify all three halves. One conflict still needs an operator ruling: `comment-pipeline.ts` returns early for a reply whose parent is hidden or deleted, BEFORE any moderation pass, which contradicts "moderation still runs on it" for that subset. |

## C. Rulings in docs/DECISIONS.md

| ID | Order (his words where quoted) | Asked | Status | Evidence | Note |
|---|---|---|---|---|---|
| ORD-030 | Learned examples NEVER feed the prompt - the whole path is DELETED, not disconnected: the `examples` module type, `formatExamplesBlock`, `SystemPromptOptions.examples`, `ExamplesService`, the `skipExamples` replay flag, the `agentReview.exampleLimit` cap. Graded marks stay, replayed as a regression harness | `docs/DECISIONS.md:166-179` (2026-07-25) | UNVERIFIED | | The doc claims it is pinned by `packages/shared/src/agent/graded-examples-never-in-prompt.test.ts`, which exists. Not re-run or grepped for a partial deletion in this pass, and CLAUDE.md's stale-dist rule means `packages/shared/dist` needs checking too. |
| ORD-031 | Shadow means propose-and-approve, not "never posts" - a shadow-gated action becomes an approve item on Needs action with its draft; accepting posts through the review path audited as human, rejecting discards. No autonomous send is possible in shadow | `docs/DECISIONS.md:39-43` (2026-07-22) | UNVERIFIED | | The doc claims an invariant test in `ig-comment.service.test.ts`. Not run in this pass. Verify by mutation: force the shadow branch to send and watch it go red. |
| ORD-032 | CRM actions widen beyond add-lead - append notes, update name and fields. An action on a lead that does not exist in LeadRat is FLAGGED, never silently dropped, with a seeded per-action fallback; a duplicate add UPSERTS rather than creating a second row; every fallback leaves a run-log entry | `docs/DECISIONS.md:219-223` (2026-07-24) | UNVERIFIED | | A specification with no implementation evidence cited anywhere. |
| ORD-033 | AI and flows must not conflict, automatically - a contact mid-flow never receives a simultaneous agent reply, and a comment matched by a flow trigger draws no agent comment-reply. Precedence is designed into the engine, not left to the operator. Moderation stays first in every path | `docs/DECISIONS.md:224-228` (2026-07-24) | UNVERIFIED | | No test, file or commit cited. Check both directions: `DECISIONS.md:50-54` documents the inverse as an orchestrator default. |
| ORD-034 | A per-contact knowledge system - it must keep contact knowledge from being lost or falling out of context without bloating. Mechanism is research-first; rewriting summaries is approved spend | `docs/DECISIONS.md:163-165` (2026-07-24) | UNVERIFIED | | The doc itself says "wanted but NOT designed". `plans/rethink/contact-memory-research.md` exists - check whether it is the research round or a stub, and whether the board carries a work-package so it is backlogged rather than lost. |
| ORD-035 | Engine v2 shape: flat rules with Flows owning branching, AND/OR condition GROUPS not a flat AND-list, one rule list with type tags, retention 90d fires and 1yr failures plus escalations both as settings, condition-skips logged but filtered out by default, Reminders as a lens, reminder constants seeded and applying to NEW leads only, and the two reminder behaviours shipped as system-owned preset rules that are pausable and never deletable | `docs/DECISIONS.md:242-248` (2026-07-20) | UNVERIFIED | | A multi-part spec with no per-item implementation evidence. Each clause needs its own check; consider splitting this row when it is verified. |
| ORD-036 | Rules are deleted into Flows and the n8n-registry `Flow` screen and model retire with scope-5. End state Automation equals Flows, Sequences, Runs; the engine underneath is carried, existing seeded rules migrate, External Request still POSTs to n8n webhooks | `docs/DECISIONS.md:203-208` (2026-07-23) | UNVERIFIED | | "Retires with scope-5" is future tense and no migration commit is cited. |
| ORD-037 | A capability flag blocked on a spike must name the channel the spike is FOR - `supportsTappableButtons` defaulted false everywhere "until the IG button spike passes", silently blocking WhatsApp, the only channel the funnel engine can send on | `docs/DECISIONS.md:385-388` (2026-07-27) | DONE | `packages/shared/src/flows/render-message.ts:60`, `apps/api/src/flows/flow-engine.service.ts:809` | The default reversed for WhatsApp on 2026-07-27 and the interactive sender ships. See ORD-038. |

## D. Flows, ManyChat parity and the funnel engine

| ID | Order (his words where quoted) | Asked | Status | Evidence | Note |
|---|---|---|---|---|---|
| ORD-038 | A sender that can emit a tappable button or quick-reply - named as a no-owner gap in the ManyChat parity audit | `plans/rethink/scope-board.md:452-500` (2026-07-25) | DONE | `packages/shared/src/flows/render-message.ts:109`, `apps/api/src/flows/flow-engine.service.ts:809` | CLOSED, and the board has not been updated: `scope-board.md:475-486` and `:513-522` still describe this as unbuilt and cite symbols that moved. `manychat-parity-gap.md:115` row B2 is also stale. Correct those lines before the next seat plans against them - they are one of the two directions the status map was wrong in. |
| ORD-039 | Flows are a full ManyChat-parity funnel builder in Automation - triggers, branching, conditions, tags, custom fields, delays within Meta's window; a literal 1:1 copy of ManyChat's layout and ergonomics in our token sheet. Build the FULL system, the slice model is dead | `docs/DECISIONS.md:189-199`, `plans/rethink/scope-board.md:403-407` (2026-07-23) | OPEN | | In flight, not done, and everything remains gate-off. `channels-scope-5-kickoff.md` logs Stage 1 deployed, Stage A verified, Stages C and D through four review rounds, most of Stage G. `HANDOFF.md:60-66` puts Stage E front half - the canvas screen, flows home with folders and state pills, draft-vs-published rendering, wiring the canvas to the write path, and the Runs tab - as next up, with the backend and the ten `cm-*` primitives already merged. |
| ORD-040 | Sequences - named as a no-owner gap; his live *Client Follow-up #1* has no path to working | `plans/rethink/scope-board.md:502-545` (2026-07-25) | OPEN | | Zero hits for a sequence model in `packages/db/prisma/schema.prisma`; only the trigger vocabulary advertises `contact.sequence_subscribed` and `_unsubscribed`, pointing at an object that does not exist. Needs a `Sequence` plus `SequenceStep` plus `SequenceSubscription` model, an additive migration, subscribe and unsubscribe actions in the funnel action vocabulary, a BullMQ tick that walks due subscriptions (reuse the smart-delay resume rig), and the emitter from ORD-042. |
| ORD-041 | `{{field}}` message personalization - named as a no-owner gap | `plans/rethink/scope-board.md:502-545` (2026-07-25) | OPEN | | The interpolation engine exists (`packages/shared/src/flows/interpolate.ts`) and is used for CRM notes and JSON bodies, but message text never goes through it: `renderContentBlock` returns `block.text` raw, and neither `renderMessageNodeAsText` nor `buildFunnelInteractive` accepts a vars bag. Add the parameter, interpolate text, media captions, card title and subtitle, and button titles, feed the bag from `executeMessageNode` which already loads contact attributes, and apply the 20-char button cap and the 1024 body clamp AFTER substitution. |
| ORD-042 | A contact-event trigger emitter - named as a no-owner gap | `plans/rethink/scope-board.md:502-545` (2026-07-25) | OPEN | | Matching is 100 percent built and the catalog marks these triggers `available: true`, but nothing ever emits them: every `surface` literal reaching the funnel trigger service is `post_comment` or `dm`. Needs a hook on the contact write path calling `FunnelTriggerService.handleInbound` on create, attribute change, tag add or remove, and date reached. While in there: `story_reply` and `live_comment` are also `available: true` with no emitter, so three catalogued trigger types can never fire while the UI says they can - emit them or flip `available` to false. |
| ORD-043 | Real media sends - named as a no-owner gap | `plans/rethink/scope-board.md:502-545` (2026-07-25) | OPEN | | The funnel stringifies media instead of sending it (`[image] ${url}`, `[file]`, `[audio]`, `[video]` in `packages/shared/src/funnel-flows/render-message.ts`). `ImageMessage` and `DocumentMessage` exist in the WhatsApp union; VIDEO AND AUDIO DO NOT and must be added there and in the message builder first. Then `executeMessageNode` emits one send per media block, keeping the text render as the shadow draft and the failure fallback - the same shape the interactive path established. Multi-block ordering and the delay block's typing pause need a ruling. |
| ORD-044 | Delivery analytics for flows - named as a no-owner gap | `plans/rethink/scope-board.md:502-545` (2026-07-25) | OPEN | | `model FunnelFlowRunEvent` has no `messageId` and no `wamid`, so nothing joins a flow node to a delivery status. `gatedSend` already holds the send result and throws the id away. Add `messageId String?` plus an index and write it on the `sent` event - that alone unlocks Sent, Delivered and Read per node by joining `Message.deliveredAt` and `readAt`, which the Meta status webhook already populates. Clicked and CTR additionally need ShortLink rewriting inside funnel bodies. |
| ORD-045 | Reaction and story-mention auto-responders - named as a no-owner gap | `plans/rethink/scope-board.md:502-545` (2026-07-25) | OPEN | | No trigger kind exists for either: the catalog has no `story_mention` and no `message_reaction`. WhatsApp reactions are already parsed and thrown away; IG reactions are explicitly skipped. Needs both kinds added to the trigger union, the catalog, `watches()` and the matcher, then emitted. Note `specific_words_or_reaction` is a story_reply FILTER, not this. |
| ORD-046 | Build for WhatsApp NOW - a channel-agnostic engine, a real WhatsApp tab, configurable triggers, even while the number is Meta-disabled | `docs/DECISIONS.md:232-234` (2026-07-24) | UNVERIFIED | | Check for a WhatsApp tab in the flows trigger UI and whether triggers are channel-configurable rather than IG-hardcoded. `FUNNEL_LIVE_SEND_CHANNELS` is `["whatsapp"]` and the interactive sender ships, so the send half is real. |
| ORD-047 | Triggers copy ManyChat's real enumerated catalog, exact keywords only, Default Reply omitted. Ref-URL, QR and Ads triggers deferred but NOT abandoned - "don't abandon it" - shown greyed with a reason in the picker until built. Contact-events get one tab and one vocabulary | `docs/DECISIONS.md:235-241`, `plans/rethink/scope-board.md:594-597` (2026-07-24) | UNVERIFIED | | The greyed-with-reason UI is by definition unbuilt if no `disabledReason` or equivalent exists in the picker. Check both the catalog and the web trigger picker. |
| ORD-048 | Wire the funnel session expiry sweep to a real schedule - mid-funnel per-user state must persist across days, expire on window close and resume on re-entry, adopted as a BINDING constraint | `plans/rethink/channels-scope-5-kickoff.md:1071-1074` (2026-07-22) | UNVERIFIED | | Self-declared "NOT wired yet" and pushed to Stage B/E housekeeping; `expireStale` exists and is unit-tested but the BullMQ repeat job was never registered. No later entry records it being wired. |
| ORD-049 | One-shot IG button-support spike before Stage B closes - the code asserts IG private replies carry no tappable buttons while Meta docs and ManyChat's live funnel say quick-replies and button templates ARE supported | `plans/rethink/channels-scope-5-kickoff.md:197-203` (2026-07-23) | UNVERIFIED | | Filed as a conflict to spike, not assume, with no result in the stage log. Note the WhatsApp half of this question was settled on 2026-07-27 (ORD-037) without the IG spike being run. |
| ORD-050 | Smart-delay fork: a contact who replies mid-wait currently gets no answer. Recommendation attached - let the agent answer while the flow keeps waiting. Needed before any flow goes live | `HANDOFF.md:54-56` | UNVERIFIED | | An unratified fork with a recommendation attached, awaiting his ruling. |
| ORD-051 | Freeze keyword-trigger creation, inventory every existing FlowRule with its proposed destination, and record it for the operator's ruling - each legacy rule becomes situation content or is listed for a kill decision, nothing dropped silently | `plans/rethink/automation-scope-1-kickoff.md:87-91` (2026-07-21) | UNVERIFIED | | Explicitly incomplete in its own stage log: "FRAMEWORK DONE, recorded-inventory table is empty pending that live query". The freeze is claimed proven in code; the per-rule inventory in `plans/rethink/automation-scope-1-migration-inventory.md` and his ruling are not. |
| ORD-052 | LeadRat-side webhook wiring, plus his live look | `plans/rethink/README.md:35` (e2e proven on prod 2026-07-22) | UNVERIFIED | | The only item left open on automation-scope-1. |

## E. App shell, design system and analytics

| ID | Order (his words where quoted) | Asked | Status | Evidence | Note |
|---|---|---|---|---|---|
| ORD-053 | "When I say sweep, I mean a sweep." Build a permanent conformance oracle and sweep EVERY routable screen to ZERO violations, no fences - Agent, Settings, Inbox, Contacts, Campaigns, Templates, Flows, dormant screens included | `plans/rethink/appshell-scope-5-kickoff.md:23-44` (2026-07-22) | UNVERIFIED | | The oracle exists (`apps/web/src/styles/conformance.test.ts`) and is the precedent this ledger's test follows. But `plans/rethink/README.md:28` records roughly 1925 legacy markers remaining under a ratchet - a ceiling, not the zero he ordered. Read the baseline constant and decide whether the ratchet is an accepted answer or a converted order; that is a ruling, not a check. |
| ORD-054 | Build the form-control primitives `cm-select`, `cm-input`, `cm-field` on the tokens - unstyled native fields and dropdowns are everywhere, Settings worst | `docs/DECISIONS.md:332-333`, `plans/rethink/scope-board.md:356-361` (2026-07-24) | DONE | `apps/web/src/components/ui/ui.css:135`, `apps/web/src/components/ui/ui.css:145`, `apps/web/src/components/ui/Input.tsx:10`, `apps/web/src/components/ui/Field.tsx:21`, `apps/web/src/components/canvas/CmSelect.tsx:15` | `cm-select` was built by the canvas seat and lives in `components/canvas`, not beside its siblings in `components/ui` - a sweeper importing from `components/ui` finds no Select. Rehome or re-export it before ORD-055 starts or a second seat reinvents it. |
| ORD-055 | Then retire the raw controls screen by screen, Settings first - the sweep half of the same order | `docs/DECISIONS.md:332-333`, `plans/rethink/scope-board.md:356-361` (2026-07-24) | OPEN | | Not started. The 2026-07-28 census counted 244 raw controls outside tests (155 `<input>`, 64 `<select>`, 25 `<textarea>`); the kickoff measured 249 on 2026-07-27, so about 5 retired while the order stood. Settings alone still has 38 raw controls and only 2 `cm-input` usages, while importing the primitives and hand-rolling inline-style fields anyway. Owner is `appshell-scope-6` Stage 3 and its stated dependency (the primitives seat) is now satisfied. Also missing: a sixth rule in `apps/web/src/styles/conformance-scan.ts` for a raw control without the `cm-*` chrome class, baselined shrink-only, or the sweep can silently regress. |
| ORD-056 | App-wide `so-btn` and `so-pill` sweep - retire the legacy button and pill chrome across Campaigns, Contacts, Inbox, Templates, Settings and Flows. Deferred out of appshell-scope-4 because no `cm-btn` primitive existed; it exists now | `plans/rethink/appshell-scope-4-kickoff.md:170-181` (2026-07-20), count restated `plans/rethink/README.md:34` (2026-07-27) | UNVERIFIED | | The index states 314 usages across 40 files remaining, with the Agent-menu half done. Re-count before planning: that number is a doc claim, and doc claims in this repo have been wrong in both directions. |
| ORD-057 | appshell-scope-6 design sweep, staged by leverage: `global.css`, then shared components, then form controls, then live screens. He added: refactor the HTML and semantics too, not just the styling | `HANDOFF.md:73-76` | UNVERIFIED | | The semantics half is the part most likely to be quietly dropped - it is not measurable by the token conformance oracle. |
| ORD-058 | Analytics scope-2 steers: kill the "five questions" and "QUESTION n" framing in the UI, labels are nouns, NO question-phrased copy anywhere including headings, full-width layout, every card leads with its answer | `plans/rethink/scope-board.md:623-636` (2026-07-22) | DONE | `apps/web/src/routes/Analytics.tsx:123`, `apps/web/src/routes/analytics.css:9`, `apps/web/src/routes/Analytics.test.tsx:110-120`, commits `db86e6d`, `2ac23bd` | The grep bars are pinned as tests. One stale JSDoc in `Analytics.tsx` still says "its heading is the question it answers" and now contradicts the code. |
| ORD-059 | Analytics redo - verdict on the live screen is negative, "still confusing and you really don't know what to look at or how". He will redo it with 21st.dev components and wants to define WHAT to show and HOW first | `plans/rethink/scope-board.md:29-33`, `HANDOFF.md:57-58` (2026-07-27) | BLOCKED | | Blocked on two operator decisions, both his alone. First, the definition round: no artifact anywhere states what analytics should show and how - no `analytics-scope-3-kickoff.md`, no definition doc, no grill ledger. Second, the 21st.dev ruling (ORD-062), which decides whether a dependency lands. Clears when he runs the definition round; then the rebuild of `apps/web/src/routes/Analytics.tsx` and its drill is ordinary work. The API side is fine - no new metrics were ordered. Doc conflict to fix while there: `plans/rethink/README.md:27` still says the package waits on his live reaction, but the reaction was given on 2026-07-27 and failed. |
| ORD-060 | Re-glance Activity and Behaviour after the fixes - the post-fix screens have never been seen | `HANDOFF.md:40-44` (prior glance 2026-07-27, which failed and produced agent-scope-14) | BLOCKED | | Blocked on the operator's glance; nothing technical remains. Defect 12 (ORD-016) can only close on his eye because "visually separate but subtle" is a look and every test is jsdom. |
| ORD-061 | Analytics data correctness - "the analytics doesn't seem complete and even retrieve the data. there is fields and data that exist but the analytics says it doesn't exist." Deliverable: a per-metric truth table, endpoint value against an independent SQL count, all matching on prod, every mismatch root-caused at source and pinned with a test | `plans/rethink/analytics-scope-2-kickoff.md:52-71` (2026-07-22) | UNVERIFIED | | Two fix commits landed on `apps/api/src/analytics` (`a2edfbd`, `b0fe928`) and are not in any stage log. The ordered ARTIFACT - the truth table itself - was not found by the audit. Confirm whether the table exists anywhere, or whether the fixes closed the symptom without producing the deliverable. |
| ORD-062 | 21st.dev fork - confirm the ruling (reference by default, adopt a specific element when wanted) before the shared components are rebuilt | `HANDOFF.md:48-50` | BLOCKED | | Blocked on his confirmation; blocks `appshell-scope-6` Stage 2 and ORD-059. The standing recommendation is reference-only, rebuilt as `cm-*` on `--cm-*` tokens, because 21st.dev is React plus Tailwind plus shadcn/Radix and this app has none of those. His answer changes whether a dependency lands. |
| ORD-063 | A tokens.md sheet amendment is owed - the sheet has ONE floating-shadow token but the canvas spec wants two weights, sm on a node and lg on a panel | `HANDOFF.md:95-96` | UNVERIFIED | | The `cm-*` primitives currently fall back to `--cm-shadow-raised` and flag it inline. `design/tokens.md` is law, so this is an amendment to the sheet, not a local override. |
| ORD-064 | Two live Agent Overview defects he found himself: Overview's redirect applies INVISIBLE filters that need a refresh to remove (make them visible and clearable in place, repro test first), and channels are missing on Overview (derive the list from the shared registry, never hardcode) | `plans/rethink/appshell-scope-5-kickoff.md:9-20` (2026-07-22) | UNVERIFIED | | The kickoff's entire stage log is "kickoff written; Seats G+H dispatched". No stage entry, no commit and no oracle output for this seat anywhere. |
| ORD-065 | Playground case settings are incomplete AND wrong - not all channels listed, impossible combos selectable. Every real channel listed, impossible combos unselectable, the matrix derived from the shared channel/case registry, with a test that pins the UI matrix to the engine registry so drift is impossible | `plans/rethink/agent-scope-9-kickoff.md:16-17` (2026-07-22) | UNVERIFIED | | The decisive artifact is the drift test. If no test imports the shared registry and asserts the UI matrix equals it, the order's core bar is unmet regardless of how the screen looks. |
| ORD-066 | No auto-interpretation of new posts on live. Code wiring was verified complete, so the failure is prod-side - "Root-cause it, don't symptom-patch": diagnose on the VPS, fix the root cause, deploy, and prove it with a live interpreted post | `plans/rethink/agent-scope-9-kickoff.md:22-26` (2026-07-22) | UNVERIFIED | | `plans/rethink/README.md:22` asserts all seats landed and verified, but cites no commit, file:line or oracle output, and a formal gate note is still owed. The proof this order demands is a live interpreted post. |
| ORD-067 | Comment replies must CTA to DMs, and the CTA is DATA - "Seed = data (seeded default, operator editable), never a hardcoded constant" | `plans/rethink/agent-scope-9-kickoff.md:18-21` (2026-07-22) | UNVERIFIED | | SUPERSEDED by ORD-012 on 2026-07-27, which reverses the direction: we open the DM ourselves and the public reply says to check the DMs. The CTA text is live at `packages/shared/src/agent/comment-behaviour.ts:29`, but the data half of the order (seeded and operator-editable, with the golden pin changes logged) was never verified. Kept as a row because the supersession is itself a fact a future session needs. |
| ORD-068 | Knowledge screen is below the bar - "behaviour and knowledge is not at the level of customizability and transparency that I want". Knowledge needs its own package | `plans/rethink/scope-board.md:133-137` (2026-07-17) | UNVERIFIED | | The Behaviour half was answered by agent-scope-2 Stage 6. The Knowledge half has a candidate scope number and no kickoff. agent-scope-13, 14 and commit `3bda3bb` all touch KB - check those before concluding it is unowned. |
| ORD-069 | Console keyboard shortcuts are dead on live | `plans/rethink/scope-board.md:114-116` (2026-07-18) | UNVERIFIED | | Board says a fix seat is in flight and that tests passed via direct element events, which is exactly the shape of a live focus gap: check whether the handler is bound to document/window or to an element that may never hold focus. The only real oracle is a live click-through. |

## F. Channels: Instagram DMs, Facebook, deploys and spikes

| ID | Order (his words where quoted) | Asked | Status | Evidence | Note |
|---|---|---|---|---|---|
| ORD-070 | IG DMs - two operator actions to unblock the only live Meta blocker: Instagram app, Settings, Messages and story replies, Connected tools, Allow access to messages (mobile only), then add the Messenger product to the app | `HANDOFF.md:26-32` (verified 2026-07-28) | BLOCKED | | Blocked on the operator, on his phone; nothing technical is claimed remaining. Clears when both toggles are set and the Messenger product is added to the app. Before any DM work, inspect the VPS `git stash` holding `ig-dm-spike.mjs` per `HANDOFF.md:99-100` - its `--subscribe` replaces the whole field list. |
| ORD-071 | Stage 0 spike, SEND half: "ONE message, operator-witnessed" test DM via Graph to prove `instagram_manage_messages` works at standard access - the precondition for flipping `instagram:dms` from off to shadow | `plans/rethink/channels-scope-1-kickoff.md:82-93` (approved 2026-07-16; subscribe half run 2026-07-22) | BLOCKED | | Blocked on the operator: the send half was deliberately not run and needs him to witness it. His decision 2, approving the shadow DM turns' LLM spend, is also still open. Clears when he witnesses one test DM. |
| ORD-072 | Gate flips remaining - `instagram:comments` is still shadow, `instagram:comment_to_dm` unset and off, `instagram:dms` off. comment_to_dm is safe to flip because moderation runs first, but do not strip the public DM-CTA until the private-reply path ships | `HANDOFF.md:33-39` (read live 2026-07-28) | BLOCKED | | Blocked on the operator's flip decision, which is downstream of the witness in ORD-020 and ORD-060. The ordering constraint is load-bearing: see ORD-011. |
| ORD-073 | "What about facebook dm?" - build the Facebook Messenger RECEIVE arm: a real Messenger DM produces a webhook event ingested rather than dropped as unknown, a `facebook` contact plus conversation plus inbound message, visible in Inbox with its Facebook chip, shadow trace. Gate stays OFF, no send arm | `plans/rethink/channels-scope-4-kickoff.md:1-15` (2026-07-22) | UNVERIFIED | | The entire stage log is "kickoff written; Seat I dispatched". Check whether `packages/shared/src/meta/classify.ts` has a page-`messaging[]` arm at all, or whether such a payload still classifies unknown. |
| ORD-074 | Ship the FB comments work, Stages 0 to 2, to prod: deploy, health-check, web publish. Three new cross-module DI edges only resolve at RUNTIME | `plans/rethink/channels-scope-2-kickoff.md:656-673` (2026-07-17, deferred with no closing date) | UNVERIFIED | | The kickoff's own last word is "Not yet deployed/health-checked or web-published", while `plans/rethink/README.md:37` says the stages are shadow-live. That is a direct contradiction between two documents and a session must not silently reconcile it - check prod HEAD and `pm2 logs channels-api` for a Nest DI resolution error, then fix whichever doc is wrong. |
| ORD-075 | Before any live flip of `facebook:comments:moderation`, an empirical hide test on ONE real comment is REQUIRED - which permission gates hiding a USER's comment is publicly undocumented, so the rescope list alone is not proof. Operator-witnessed, one comment, read-back confirmed | `plans/rethink/channels-scope-2-kickoff.md:389-395` (2026-07-18) | BLOCKED | | Blocked on the operator witnessing one hide on one real comment with the read-back confirmed. No execution is recorded anywhere. `plans/rethink/README.md:37` records a different outstanding test (gate bar 2), not this one, so this bar is easy to miss at flip time - which is why it was written down. |
| ORD-076 | Deploy the funnel and ManyChat-cutover work - the stage log says it in the imperative and flags the cost, "STILL NOT DEPLOYED, 30 commits now undeployed", with a mandatory 7-step checklist that builds `@channels/shared` FIRST and two pending Prisma migrations | `plans/rethink/channels-scope-5-kickoff.md:672-709` (2026-07-25) | UNVERIFIED | | Last recorded attempt failed on an SSH timeout. Compare prod HEAD against `origin/rethink` for the real gap, check `prisma migrate status`, and note the doc's own warning: stored old-shape graphs may fail `FunnelGraphSchema.parse` after the restart, and `BasicFlowEngine` takes an optional param so a missing provider degrades silently to "no funnel on WhatsApp" rather than failing boot. |
| ORD-077 | Retire `routing.ts` - agent-scope-4 Stage 6c, described as a byte-noop with 0 custom cases. Deferred and queued | `plans/rethink/README.md:33` | UNVERIFIED | | If the file still exists with live importers, the stage never ran. Per CLAUDE.md, also check `packages/shared/dist` for an orphaned `routing.js` if the source is ever deleted. |
| ORD-078 | WhatsApp silence must surface as a standing UI status, not repeat notifications - the detector is correct, its output spams while the number is known-dead and buries real escalations | `docs/DECISIONS.md:277-279`, `plans/rethink/scope-board.md:286-289` (2026-07-24) | OPEN | | The detector is untouched and correct, and per-channel muting shipped 2026-07-21 (see ORD-023, which is the operator half). Three gaps remain, all code: (1) COLLAPSE does not exist anywhere - each repeat of one dedup key inserts a fresh notification row every cooldown window, and the audit counted 70 rows for `channel:silent:whatsapp` in 14 days; collapse at write time in `AlertDeliveryService.deliver` by updating the unread row with a repeat count. (2) The standing status on Home says "silent" but not WHY - add `muted` and the Meta phone-number status to the Home channel payload and render a distinct pill, "number disabled by Meta, alerts muted", in info tone rather than warn. (3) The known-dead signal already exists as `phoneNumberStatus`/`isRestricted()` in the analytics service and is consulted by neither the detector nor Home. |
| ORD-079 | A formal gate note is owed on agent-scope-9 after the operator glance - all seats landed and verified 2026-07-22, package still marked OPEN | `plans/rethink/README.md:22` | UNVERIFIED | | Process debt, and the mechanism behind failure mode 2: a package that closes without a gate is never re-read against the order list. ORD-064 through ORD-067 are that package's orders and every one of them is UNVERIFIED for exactly this reason. |
