phase: 104_chip_sizing_question_cap
Build and Push Containers / build-and-push-app (push) Successful in 1m48s
Build and Push Containers / build-and-push-db (push) Successful in 12s

All completion criteria verified. Final report:

**Phase 104 — final verification pass: all green**
- Verified (no code changes needed): single-line ellipsized `.suggestion-chip` CSS + deleted `.maybe-try` override, `renderChips` full-text `title` + `aria-label`-when-clipped, `maxlength="4000"` + `#char-count` counter + `handleSend` over-cap guard, unit pins file, schemas boundary pins, dedicated E2E suite.
- E2E (isolation): `uv run pytest tests/e2e/test_chip_sizing_question_cap.py -v --no-cov` → **6 passed**; regressions: `test_suggestion_chips.py` 8 passed, `test_pinned_composer.py` 4 passed, `test_responsive_polish.py` 7 passed, `test_chat_history.py` 5 passed.
- `uv run pytest` → **2102 passed**; `--cov=app` → **99%** (>90%); `uv run ruff check . && uv run pyright` → clean, 0 errors.
- Criteria: chip E2E (single-line, clipped, title+aria-label full text) ✅; paste caps at exactly 4,000, send streams, counter hides ✅; programmatic 5,000-char fill → banner, no turn, text kept ✅; 4,000/4,001 boundary pinned + HTML maxlength == JS constant cross-file pin ✅.
- Diff scope: `frontend/`, new unit file, `tests/unit/test_schemas.py`, new E2E file, phase files — **no `app/` diff, no migration, no `shared.js` diff**.
- Deviations: 4 regression test files touched — 2 genuine DOM-pin conflicts from the new `#char-count` child (explicitly anticipated by the overview) + 3 documented **pre-existing E2E flake fixes** (smooth-scroll race, tab-walk heuristic, 10 ms timeout), each verified pre-existing on the pre-phase-104 tree.
- No commit made (harness commits per the execution protocol override).
- Next pending phase: `98_sync_summary_visibility`.
This commit is contained in:
2026-09-12 19:45:00 -04:00
parent 1f1c01c9f7
commit ecc921098a
31 changed files with 1874 additions and 36 deletions
+31 -8
View File
@@ -703,14 +703,6 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
gap: 0.45rem;
padding-inline: 0.25rem;
}
/* As flex items these chips must be allowed to shrink (min-width:auto
would let a long title-derived chip exceed the column on phones —
phase 07 overflow fix); the label text then wraps inside the pill. */
.maybe-try .suggestion-chip {
min-width: 0;
max-width: 100%;
}
/* ---------- Steering notes (phase 15) ---------- */
/* "Tune" button in the meta row of every completed brain bubble: ghost
pill, ≥44px, right-aligned after the source chips. ink-soft on surface
@@ -1230,6 +1222,21 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
margin: 0;
padding: 0;
}
/* The shared chip pill — both chip rows (the onboarding row in the
empty state and the "Maybe try" row under a deflected bubble) reuse
it. Phase 104 (owner 2026-09-12, A1): a chip is ONE line at every
viewport width — `white-space: nowrap` stops a 400-char question
from wrapping the pill into a multi-line "chonk"; `overflow: hidden`
zeroes the flex item's automatic minimum size (min-width:auto would
pin the item to the full text width), so `max-width: 100%` binds and
`text-overflow: ellipsis` clips the text at the row edge — in the
desktop wrap row 100% is the chat column, in the ≤640px row
(nowrap + overflow-x: auto) 100% is the VISIBLE width and the row
scrolls. That clipping is the phase-07 overflow fix the old
"Maybe try" chip override used to carry (min-width:0 + max-width:
100%) — now fully subsumed here and deleted, provenance folded into
this comment so the history lives with the contract. The full text
stays one hover away (the title tooltip, phase 104 task 02). */
.suggestion-chip {
font: inherit;
font-size: 0.92rem;
@@ -1242,6 +1249,11 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
min-height: 44px;
cursor: pointer;
transition: background 0.15s ease, transform 0.05s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
min-width: 0;
}
.suggestion-chip:hover { background: var(--brand-soft); }
.suggestion-chip:active { transform: scale(0.98); }
@@ -1321,6 +1333,17 @@ details.thinking .thinking-text ul { margin: 0 0 0.5rem; }
#view-chat:not(.chat-booted) .composer {
position: static;
}
/* Phase 104 (owner 2026-09-12, A4): the question-length counter —
right-aligned above the composer, a child of the .chat-bottom sticky
unit (between the actions row and the form), hidden until 80% of the
4,000-char cap (app.js updateCharCount). It sits on the APP
background behind the transparent .chat-bottom unit, so both pairings
are verified against --bg: --ink-soft on --bg = 8.6:1 (≥4.5:1, WCAG
AA) and the .is-max state's --err-ink on --bg = 10.4:1 (AA). The
.is-max state ALSO changes the copy ("— character limit") — text +
color, never color alone (B3). */
.char-count { margin: 0; text-align: right; font-size: 0.75rem; line-height: 1.2; color: var(--ink-soft); }
.char-count.is-max { color: var(--err-ink); }
.composer {
display: flex;
align-items: flex-end;