Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 70 additions & 5 deletions docs/family-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,75 @@ captured from this worktree with a scratch sqlite database.
- **Email preview frame stays light in dark mode** — sanctioned: it renders
message content, not app chrome.

## Independent judge rounds (2026-10-02, evening)

The self-review above was later checked by independent visual judges — fresh
subagents shown only the dakit family-reference screenshots and full-page
captures of every operator surface (14 pages + drawer, 1440x900 and 390x844,
light and dark). Evidence lives in `/tmp/relay-family-judge/`
(`judge-round1.md`, `judge-round3.md`, `judge-round4.md` under
`shots-roundN/`; rounds 5–6 under this worktree's `tmp-family-judge-r5/`
and `tmp-family-judge-r6/`).

- **Round 1 — FAIL (14/15 surfaces FAMILY-BREAK).** The shell itself was the
gap: navigation pills in the toolbar, a bare "Menu" phone bar, free-floating
section headings over loose tables, card grids instead of segmented strips,
missing stat dots and empty-state pills. Fixes: `05c4e5d` (bundle refresh),
`5201a86` (global nav moved into the sidebar, quiet icon toolbar),
`9016a80` (page title in the phone top bar).
- **Round 2 — PASS at `5912faf`** (zero blocking findings; the judge
recommended re-judging since `12983d7`/`3bd10ad` were about to touch
the same templates).
- **Round 3 — FAIL on the content layer and mobile; shell now passes.** The
judge confirmed the shell frame ("the family gap is now in the content layer
and on mobile, not in the shell frame") and flagged: broken mobile
table-to-row conversion (no padding, mixed label fonts), underlined accent
row links, strips without visible hairlines (cell backgrounds painted over
the pulled borders) and a dark wrong-surface strip, unpadded filter panels,
an empty badge cell, redundant decorative pills, ragged per-row actions, and
tinted code blocks on api-docs. Fixes: `7d7dcb3` (dashboard panels, stat
dots), `8abe727` (list pages into banded panels with count meta and empty
pills), `b73b23f` (detail pages, forms, and API docs into the panel pattern;
segmented stage control), plus `12983d7`/`3bd10ad` (gap-technique strip
hairlines on the surface tint, ink row links with hover underline, bordered
mobile record cards with label/value lines, content-width phone controls,
pill discipline, em-dash empty markers, drawer close control).
- **Round 4 — FAIL on one blocking item (B-1)**: campaign-detail summary
and client-detail identity meta rendered as loose stat boxes instead of
single segmented-strip containers, plus eight polish findings (stacked
badge stretching, label colons, group-label letter-spacing, underlined
row links, mobile record padding, empty badge cells, floating inline
form actions; the missing global search stayed a documented deviation).
- **Round 5 — PASS**: B-1 and every polish item verified fixed on the
surfaces they named; two new findings were refinement-level (strip
hairline fidelity, one width inconsistency) with no recipe violations.
- **Round 6 — PASS, final fresh-judge verification** (evidence:
`tmp-family-judge-r6/VERDICT-round6-judge-cc6affd.md`): all ten rubric
criteria hold in both themes at both viewports against the dataops
references, with B-1 and the polish items confirmed fixed from direct
visual evidence. One non-blocking follow-up recorded (P-9: ~20
hardcoded `font-size` literals instead of `--dk-text-*` tokens — the
dataops reference shares this habit; re-point them in a later
token-discipline pass).
- **Round 7 — PASS, the icon-geometry migration** (evidence:
`tmp-family-judge-r7-icons/`, 34 renders, both themes, 1440 and 390):
dakit `71a39d3` retired the 16-box/stroke-1.5 set, so all twenty
sidebar, toolbar, theme, and action glyphs moved to the 24 grid at
20px, stroke 1.8 (`d6fa542`), and the vendored bundle was refreshed to
the family focus recipe (`83e536c`, dakit `dea377c`). The judge read
every render at one family weight with no icon-bump layout regressions
and genuinely dark darks; the only finding (campaign-detail's
recipients table scrolling its clipped "Assume sent" button) is
pre-existing, reachable by scroll, and not user-rejecting.

Tests stayed green throughout (721 passed after each fix batch, and
again after the icon migration).

## Result

**Accepted.** Side-by-side with the family reference, the operator UI shares
the shell geometry, page-header scale, row rhythm, button hierarchy, status
language, icon strokes, and `--dk-*` palette in both themes. All fixes from
the completeness pass are covered by tests (721 passed) and the screenshots
listed above.
**Accepted by the owner's standard after independent judging.** Side-by-side
with the family reference, the operator UI shares the shell geometry, sidebar
recipe, page-header scale, banded panel rhythm, segmented strips, button
hierarchy, status language, icon strokes, and `--dk-*` palette in both themes
at both viewports. All fixes from the judge rounds are covered by tests
(721 passed) and the screenshots listed above.
1 change: 1 addition & 0 deletions mailing/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def __init__(self, *args, active_client=None, **kwargs):
"preview_text"
].help_text = "Optional inbox preview text shown after the subject by many email clients."
self.fields["scheduled_at"].help_text = "Optional. Leave blank to keep the draft unscheduled."
self.fields["audience"].empty_label = "Select audience"
self.fields["audience"].queryset = Audience.objects.select_related("organization").order_by(
"organization__slug", "slug"
)
Expand Down
2 changes: 0 additions & 2 deletions mailing/tests/test_operator_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,6 @@ def test_client_detail_renders_identity_status_and_api_key_rows(client, operator
assert response.status_code == 200
assert "Integration summary" in page
assert "<code>dtc</code>" in page
assert '<span class="badge success">1 active API keys</span>' in page
assert '<span class="badge neutral">1 revoked</span>' in page
assert "Key and purpose" in page
assert "Safe prefix" in page
assert "Used by public signup." in page
Expand Down
14 changes: 7 additions & 7 deletions mailing/tests/test_operator_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,14 +1242,14 @@ def test_audience_list_and_detail_render_summaries_members_history_and_events(
assert '<th scope="col">Tags</th>' not in detail_html
assert ">+2</span>" in detail_html
assert "Missing email DNS" in detail_html
assert "No MX: 1" not in detail_html
assert "Valid email: 1" in detail_html
assert "No validation data: 1" in detail_html
assert "No MX 1" not in detail_html
assert "Valid email 1" in detail_html
assert "No validation data 1" in detail_html
assert "Malformed email: 0" not in detail_html
assert "Sent: 2" in detail_html
assert "Skipped: 1" in detail_html
assert "Sent 2" in detail_html
assert "Skipped 1" in detail_html
assert "Failed: 0" not in detail_html
assert "Invalid email: 1" in detail_html
assert "Invalid email 1" in detail_html
assert "Client unsubscribe: 0" not in detail_html
assert "Hard bounced" in detail_html
assert "invalid@example.com" in detail_html
Expand Down Expand Up @@ -1413,7 +1413,7 @@ def test_audience_detail_renders_clickable_and_deferred_summary_stats(
"?suppression=hard_bounced#audience-members",
"?suppression=complained#audience-members",
):
assert f'class="stat-value stat-link" href="{href}"' in html
assert f'class="stat-value" href="{href}"' in html
# Deferred stats are present as labels but never rendered as links.
assert "Inactive" in html
assert "Opened" in html
Expand Down
2 changes: 1 addition & 1 deletion static/dakit/PROVENANCE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Vendored from dakit (15ca9e6 2026-10-02).
Vendored from dakit (dea377c 2026-10-03).
Do not edit; regenerate with scripts/sync_dakit.sh after rebuilding dakit.
5 changes: 3 additions & 2 deletions static/dakit/dist/dakit.css
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ code, kbd, samp { font-family: var(--dk-font-mono); font-size: 0.92em; backgroun

::selection { background: var(--dk-selection); }

:focus-visible { outline: 2px solid var(--dk-focus-ring); outline-offset: 2px; }
:focus-visible { outline: 3px solid var(--dk-focus-ring); outline-offset: 2px; }

[hidden] { display: none !important; }

Expand Down Expand Up @@ -319,7 +319,8 @@ code, kbd, samp { font-family: var(--dk-font-mono); font-size: 0.92em; backgroun
.dk-select { padding-right: calc(2 * var(--dk-space-3) + 10px); }
.dk-input::placeholder, .dk-textarea::placeholder { color: var(--dk-text-faint); }
.dk-input:hover, .dk-select:hover, .dk-textarea:hover { border-color: var(--dk-border-strong); }
.dk-input:focus-visible, .dk-select:focus-visible, .dk-textarea:focus-visible { outline: 2px solid var(--dk-focus-ring); outline-offset: 0; border-color: var(--dk-accent-default); }
.dk-input:focus-visible, .dk-select:focus-visible, .dk-textarea:focus-visible { outline: 3px solid var(--dk-focus-ring); outline-offset: 2px; border-color: var(--dk-accent-default); }
.dk-input:focus, .dk-select:focus, .dk-textarea:focus { outline: none; border-color: var(--dk-accent-default); box-shadow: 0 0 0 3px var(--dk-focus-ring); }

/* --- cards & surfaces ------------------------------------------------------- */

Expand Down
Loading
Loading