# Target Picture — the channels-manager rethink

> Status: **DRAFT for operator tear-apart** (2026-07-08). No code follows from
> this until the operator approves it section by section. Decision points the
> operator must still call are marked **⟨DECIDE⟩**.

## 1. What this app is

**Mission control for Silver Oak Properties' conversational channels.**
One app where the internal team (marketing, IT, admin) can answer, at a glance
and in depth:

1. **Is everything alive and behaving?** — channels connected, webhooks
   receiving, bot replying correctly, moderation catching without misfiring,
   campaigns delivering, nobody silently not receiving messages.
2. **What is the AI doing, and why?** — every reply traceable to exactly what
   the bot knew and decided at that moment. The operating principle for the
   entire agent surface: **"If I can't see it, I can't trust it."**
3. **Can I change its behaviour safely?** — knowledge, flows, triggers, post
   interpretations, tone: all visible and editable in a clean way, with a
   review queue where the bot isn't confident.

Channels: **WhatsApp** (number currently disabled — pending re-enable),
**Instagram** (app set up, connection pending — first live proving ground),
**Facebook** (future, mirrors IG comment/DM model), **website chat** (future,
custom widget). Everything is designed channel-agnostic from day one; nothing
new may be WhatsApp-shaped.

Humans do NOT reply to customers from this app as a workflow (consultants work
out of LeadRat; handoff happens in the CRM, not here). The app's human actions
are: **monitor, moderate/approve, configure, campaign.**

## 2. Design language

### Direction (settled by operator reaction)

Reference points: **Intercom's Fin agent UI** (information presentation,
per-conversation context panels, agent analytics) and **Attio's workflow
runs view** (canvas + live run list side by side). Not Linear, not Stripe.

Translated into rules:

- **Light theme first** (stakeholder preference). Dark can come later as a
  toggle; never design a screen that only works in one.
- **Near-white neutral surfaces, one restrained accent.** Color carries
  *meaning only* (status, decisions, deltas) — never decoration. Status pills
  always carry text, never color alone.
