# Agent scope — work-package 1E: Posts v2 (the reacted design lands)

> **Kickoff plan. Written by Fable (the senior seat), 2026-07-16.**
> Small package: it implements the operator's LOCKED reaction-round results
> (rounds 5 → 5d, locks in `docs/DECISIONS.md` 2026-07-16). No open design
> questions remain except the hover overlay's exact info, which iterates live.
> Contract mocks: `design/tiles/round-5c/posts-ig-grid.html` (grid) +
> `design/tiles/round-5d/posts-detail-v2.html` (detail + comments + hover A).

## Locked scope (build exactly this)

1. **IG-profile grid** (`Posts.tsx`): tight square thumbnail tiles (4/row
   desktop, 3 phone), bottom scrim with comment count + interpretation-state
   dot, NO captions on tiles. **Tabs: Organic | Ads & boosted** (separate
   tabs, not groups).
2. **Thumbnails, real**: additive `SocialPost.thumbnailUrl` (+`thumbnailFetchedAt`)
   columns; populated from Graph (`thumbnail_url`/`media_url`) during sync and
   ads ingest. IG CDN URLs expire — store + refresh on each sync/ingest, render
   with a graceful CSS-placeholder fallback on broken images, and mark the
   ceiling with a `ponytail:` comment (proxy/refresh-on-403 is the upgrade
   path; do NOT build it now — consult first if the fallback proves ugly).
3. **Hover = variant A** (IG center overlay): comment count + state
   icon+label. Content of the overlay is deliberately iterable — keep it one
   small component.
4. **Per-post view**: IG-structure caption block (bold account inline,
   hashtags, "Edited" line when edited); comments as boxless IG rows (bold
   username inline, muted `time · Reply` meta row, tiny muted decision pill,
   SUBTLE `details ⌄` expanding the full decision block downward: decision,
   confidence meter, reason, moderation line, view-trace link). TracePanel
   stays reachable from the expansion.
5. **Interpretation states renamed/simplified everywhere they render**:
   `interpreted` / `edited interpretation` / `not interpreted` + the
   `no caption available` edge (caption fetch failed — shown on ads posts
   whose Graph read gave nothing). Ads-ness is expressed ONLY by the tab.

## Bar

Web + api suites green; typecheck/lint/builds exit 0; additive migration only
(the two thumbnail columns); the four states render per the lock; tabs pinned
by test; no em-dashes; tokens law; committed; fence held (no App.tsx /
AppShell.tsx / Home / Dashboard / Settings edits — a concurrent package owns
them); deferrals to the board.

## Stage log

- **DONE + GATE 1E: PASS (Fable, 2026-07-16).** All five locked items built
  TDD, fence held, merged at `e780511`; gate-verified fresh on the combined
  tree: shared 664 · api 1106 · web 506 · worker 47; lint 0 errors; typecheck
  + all builds exit 0. Additive migration `20260716090000_social_post_thumbnail`
  (pending deploy). Fable rulings: the caption "Edited" line correctly OMITTED
  (Graph exposes no edit signal — never fake one; `caption_edited_at` filed to
  the Agent backlog); on-dark dot literals in the overlay accepted (tokens
  illegible on the scrim, commented). Post-deploy: operator views the real
  grid (thumbnails populate at the next real sync) and iterates the hover
  overlay content live.
