agentgui 1.0.969 → 1.0.970
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/workflows/gui-design-consolidation.js +101 -0
- package/AGENTS.md +6 -0
- package/PUNCHLIST-DESIGN.md +196 -0
- package/package.json +1 -1
- package/site/app/index.html +5 -240
- package/site/app/js/app.js +7 -10
- package/site/app/vendor/anentrypoint-design/247420.css +357 -69
- package/site/app/vendor/anentrypoint-design/247420.js +12 -12
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'gui-design-consolidation',
|
|
3
|
+
description: '13th run: consolidate ALL design content into the kit (../design) and hunt every immature/half-finished design aspect across every agentgui surface; adversarially verify each against real source',
|
|
4
|
+
whenToUse: 'Run when the mandate is "all design content must live in ../design" + "fix every aspect of the design". Distinct from the 12 prior sweeps: this one is RESIDUE-FIRST - it treats any design decision still living in agentgui (index.html inline <style>, app.js inline style=, local color vars) as a defect to migrate into the kit, then sweeps the kit itself for design immaturity. Kit at /config/workspace/design; app at /config/workspace/agentgui.',
|
|
5
|
+
phases: [
|
|
6
|
+
{ title: 'Hunt', detail: 'one hunter per design lens against real source in both repos' },
|
|
7
|
+
{ title: 'Verify', detail: 'adversarial verifier per finding (kept-typography guard)' },
|
|
8
|
+
],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const KIT = '/config/workspace/design'
|
|
12
|
+
const APP = '/config/workspace/agentgui'
|
|
13
|
+
|
|
14
|
+
const COMMON = `
|
|
15
|
+
You are auditing the agentgui GUI (a Claude-Code-web-class multi-agent chat product) and its design kit.
|
|
16
|
+
Repos: app at ${APP} (client: site/app/js/app.js, site/app/js/backend.js, site/app/index.html; server: lib/http-handler.js, server.js), kit at ${KIT} (src/components/*.js, *.css at repo root - app-shell.css/chat.css/colors_and_type.css/community.css/editor-primitives.css, scripts/build.mjs; the kit is the published anentrypoint-design library and OWNS all visual CSS - the app must only wire state/callbacks).
|
|
17
|
+
THE CENTRAL MANDATE THIS RUN: all design content must live in the kit (../design). Any visual/design decision still residing in the app (index.html inline <style> block, app.js inline style= props, --agentgui-* local color vars, hardcoded colors/spacing in app.js) is a DEFECT to migrate into the kit, not to leave in place.
|
|
18
|
+
Reference bar: claude.ai/code (calm, content-first, quiet chrome, generous thread width).
|
|
19
|
+
HARD GUARDS:
|
|
20
|
+
- The middot separator, the ellipsis char, and em/en dashes in prose are KEPT product typography - never flag them.
|
|
21
|
+
- Status is words + CSS-drawn .status-dot-disc - never propose decorative glyphs.
|
|
22
|
+
- GUI fixes land in the KIT (CSS/components); app gets wiring only; index.html gets NO new !important and ideally NO inline design CSS at all.
|
|
23
|
+
Read the REAL source before claiming anything; every finding needs file:line evidence. Findings must be IMPLEMENTABLE this session (no full rewrites). Each finding states the KIT destination for the migrated/fixed design content.
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
const FINDINGS_SCHEMA = {
|
|
27
|
+
type: 'object',
|
|
28
|
+
required: ['findings'],
|
|
29
|
+
properties: {
|
|
30
|
+
findings: {
|
|
31
|
+
type: 'array',
|
|
32
|
+
items: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
required: ['id', 'title', 'evidence', 'fixLocation', 'fix', 'severity'],
|
|
35
|
+
properties: {
|
|
36
|
+
id: { type: 'string', description: 'kebab-case slug' },
|
|
37
|
+
title: { type: 'string' },
|
|
38
|
+
evidence: { type: 'string', description: 'file:line + what the source shows' },
|
|
39
|
+
fixLocation: { type: 'string', enum: ['kit', 'app', 'server', 'kit+app'] },
|
|
40
|
+
fix: { type: 'string', description: 'concrete implementable change, naming the kit destination file' },
|
|
41
|
+
severity: { type: 'string', enum: ['high', 'medium', 'low'] },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const VERDICT_SCHEMA = {
|
|
49
|
+
type: 'object',
|
|
50
|
+
required: ['isReal', 'reason'],
|
|
51
|
+
properties: {
|
|
52
|
+
isReal: { type: 'boolean' },
|
|
53
|
+
reason: { type: 'string' },
|
|
54
|
+
refinedFix: { type: 'string' },
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const LENSES = [
|
|
59
|
+
{
|
|
60
|
+
key: 'design-residue',
|
|
61
|
+
prompt: `Lens: DESIGN-CONTENT RESIDUE (the central lens). Enumerate EVERY design decision still living in the app instead of the kit. Read ${APP}/site/app/index.html lines 26-265 (the inline <style> block) in full and ${APP}/site/app/js/app.js for inline style= props. For each rule/class/var, decide its kit destination: which classes belong in a new kit CSS file (e.g. app-surfaces.css) wired into scripts/build.mjs CSS_FILES, which --agentgui-* color vars should become kit tokens in colors_and_type.css (theme-tuned light+dark), which inline app.js style= margins should become kit utility classes, and which rules are genuinely app-bootstrap-only (pre-kit-paint token fallbacks) that may stay. Flag any class that duplicates an existing kit rule (should be deleted not migrated). 6-10 findings, each naming source file:line and kit destination.`,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: 'chat-thread',
|
|
65
|
+
prompt: `Lens: CHAT THREAD design maturity (kit chat.css + agent-chat.js + chat components). Audit the flat-turn thread, role labels, assistant-turn tint, composer shell, tool cards, code blocks, streaming caret, suggestions chips, avatar mark, banners-above-thread against claude.ai/code. Find unfinished/rough edges: inconsistent spacing in the turn rhythm, tool-card states (running/error/done) visual coherence, code-block header/copy affordance polish, composer focus-ring + send-button alignment, measure/width at various viewports, empty-state starter. 4-7 findings, kit destination named.`,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: 'files-live',
|
|
69
|
+
prompt: `Lens: FILES + LIVE dashboard design maturity (kit FileGrid/BulkBar/SessionDashboard/SessionCard + their CSS). Audit density modes, multi-select affordances, thumbnails, breadcrumb, toolbar, bulk bar, session cards (status tones, stale bar, cost/token stat, arrival cue), the status-bucketed grid, heartbeat disc. Find rough edges: alignment, spacing rhythm, hover/selected states, empty/loading/error tri-states, the move/rename/delete dialogs visual polish. 4-7 findings, kit destination named.`,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: 'shell-chrome',
|
|
73
|
+
prompt: `Lens: SHELL + APPLICATION CHROME design maturity (kit shell.js WorkspaceShell/WorkspaceRail + app-shell.css .ws-*). Audit the three-column shell, fluid clamps, resizer handles, rail icon strip + labels, sessions collapse, crumb bar, drawers + scrim, thin status strip, the staged column-yield breakpoints. Find rough edges: resizer affordance/contrast, rail active-tone coherence, drawer transitions, crumb min-height/gutter alignment, status-strip ellipsis, the 60px rail label-hiding, coarse-pointer targets. 4-7 findings, kit destination named.`,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'tokens-theme',
|
|
77
|
+
prompt: `Lens: TOKENS + THEME SYSTEM design maturity (kit colors_and_type.css). Audit the token system both themes (ink/dark + paper/light): color tokens (--bg/--fg/--accent/--flame/--amber/--code-*/--rule), spacing scale (--space-*), radii (--r-*), the --measure/--density tokens, font tokens (--ff-display/--ff-mono). Find: tokens referenced-but-undefined, hardcoded hex literals in component CSS that bypass tokens (grep), WCAG contrast failures in either theme (status tones, accent button text, hairlines on paper), missing dark/light pairs, inconsistent token naming. Also: should the --agentgui-* app colors become first-class kit tokens? 4-7 findings, kit destination named.`,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: 'a11y-motion',
|
|
81
|
+
prompt: `Lens: ACCESSIBILITY + MOTION design maturity (kit CSS + components). Audit focus-visible rings (coherent across buttons/inputs/rows/dialogs?), reduced-motion guards on every transition/animation/keyframe, ARIA roles on custom controls, color-only status encoding, hit-target sizes (44px coarse), the event-flash keyframe + scroll-behavior + shell column transition + scrim fade. Find: any animation without a prefers-reduced-motion guard, any focus ring inconsistency, any motion that lives in app index.html instead of the kit. 4-7 findings, kit destination named.`,
|
|
82
|
+
},
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
phase('Hunt')
|
|
86
|
+
const results = await pipeline(
|
|
87
|
+
LENSES,
|
|
88
|
+
l => agent(COMMON + '\n' + l.prompt + '\n\nReturn findings via the structured output schema; ids prefixed ' + l.key + '-.', { label: 'hunt:' + l.key, phase: 'Hunt', schema: FINDINGS_SCHEMA }),
|
|
89
|
+
(res, l) => {
|
|
90
|
+
if (!res || !res.findings || !res.findings.length) return []
|
|
91
|
+
return parallel(res.findings.map(f => () =>
|
|
92
|
+
agent(
|
|
93
|
+
COMMON + `\nYou are an ADVERSARIAL VERIFIER. A hunter claims:\nTITLE: ${f.title}\nEVIDENCE: ${f.evidence}\nPROPOSED FIX: ${f.fix}\nfixLocation: ${f.fixLocation}\n\nRead the cited source yourself. Refute it if: the evidence is wrong or stale, the behavior is intentional kept design (especially kept typography: middot/ellipsis/dash), the fix is not implementable this session, the severity is inflated, or it proposes leaving design content in the app when it should move to the kit. Default isReal=false when uncertain. If real, refine the fix to the most concrete minimal change naming the exact kit destination file.`,
|
|
94
|
+
{ label: 'verify:' + f.id, phase: 'Verify', schema: VERDICT_SCHEMA }
|
|
95
|
+
).then(v => ({ ...f, verdict: v }))
|
|
96
|
+
))
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
const confirmed = results.flat().filter(Boolean).filter(f => f.verdict && f.verdict.isReal)
|
|
100
|
+
log(`confirmed ${confirmed.length} findings`)
|
|
101
|
+
return { confirmed: confirmed.map(f => ({ id: f.id, title: f.title, severity: f.severity, fixLocation: f.fixLocation, evidence: f.evidence, fix: f.verdict.refinedFix || f.fix, verifierReason: f.verdict.reason })) }
|
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# AgentGUI — Agent Notes
|
|
2
2
|
|
|
3
|
+
## Design-content consolidation — ALL design lives in the kit now (2026-06-18) — thirteenth run
|
|
4
|
+
|
|
5
|
+
The mandate: every design decision must live in the kit (`../design` = `/config/workspace/design`), none in the agentgui app. **`site/app/index.html` no longer carries an inline `<style>` block** — the ~240-line block (28 classes: `.pill`/`.cwd-bar`/`.resume-banner`/`.health-chip`/`.settings-grid`/`.history-empty`/`.boot-splash`/`.chat-controls`/`.status-dot`/`.ds-event-list` overrides + `event-flash` keyframe + scrollbar theming + focus rings + responsive + print) moved to a NEW kit file **`../design/app-surfaces.css`** (wired into `scripts/build.mjs` CSS_FILES, scoped `.ds-247420`, reads kit tokens directly with NO `--agentgui-*` fallbacks — those local color vars are deleted). `app.js` carries **zero inline `style=` props** (the 6 margin literals -> `.agentgui-field-mb`/`.agentgui-field-my` kit utilities; the `mainStyle` layout string -> `.agentgui-main`/`.agentgui-main-chat` kit rules). **Rule going forward: no design content in agentgui — new surface styling is a kit CSS rule, never an inline `<style>` or `style=`.** `app-surfaces.css` selectors are written pre-scoped `.ds-247420 ...` so the build's selector-prefixer (handles `:root`/`html`/`body`, compounds `[attr]`/`*`) leaves them untouched. Because `247420.css` is a render-blocking `<link>` in head and `<html class="ds-247420">` is static, there is NO FOUC from moving the base/boot-splash styling into the kit.
|
|
6
|
+
|
|
7
|
+
**Workflow `.claude/workflows/gui-design-consolidation.js`** (residue-first 13th run, 6 lenses: design-residue/chat-thread/files-live/shell-chrome/tokens-theme/a11y-motion) ran 44 agents -> 19 confirmed findings (`PUNCHLIST-DESIGN.md`), ALL landed in the kit: composer send-row `.chat-composer-toolbar` (was unstyled), role-monotonic chat hover tints, tool-card `done` success tone, base-`Chat` `.chat-empty-suggestion` styling, error-card flame inset rail, `.ds-check-box` CSS-drawn multi-select checkbox (replaced the `[x]/[ ]` bracket text in files.js+sessions.js with a bordered box that fills on `.is-marked`/`[aria-checked]` — border-drawn tick, NOT a glyph), thin 26px status strip, status-item ellipsis, `.ws-crumb` gutter align, fluid `clamp()` shell width tokens, `:focus`->`:focus-visible` (no ring on mouse click), `--warn`/`--sky` dark-theme pairs, unified `--focus-*` tokens, reduced-motion skeleton static-fill, color-blind-safe rail SHAPE differentiation + sr-only status word in `Row()`. Amber/purple hardcoded `var(--token, #hex)` fallbacks stripped (the dark-hex could wrongly paint the light theme). **`lint-glyphs` was extended** with `SCAN_ROOT_FILES` (app-shell/chat/colors_and_type/community/community-app/editor-primitives/app-surfaces.css) — the root bundled CSS previously escaped the glyph lint, which is how 39 box-drawing comment dividers in app-shell.css slipped in (now converted to ASCII). Witnessed live (localhost:3009/gm/, fresh bundle): 0 console/page errors, migrated classes resolve from the kit bundle (`.pill` cursor:pointer/radius 999px, `.ds-check-box` 15px, cwd mono), no h-scroll.
|
|
8
|
+
|
|
3
9
|
## CRITICAL — `authedFetch` must NOT set `Authorization: Bearer` behind an nginx Basic-Auth proxy (2026-06-18)
|
|
4
10
|
|
|
5
11
|
Witnessed on the boxone.off.l-inc.co.za/gm deploy: the page HTML/JS/CSS loaded (browser sends its cached HTTP Basic creds) but every app `fetch()` (`/gm/health`, `/gm/v1/history/sessions`, `/api/*`) returned **401 from nginx**, and the user saw a repeating Basic-Auth prompt. Root cause: `site/app/js/backend.js` `authedFetch` set `Authorization: Bearer <window.__WS_TOKEN>`, which **overwrites** the browser's cached `Authorization: Basic` credentials. nginx `auth_basic` only accepts `Basic`, so a `Bearer` header is rejected at the proxy before it ever reaches agentgui. The WS survived because it auths via `?token=` query on an `auth_basic off` path. Fix: thread the token via the **`?token=` query param** (`withToken()`, exactly like the WS / EventSource / image / download URLs) and never override `Authorization` — the query param coexists with the upstream Basic auth, and agentgui accepts `?token=` on every HTTP route. Rule: **same-origin app auth must never use the `Authorization` header when an upstream proxy may own Basic auth** — use the query param + the `agentgui_token` cookie. boxone runs the published `agentgui@latest`, so this reaches it only after an npm publish + service restart (or via the gmweb nginx `auth_basic off` mitigation on the agentgui-proxied API paths).
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# PUNCHLIST-DESIGN (13th run - gui-design-consolidation)
|
|
2
|
+
|
|
3
|
+
Design content consolidated into the kit (../design). 19 confirmed findings, all kit-located.
|
|
4
|
+
|
|
5
|
+
## [high] chat-thread-composer-toolbar-unstyled (kit)
|
|
6
|
+
**Composer send-button row (.chat-composer-toolbar) has zero CSS - send button wraps and misaligns**
|
|
7
|
+
|
|
8
|
+
Evidence: chat.js:535 renders h('div',{class:'chat-composer-toolbar'}, ...send/cancel buttons...) inside the flex-wrap .chat-composer (app-shell.css:2124-2138, align-items:flex-end, flex-wrap:wrap). A grep of all kit *.css finds NO .chat-composer-toolbar rule. With the textarea at flex:1 and the toolbar a default display:block auto-width sibling, on any draft that grows the textarea the toolbar wraps to its own full line, dropping the 36px send button (app-shell.css:2153) below the input instead of pinning it to the bottom-right corner like claude.ai/code.
|
|
9
|
+
|
|
10
|
+
Fix: Add the toolbar rule to the kit at /config/workspace/design/chat.css (loads after app-shell.css, so it wins on order), grouped with the existing .chat-composer-hint/.chat-composer-context rules: .chat-composer-toolbar { display: flex; align-items: center; gap: var(--space-1); flex: 0 0 auto; margin-left: auto; align-self: flex-end; } This holds the buttons in a non-wrapping row, pins them bottom-right against the flex-end composer baseline, and uses the kit spacing token (var(--space-1)) rather than a hardcoded 4px so it matches the existing composer rhythm. No app/index.html change; design stays in the kit.
|
|
11
|
+
|
|
12
|
+
Verifier: VERIFIED in the shipped bundle. chat.js:535 renders h('div',{class:'chat-composer-toolbar'}, ...) holding the composer-btn buttons + the .send/.cancel button, as a flex child of .chat-composer (app-shell.css:2124-2138: display:flex; align-items:flex-end; flex-wrap:wrap). Grepping the SHIPPED CSS confirms there is NO .chat-composer-toolbar rule: it is absent from app-shell.css (2124-2182), chat.css, and the built dist/247420.css (grep returns nothing). The only .chat-composer-toolbar rule in the repo is at src/kits/os/theme.css:1246, which build.mjs does NOT bundle (build.mjs:51-60 lists only the root CSS files), so it never reaches the app. So the toolbar ships as an unstyled default-block flex item with no guaranteed horizontal button row, no bottom-baseline alignment against the flex-end composer, and no right-pin (no margin-left:auto) — the send button is not reliably anchored bottom-right like claude.ai/code. CAVEAT on the hunter's mechanism: the specific 'textarea grows → toolbar wraps to its own line' claim is overstated — the textarea is flex:1 with min-width:0 (app-shell.css:2140), so it shrinks to yield room and the toolbar (content-width, non-zero min) stays on the line; the real defect is the missing row/align/right-pin styling, not draft-triggered wrapping. The defect and the kit-located fix are valid; severity is moderate (alignment/anchoring, not a crash), not the inflated 'send button drops below the input on every draft'.
|
|
13
|
+
|
|
14
|
+
## [medium] chat-thread-assistant-hover-inverted (kit)
|
|
15
|
+
**Assistant-turn hover tint equals its rest tint (3%) while user-turn hover (2%) is weaker than assistant rest - no hover feedback / inverted affordance**
|
|
16
|
+
|
|
17
|
+
Evidence: chat.css:690 .chat-msg-flat.them { background: color-mix(in oklab, var(--fg) 3%, transparent); } and chat.css:698 .chat-msg-flat:hover { ...var(--fg) 3%... } are identical, so hovering an assistant turn produces zero visual change (the hover-revealed .chat-msg-actions then appear with no row feedback). chat.css:699 .chat-msg-flat.you:hover is 2%, which is LESS contrast than the assistant's 3% rest tint - the hover ladder is non-monotonic.
|
|
18
|
+
|
|
19
|
+
Fix: In /config/workspace/design/chat.css, make each role's hover tint exceed its own rest tint. Repurpose line 698 as the user-turn hover and add a dedicated assistant hover: change `.chat-msg-flat:hover { background: color-mix(in oklab, var(--fg) 3%, transparent); }` to apply to the user turn, and add `.chat-msg-flat.them:hover { background: color-mix(in oklab, var(--fg) 5%, transparent); }` (above the assistant's 3% rest on line 690) plus `.chat-msg-flat.you:hover { background: color-mix(in oklab, var(--fg) 4%, transparent); }` (above the user's transparent rest). Net: assistant hover 5% > 3% rest; user hover 4% > transparent rest. All three .them:hover / .you:hover selectors are (0,3,0), winning over the (0,2,0) rest rules regardless of order, so the cascade is unambiguous. Keep line 700 (.chat-msg-flat:hover .chat-bubble reset) intact. Kit destination: chat.css (no app change).
|
|
20
|
+
|
|
21
|
+
Verifier: Verified at the cited lines in /config/workspace/design/chat.css. Line 690 sets assistant-turn (.chat-msg-flat.them) rest background to color-mix(var(--fg) 3%). Line 698 .chat-msg-flat:hover is also 3% and, being later in source with equal (0,2,0) specificity to line 690, wins on hover for the assistant turn -- so hovering an assistant turn yields an IDENTICAL background, i.e. zero hover feedback right when the hover-revealed .chat-msg-actions appear. Line 699 .chat-msg-flat.you:hover is 2%, monotonic over the user's transparent rest (line 691) but below the assistant's 3% rest cross-role. The dead-assistant-hover defect is real, at the exact lines claimed, and the cascade analysis holds. Fix is a few-line CSS edit in the kit (chat.css), no rewrite, kit-owned visual CSS. Not kept typography. Severity is modest (subtle affordance polish) but the finding is genuine.
|
|
22
|
+
|
|
23
|
+
## [medium] chat-thread-tool-done-untoned (kit)
|
|
24
|
+
**Tool-card done state has no positive tone - a completed tool call is visually indistinguishable from an idle/neutral one**
|
|
25
|
+
|
|
26
|
+
Evidence: chat.css:731-732 only tone tool-running (accent) and tool-error (flame). There is no .chat-tool.tool-done .chat-tool-status rule, so a finished tool keeps the neutral pill from chat.css:724-730 (color:var(--fg-3), 6% --fg background) - identical to any un-toned status. running/error/done therefore do not read as one coherent three-state set (claude.ai/code marks completion).
|
|
27
|
+
|
|
28
|
+
Fix: Add to /config/workspace/design/chat.css immediately after line 732: `.chat-msg .chat-tool.tool-done .chat-tool-status { color: var(--success); background: color-mix(in oklab, var(--success) 12%, transparent); }`. Use --success (var(--green-2)) rather than --green so the done pill stays distinct from the running pill, which is already accent-toned and --accent resolves to --green (colors_and_type.css:65). Kit-only change in chat.css; no app/index.html edits; rebuild via scripts/build.mjs and re-vendor the dist into site/app/vendor/anentrypoint-design/.
|
|
29
|
+
|
|
30
|
+
Verifier: Verified against real source. chat.css:731-732 tones only .tool-running (accent) and .tool-error (flame); there is NO .tool-done rule, so a completed tool falls through to the neutral default at chat.css:724-730 (color:var(--fg-3), 6% --fg bg) — visually identical to any un-toned/neutral pill. chat.js:211,217,218 confirm the kit DOES emit class `tool-' + status` with status='done' (icon 'check', pill text 'done'), so the hook class exists and the three-state set (running/error/done) genuinely lacks a positive completion tone. Evidence is accurate and current, not stale. Fix is a single minimal kit-only CSS rule (no app changes), implementable this session. Caveat refining the proposed fix: the proposed `var(--green)` collides with the running tone because colors_and_type.css:65 already defines `--accent: var(--green)` — done and running would share a hue. Use `--success` (=`--green-2`, #3A9A34) instead to keep done distinct from the accent-toned running pill while still reading as positive/ok.
|
|
31
|
+
|
|
32
|
+
## [medium] chat-thread-empty-suggestion-unstyled-variant (kit)
|
|
33
|
+
**Non-flat Chat empty-state suggestion chips (.chat-empty-suggestion) have no CSS - only the AgentChat variant is styled**
|
|
34
|
+
|
|
35
|
+
Evidence: chat.js:567 renders class:'chat-empty-suggestion' for the base Chat component's starter chips, but chat.css only defines .agentchat-empty-suggestion (chat.css:187-198). A grep finds no .chat-empty-suggestion rule, so those chips render as bare unstyled UA buttons (no pill, no border, no hover) - the empty-state starter is rough wherever the base Chat is used.
|
|
36
|
+
|
|
37
|
+
Fix: In /config/workspace/design/chat.css, extend the four existing rule selectors to also cover the base-Chat prefix (no new declarations, no new rules): line 180 `.agentchat-empty-suggestions` -> `.agentchat-empty-suggestions, .chat-empty-suggestions`; line 187 `.agentchat-empty-suggestion` -> `.agentchat-empty-suggestion, .chat-empty-suggestion`; line 197 `.agentchat-empty-suggestion:hover` -> add `, .chat-empty-suggestion:hover`; line 198 `.agentchat-empty-suggestion:focus-visible` -> add `, .chat-empty-suggestion:focus-visible`. Then `node scripts/build.mjs` and re-vendor dist/247420.css. No app.js/index.html edits.
|
|
38
|
+
|
|
39
|
+
Verifier: Evidence verified against real source. chat.js:566-567 renders the base `Chat` component's empty-state chips as `chat-empty-suggestions`/`chat-empty-suggestion`, while chat.css only defines the `agentchat-` prefixed variants (chat.css:180,187,197,198). `grep -rn "\.chat-empty-suggestion" *.css` in /config/workspace/design returns nothing, confirming the base-Chat chips fall back to bare UA buttons (no pill/border/hover). `Chat` is an exported public kit component (chat.js:546), so this is a genuine kit CSS gap. The fix is a one-line selector grouping in the kit, no app change. Severity caveat: agentgui itself ships `AgentChat` (app.js:1827), which uses the fully-styled `agentchat-empty-suggestion` (agent-chat.js:319,333,335) — so this defect does NOT affect the live agentgui surface; it only affects other consumers of the base `Chat`. So the gap is real but lower-severity than implied. Fix correctly lands in the kit per the mandate (design content stays in ../design); no app/index.html change.
|
|
40
|
+
|
|
41
|
+
## [high] files-live-checkbox-bracket-glyphs (kit)
|
|
42
|
+
**Multi-select checkboxes are raw [x]/[ ]/[-] mono text glyphs, not a CSS-drawn box**
|
|
43
|
+
|
|
44
|
+
Evidence: design/src/components/files.js:100 renders `h('span',{'aria-hidden':'true'}, marked?'[x]':'[ ]')` inside `.ds-file-check`; same bracket text in FileCell (files.js:332-ish), SessionCard select (sessions.js:189 `selected?'[x]':'[ ]'`) and SessionDashboard select-all (sessions.js:313 `'[x]'/'[-]'/'[ ]'`). The CSS `.ds-file-check` (app-shell.css:1254-1265) only colors the mono text - there is no drawn box, so the selection affordance reads as literal ASCII brackets rather than a checkbox.
|
|
45
|
+
|
|
46
|
+
Fix: Kit-only, two CSS files plus four JS sites — all in /config/workspace/design.
|
|
47
|
+
|
|
48
|
+
(1) JS — replace the bracket-text child with an empty drawn box span in all four sites: files.js:100 and files.js:337 -> `h('span', { class: 'ds-check-box', 'aria-hidden': 'true' })`; sessions.js:189 -> same span (drop the raw `'[x]'/'[ ]'` string child); sessions.js:313 -> `h('span', { class: 'ds-check-box', 'aria-hidden': 'true' })` (keep the adjacent `h('span', {}, 'all')` label). Leave all `role=checkbox` + `aria-checked` attributes untouched — AT keeps the proper name/state and the box is purely visual.
|
|
49
|
+
|
|
50
|
+
(2) CSS — add ONE shared `.ds-check-box` rule. Put it in app-shell.css near the existing `.ds-file-check` block (~line 1253) since it is the multi-select section, and it will apply to the dash controls too because the bundle concatenates all CSS:
|
|
51
|
+
`.ds-check-box{width:15px;height:15px;border:1.5px solid var(--rule);border-radius:4px;box-sizing:border-box;position:relative;flex:0 0 auto;transition:background var(--dur-snap) var(--ease),border-color var(--dur-snap) var(--ease)}`
|
|
52
|
+
Marked/checked filled + tick (drive off BOTH the kit's `.is-marked` class hook AND `[aria-checked="true"]` so it covers FileRow/FileCell (`.is-marked`) and the dash buttons (`aria-checked`)):
|
|
53
|
+
`.is-marked > .ds-check-box,[aria-checked="true"] > .ds-check-box{background:var(--accent);border-color:var(--accent)}`
|
|
54
|
+
`.is-marked > .ds-check-box::after,[aria-checked="true"] > .ds-check-box::after{content:"";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--bg);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}`
|
|
55
|
+
Indeterminate dash (dash-only) for the select-all mixed state:
|
|
56
|
+
`[aria-checked="mixed"] > .ds-check-box{background:var(--accent);border-color:var(--accent)}`
|
|
57
|
+
`[aria-checked="mixed"] > .ds-check-box::after{content:"";position:absolute;left:2px;right:2px;top:50%;height:1.5px;margin-top:-0.75px;background:var(--bg)}`
|
|
58
|
+
|
|
59
|
+
(3) Since `.ds-file-check`/`.ds-dash-select` previously sized themselves around mono text, keep their existing flex centering (`display:inline-flex;align-items:center;justify-content:center`) so the new box centers; the `font-family: var(--ff-mono)` declarations on those buttons can stay (harmless, no text) or be dropped.
|
|
60
|
+
|
|
61
|
+
(4) Rebuild: `node scripts/build.mjs` in /config/workspace/design, then copy dist/247420.{js,css} into agentgui site/app/vendor/anentrypoint-design/ per the standard vendoring flow. No app.js wiring change, no index.html change, no new !important.
|
|
62
|
+
|
|
63
|
+
Verifier: Verified against real source. The bracket-text affordance is exactly as claimed: design/src/components/files.js:100 (FileRow checkCtl) and :337 (FileCell) render `h('span',{'aria-hidden':'true'}, marked?'[x]':'[ ]'); sessions.js:189 (SessionCard select) renders `selected?'[x]':'[ ]'` directly; sessions.js:313 (SessionDashboard select-all) renders `'[x]'/'[-]'/'[ ]'`. The CSS confirms no drawn box exists: app-shell.css:1254-1265 `.ds-file-check` only sets `font-family: var(--ff-mono); color: var(--fg-3)` and recolors `.is-marked` to accent — it styles literal mono bracket text. The SessionDashboard equivalents (chat.css:497 `.ds-dash-select`, :780 `.ds-dash-selectall`) are also text-only. So all four selection controls present as literal ASCII brackets, a primitive look versus claude.ai/code's drawn checkboxes. This is NOT kept typography (the kept set is middot/ellipsis/em-en-dash only); brackets are an affordance glyph. A CSS-drawn checkbox is an interactive affordance, not a status glyph, so it does not violate the disc/words rule. The fix is kit-only and scoped (no rewrites). Severity is moderate-real (design-residue/primitiveness, not a crash).
|
|
64
|
+
|
|
65
|
+
## [medium] files-live-error-card-no-status-rail (kit)
|
|
66
|
+
**Error session cards get only a full flame border while idle/active/new get a crisp inset status rail - inconsistent tone hierarchy**
|
|
67
|
+
|
|
68
|
+
Evidence: chat.css:396 `.ds-dash-card.is-error{border-color:var(--flame)}` (full perimeter, no left bar) but `.is-stale` (chat.css:512), `.is-active` (507) and `.is-new` (766) all use `box-shadow: inset 2px 0 0 <tone>`. The most severe state (error) thus has the weakest scan signal, and on an active+error card the inset accent bar (is-active) visually overrides the error entirely since error has no inset bar of its own.
|
|
69
|
+
|
|
70
|
+
Fix: In /config/workspace/design/chat.css, add a dedicated error-rail rule placed AFTER the existing inset-bar rules so it wins precedence by source order (e.g. just after line 766's .is-new block, around the live command-center section): `.ds-dash-card.is-error { box-shadow: inset 2px 0 0 var(--flame); }`. Keep the existing line 396 `border-color: var(--flame)` for extra severity emphasis. Source order guarantees the flame inset overrides the .is-active accent inset (507) and .is-stale amber (512) when an error card also carries those classes, establishing one consistent left-bar tone language (error flame > stale amber > active/new accent) across all four states. No app changes; pure kit CSS.
|
|
71
|
+
|
|
72
|
+
Verifier: Evidence verified against /config/workspace/design/chat.css. Line 396 `.ds-dash-card.is-error{border-color:var(--flame)}` gives the most severe state only a full perimeter border, while .is-active (507), .is-stale (512) and .is-new (766) all use `box-shadow: inset 2px 0 0 <tone>`. The inconsistency is real and load-bearing: box-shadow does not merge across class rules (one declaration wins by cascade), so on an is-active+is-error card the accent inset (507) is the only bar rendered and error's left-bar signal is absent entirely — the gravest state has the weakest scan cue. Fix lives correctly in the kit (chat.css), is a one-rule addition, implementable this session, no glyph/typography concerns.
|
|
73
|
+
|
|
74
|
+
## [low] files-live-stale-amber-hardcoded-fallback (kit)
|
|
75
|
+
**Stale/idle/connecting tones still carry hardcoded #d9a93a fallbacks instead of relying on the defined --amber token**
|
|
76
|
+
|
|
77
|
+
Evidence: chat.css:468 `.ds-dash-status.is-stale{color:var(--amber,#d9a93a)}` and the same `var(--amber,#d9a93a)` literal repeats at chat.css:478,486,512,669,778. `--amber` is now a defined theme-tuned pair (colors_and_type.css:43 light #8A6512, :262 dark #D9A93A), so the inline dark-only #d9a93a fallback silently defeats the light-theme AA tuning anywhere the var ever fails to resolve and is dead duplication.
|
|
78
|
+
|
|
79
|
+
Fix: In the kit file /config/workspace/design/chat.css, strip the `, #d9a93a` fallback from all SEVEN `var(--amber, #d9a93a)` occurrences (lines 300, 301, 468, 486, 512, 669, 778 — NOT 478 as the hunter claimed; verify with `grep -n 'var(--amber' chat.css`), leaving `var(--amber)`. The token is defined for both themes in colors_and_type.css (:43 light, :262/:295/:361 dark) so the themed value is the single source and the dark-hex fallback can never wrongly paint the light theme.
|
|
80
|
+
|
|
81
|
+
Verifier: Verified against real source. /config/workspace/design/colors_and_type.css:43 defines `--amber: #8A6512` at `:root` (light) and :262/:295/:361 restore `--amber: #D9A93A` for dark — a proper theme-tuned pair that resolves in every scope. Yet chat.css hardcodes the DARK hex `#d9a93a` as the var() fallback at 7 occurrences (the hunter's 478 is wrong and it missed 300,301; actual lines are 300,301,468,486,512,669,778). Since --amber is always defined, the fallback is dead duplication; and because the fallback is the dark hex, any resolution failure would silently ship the dark tone on the light paper theme, defeating the #8A6512 AA tuning documented in the colors_and_type.css:38-41 comment. This is a kit-only, low-risk, mechanical change aligned with the single-source-of-truth mandate.
|
|
82
|
+
|
|
83
|
+
## [low] files-live-bulkbar-count-rhythm (kit)
|
|
84
|
+
**BulkBar count label tone/size is lighter than the dashboard count, breaking cross-surface selection-count rhythm**
|
|
85
|
+
|
|
86
|
+
Evidence: Files BulkBar count `.ds-bulkbar-count{font-size:var(--fs-tiny);color:var(--fg-2)}` (app-shell.css:1283) vs the live dashboard selection count `.ds-dash-count{font-size:var(--fs-sm);color:var(--fg-2);font-weight:600}` (chat.css:379) and `ds-dash-count` for 'N selected' (sessions.js:337). The same 'N selected' fact reads at two different sizes/weights between the two multi-select surfaces.
|
|
87
|
+
|
|
88
|
+
Fix: In /config/workspace/design/app-shell.css:1283 align the BulkBar selection count with the dashboard selection count token: change `.ds-bulkbar-count { font-size: var(--fs-tiny); color: var(--fg-2); }` to `.ds-bulkbar-count { font-size: var(--fs-sm); font-weight: 600; color: var(--fg-2); }`, matching `.ds-dash-count` (chat.css:379). One-line, kit-only, no app or index.html change. (Optional follow-up, not required this session: extract a shared `.ds-selcount` rule both classes reference to prevent future drift.)
|
|
89
|
+
|
|
90
|
+
Verifier: Evidence verified exactly against real source. /config/workspace/design/app-shell.css:1283 `.ds-bulkbar-count { font-size: var(--fs-tiny); color: var(--fg-2); }` (used by BulkBar, files.js:367, role=status/aria-live=polite). /config/workspace/design/chat.css:379 `.ds-dash-count { font-size: var(--fs-sm); color: var(--fg-2); font-weight: 600; }` (used in sessions.js:337-338 for the 'N selected' live-dashboard count, also role=status/aria-live=polite). Both are the multi-select selection-count surface but render the same 'N selected' fact at different sizes (tiny vs sm) and weights (normal vs 600). This is a genuine cross-surface typographic-rhythm inconsistency, not kept product typography (no middot/ellipsis/dash involved). Fix is one CSS line, kit-only (app-shell.css owns the visual CSS), no app/index.html change, implementable this session. Severity is appropriately minor.
|
|
91
|
+
|
|
92
|
+
## [medium] shell-chrome-status-strip-not-thin (kit)
|
|
93
|
+
**Status strip is 42px (--size-base) + 10px padding, not the calm thin bar the design intends**
|
|
94
|
+
|
|
95
|
+
Evidence: app-shell.css:193 `--app-status-h: var(--size-base)` and colors_and_type.css:198 `--size-base: 42px`; app-shell.css:387-388 `.app-status { min-height: var(--app-status-h); padding: 10px var(--pad-x); }` so the bottom status footer reserves ~42px min-height + 10px top/bottom padding. Reference bar (claude.ai/code) keeps chrome quiet; the prior design note targeted a 26px strip.
|
|
96
|
+
|
|
97
|
+
Fix: In /config/workspace/design/app-shell.css line 193, replace `--app-status-h: var(--size-base);` with a dedicated thin token `--app-status-h: 26px;` (decouple the status strip from the 42px control-height scale). In the same file at line 388, change `.app-status` padding from `10px var(--pad-x)` to `2px var(--pad-x)` so the 26px min-height governs the strip height while keeping a hair of vertical breathing room; `align-items: center` (line 385) keeps the single --fs-xs line centered. Leave the height calc at line 2896 and the mobile override at 2983-2988 untouched — both inherit the new value correctly. No app.js or index.html change.
|
|
98
|
+
|
|
99
|
+
Verifier: Evidence verified against real source. design/app-shell.css:193 `--app-status-h: var(--size-base)`; design/colors_and_type.css:198 `--size-base: 42px`; design/app-shell.css:387-388 `.app-status { min-height: var(--app-status-h); padding: 10px var(--pad-x); }`. So the desktop status footer reserves 42px min-height + 20px combined vertical padding (~52px) for one ellipsized --fs-xs line. AGENTS.md's screen-real-estate note documents the INTENDED design as "a thin 26px ellipsized strip (--app-status-h:26px)", so aliasing --size-base (a 42px control-height token meant for topbar/crumb controls, not a thin status strip) is a regression from documented intent and contradicts the calm/quiet-chrome reference bar. The token is also consumed in a height calc at app-shell.css:2896 (calc(100% - --app-topbar-h - --app-status-h)), and a 26px literal flows correctly there. The mobile @media block (2983-2988) sets its own padding/font, so it is unaffected. Fix is kit-local, minimal, no app change, no new !important — implementable this session.
|
|
100
|
+
|
|
101
|
+
## [medium] shell-chrome-status-item-no-ellipsis (kit)
|
|
102
|
+
**Status footer items clip mid-word instead of ellipsizing**
|
|
103
|
+
|
|
104
|
+
Evidence: app-shell.css:394 `.app-status .item { color: inherit; white-space: nowrap; }` and :400 `.app-status { flex-wrap: nowrap; overflow: hidden; }` — items never wrap and the parent hides overflow, but no `text-overflow: ellipsis` exists on `.item`, so a long agent label (app.js:558-560 `agentLabel = 'agent: ' + name + ' · ' + model`) is hard-cut with no ellipsis. The design note claims an 'ellipsized strip'.
|
|
105
|
+
|
|
106
|
+
Fix: In /config/workspace/design/app-shell.css extend the existing `.app-status .item` rule at line 394 to: `.app-status .item { color: inherit; white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }`. The `min-width: 0` is load-bearing — without it the flex item keeps its intrinsic min-width and will not ellipsize. No `max-width` or per-item `flex` override is needed: `.spread` (flex:1) already absorbs free space and the `.item` flex children (default flex:0 1 auto) will now shrink-and-ellipsize the long agent label (`right[0]`, app.js:556) instead of being hard-cut by the parent `overflow:hidden` at line 400. No app or index.html change; no new !important. Leave the existing priority-drop media query (401-402) as-is.
|
|
107
|
+
|
|
108
|
+
Verifier: Verified against real source. app-shell.css:394 `.app-status .item { color: inherit; white-space: nowrap; }` and :400 `.app-status { flex-wrap: nowrap; overflow: hidden; }` are exact and current. There is no `text-overflow: ellipsis` and no `min-width: 0` on `.item` anywhere in that block (385-403). Status items are flex children (Status() in shell.js:249-255 emits `.item` spans around a `.spread` flex:1 spacer); a default flex item has `min-width:auto`, so even with `text-overflow:ellipsis` it would refuse to shrink — `min-width:0` is genuinely required. The agentLabel (app.js:555-556, `'agent: '+name+' · '+model`) is `right[0]`, NOT `:last-of-type`; the priority-drop at app-shell.css:401-402 hides the LAST item (the 'press ? for shortcuts' hint), so the long agent label is never the one dropped and gets hard-cut by the parent's `overflow:hidden` at constrained widths. This contradicts the stated design intent of an 'ellipsized strip' (AGENTS.md: 'thin 26px ellipsized strip'), so it is a real implementation gap, not kept typography. The fix is a small CSS-only change that lands in the kit (correct fixLocation). The middot in the label is kept product typography and is not being flagged. Severity is low/cosmetic but legitimate. The hunter slightly over-scoped the fix (suggesting max-width / flex:0 1 auto on the trailing item); the minimal change is just adding the three ellipsis properties to the shared `.item` rule.
|
|
109
|
+
|
|
110
|
+
## [low] shell-chrome-crumb-gutter-misalign (kit)
|
|
111
|
+
**Crumb left gutter (space-4) does not align with ws-main content gutter (space-5)**
|
|
112
|
+
|
|
113
|
+
Evidence: app-shell.css:3113 `.ws-crumb { ... padding: 0 var(--space-4); ... }` vs :3119 `.ws-main { ... padding: var(--space-4) var(--space-5); }`. The crumb comment at 3110-3112 explicitly states the crumb should carry 'a left gutter matching .ws-main so the top edge aligns', but the horizontal pads differ (space-4 vs space-5), so the crumb leaf text sits ~one space step left of the main content's left edge — a visible vertical misalignment between the crumb bar and the content below it.
|
|
114
|
+
|
|
115
|
+
Fix: In /config/workspace/design/app-shell.css line 3113, change `.ws-crumb` horizontal padding from `padding: 0 var(--space-4)` to `padding: 0 var(--space-5)` so the crumb's left gutter matches `.ws-main` (line 3119) exactly, satisfying the existing 3110-3112 comment. Pure kit CSS; no app or index.html change. On the chat tab the main is `.ws-main--flush` (padding:0) and the thread self-gutters at --measure, so the crumb gutter remaining at space-5 is acceptable and consistent.
|
|
116
|
+
|
|
117
|
+
Verifier: Evidence is accurate and current. /config/workspace/design/app-shell.css:3113 `.ws-crumb { ... padding: 0 var(--space-4); ... }` vs :3119 `.ws-main { ... padding: var(--space-4) var(--space-5); }`. The crumb comment at 3110-3112 explicitly states the crumb should carry 'a left gutter matching .ws-main so the trail text is not flush against the rail border', yet the horizontal pads differ by one step (space-4 vs space-5). The crumb leaf/trail text therefore starts ~one space step left of the content column's left edge below it — a self-documented intent violation and a visible vertical misalignment on every non-flush tab (files/live/history/settings). The fix is fully kit-resident (no app/index.html change), a single-value edit, implementable this session, and not a flagged-typography case. Severity is modest (cosmetic one-step offset on the top band) but the finding is real.
|
|
118
|
+
|
|
119
|
+
## [medium] shell-chrome-fixed-widths-not-fluid (kit)
|
|
120
|
+
**Shell columns are fixed px (232/296/320), not the fluid clamps the design note claims; chrome does not scale with the viewport**
|
|
121
|
+
|
|
122
|
+
Evidence: app-shell.css:3010-3013 `--ws-rail-w: 232px; --ws-rail-w-collapsed: 60px; --ws-sessions-w: 296px; --ws-pane-w: 320px;` — all literal px. The AGENTS.md screen-real-estate note describes `clamp(200px,16vw,260px)` fluid clamps and drag `.ws-resizer` handles, but neither exists in this kit dist (grep for `ws-resizer`/`clamp(.*vw` in shell.js and the .ws-* block returns nothing). On a wide desktop the ~848px of chrome stays fixed while content should reclaim space.
|
|
123
|
+
|
|
124
|
+
Fix: In /config/workspace/design/app-shell.css convert the .ws-shell base width tokens (lines 3010-3013) from literal px to viewport-scaled clamps so chrome scales with the viewport while the collapse overrides at 3032-3033 still win:
|
|
125
|
+
|
|
126
|
+
--ws-rail-w: clamp(208px, 14vw, 248px);
|
|
127
|
+
--ws-rail-w-collapsed: 60px; /* unchanged - icon-rail floor */
|
|
128
|
+
--ws-sessions-w: clamp(260px, 17vw, 320px);
|
|
129
|
+
--ws-pane-w: clamp(296px, 19vw, 360px);
|
|
130
|
+
|
|
131
|
+
Leave .ws-shell.ws-rail-collapsed { --ws-rail-w: var(--ws-rail-w-collapsed); } and .ws-shell.ws-pane-collapsed { --ws-pane-w: 0px; } (3032-3033) untouched — they re-declare the token at higher specificity and continue to pin collapsed widths. No change to grid-template-columns, the media-query drawer blocks, or any app wiring (index.html/app.js); all design content stays in the kit. The pointer/keyboard .ws-resizer drag handles remain a deferred follow-up.
|
|
132
|
+
|
|
133
|
+
Verifier: Verified against real source. /config/workspace/design/app-shell.css:3010-3013 defines the WorkspaceShell column tokens as literal px (--ws-rail-w:232px / --ws-sessions-w:296px / --ws-pane-w:320px), and grep of shell.js for ws-resizer/clamp/seedWsWidths/--ws-*-w returns NOTHING — the fluid clamps and drag .ws-resizer handles the AGENTS.md screen-real-estate note describes are absent from this kit dist (the note documents kit 97dc646 work that was not actually vendored here). So ~848px of chrome stays fixed regardless of viewport — content does not reclaim space on a wide desktop, contrary to the claim and the reference bar (claude.ai/code gives the thread generous fluid width). The fix is real design content living in the wrong shape inside the kit, and is correctly kit-located. It is minimal, pure-CSS, implementable this session, and safe: the collapse classes at 3032-3033 override --ws-rail-w/--ws-pane-w with concrete values (var(--ws-rail-w-collapsed) and 0px) so they continue to win over the clamped base; the media-query drawer blocks key off viewport width, not the token, so the clamp does not disturb them. Severity is moderate (density/real-estate, not a crash), matching a real-estate finding.
|
|
134
|
+
|
|
135
|
+
## [high] tokens-theme-warn-no-dark-pair (kit)
|
|
136
|
+
**--warn (and --sky) have no dark/light pair — single value across both themes**
|
|
137
|
+
|
|
138
|
+
Evidence: colors_and_type.css:45 --warn: #E0241A defined once in :root; grep of the [data-theme=ink|dark] block (lines 239-268) and the auto block (274-302) shows NO --warn override. Same for --sky:44 #3A6EFF. Both --flame and --amber DO have theme pairs (42-43 paper, 261-262 dark), so --warn was missed. --warn is the destructive/error ACTION tone (used in chat.css/app-shell.css), and #E0241A sits at ~4.0:1 on --ink #131318 while the paper value passes comfortably — the dark theme is the weak side.
|
|
139
|
+
|
|
140
|
+
Fix: In /config/workspace/design/colors_and_type.css, add dark-tuned overrides mirroring the existing --flame/--amber pairing. Insert `--warn: #FF5A52;` and `--sky: #6E9BFF;` into BOTH dark blocks: the [data-theme="ink"],[data-theme="dark"] block (alongside lines 261-262 --flame/--amber) AND the @media (prefers-color-scheme:dark) [data-theme="auto"] block (alongside lines 294-295). Leave the :root paper values (#E0241A / #3A6EFF) unchanged. These clear AA 4.5:1 on --ink (#FF5A52=6.03:1, #6E9BFF=6.87:1) and match the dark --flame brightness. No app/index.html change; rebuild via scripts/build.mjs and re-vendor 247420.css.
|
|
141
|
+
|
|
142
|
+
Verifier: Verified against real source. colors_and_type.css:45 defines --warn:#E0241A and :44 --sky:#3A6EFF only in :root; the [data-theme=ink],[data-theme=dark] block (239-268) and the auto @media block (274-302) override --flame/--amber but NOT --warn or --sky — confirmed by reading both blocks. So both tokens carry one value across themes while their sibling error tones are properly paired; this is an oversight, not kept design. Usage confirms the weak side matters: --warn is rendered as TEXT/border foreground on dark backgrounds (app-surfaces.css:99 .field-error color, app-shell.css:1450/1520/2459 color, .ds-alert-error tone, .ds-bulkbar .danger color, .toast.error), and --sky is the dark --code-keyword (colors_and_type.css:264/297) and .ds-alert-info tone foreground. Measured contrast on --ink #131318: --warn #E0241A = 3.91:1, --sky #3A6EFF = 4.28:1 — both fail WCAG AA 4.5:1 for text. The proposed dark values clear it (FF5A52 = 6.03:1, 6E9BFF = 6.87:1), matching the existing dark --flame brightness (#FF5A1F = 5.94:1). Real, low-risk, implementable.
|
|
143
|
+
|
|
144
|
+
## [medium] tokens-theme-wrong-amber-fallback (kit)
|
|
145
|
+
**chat.css --amber fallbacks hardcode the DARK amber value, leaking the wrong theme on paper**
|
|
146
|
+
|
|
147
|
+
Evidence: chat.css:300,301,468,486,512,669,778 all use `var(--amber, #d9a93a)` and chat.css:363,799 use `var(--purple-2, #7F18A4)`. --amber resolves to #8A6512 on paper but to #D9A93A on dark (colors_and_type.css:43 vs 262); the literal fallback #d9a93a is the DARK value, so any moment the token is unset (nested-scope edge, a paper island) renders the dark amber on light paper at ~2.0:1 — exactly the contrast failure the token pairing was added to fix.
|
|
148
|
+
|
|
149
|
+
Fix: In the kit (no app changes): remove the literal fallbacks so the always-defined tokens cannot be overridden by an opposite-theme literal. In /config/workspace/design/chat.css replace `var(--amber, #d9a93a)` with `var(--amber)` at lines 300,301,468,486,512,669,778 and `var(--purple-2, #7F18A4)` with `var(--purple-2)` at lines 363,799. In /config/workspace/design/app-shell.css:591 replace `var(--purple-2, #7F18A4)` with `var(--purple-2)`. Then rebuild (node scripts/build.mjs) and re-vendor dist/247420.{js,css} into site/app/vendor/anentrypoint-design/. The amber lines are the load-bearing fix (they removed a wrong-theme contrast leak); the purple-2 lines remove dead fallbacks for a token that is unconditionally defined on :root (colors_and_type.css:28).
|
|
150
|
+
|
|
151
|
+
Verifier: Evidence verified against real source. `--amber` is a root-level token defined on `:root` as the DARK value `#D9A93A` (colors_and_type.css:262) and overridden to the paper value `#8A6512` on `[data-theme="paper"]` (colors_and_type.css:43,361). chat.css uses `var(--amber, #d9a93a)` at lines 300,301,468,486,512,669,778 and app-shell.css:591 / chat.css:363,799 use `var(--purple-2, #7F18A4)`. The literal `#d9a93a` fallback is the dark amber, so any unset-token moment on paper renders ~2.0:1 dark amber on light paper — exactly contradicting the intent documented at colors_and_type.css:355 ("A paper island under a dark ancestor must not inherit the dark signal pair"). The fix is kit-local, minimal, and implementable this session. Note: `--purple-2` is single-valued (no theme pair), so its fallback is dead code rather than a contrast leak — still correct to drop, just lower stakes than the amber lines.
|
|
152
|
+
|
|
153
|
+
## [medium] a11y-motion-focus-ring-mode-split (kit)
|
|
154
|
+
**Focus ring is split between an outline model and an inset box-shadow model with no shared token**
|
|
155
|
+
|
|
156
|
+
Evidence: colors_and_type.css:205-235 and app-shell.css:426,454 use `outline: 2px solid var(--accent)` (+`outline-offset:2px`), while inputs/composer/search use an INSET ring `box-shadow: inset 0 0 0 2px var(--accent)` (app-shell.css:829,1107,1590,1687,2234; app-surfaces.css:178). The two models look visibly different (outset halo vs inset stroke) and the accent color/width are duplicated literally at ~20 sites with no single source of truth.
|
|
157
|
+
|
|
158
|
+
Fix: In /config/workspace/design/colors_and_type.css (the kit's token home), add to :root: `--focus-color: var(--accent); --focus-w: 2px; --focus-offset: 2px; --focus-ring-inset: inset 0 0 0 var(--focus-w) var(--focus-color);`. Then mechanically replace: (1) outset sites — `outline: 2px solid var(--accent)` -> `outline: var(--focus-w) solid var(--focus-color)` and `outline-offset: 2px` -> `outline-offset: var(--focus-offset)` across colors_and_type.css, app-shell.css, app-surfaces.css (note: keep the deliberate negative offsets like -2px and the 1px toolbar offsets as-is — only swap the color/width, since `outline` is a property and cannot consume a box-shadow token); (2) inset sites — `box-shadow: inset 0 0 0 2px var(--accent)` -> `box-shadow: var(--focus-ring-inset)` at app-shell.css:829,1107,1133,1687,2234,2557 and the app-surfaces inset field. Keep the one deliberate rule (bordered fields = inset token; everything else = outline). Do NOT use a single box-shadow `--focus-ring` for the outline sites — they use the `outline` property, so tokenize color+width there rather than forcing a box-shadow conversion.
|
|
159
|
+
|
|
160
|
+
Verifier: Verified against real source. Two focus-ring models coexist with the accent color/width hardcoded everywhere and no shared token: 70 outset `outline: 2px solid var(--accent); outline-offset: 2px` sites (colors_and_type.css:206-236, app-shell.css:32-33,248,283,325,427,454,729(-2px),1121,1141,1189,1264,1274,1311,1341,1530,1590,2177,2605,2960,3061,3074,3084(-2px),3140,3256,3276,3289; app-surfaces.css:94,149,182) and 7 inset `box-shadow: inset 0 0 0 2px var(--accent)` sites (app-shell.css:829,1107,1133,1687,2234,2557; matching the cited fields). The cited line numbers (colors_and_type.css:205-235, app-shell.css:426/454/829/1107/1590/1687/2234, app-surfaces.css:178) all check out. This is a genuine missing-design-token defect: the accent literal and 2px width are duplicated ~40+ times with no single source of truth, and the two visual models (outset halo vs inset stroke) are applied with no governing rule. All visual CSS is correctly in the kit; the fix is purely additive token definitions plus mechanical replacement, fully implementable this session.
|
|
161
|
+
|
|
162
|
+
## [medium] a11y-motion-focus-on-mouse (kit)
|
|
163
|
+
**Several inputs draw the focus ring on :focus (mouse click) instead of :focus-visible**
|
|
164
|
+
|
|
165
|
+
Evidence: app-shell.css:828-829 (.row-form input/textarea:focus), app-shell.css:1107 (.ds-chat-composer input:focus), app-shell.css:1590 (.ds-modal-input:focus) all key the accent ring on `:focus`, so the ring flashes on plain mouse clicks. Sibling controls correctly use `:focus-visible` (app-shell.css:1133,1141,1687).
|
|
166
|
+
|
|
167
|
+
Fix: In the kit file /config/workspace/design/app-shell.css, change the three bare `:focus` selectors that draw the 2px accent ring to `:focus-visible`, matching the kit's established pattern (1133/1687/2234/2552-2555): (1) lines 828-829 `.row-form input:focus,/.row-form textarea:focus` -> `:focus-visible`; (2) line 1107 `.ds-chat-composer input:focus` -> `:focus-visible`; (3) line 1590 `.ds-modal-input:focus` -> `:focus-visible`. Then rebuild (`node scripts/build.mjs`) and re-vendor dist/247420.css into site/app/vendor/anentrypoint-design/. App gets no change. Optionally keep a quieter `:focus` border/background tint on the text inputs for the typing affordance, but the accent ring belongs on `:focus-visible` only.
|
|
168
|
+
|
|
169
|
+
Verifier: Verified against real kit source (the app loads CSS from the vendored kit bundle; the cited file is /config/workspace/design/app-shell.css). All three citations are accurate: line 828-829 `.row-form input:focus,/.row-form textarea:focus { box-shadow: inset 0 0 0 2px var(--accent); }`, line 1107 `.ds-chat-composer input:focus { box-shadow: inset 0 0 0 2px var(--accent); }`, and line 1590 `.ds-modal-input:focus { outline: 2px solid var(--accent); outline-offset: 2px; }` all key the accent ring on bare `:focus`, firing on mouse click. The inconsistency is real and self-evidenced inside the SAME file: sibling input controls deliberately gate the identical accent ring on `:focus-visible` (1133 `.ds-file-filter-input:focus-visible`, 1687 `.ds-filter-input:focus-visible`, 2234 `.ds-select:focus-visible`, 2552-2555 `.ds-field input/textarea/.ds-select/.ds-search-input:focus-visible`). No kept-typography or decorative-glyph concern. Fix is a 3-selector swap in the kit, implementable this session.
|
|
170
|
+
|
|
171
|
+
## [low] a11y-motion-infinite-anims-rely-on-global-killswitch (kit)
|
|
172
|
+
**Looping animations have no local reduced-motion guard and depend solely on the global duration kill-switch**
|
|
173
|
+
|
|
174
|
+
Evidence: app-shell.css:2406 (pulse-line infinite), 2429 (context-menu-in), 2711 (skeleton-load infinite), 2736 (toast-slide-in), 2776 (ds-bounce infinite), 2810 (ds-shimmer infinite) declare `animation:` with no per-rule `prefers-reduced-motion` guard. They are only neutralized by the broad `@media (prefers-reduced-motion: reduce){ .ds-247420 *,.app *{animation-duration:.01ms!important;animation-iteration-count:1!important } }` at app-shell.css:2620. The newer chat.css/community.css pattern instead gates each motion in a `no-preference` block (chat.css:754-792), so the older app-shell block is inconsistent and a 0.01ms-once shimmer still paints one jarring frame.
|
|
175
|
+
|
|
176
|
+
Fix: Minimal, highest-value change in /config/workspace/design/app-shell.css only: inside the existing reduce block at line 2620, after the blanket rule, add a static-fill override that eliminates the residual paint frame — `.skeleton, .ds-skeleton, .ds-session-row-skeleton .ds-skel { animation: none !important; background: var(--bg-2) !important; }`. This gives reduced-motion users a steady placeholder. Do NOT rewrite all six keyframe sites into `no-preference` wrappers — the blanket guard already prevents the looping motion and that churn adds marginal benefit; just kill the static residual. (Optional consistency follow-up, lower priority: gate `pulse-line` and `ds-shimmer` decorative loops behind `no-preference` to match chat.css, but the static-fill above is the load-bearing fix.)
|
|
177
|
+
|
|
178
|
+
Verifier: Evidence verified against real source. app-shell.css:2406 (pulse-line infinite), 2429 (context-menu-in), 2711 (skeleton-load infinite), 2736 (toast-slide-in), 2776 (ds-bounce infinite), 2810 (ds-shimmer infinite) all declare `animation:` with no per-rule guard, and are neutralized only by the blanket `@media (prefers-reduced-motion: reduce){ .ds-247420 *,.app *{animation-duration:.01ms!important;animation-iteration-count:1!important } }` at 2620. The newer chat.css:754-792 pattern does gate each motion in a `no-preference` block, so the inconsistency is real. Fix destination is correctly the kit. Severity is LOW, not the implied medium: the blanket selector `.ds-247420 *, .app *` genuinely DOES reach every cited rule, so reduced-motion users never see looping motion — the only true residual is a single ~0.01ms shimmer/slide paint frame on skeletons/toasts. Kept-typography guard not triggered.
|
|
179
|
+
|
|
180
|
+
## [medium] a11y-motion-color-only-rail-status (kit)
|
|
181
|
+
**Session/list status rail encodes ok/subagent/error by color bar alone**
|
|
182
|
+
|
|
183
|
+
Evidence: app-shell.css:587-592 renders the row status purely as a colored `::before` bar (rail-green=accent, rail-purple, rail-flame) with no shape or text differentiation inside the kit component. Per the kit Row rail contract this is the only visual carrier of green=ok / purple=subagent / flame=error, which fails on color-blind perception and is invisible to AT.
|
|
184
|
+
|
|
185
|
+
Fix: KIT-only, minimal. In src/components/content.js Row(): when `rail` is set, append a visually-hidden status token to the row's children ONLY for the meaningful non-default tones — `h('span',{class:'sr-only'}, rail==='flame' ? 'error' : rail==='purple' ? 'subagent' : '')` (emit nothing for `green`, which is the unremarkable default — announcing "ok" on every row would be AT noise). Use the existing words pattern (error/subagent) so it matches the .status-dot-disc vocabulary. In app-shell.css add a `.sr-only` clip rule if one is not already defined, and differentiate the bar by SHAPE not just hue: keep rail-green at height:56%, but give .row.rail-flame::before { height:64% } (matching the row-state-error bar at line 583) and .row.rail-purple::before a gapped fill via a repeating-linear-gradient, so error/subagent read differently from ok without relying on color. Both edits are in the kit (src/components/content.js + app-shell.css); rebuild via scripts/build.mjs and re-vendor 247420.{js,css}. No app change beyond the existing rail prop wiring.
|
|
186
|
+
|
|
187
|
+
Verifier: Verified against real source. The cited file is /config/workspace/design/app-shell.css (the KIT, not the app as the evidence string implied — the fix correctly lands in the kit). Lines 587-592 confirm the rail tones render as a color-only ::before bar: rail-green=var(--accent), rail-purple=var(--purple-2), rail-flame=var(--flame), all height:56%/opacity:1, with zero shape or text differentiation. The Row component (src/components/content.js:60,103-107) emits only the class `rail-<tone>` and never adds an aria-label or visually-hidden status word; the rail bar is a pure ::before with no accessible name. In the app (site/app/js/app.js:686,727) ConversationList rows pass rail flame/purple/green as the ONLY error/subagent/ok carrier — those rows render title/project/time but no status word, so error and subagent state are conveyed by color alone. This is a real WCAG 1.4.1 (use of color) failure and is invisible to assistive tech. Severity is moderate-not-inflated: it affects the error/subagent distinction on every history/conversation row. Implementable this session as a small kit-only change.
|
|
188
|
+
|
|
189
|
+
## [low] a11y-motion-modal-input-offset-drift (kit)
|
|
190
|
+
**Inset/outline focus offsets drift across file/upload/density controls (1px vs 2px vs -2px)**
|
|
191
|
+
|
|
192
|
+
Evidence: app-shell.css:1141 (.ds-file-sort-btn outline-offset:1px), 1189 (.ds-file-open offset:2px), 1264/1274/1311 (file-check/selectall/density offset:1px), 1341 (.ds-file-cell-open offset:-2px), 1530 (.ds-upload-act offset:1px), 1590 (.ds-modal-input offset:2px). The ring jumps inward/outward unpredictably between adjacent controls in the same toolbar.
|
|
193
|
+
|
|
194
|
+
Fix: In the kit, add `--focus-ring-offset: 2px;` to the :root token block in /config/workspace/design/colors_and_type.css (near the existing --accent / --r-* tokens). In /config/workspace/design/app-shell.css, replace the seven stray `outline-offset: 1px` literals (lines 1141, 1264, 1274, 1311, 1530, 3276, 3289) and the positive `outline-offset: 2px` focus rings with `outline-offset: var(--focus-ring-offset)` so every non-inset focus ring shares one value. PRESERVE the three negative insets unchanged (-2px on .ds-file-cell-open:1341 and .ws-rail-item:3084; -6px dashed dropzone:1490) because they are intentional inset rings on clipping/edge-flush containers - add a one-line comment at .ds-file-cell-open documenting that the inset is deliberate (the cell-open button is flush inside a grid cell that clips an outset ring). Rebuild via scripts/build.mjs and re-vendor 247420.css. CSS-only, no app or index.html change.
|
|
195
|
+
|
|
196
|
+
Verifier: Evidence verified against real source. The cited file `app-shell.css` lives in the KIT (/config/workspace/design/app-shell.css), not the app, so the fixLocation:kit is correct and no app-to-kit migration is needed. Every cited line matches: :1141 .ds-file-sort-btn offset 1px, :1189 .ds-file-open 2px, :1264/:1274/:1311 file-check/selectall/density 1px, :1341 .ds-file-cell-open -2px, :1530 .ds-upload-act 1px, :1590 .ds-modal-input 2px. The drift is genuine: tallying all 30 outline-offset declarations gives a dominant 2px convention (~16 uses) with 7 controls at 1px and 3 intentional negative insets (-2px/-6px) on clipping containers. Adjacent controls in the same file/upload toolbars (sort-btn 1px vs file-open 2px; density 1px vs cell-open -2px) do jump the ring inward/outward inconsistently. No --focus-offset token exists. Severity is low (cosmetic focus-ring polish, not a functional/a11y break - all rings are still visible), but the fix is concrete and fully implementable this session in CSS only. This is not kept typography (no glyphs involved) and not intentional design - the 1px values are stray, since the rest of the sheet uses 2px uniformly.
|