- **Typography does the hierarchy.** One typeface family (Inter or equal),
  a fixed scale (e.g. 12/13/15/18/24), generous line-height in reading
  surfaces, `SMALL CAPS`-style muted labels for field names (the Fin
  screenshots' "USER INFORMATION / NAME / COMPANY" pattern the operator liked).
- **Kill the roundness.** Radius 4–6px on cards/inputs, not pill-everything.
- **Density like Attio tables**: compact rows, clear column alignment, no
  wasted vertical air, but breathing room around groups.
- **Every field styled.** No raw/unstyled inputs anywhere. One input spec, one
  select spec, one segmented-control spec, reused everywhere.
- **4/8px spacing grid, consistent card/section chrome app-wide.** The current
  gold-vs-navy / per-page style drift dies.
- Desktop-first layouts; **Analytics and Home must also work on a phone**
  (single-column reflow) — that's the real mobile use case.

### Process (operator explicitly opted in)

Design is iterated, not decreed:

1. **Style tiles first**: 2–3 clickable HTML mockups of *our real screens*
   (Home + one Agent screen with real-ish Silver Oak data), differing in
   type/density/accent. Operator reacts; we converge in rounds — the operator
   offered "tens of rounds"; we use as many as it takes.
2. Winning tile becomes a **token sheet** (colors, type scale, spacing, radii,
   the input/table/pill/card specs) — the single source every screen uses.
3. Screens are then rebuilt on the tokens, section by section, each shown for
   reaction before the next.

**⟨DECIDE⟩ Branding:** Silver Oak-branded identity (brand colors as the
accent) vs neutral-professional with the logo simply present. Unanswered from
round 2 — the style tiles will include one branded and one neutral variant so
this is decided by reaction, not in the abstract.

### Method source: the Harness (`../../harness`, sibling repo)

The operator's harness repo holds authored design/frontend playbooks; they are
the **method layer** for this rethink — read the relevant playbook before
executing its phase (reference in place, never copy into this repo):

- **Phase 0 (tiles → tokens):** design `C1` layout · `C2` color · `C3`
  typography · `D2` tokens · `D1`/`D6` design system · `D5` a11y-at-design-time;
  reaction rounds run per `A4` usability evaluation.
- **IA/nav (§3):** design `B1` information architecture · `B2` interaction
  design. **Home/Analytics:** `C7` data viz · `B3` UX writing.
- **Phase 1+ implementation:** frontend `B1` CSS architecture · `B2` tokens ·
  `B4` font engineering · `C1` component architecture · `A1` semantic HTML.

Playbooks inform the mocks; operator reaction still decides taste. Frontend
units are `draft` — sanity-check each on first use.

## 3. Information architecture (new nav)

```
Home            — health & attention (the 90% screen)
Inbox           — all conversations, channel filter (read/monitor-first)
Agent           — the AI section (rebuilt; see §5)
Campaigns       — campaigns + API campaigns + QR + CSV as sub-views; Templates live here
Contacts        — contacts + Segments folded in as saved, channel-aware filters
Automation      — trigger → condition → action engine; Reminders is a view on it (§7)
Analytics       — rebuilt around 5 questions (§8)
Settings        — Connections (channels + LLM providers), webhooks, users, API docs
```

Cut / merged / moved:

- **Instagram comments page** → dissolves into Agent → Review queue +
  Agent → Posts (the standalone page was a stopgap).
- **Quality** → already merged; becomes one card/section inside Analytics.
- **Segments** as a nav item → becomes saved filters inside Contacts,
  channel-aware. (The segment engine stays; the lonely page goes.)
- **Reminders** as a black-box page → replaced by the Automation-backed view
  in §7.
- **Flows/n8n overlay** → already retired; stays dead.
- **Roles/permissions work** → explicitly deferred until a stakeholder asks.
- **API Docs** → stays, filed under Settings (it's reference, not a
  destination).

**⟨DECIDE⟩ Templates placement:** proposal is under Campaigns (they exist to
be sent); keeping them top-level is the alternative if template approval
babysitting turns out to be frequent.

## 4. Home — "is there an issue?"

One screen, three zones, readable in 10 seconds:

1. **Channel cards** (one per configured channel):
   connected? · webhook receiving (age of last event, with a *silence
   detector*: no events for N business-hours = warning) · agent gate state
   (off/shadow/canary/live) · last inbound/outbound · errors last 24h.
2. **Needs attention** (the only feed that matters): review-queue items
   waiting, failed/stuck sends, campaign anomalies, LLM provider errors,
   token/credential expiry warnings, delivery-failure spikes. Each row links
   straight to the fix location. Empty state = calm "all clear".
3. **Today strip**: messages in/out per channel, bot replies, leads captured,
   campaigns in flight (with live progress), token spend today.

Candidate signals the operator asked to see listed (each is a yes/no/later on
review of the mock, not now): median bot response time · bot reply rate
(replied vs skipped/silent) · review-queue aging (oldest waiting item) ·
BullMQ queue depth · DB/Redis health · Meta quality rating & messaging limits
(from Quality) · webhook signature failures · per-channel automation rate.

### Alerting (email)

Tiered, deliberately anti-spam:

- **Immediate email — "red" only**: channel/webhook down or silent during
  business hours; campaign stuck or failure spike; LLM provider hard-failing;
  health check failing. Per-issue dedupe + cooldown (one email per issue per
  X hours, not one per occurrence).
- **Daily digest** for everything yellow: review-queue summary, delivery
  failures, spend, aging items.
- Everything alertable is *also* on Home — email is the pager, Home is the
  truth.

**⟨DECIDE⟩** react to the red-list above; add/remove.

## 5. Agent — the centerpiece

The old model (module-by-module prompt internals, gates scattered, one
Behaviour textarea per audience) is replaced by a section organised around
**what the bot does**, not how the prompt is assembled. Sub-navigation:

### 5.1 Overview
Per-channel status (gate state, activity today, automation rate — the
"% handled without review" number, like Fin's), review-queue count, cost
today, model in use. The landing screen answers "is the bot okay?".

### 5.2 Activity — the transparency core
Every agent interaction (DM turn, comment decision, silence decision) as a
stream, filterable by channel/outcome/case. Opening one shows the **trace**:

- channel, case/flow it ran (client / job-seeker / comment / trigger-X),
- **exactly what it knew**: lead profile so far, retrieved knowledge chunks
  (the actual text), the post interpretation used (for comments), conversation
  window it saw,
- trigger matched (if any) and why it replied / stayed silent / skipped,
- tool calls made (CRM writes, handoffs) with their payloads,
- confidence, model, tokens, cost, latency.

This is the direct answer to "when it converses, what knowledge does it have,
what is it doing?" — a per-reply flight recorder, not a log grep. (Backend:
this requires persisting a per-turn context snapshot; today it exists only as
one log line. New capability, listed in §9.)

The Activity log is also the raw material for the operator's improvement loop:
**export a slice of traces → feed to a stronger external LLM → mine KB gaps
and behaviour fixes.** V1 of that is a clean "export selected traces
(JSON/markdown)" button; automating the mining loop is a later phase.

### 5.3 Review queue — a learning loop, not a send gate

**Settled 2026-07-09.** The queue never makes a contact wait. It behaves
differently per surface, because the contact's expectation differs:

**DMs (WhatsApp, Instagram) — the bot always answers immediately.** When it
is not confident, it *still replies*, safely: it gives the part it knows and
omits the fact it cannot stand behind, rather than guessing or going silent.
What lands in the queue is the **desired response** — the fuller answer it
wanted to give but held back — beside the safe reply it actually sent.

The reviewer's actions are therefore **not** "send this to the customer".
They are labels:

- **Accept** (without sending) — "this answer was right; the bot should have
  been allowed to say it."
- **Reject with a reason** — "this was wrong, and here is why."

Nothing is dispatched to the contact either way; that conversation moved on
long ago. The point is the **dataset**: every accept/reject with its reason
and its full trace becomes labelled training evidence for improving the
system prompt and closing knowledge-base gaps. A recurring accept is a KB
gap to fill. A recurring reject is a prompt or boundary to tighten. This is
the operator's improvement loop (§5.2) with a human label attached, and it
is the only mechanism in the product that systematically makes the agent
better over time.

**Comments (Instagram, later Facebook) — holding is fine**, because nobody
expects an instant public reply:

- low confidence on a drafted public reply → hold it, then approve / edit &
  send / reject;
- the bot wants to **delete** a bad comment but isn't confident → the comment
  is *hidden immediately* (safe default) and queued: delete / restore.

> **AMENDED 2026-07-15 (operator):** moderation is now its own pass with its
> own gate, decision set **`delete | escalate | leave`** — and **the bot MAY
> delete outright** on blatant hate/abuse/scam ("intended permanent doesn't
> deserve to be only hidden"), overturning the never-deletes rule above.
> `escalate` = hide immediately + review; review's "leave" unhides and then
> runs the reply pass. Negative feedback is escalated (hidden pending
> review), not left standing — this audience arrives from ad campaigns and
> negative comments are overwhelmingly targeted, not help-seeking. Guardrail:
> a low-confidence delete downgrades to escalate. Full ruling:
> `plans/rethink/escalation-verdict.md` + `plans/rethink/agent-scope-1d-kickoff.md`.

Across both surfaces, content that makes claims or promises beyond the KB is
**never auto-sent publicly**, and every queue item shows its full trace (the
same component as 5.2). Queue depth and aging surface on Home.

(Backend: confidence scoring, the queue model, the accept/reject-with-reason
label store, and the desired-vs-sent response pair — new capability, §9.)

### 5.4 Knowledge
Keeps the structured KB (kinds, facts, chunk view) but adds the missing
transparency: **scope visibility** — for each doc, which channels/cases see
it; for each case, a "what the bot sees" preview (already partially exists as
Under-the-hood; it becomes a first-class, readable view instead of a
collapsible afterthought).

### 5.5 Posts — per-post interpretation (new)
For Instagram (later Facebook): the list of the account's posts. Each post
shows:

- the caption (fetched from Meta),
- the **cached interpretation** the bot derived from the caption — *caption
  text only, no media interpretation* (settled) — **editable**: the operator
  can rewrite the interpretation and the edited version is what enters
  context for that post's comment replies,
- triggers attached to this post, comment activity + decisions on it.

Pipeline: new post seen (via webhook/first comment or periodic sync) →
interpret caption once → cache → operator can review/edit anytime.

### 5.6 Triggers
Keyword/phrase triggers on comments and DMs, ManyChat-replacement:

- **Scope: global (all posts) or a specific post** — both, settled. Created
  by the operator (admin), typically once, mostly global.
- **Action on match**: run an AI flow · send a canned reply ("check your
  DMs") · start a DM flow · **stay silent** (explicit suppression so an
  external automation can own the reply without the bot stepping on it).
- Trigger hits are visible in Activity with the matched rule in the trace.

### 5.7 Behaviour & Flows
The cases the bot can run (present services, qualify client, job-seeker/CV →
careers@, direct to a Silver Oak arm, follow-ups…), what each collects
(slots), what it never does, per-channel profile (which interactive elements
the channel supports — buttons/lists/CTA/links — and tone differences).
Editable per case, not one blob.

**⟨DECIDE⟩ Editor style — the biggest open fork.** Two candidates, and per
the operator's "I am not sure": **both get mocked in the style-tile phase**
and the winner is chosen by reaction:
- (a) **Structured forms** — each case a page of ordered, editable blocks
  (goal, steps, slots to collect, boundaries, examples). Cleaner, harder to
  break, less "builder".
- (b) **Canvas** — Attio/ManyChat-style node graph with a live runs panel.
  More powerful-feeling, matches the Attio screenshot the operator saved,
  heavier to build and to keep honest with what the LLM actually does.
A hybrid is likely: forms for *behaviour* (LLM-led cases), canvas for
*Automation* (§7, deterministic trigger→action), since a canvas that pretends
to control an LLM step-by-step would be a lie — flagging this tension openly.

### 5.8 Playground
Multi-channel simulator: pick channel + case, converse, interactive elements
render as they would on that channel, **full trace beside every reply** (same
trace component). Also the place to test a trigger or a post's interpretation
before it's live.

### 5.9 Model
LLM configuration, made **model-agnostic** (settled requirement):

- Connections gain a provider field beyond Anthropic — **DeepSeek-v4 via its
  OpenAI-compatible API is the concrete first target** (operator holds the
  key). Per-role model choice (main agent vs comment policy vs interpreter),
  fallback chain, timeouts.
- Cost meters per channel/case/day live here and on Overview.
- Any model swap is gated by an eval run (the harness exists; runs remain
  operator-approved spend).

## 6. Channels & Connections (Settings)

The channel-agnostic promise, finished:

- **Connections** supports every channel's credentials (WhatsApp, Instagram,
  Facebook, website-chat widget key) *and* every LLM provider — add, label,
  rotate, test ("send test / fetch profile" button per connection).
- **Webhook status per channel**: subscribed fields, callback URL, signing
  secret verified, last event received — visible, not folklore. (Today the
  operator cannot tell whether webhooks are set up for non-WhatsApp channels;
  that becomes a screen, not an SSH session.)
- **Channel filter everywhere**: Inbox, Contacts, Segments-as-filters,
  Analytics, Activity — one consistent channel chip/filter component.

## 7. Automation & Reminders

**Engine (one, generic):** trigger → conditions → actions, channel-agnostic.
Triggers: keyword/comment (shared with Agent Triggers), schedule/date, events
(lead created, lead gone quiet, campaign finished, CRM state). Actions: send
message (any channel), start AI flow, tag, notify (email), stay silent.
Every run is recorded and inspectable (the Attio runs-beside-canvas pattern).

**Reminders = a system automation with its own lens** (answering the "special
case" worry): the *engine* underneath is the generic one (schedule trigger
off CRM-assigned leads → WhatsApp message to the internal agent — WhatsApp
stays the delivery endpoint, one channel per agent, settled). What was wrong
with the old Reminders section was opacity, so the **Reminders view** shows
exactly what the black box hid: which leads are in reminder state, the
upcoming queue (who gets reminded, about which client, when), what was sent,
what failed, pause/skip per lead. Same data model as any automation run —
just a purpose-built presentation. The old n8n-built reminder system is
superseded by this, not wrapped.

**⟨DECIDE⟩** operator asked for this proposal — react to it. If it holds,
Automation/Reminders is **kept** (not cut), but sequenced last (§10) and
WhatsApp-gated parts wait for the number.

## 8. Analytics — rebuilt around five questions

Operator said "not sure", so this is a proposal to react to. The page answers
exactly five questions, one section each, all channel-filterable, phone-friendly:

1. **Volume** — conversations / comments / messages in & out, per channel,
   over time.
2. **Agent outcomes** — leads captured, CV handoffs, automation rate
   (handled vs review-queued vs human), silence/skip decisions.
3. **Campaigns** — delivered / read / replied / failed per campaign; API
   campaign call volume.
4. **Delivery health** — failure rates by error class, the "are people not
   receiving messages?" answer; Meta quality rating + messaging limits
   (absorbing Quality).
5. **Cost** — token spend per channel / per case / per day; cost per
   captured lead.

Nothing else. Anything not answering one of the five gets deleted from the
current dump.

## 9. New backend capabilities this demands (build list, not build order)

The rethink is mostly *not* a re-skin; these are real capabilities:

1. **Turn-trace persistence** — per-interaction context snapshot (knowledge
   chunks, lead profile, flow/case, trigger, tool calls, confidence, cost)
   stored and queryable. Foundation for Activity, Review queue, Playground
   traces, the export-to-stronger-LLM loop.
2. **Confidence scoring + review queue** — model self-report + rule-based
   floors (out-of-KB claims ⇒ never auto-send publicly). Per-surface
   behaviour (§5.3): DMs persist a **(safe reply sent, desired reply
   withheld)** pair and queue the desired one for an accept/reject-with-reason
   **label**, never a send; comments hold the draft, and uncertain deletions
   hide the comment first. The label store is the improvement dataset.
3. **Post ingestion & interpretation cache** — fetch IG posts/captions,
   one-time caption interpretation, editable stored version, wired into
   comment context.
4. **Trigger engine** — global/per-post keyword rules with the four action
   types incl. explicit silence.
5. **Model-agnostic LLM layer** — provider field on connections
   (OpenAI-compatible transport for DeepSeek-v4), per-role model selection,
   eval-gated swap.
6. **Webhook management surface** — per-channel subscription state +
   signing-secret verification exposed via API to the Settings screen.
7. **Email alerting** — red-tier immediate with dedupe/cooldown, daily digest.
8. **Channel filters** — `channel` columns exist (Wave 1); list endpoints and
   UIs grow the filter uniformly.
9. **Automation engine v2** — generic trigger/condition/action + run log;
   Reminders view on top.
10. **LeadRat notes strategy** — bot writes leads + appends notes; **their API
    docs are unreliable** (documented-but-unbuilt features), so nothing is
    assumed: a small **verification spike against the real LeadRat API**
    (create/append/read-back on a test lead) decides whether notes are
    append-safe or we adopt a bot-owned custom field. No specialist fields
    (budget etc.) — per their CRM specialist's instruction.
11. **Fresh code review** — operator asked; a full review of the agent,
    webhook, and send paths runs before the build phases start (the June-19
    audit predates the agent-unify waves).

## 10. Sequencing (proposal — SUPERSEDED)

> **2026-07-15: the phase queue below is superseded by the scope board**
> (`plans/rethink/scope-board.md`) — the remaining chunks are independent
> scopes run as gated work-packages, not a sequence. Operator decision;
> rationale in `plans/rethink/escalation-verdict.md` §7. Phases 0–1C shipped
> under this original model. The section is kept for the WHAT it records
> (what each chunk contains), not the order.

Pain order given: **Agent ≫ design system > analytics > home**. But every
rebuilt screen must land on the new design system, so:

- **Phase 0 — Design system via the Agent's own screens.** Style tiles =
  mocked Agent Activity/trace + Home, 2–3 directions (incl. branded vs
  neutral, forms vs canvas mocks). Operator reaction rounds → token sheet.
  No product code; pure HTML mocks. *Also in phase 0:* the fresh code review
  (§9.11) and the LeadRat spike (§9.10) — both derisk everything after.
- **Phase 1 — Agent rebuilt** on the tokens: traces + Activity + Review queue
  + Posts + Triggers first (that's the live Instagram proving ground —
  **live verification starts on IG**, settled), then Behaviour/Knowledge/
  Playground/Model.
- **Phase 2 — Home + alerting.**
- **Phase 3 — Channel-agnostic sweep**: Connections multi-channel + webhook
  surface, filters everywhere, Segments→Contacts, model-agnostic LLM +
  DeepSeek eval.
- **Phase 4 — Analytics** (the five questions).
- **Phase 5 — Automation + Reminders** (engine v2 + the lens; WhatsApp parts
  activate when the number returns).

Each phase ships to the VPS and is *used* before the next starts; every phase
ends with an operator reaction round, same as the mocks.

## 11. Collected ⟨DECIDE⟩ list

1. ✅ **Branding: neutral** + the real logo, name always "Silver Oak
   Properties" (EB Garamond wordmark). Settled 2026-07-09; token sheet
   `design/tokens.md` **approved**.
2. ✅ **Home red-alert list + today numbers: accepted as mocked.** Per-account
   Home customisation noted as a later idea.
3. ✅ **Behaviour editor: hybrid.** Canvas owns structure (routing, branch,
   merge — all deterministic); forms own content at company / case / branch
   level, one field set (goal, tone, constraints, steps, slots, examples),
   each level showing only what it fills. Inheritance is **implied, never
   displayed**. Settled 2026-07-09.
4. Reminders proposal (§7): accept/adjust. **Still open.**
5. Analytics five questions (§8): accept/adjust. **Still open.**
6. Templates under Campaigns: yes/no. **Still open.**
7. ✅ **LeadRat spike: run, verdict recorded** in
   `plans/rethink/phase-0-leadrat-spike.md`. Notes **overwrite**, they do not
   append (live data-loss bug); read-modify-write via the newly-found
   `GET /api/v1/lead/{id}` is the proven fix.
8. DeepSeek-v4 swap scope (which roles first) + eval budget approval.
   **Still open.**

Also settled 2026-07-09, outside the original list:

- **Review queue is a learning loop, not a send gate** (§5.3). DMs always get
  an immediate safe reply; the withheld desired reply is queued for an
  accept/reject-with-reason label that feeds prompt and KB improvement.
- **Arms**: the ten Silver Oak Properties service lines. Phase 1 activates
  **Real Estate Investment Advisory** and **Golden Visa Assistance** only;
  the other eight are *mentioned* by the bot, which collects generic contact
  details and routes to a human.
