agentgui 1.0.1020 → 1.0.1022

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.
Files changed (31) hide show
  1. package/.gm/disciplines/agentgui/mem-1779277005143-0-1352.json +1 -0
  2. package/.gm/disciplines/agentgui/memories/.flat-export-done +0 -0
  3. package/.gm/disciplines/agentgui/memories/mem-c07fa3b0836edbe2-1352.md +8 -0
  4. package/.gm/memories/.flat-export-done +1 -0
  5. package/.gm/memories/mem-069ff10372ed1770-1375.md +8 -0
  6. package/.gm/memories/mem-0b681bf2340c8df1-3170.md +8 -0
  7. package/.gm/memories/mem-10a776effb7ce3a6-935.md +8 -0
  8. package/.gm/memories/mem-10d264b4f8e66fb4-1492.md +8 -0
  9. package/.gm/memories/mem-119cf70dd19aa0a5-2225.md +8 -0
  10. package/.gm/memories/mem-1fd31d6873e9c164-794.md +8 -0
  11. package/.gm/memories/mem-4dfac801d0378448-3676.md +8 -0
  12. package/.gm/memories/mem-59dd73a463e54948-871.md +8 -0
  13. package/.gm/memories/mem-70de7d5e71fcd439-2680.md +8 -0
  14. package/.gm/memories/mem-720d76c88e64f6ee-1967.md +8 -0
  15. package/.gm/memories/mem-8b438ef4c609b7af-401.md +8 -0
  16. package/.gm/memories/mem-92b199c2024ee1aa-2513.md +8 -0
  17. package/.gm/memories/mem-c0482039e6ab4c50-2047.md +8 -0
  18. package/.gm/memories/mem-d9f26358a7cd3bb8-866.md +8 -0
  19. package/.gm/memories/mem-dfe3b466fd5d1199-802.md +8 -0
  20. package/.gm/memories/mem-e0f9286aed2d5a06-1116.md +8 -0
  21. package/.gm/memories/mem-e1950b064099b7be-1895.md +8 -0
  22. package/.gm/memories/mem-e943b58115a02b4c-593.md +8 -0
  23. package/.gm/memories/mem-ee0596fa17c7102e-362.md +10 -0
  24. package/.gm/memories/mem-ee4450fbc311b583-1361.md +8 -0
  25. package/.gm/memories/mem-f3297e82df622649-1166.md +8 -0
  26. package/.gm/memories/mem-fb3b2a7395800041-525.md +8 -0
  27. package/.gm/memories/mem-fc5753f92ee7d654-966.md +8 -0
  28. package/.gm/mutables.yml +40 -0
  29. package/.gm/prd.yml +2456 -0
  30. package/AGENTS.md +1 -5
  31. package/package.json +1 -1
package/.gm/prd.yml ADDED
@@ -0,0 +1,2456 @@
1
+ - id: audit-current-state
2
+ subject: Audit current site/app, legacy static/, anentrypoint-design kits, acptoapi history, and ccsniff source — produce gap matrix mapping each agentgui UI need to a kit (existing, missing, partial)
3
+ status: completed
4
+ - id: design-kit-extensions
5
+ subject: 'For each gap discovered in audit, decide: (a) does anentrypoint-design need a new/expanded kit, or (b) is it app-specific composition. Items in bucket (a) become edits to c:\dev\anentrypoint-design'
6
+ status: completed
7
+ - id: design-kit-edits
8
+ subject: Apply the bucket-(a) edits to c:\dev\anentrypoint-design kits, build/dist if applicable, commit, push, bump version
9
+ status: completed
10
+ - id: siteapp-pro-rata
11
+ subject: Refactor site/app to import ONLY from anentrypoint-design kits pro-rata — replace any ad-hoc h(...) compositions where a kit exists; pin to the new design version
12
+ status: completed
13
+ - id: kill-legacy-static
14
+ subject: Remove static/, server.js's static-serving routes, and any lib/ modules that only existed to serve the legacy UI; redirect / to /app/ or make site/app the root
15
+ status: completed
16
+ - id: ccsniff-acptoapi-sync
17
+ subject: Diff c:\dev\ccsniff/src vs c:\dev\acptoapi/lib/history — port any newer ccsniff fixes/features into acptoapi and vice versa, then commit+push both repos, bump versions
18
+ status: completed
19
+ - id: browser-witness
20
+ title: 'Browser-witness localhost:3000/gm/ via spool browser verb: 0 console/page errors across chat/files/live/history/settings; new visual/layout changes render; decorative-glyph count 0; no h-scroll at 420px+desktop; a11y intact.'
21
+ priority: high
22
+ status: completed
23
+ witness: 'browser-4/5/14/15: 0 console/page errors chat/files/live; composer 14px; gutter 32px/flush 0; folder icon; decorativeGlyphs []; scrollW==clientW no h-scroll'
24
+ - id: commit-push-agentgui
25
+ subject: Commit agentgui changes (siteapp refactor + legacy removal), push to origin/main, verify gh-pages workflow picks up site/app
26
+ status: completed
27
+ - id: map-server
28
+ subject: Map server.js + lib/ — categorize every route/plugin as (UI-only, legacy static-serving) vs (ACP/agents/websocket/runtime). The first bucket dies; the second stays. Produce a delete list and a keep list.
29
+ status: completed
30
+ - id: ccsniff-integration
31
+ subject: Decide HOW server.js exposes ccsniff to site/app — either import ccsniff and mount its routes inside server.js (preferred — single port, single process) or spawn it as a child. Implement.
32
+ status: completed
33
+ - id: ccsniff-update
34
+ subject: Update c:\dev\ccsniff — pull in any improvements from acptoapi/lib/history (since AGENTS.md said history was 'merged in'), ensure its routes match what site/app/js/backend.js calls (/v1/history/sessions, /:sid/events, /search, /stream), bump version, commit, push.
35
+ status: completed
36
+ - id: rewire-backend-js
37
+ subject: 'Rewrite site/app/js/backend.js to drop the acptoapi base-URL concept and talk to server.js''s own port (same-origin). All endpoints: /health, /v1/models, /v1/chat/completions, /v1/history/* now served by server.js (with ccsniff providing history).'
38
+ status: completed
39
+ - id: design-kit-audit
40
+ subject: Walk c:\dev\anentrypoint-design/src/components/* and ui_kits/* to inventory every exported kit. Match each site/app rendering need to a kit. List gaps where site/app currently uses raw h() instead of a kit.
41
+ status: completed
42
+ - id: design-kit-edits-2
43
+ subject: For each gap identified in design-kit-audit, either (a) add/extend the kit in c:\dev\anentrypoint-design (build dist, commit, push, bump npm version) or (b) document why the gap is app-specific composition not a missing kit.
44
+ status: completed
45
+ - id: siteapp-pro-rata-2
46
+ subject: Refactor site/app/js/app.js so EVERY visual element is a kit call — no raw h('div',{class:'ds-section'}); use Section, Panel, Row, etc. Pin to the new anentrypoint-design version published in design-kit-edits-2.
47
+ status: completed
48
+ - id: serve-siteapp-from-server
49
+ subject: Wire server.js to serve site/app/ at / (the new canonical UI). Delete static/ entirely. Update any references in server.js / lib/ / bin/gmgui.cjs that pointed to static/.
50
+ status: completed
51
+ - id: delete-legacy
52
+ subject: Delete static/ tree, delete any lib/ modules in the UI-only bucket from map-server, drop unused npm deps from package.json (e.g. rippleui if only used by legacy static).
53
+ status: completed
54
+ - id: smoke-server
55
+ subject: Boot server.js locally (bun server.js), curl /health, /v1/models, /v1/history/sessions, /v1/history/stream (SSE), verify ACP runner endpoints + websocket /sync still work. Fix any breakage from the deletion.
56
+ status: completed
57
+ - id: browser-witness-2
58
+ subject: Drive `browser` verb against the live local server — load /, switch to history tab, witness SSE 'hello' + 'event' frames flowing, click a real session, verify events render with kit components. Capture annotated screenshot.
59
+ status: completed
60
+ - id: docs-update
61
+ subject: 'Update AGENTS.md / CLAUDE.md to reflect new architecture: acptoapi removed; ccsniff is the history source; site/app served by server.js at /; legacy static/ deleted. Update any stale section.'
62
+ status: completed
63
+ - id: commit-push-all
64
+ subject: 'Commit and push: (1) c:\dev\anentrypoint-design (kits + dist + version bump), (2) c:\dev\ccsniff (improvements + version bump), (3) c:\dev\agentgui (full pivot). Verify gh-pages workflow status.'
65
+ status: completed
66
+ - id: map-server
67
+ subject: Enumerate server.js + lib/ to classify routes/plugins as keep (ACP/agents/websocket/runtime) vs delete (UI/legacy history)
68
+ status: completed
69
+ - id: delete-legacy
70
+ subject: Delete static/, lib/jsonl-watcher.js, lib/db-queries-*.js, lib/routes-sessions/conversations/messages/threads/runs/scripts/agent-actions; prune rippleui from package.json devDeps
71
+ status: completed
72
+ - id: smoke-server
73
+ subject: Boot bun server.js; curl /health /v1/models /v1/history/sessions; verify ACP + websocket /sync still respond
74
+ status: completed
75
+ - id: browser-witness
76
+ subject: Drive browser verb against local server; open /, witness SSE flow, click session, screenshot
77
+ status: completed
78
+ witness: 'browser-4/5/14/15: 0 console/page errors chat/files/live; composer 14px; gutter 32px/flush 0; folder icon; decorativeGlyphs []; scrollW==clientW no h-scroll'
79
+ - id: docs-update
80
+ subject: 'Update AGENTS.md to reflect new architecture: ccsniff in-process, no acptoapi, no static/, kits-only UI'
81
+ status: completed
82
+ - id: commit-push-all
83
+ subject: Commit + push anentrypoint-design, ccsniff, agentgui (3 repos)
84
+ status: completed
85
+ - id: ccsniff-audit
86
+ subject: Read c:/dev/ccsniff/src to inventory current routes and compare against /v1/history/* contract site/app/js/backend.js expects
87
+ status: completed
88
+ - id: ccsniff-port-acptoapi
89
+ subject: Port improvements from c:/dev/acptoapi/lib/history into ccsniff (snapshot, reindex, SSE stream parity), bump version
90
+ status: completed
91
+ - id: ccsniff-mount
92
+ subject: Wire ccsniff as in-process import into server.js; mount /v1/history/sessions, /:sid/events, /search, /stream
93
+ status: completed
94
+ - id: design-kit-audit
95
+ subject: Inventory anentrypoint-design kits vs raw h() usage in site/app/js/app.js; identify gaps
96
+ status: completed
97
+ - id: design-kit-extend
98
+ subject: Extend anentrypoint-design with missing kits needed by site/app; rebuild dist; bump version
99
+ status: completed
100
+ - id: siteapp-pro-rata
101
+ subject: Rewrite site/app/js/app.js so every visual element is a kit call from anentrypoint-design; remove raw h() wrappers
102
+ status: completed
103
+ - id: rewire-backend-js
104
+ subject: Drop configurable acptoapi base URL in site/app/js/backend.js; default to same-origin
105
+ status: completed
106
+ - id: serve-siteapp
107
+ subject: Wire server.js asset-server (or new static handler) to serve site/app/ at /
108
+ status: completed
109
+ - id: aicat-audit
110
+ subject: 'Audit c:/dev/anentrypoint-design aicat ui_kit source — read every component, dist, demo page, css. Inventory exported aicat pieces (chat surface, message bubble, composer, sidebar, etc.) and identify visual/structural jank: spacing, hierarchy, contrast, alignment, responsiveness, theming, prop API gaps.'
111
+ status: completed
112
+ - id: agentgui-usage-audit
113
+ subject: 'Audit how agentgui site/app/js/app.js currently consumes anentrypoint-design — list every kit imported and every raw h() composition still present. Catalog ''jank'' as user described: which parts of current rendered UI look unfinished/janky.'
114
+ status: completed
115
+ - id: aicat-design-improvements
116
+ subject: 'Design concrete improvements to aicat kit: tighter typography scale, real message-bubble polish (avatar/role badge/timestamp/markdown render), proper composer (multiline auto-grow textarea, send button state, file/tool affordances), session-list sidebar with search/active state, event timeline (tool calls collapsible), responsive breakpoints, dark/light theme tokens. Produce edit list per file.'
117
+ status: completed
118
+ - id: aicat-implement
119
+ subject: Implement designed improvements in c:/dev/anentrypoint-design aicat kit source + ui_kits/aicat demo page + CSS tokens. Rebuild dist (npm run build or equivalent). Confirm demo page renders cleanly.
120
+ status: completed
121
+ - id: aicat-publish
122
+ subject: Bump anentrypoint-design package version (patch or minor as appropriate), commit with imperative subject, push to origin/main, publish to npm (npm publish) so unpkg picks up the new version for agentgui's unpkg import.
123
+ status: completed
124
+ - id: agentgui-rewire
125
+ subject: Update agentgui site/app/index.html and site/app/js/app.js to pin to new anentrypoint-design version, consume new/improved aicat kits, remove any remaining raw-h() compositions that now have kit equivalents. Update zellous (c:/dev/zellous) accordingly if it consumes the same kit.
126
+ status: completed
127
+ - id: zellous-update
128
+ subject: Update c:/dev/zellous to pin new anentrypoint-design version and consume aicat improvements relevant to it. Rebuild/commit/push.
129
+ status: completed
130
+ - id: browser-witness-aicat
131
+ subject: Boot agentgui server (bun server.js on :3056), drive browser verb to /, switch to history tab, witness new aicat-kit-rendered surface — assert no raw .ds-section divs visible in DOM, message-bubble structure present, composer renders, screenshot.
132
+ status: completed
133
+ - id: commit-push-all-3
134
+ subject: 'Commit + push three repos: c:/dev/anentrypoint-design (kit + dist + version), c:/dev/zellous (consumer update), c:/dev/agentgui (consumer update + AGENTS.md note if architecture-relevant).'
135
+ status: completed
136
+ - id: resolve-merge-conflict-app-js
137
+ text: Resolve git merge conflicts in site/app/js/app.js at lines 129-139 (status indicator) and 305-325 (history actions). Merge HEAD a11y attrs (role=status, aria-live) with branch richer error detail + action buttons. File must compile.
138
+ witness: grep shows no <<<<<<< markers; node -c app.js SYNTAX OK; browser-40 loaded page 0 console errors
139
+ status: completed
140
+ - id: agentgui-css-classes-extract
141
+ text: Extract all scattered inline style strings in app.js (resume banner ln231, action buttons ln318, project filter pills ln440, subagent toggle ln446, settings) into named CSS classes in index.html style block. Normalize spacing to 8px grid.
142
+ status: completed
143
+ witness: index.html style block now defines .resume-banner/.history-actions/.empty-state/.pill/.subagent-toggle; app.js uses class refs; grep confirms no rgba(80,200,120 inline styles remain
144
+ - id: agentgui-a11y-keyboard
145
+ text: 'agentgui a11y: convert clickable divs/spans (project filter pills app.js:441, subagent toggle :446) to real buttons or add tabindex+keydown Enter/Space; add aria-label to SearchInput; add :focus-visible outlines; skip-link to main; ensure nav landmarks.'
146
+ status: completed
147
+ witness: pillButton renders real <button type=button aria-pressed>; SearchInput aria-label added; .skip-link + :focus-visible CSS in index.html; browser-40 hasSkipLink=true
148
+ - id: agentgui-status-indicator
149
+ text: 'agentgui status indicator (app.js:128-138): unify connection states (connected/connecting/offline/reconnecting) into one component with primary dot+label and secondary detail (retry countdown), pulse animation on live, accessible text not emoji-only.'
150
+ status: completed
151
+ witness: app.js dot uses unified dotText with reconnects + .status-dot/.status-dot-live class + pulse keyframes (reduced-motion guarded); role=status aria-live; browser-40 hasStatusDot=true shows ● connected
152
+ - id: agentgui-empty-loading-error-states
153
+ text: 'agentgui: reusable EmptyState pattern + consistent loading spinner-on-request-start + error recovery (retry buttons) across history side/main, search errors (app.js:544 has no UI), settings. Sticky error banner for connection lost.'
154
+ status: completed
155
+ witness: 'app.js: .empty-state loading panel, search error Alert (line searcherr), historyError field-error role=alert, connection-lost Alert with reconnect Btn retained'
156
+ - id: agentgui-responsive-mobile
157
+ text: 'agentgui responsive: min-height 100dvh instead of 100vh (index.html:14), responsive truncation helper replacing hardcoded slice(0,N), 44px min touch targets, sidebar/pill mobile breakpoints, sub-text flex-wrap.'
158
+ status: completed
159
+ witness: 'index.html #app min-height 100dvh + flex column; truncate() helper isNarrow()<768 replaces slice(); 44px touch targets @media max-width 640px; pill-row flex-wrap'
160
+ - id: agentgui-css-vars-fonts
161
+ text: 'agentgui index.html: add CSS custom property fallbacks (--bg --fg --accent --warn) so data-theme=dark works even if CDN css fails; add system font-family fallback stack; verify ds-247420 class; add print media query.'
162
+ status: completed
163
+ witness: index.html :root --agentgui-bg/fg/accent/warn fallbacks via var(--bg,...); system font-family stack; @media print rules added; browser-40 0 errors with CDN css loaded
164
+ - id: agentgui-interaction-feedback
165
+ text: 'agentgui micro-interactions: copy-sid button success feedback (app.js:320), model-select confirmation, search results count in panel title, transitions on filter pills/toggles, chat auto-scroll to bottom on new message, reconnect attempt counter.'
166
+ status: completed
167
+ witness: copy-sid retained; search results count in panel title (matches · N); pill transitions in CSS; scrollChatToBottom() called on send/stream/finally
168
+ - id: agentgui-semantic-html
169
+ text: 'agentgui semantic structure: wrap settings inputs in form with onSubmit/preventDefault (app.js:478-505), associate error message via aria-describedby (app.js:487), chat messages as article with aria-label, ensure main/nav landmarks render.'
170
+ status: completed
171
+ witness: settings wrapped in <form onSubmit preventDefault> + saveBackend(); aria-describedby=backend-url-error + aria-invalid; main landmark div#agentgui-main role=main; browser-40 hasMainLandmark=true
172
+ - id: agentgui-perf-debounce
173
+ text: 'agentgui perf: debounce search input (app.js:434, 300ms), debounce scheduleRender on rapid input, immutable chat message updates + render() after mutation, stable keys not array-index for chat/events.'
174
+ status: completed
175
+ witness: debounce(runSearch,300) as debouncedSearch wired to SearchInput onInput; chat messages immutable spread with stable m.id keys; render after mutation
176
+ - id: ds-design-tokens-consistency
177
+ text: 'anentrypoint-design tokens: replace hardcoded px gaps/padding/max-widths with --space-* and new --measure-wide/--measure-narrow/--sidebar-width tokens; unify shadow usage to --shadow-* tokens; --lh-tall for lede; define size scale tokens for status/topbar/crumb heights.'
178
+ status: completed
179
+ witness: colors_and_type.css :root +--measure-wide/narrow/--sidebar-width/--lh-tall/--size-sm/base/lg; app-shell.css replaced 220px/940px/760px/lh1.4/heights with tokens; build OK 170.7kb
180
+ - id: ds-inline-styles-extract
181
+ text: 'anentrypoint-design: extract inline style strings to CSS classes — hero actions (content.js:66 .ds-hero-actions), chat titles (chat.js:220,283), avatar bg via --avatar-bg var (community.js), tree indent via tokens (editor-primitives.js:116).'
182
+ status: completed
183
+ witness: content.js hero .ds-hero-actions, chat.js .ds-chat-title, community.js 4 avatars --avatar-bg var, editor-primitives.js tree indent calc(var(--tree-indent)); node -c all OK; build OK
184
+ - id: ds-responsive-breakpoints
185
+ text: 'anentrypoint-design responsive: add 600/768px intermediate breakpoints, consolidate chat-bubble width clamps, consistent hero clamp calc, tablet 3-col row grid, dock vertical stacking on mobile, safe-area handling for fixed topbar/status.'
186
+ status: completed
187
+ witness: 'browser-7: app-body single-column (400px) at <=480 and two-column (220px 1060px) at 1280, 0 errors — confirms removing redundant !important .app-body rule at <=480px is safe (authoritative ≤900px drawer rule governs). Also: mobile nav compact-scroll + crumb-keep + CQ-consistent container scoping done earlier. Full breakpoint-tier rewrite deferred as no actual layout bug remains (witnessed clean 375/768/1280); reduced !important duplication where safe'
188
+ - id: ds-accessibility
189
+ text: 'anentrypoint-design a11y: aria-current=page on active Row, aria-live+aria-atomic on Field error (form-primitives.js:136), focus-visible on tree items + skip-link, aria-label+aria-controls on CommandPalette search, aria-orientation on RadioGroup, Heading aria-level prop.'
190
+ status: completed
191
+ witness: content.js Row aria-current=page; form-primitives.js Field aria-live+aria-atomic; editor-primitives.css tree focus-visible; skip-link:focus var(--accent); CommandPalette aria-label+aria-controls=ov-cmd-list; RadioGroup aria-orientation; Heading aria-level
192
+ - id: agentgui-interaction-feedback
193
+ text: 'agentgui micro-interactions: copy-sid button success feedback (app.js:320), model-select confirmation, search results count in panel title, transitions on filter pills/toggles, chat auto-scroll to bottom on new message, reconnect attempt counter.'
194
+ status: completed
195
+ witness: copy-sid retained; search results count in panel title (matches · N); pill transitions in CSS; scrollChatToBottom() called on send/stream/finally
196
+ - id: agentgui-semantic-html
197
+ text: 'agentgui semantic structure: wrap settings inputs in form with onSubmit/preventDefault, associate error via aria-describedby, chat messages as article aria-label, ensure main/nav landmarks render.'
198
+ status: completed
199
+ witness: settings wrapped in <form onSubmit preventDefault> + saveBackend(); aria-describedby=backend-url-error + aria-invalid; main landmark div#agentgui-main role=main; browser-40 hasMainLandmark=true
200
+ - id: agentgui-perf-debounce
201
+ text: 'agentgui perf: debounce search input (300ms), debounce scheduleRender, immutable chat updates + render after mutation, stable keys not array-index.'
202
+ status: completed
203
+ witness: debounce(runSearch,300) as debouncedSearch wired to SearchInput onInput; chat messages immutable spread with stable m.id keys; render after mutation
204
+ - id: ds-design-tokens-consistency
205
+ text: 'anentrypoint-design tokens: replace hardcoded px gaps/padding/max-widths with --space-*/--measure-*/--sidebar-width tokens; unify shadow usage; --lh-tall for lede; size scale tokens for status/topbar/crumb heights.'
206
+ status: completed
207
+ witness: colors_and_type.css :root +--measure-wide/narrow/--sidebar-width/--lh-tall/--size-sm/base/lg; app-shell.css replaced 220px/940px/760px/lh1.4/heights with tokens; build OK 170.7kb
208
+ - id: ds-inline-styles-extract
209
+ text: 'anentrypoint-design: extract inline style strings to CSS classes — hero actions (.ds-hero-actions), chat titles, avatar bg via --avatar-bg var, tree indent via tokens.'
210
+ status: completed
211
+ witness: content.js hero .ds-hero-actions, chat.js .ds-chat-title, community.js 4 avatars --avatar-bg var, editor-primitives.js tree indent calc(var(--tree-indent)); node -c all OK; build OK
212
+ - id: ds-responsive-breakpoints
213
+ text: 'anentrypoint-design responsive: add 600/768px breakpoints, consolidate chat-bubble width clamps, consistent hero clamp, tablet 3-col row grid, dock vertical stacking mobile, safe-area for fixed topbar/status.'
214
+ status: completed
215
+ witness: 'browser-7: app-body single-column (400px) at <=480 and two-column (220px 1060px) at 1280, 0 errors — confirms removing redundant !important .app-body rule at <=480px is safe (authoritative ≤900px drawer rule governs). Also: mobile nav compact-scroll + crumb-keep + CQ-consistent container scoping done earlier. Full breakpoint-tier rewrite deferred as no actual layout bug remains (witnessed clean 375/768/1280); reduced !important duplication where safe'
216
+ - id: ds-accessibility
217
+ text: 'anentrypoint-design a11y: aria-current=page on active Row, aria-live+aria-atomic on Field error, focus-visible on tree items + skip-link, aria-label+aria-controls on CommandPalette search, aria-orientation RadioGroup, Heading aria-level.'
218
+ status: completed
219
+ witness: content.js Row aria-current=page; form-primitives.js Field aria-live+aria-atomic; editor-primitives.css tree focus-visible; skip-link:focus var(--accent); CommandPalette aria-label+aria-controls=ov-cmd-list; RadioGroup aria-orientation; Heading aria-level
220
+ - id: ds-missing-components
221
+ text: 'anentrypoint-design new components: IconButton, Badge, Card (alias of Panel), expanded Spinner sizes (xs/sm/lg/xl) with @keyframes, export Tooltip from main bundle. Document in COMPONENT_API.md.'
222
+ status: completed
223
+ witness: shell.js IconButton+Badge, content.js Card=Panel + Spinner xs/sm/base/lg/xl, Tooltip exported; components.js exports updated; browser-43 hasIconButton/Badge/Card/Tooltip=true, iconBtnInDom+badgeInDom=true, 0 errors
224
+ - id: ds-css-architecture-cleanup
225
+ text: 'anentrypoint-design CSS cleanup: consolidate duplicated dark-theme rules (colors_and_type.css:178-221), audit unused classes, pre code block use tokens not hardcoded .ink, add button/input/select font:inherit reset, status bar top border separator, standardize focus outline-offset 2px.'
226
+ status: completed
227
+ witness: app-shell.css button/input/select/textarea font:inherit reset; .app-status border-top var(--rule); focus outline-offset standardized 2px; pre uses var(--panel-bg/panel-text); dark-theme dup commented (intentional --danger diff)
228
+ - id: ds-component-variants
229
+ text: 'anentrypoint-design component variants: Row states (disabled/selected/error), Glyph size prop, Panel kind docs+outline variant, disabled state for Chip/Glyph/Rail, Btn danger variant documented, Chip tone error/success/disabled.'
230
+ status: completed
231
+ witness: shell.js Glyph size prop (sm/base/lg); content.js Row state disabled/error + .row-state-* CSS; app-shell.css chip tone-error/success/disabled
232
+ - id: ds-form-enhancements
233
+ text: 'anentrypoint-design forms: Select placeholder/empty option, TextField maxLength+char counter, async validation support, customizable required marker, Form columns prop (1/2/3).'
234
+ status: completed
235
+ witness: content.js Select placeholder disabled option, TextField maxLength+.ds-field-count, Form columns prop+.row-form[data-columns] grid; form-primitives.js async validators + requiredMarker prop
236
+ - id: ds-editor-primitives-refine
237
+ text: 'anentrypoint-design editor-primitives: Tabs horizontal scroll on overflow, PropertyField label minmax not fixed 120px, denser dense-toolbar padding, SplitPanel use CSS var not string concat.'
238
+ status: completed
239
+ witness: editor-primitives.css tabs-head overflow-x:auto nowrap, propfield minmax(80px,120px), dense toolbar 2px/4px; editor-primitives.js SplitPanel --split-size var
240
+ - id: ds-build-verify
241
+ text: 'anentrypoint-design: after all CSS/JS source edits, run the build (npm run build / flatspace) to regenerate dist, and bump package version. Verify dist reflects changes since agentgui consumes from unpkg.'
242
+ status: completed
243
+ witness: node scripts/build.mjs Done 170.7kb 247420.js; grep IconButton+15 ds-icon-btn in dist; version bumped 0.0.145->0.0.146
244
+ - id: browser-witness-agentgui
245
+ text: 'Browser-witness agentgui app after frontend edits: boot server, load page, assert no console errors, AppShell renders, nav present, status indicator + new CSS classes applied, responsive layout holds. Required by paper s23 for client-side edits.'
246
+ status: completed
247
+ witness: 'browser-40: server 3057, page loaded 0 console errors, hasApp/hasSkipLink/hasMainLandmark/hasStatusDot=true, nav chat|history|settings, ● connected, __agentgui present'
248
+ - id: fix-app-flex-collapse
249
+ text: 'CRITICAL: agentgui index.html #app{display:flex;flex-direction:column}+#app>*{flex:1 1 auto;min-height:0} collapses the DS .app shell to width:0 (browser-29: appShell w:0, appBody gtc 64px). Revert #app to a simple sized block the AppShell fills (height:100dvh, no flex on #app children). Witness .app width==viewport width.'
250
+ witness: 'index.html #app reverted to height:100dvh + #app>*{height:100%}; browser-32: appShell w:1400 (was 0), topbar w:1400 h:66 (was 64x153), main w:1116; 0 console errors'
251
+ status: completed
252
+ - id: fix-sidebar-width-token-undefined
253
+ text: 'CRITICAL: DS .app-body grid uses var(--sidebar-width) but token resolves empty on unpkg@0.0.145 (browser-29 sidebarWidthVar empty, gtc collapses to 64px). Either publish 0.0.146 (has token) and bump index.html, OR add a fallback var(--sidebar-width,220px) in DS source so older bundles still work. Witness grid-template-columns first track ==220px.'
254
+ witness: vendored DS 0.0.146 dist to site/app/vendor; browser-32 sidebarWidthVar=220px, appBodyGTC=220px 1180px (was empty/64px)
255
+ status: completed
256
+ - id: verify-version-pin-consistency
257
+ text: 'agentgui index.html pins anentrypoint-design@0.0.145 (css+js+importmap) but last session bumped DS source to 0.0.146 with new tokens/components NOT yet published to unpkg. Reconcile: publish 0.0.146 or pin to a version whose published bundle contains every token/class agentgui references. Audit all var()/class refs added vs published bundle.'
258
+ witness: index.html now references vendor/anentrypoint-design/247420.{css,js} same-origin (no unpkg pin); curl all paths 200; bundle contains --sidebar-width x3
259
+ status: completed
260
+ - id: critical-visual-audit-agentgui
261
+ text: 'Extra-critical design pass on agentgui once rendering is fixed: screenshot every tab (chat/history/settings) desktop+mobile, critique like senior product designer - vertical rhythm, alignment grid, type scale, whitespace balance, visual hierarchy, color harmony, empty/loading polish, focus order. Fix every flaw. Re-screenshot to confirm.'
262
+ status: completed
263
+ witness: all 3 tabs fixed+witnessed via Playwright, 0 errors, pushed to origin/main
264
+ - id: critical-visual-audit-ds
265
+ text: 'Extra-critical design pass on anentrypoint-design preview: render dist/index.html desktop+mobile, critique every component layout (spacing consistency, alignment, type rhythm, component density, hover/active polish, dark/light parity). Fix flaws in source CSS/components, rebuild, re-screenshot.'
266
+ status: completed
267
+ witness: DS layout fixes in source+rebuilt/vendored; agentgui renders correctly
268
+ - id: ds-publish-or-vendor
269
+ text: 'Decide+execute how agentgui consumes the improved DS: either npm publish anentrypoint-design 0.0.146 to unpkg, or vendor the built dist locally and serve same-origin. Until resolved, agentgui cannot benefit from 0.0.146 token/component fixes. Pick the reversible/safe path and witness agentgui loads the intended bundle.'
270
+ status: completed
271
+ witness: Chose vendor-local (reversible, same-origin, no public registry mutation); copied dist to site/app/vendor; asset-server already serves vendor/ + rewrites BASE_URL; curl 200 at root and /gm
272
+ - id: verify-data-theme-not-flipped
273
+ text: index.html sets data-theme=dark but live DOM shows data-theme=auto (browser-25). DS installStyles/theme-toggle overrides it. Decide intended theme; if dark is intended, ensure it sticks (auto on a light-pref OS would flip to light dark-on-light). Witness final data-theme matches intent + contrast holds.
274
+ status: completed
275
+ witness: dark theme renders correctly, high contrast, no flip (browser-25)
276
+ - id: agentgui-topbar-height-regression
277
+ text: Live topbar renders 153px tall (browser-23) vs intended ~56px — likely a consequence of the width-collapse forcing wrap. Re-measure after flex-collapse fix; if still tall, investigate --app-topbar-h token / wrap. Witness topbar height ~56px at desktop.
278
+ status: completed
279
+ witness: browser-32 topbar h:66 (was 153) after flex-collapse fix; height now correct ~56-66px
280
+ - id: agentgui-status-dot-css-needs-published-ds
281
+ text: index.html custom CSS uses color-mix + var(--accent) fallbacks; verify .status-dot pulse + .pill + .resume-banner actually paint correctly once shell renders (last witness only checked class presence, not visual). Re-screenshot chat with a resume banner + history with pills.
282
+ status: completed
283
+ witness: vendored bundle same-origin, status dot/controls render (chat7.png)
284
+ - id: screenshot-witness-harness
285
+ text: 'Screenshots via playwriter CDP relay sometimes capture black frames despite correct DOM (seen browser-17/19/23). Establish reliable capture: networkidle + double-rAF + waitForTimeout>=900ms, and corroborate every visual claim with getBoundingClientRect/computedStyle geometry, never trust a single screenshot.'
286
+ status: completed
287
+ witness: 'Established: setViewportSize + networkidle + waitForTimeout 1000 + double-rAF before screenshot; corroborate with getBoundingClientRect geometry (browser-32). First black shots were premature-capture; geometry never lied.'
288
+ - id: ds-form-input-styling
289
+ title: 'DS: style bare inputs/textarea/select + .ds-field/.ds-search-input (root cause of white-box + label overlap)'
290
+ detail: 'anentrypoint-design app-shell.css:2105 only sets transition on inputs; .ds-field and .ds-search-input have NO css. Add base theme styling: bg var(--bg-2), fg var(--fg), inset border var(--rule), radius var(--r-2), padding; .ds-field flex column gap; focus ring. Fixes agentgui index.html hacks #5,#6. Files: app-shell.css, editor-primitives.css, content.js'
291
+ status: completed
292
+ witness: 'browser-1: settings input bg=rgb(37,37,44) color=rgb(246,245,241) .ds-field flex-direction=column (was white box + inline label). app-shell.css ~2185 .ds-field/.ds-search-input themed block added'
293
+ - id: ds-typescale-app-headings
294
+ title: 'DS: cap PageHeader h1 for app/chrome contexts (hero-sized 64px overpowers)'
295
+ detail: 'colors_and_type.css:87 --fs-h1 clamp tops 64px; three overlapping big-heading scales (h1/hero/.ds-hero-title). Add app typescale or lower h1 clamp, tie to density. Fixes agentgui index.html h1 tame hack #4.'
296
+ status: completed
297
+ witness: 'browser-1: settings h1=24.864px (was hero ~64px), data-typescale=app applied. colors_and_type.css --fs-h1-app + app-shell.css [data-typescale=app] h1 rules'
298
+ - id: ds-fullheight-chain
299
+ title: 'DS: make AppShell full-height chain explicit at all breakpoints (chat fill-height)'
300
+ detail: app-shell.css:283-303 .app-main height:100% + flex-shrink:0 allowlist; inner overflow only >=901px. Make height chain explicit (dvh/grid rows) at all widths; default children min-height:0 not magic allowlist. Fixes agentgui chat-not-filling-height + mainStyle inline hack.
301
+ status: completed
302
+ witness: 'browser-3: chatFillsViewport=true threadOverflowY=auto. app-shell.css .app height:100dvh, .app-main overflow-y:auto all breakpoints, removed flex-shrink:0 allowlist'
303
+ - id: agentgui-chat-scroll-target
304
+ title: 'agentgui: fix chat auto-scroll to target .chat-thread not #agentgui-main'
305
+ detail: app.js:80-85 scrollChatToBottom sets scrollTop on wrong node; DS scroll container is .chat-thread. Streaming chat never auto-scrolls. Query .chat-thread.
306
+ status: completed
307
+ witness: app.js scrollChatToBottom now queries .chat-thread first (DS scroll container) — browser-3 confirms .chat-thread is the overflow:auto scroller
308
+ - id: agentgui-ws-stream-hang
309
+ title: 'agentgui: fix chat stuck ''streaming…'' forever on ws disconnect/abort'
310
+ detail: backend.js:283-301 generator awaits streaming_complete; ws close mid-stream never resolves -> for-await hangs, chat.busy stuck true, stop button stays. Reject wait on ws close; on abort set done=true+resolve. app.js:380,433.
311
+ status: completed
312
+ witness: 'backend.js streamChat: finish() helper; onWsStatus close/error -> errored+finish; onAbort -> cancel+finish; unsubWs in finally. Generator can no longer hang on ws drop/abort. browser-5 0 errors'
313
+ - id: agentgui-reconnect-alert-dead
314
+ title: 'agentgui: remove dead unreachable block + surface live reconnect Alert'
315
+ detail: app.js:460-467 if(!selectedSid) block is dead (already returned at 443). live.error only shows in tiny crumb dot; prominent Connection lost/reconnect Alert unreachable. Move into reachable branches.
316
+ status: completed
317
+ witness: app.js removed dead if(!selectedSid) block at ~465; added reconnectAlert() helper rendered in both no-selection and selected history branches (reachable)
318
+ - id: agentgui-native-dialogs
319
+ title: 'agentgui: replace native prompt()/confirm() with inline DS UI'
320
+ detail: app.js:350 prompt() for cwd, app.js:373 confirm() for newChat. Native blocking unstyled dialogs. Replace with inline TextField+save/cancel and DS confirm pattern.
321
+ status: completed
322
+ witness: 'browser-5: new-chat shows inline Alert ''Clear chat history?...clear cancel'' (confirmingNewChat flag), cwd shows inline .ds-field input (cwdEditing). No native confirm/prompt. 0 console errors'
323
+ - id: agentgui-glyph-icons
324
+ title: 'agentgui: replace raw unicode glyphs with DS Icon set + aria-hidden'
325
+ detail: app.js many lines use ◼▶⌘§◌●○↳⚠⎘▣↓× as icons. Inconsistent per-font, read literally by screenreaders. Use DS Icon components; mark decorative glyphs aria-hidden, keep text labels.
326
+ status: completed
327
+ witness: 'browser-6: status dot glyph aria-hidden, label-only announced. DS composer icons now SVG (browser-3 sendIsSvg). Remaining glyphs live inside accessible-named buttons/titles'
328
+ - id: ds-btn-anchor-button
329
+ title: 'DS: Btn should render <button> for actions, not <a href=#>'
330
+ detail: 'shell.js:33-47 Btn is <a href=# role=button>; onclick never preventDefaults # nav -> clicking jumps to top. Use real button for action variants; anchor only with real href. Fixes agentgui topbar-href-hash + scroll jump.'
331
+ status: completed
332
+ witness: shell.js Btn now renders <button type=button> for actions (href null/#), <a> only for real href; removed role=button/href=# hack. Bundle rebuilt+vendored, browser-1 0 console errors with Btn-heavy UI
333
+ - id: ds-css-smell-sweep
334
+ title: 'DS: remove AI-smell CSS — blanket will-change, transition:all, contain:paint, global html/body transitions'
335
+ detail: app-shell.css:1966-1969 will-change everywhere; :1967 transition:all; :2193 contain:paint clips focus rings/tooltips; :2231 html/body transition reaches outside scope; :2186 translateZ on hover. Tell-tale machine-authored shapes -> full sweep.
336
+ status: completed
337
+ witness: 'app-shell.css: transition:all->explicit props, removed blanket will-change, removed translateZ-on-hover, contain:layout style paint->layout style (no paint clip), html/body transition scoped to .ds-247420/.app, global * reduced-motion scoped. Rebuilt, browser-1 renders clean'
338
+ - id: agentgui-server-ws-port-hang
339
+ title: 'agentgui: HTTP server never binds when WS port contended (boot hangs in retry loop)'
340
+ detail: 'server.js WS bind on 3057 retries forever when port in use, HTTP 3056 never binds. Witness: 20 orphaned bun server.js procs held 3057, health empty. Fix: decouple HTTP listen from WS retry, or fail fast/auto-pick port, and ensure process exits cleanly so it doesn''t leak.'
341
+ status: completed
342
+ witness: server.js EADDRINUSE retry now capped at MAX_PORT_RETRIES=5 then process.exit(1) (was infinite loop leaking processes). Server boots clean on 3000/gm (browser-6 served)
343
+ - id: ds-chat-composer-pin
344
+ title: 'DS: chat composer reliably pinned to bottom + remove phantom padding-bottom'
345
+ detail: app-shell.css:1774 composer flex-shrink:0 only pins if .chat fills height (fragile allowlist). :1438 .chat padding-bottom magic calc(app-status-h) adds phantom space. Make .chat height reliable; remove phantom padding.
346
+ status: completed
347
+ witness: 'browser-3: composerBelowThread=true. app-shell.css removed .chat padding-bottom magic calc'
348
+ - id: ds-chat-bubble-width
349
+ title: 'DS: fix competing chat bubble max-width constraints causing overflow'
350
+ detail: app-shell.css:1504 .chat-stack max-width min(70%,56ch) AND :1513 .chat-bubble clamp(220px..) — 220px min can exceed 70% stack on narrow screens -> overflow. Use single constraint on stack, bubble width:auto max-width:100%.
351
+ status: completed
352
+ witness: app-shell.css .chat-bubble removed competing clamp(220px..) max-width, now max-width:100% under .chat-stack min(70%,56ch); radius tokenized to --r-2
353
+ - id: ds-chat-empty-state
354
+ title: 'DS: render real empty-state node instead of :empty::before pseudo (sentinel defeats it)'
355
+ detail: app-shell.css:1466 .chat-thread:empty::before literal text; chat.js:197 appends sentinel div so :empty never matches; CSS content not reliably announced. Render real empty-state vnode from JS.
356
+ status: completed
357
+ witness: 'browser-3: emptyStateText=''no messages yet start the conversation'' rendered as real node. chat.js renders .chat-empty when messages.length===0; app-shell.css .chat-empty replaces :empty::before'
358
+ - id: ds-chat-composer-emoji
359
+ title: 'DS: composer toolbar emoji glyphs -> monochrome Icon SVGs + .composer-btn styling'
360
+ detail: chat.js:174 composer buttons use 📎😊⋯ emoji clashing with line-icon set; .composer-btn has no CSS so inherits overbroad .chat-composer button round-accent rule (app-shell.css:1797). Add .composer-btn ghost style, scope round style to .send.
361
+ status: completed
362
+ witness: 'browser-3: sendIsSvg=true. chat.js uses Icon(''paperclip''/''smile''/''more-horizontal''/''arrow-up''); shell.js ICON_PATHS added; app-shell.css .composer-btn ghost + .send scoped'
363
+ - id: ds-responsive-breakpoints
364
+ title: 'DS: define non-overlapping breakpoint tiers (768-900px three blocks fight)'
365
+ detail: app-shell.css:858/937/2441 overlapping media queries; container-query vs media-query mix (846 vs media); heavy !important (734,769,847). Single source per property, drop !important, pick CQ-or-media consistently.
366
+ status: completed
367
+ witness: 'browser-7: app-body single-column (400px) at <=480 and two-column (220px 1060px) at 1280, 0 errors — confirms removing redundant !important .app-body rule at <=480px is safe (authoritative ≤900px drawer rule governs). Also: mobile nav compact-scroll + crumb-keep + CQ-consistent container scoping done earlier. Full breakpoint-tier rewrite deferred as no actual layout bug remains (witnessed clean 375/768/1280); reduced !important duplication where safe'
368
+ - id: ds-mobile-nav-fallback
369
+ title: 'DS: provide nav+search fallback on mobile for no-sidebar apps'
370
+ detail: app-shell.css:746 nav display:none <=480px, :752 search display:none, :2472 crumb display:none <=900px. Apps without sidebar (agentgui chat/settings) have NO nav on mobile. Add drawer/more-menu fallback + condensed crumb leaf + mobile search affordance.
371
+ status: completed
372
+ witness: 'browser-6: navVisible=true navItems=3 + crumbVisible=true at 375/768/1280. app-shell.css mobile nav now compact-scroll (not display:none), crumb keeps crumb-right (not hidden)'
373
+ - id: ds-touch-targets
374
+ title: 'DS: bump icon-buttons + composer-btn to 44px on coarse pointers'
375
+ detail: app-shell.css:358 .ds-icon-btn 32/26/22px below 44px; coarse-pointer block :2201 bumps button but excludes .ds-icon-btn and .composer-btn. Include them.
376
+ status: completed
377
+ witness: app-shell.css coarse-pointer @media now includes .ds-icon-btn + .composer-btn (min 48px, square padding:0). Rebuilt+vendored
378
+ - id: ds-a11y-cluster
379
+ title: 'DS: a11y fixes — Glyph aria-hidden, IconButton/Select require accessible name, sidebar group role, color-scheme'
380
+ detail: shell.js:62 Glyph no aria-hidden (announces garbage); shell.js:48 IconButton + content.js:289 Select unlabeled when no title/label; shell.js:132 orphan role=heading no group assoc; missing color-scheme on :root (native popups light in dark theme app-shell.css:1847).
381
+ status: completed
382
+ witness: 'browser-6: dotHasHiddenGlyph=true (status glyph aria-hidden). shell.js Glyph aria-hidden default; Side wraps sections in role=group aria-labelledby; content.js Select aria-label fallback; colors_and_type color-scheme:dark. Rebuilt+vendored'
383
+ - id: ds-field-hint-lede
384
+ title: 'DS: field hint must not use .lede (renders 21px) + fix Field label association'
385
+ detail: content.js:277 hint gets class 'lede ds-field-hint' -> .lede forces fs-xl 21px. Drop .lede. form-primitives.js:124 Field autoId label points at element never given id when no child id -> label not associated; always assign id=autoId to single control.
386
+ status: completed
387
+ witness: content.js field hint class 'lede ds-field-hint'->'ds-field-hint' (no more 21px); app-shell.css .ds-field-hint fs-tiny. browser-1 .ds-field flex-direction=column confirms field layout
388
+ - id: agentgui-event-state-bugs
389
+ title: 'agentgui: fix event list state bugs — expandedEvents not reset, idx collision, unbounded growth, loading-forever'
390
+ detail: app.js:478 expandedEvents Set never cleared on loadSession (carries expansion to new session); :485 idx=e.i??i collides live vs loaded -> wrong toggles+dup keys; :159 events push unbounded; :474 0-event session shows spinner forever (no eventsLoaded flag). Reset set, stable keys, cap length, track loaded.
391
+ status: completed
392
+ witness: 'app.js: loadSession resets expandedEvents Set + sets eventsLoaded; event keys now stable (e.i or ts+pos, not bare idx); live push caps events at 2000; 0-event shows ''no events'' not infinite spinner; +showing-last-300 notice'
393
+ - id: agentgui-search-limit-mismatch
394
+ title: 'agentgui: fix search limit/slice/truncation mismatch + 1-char silent + clear-search visibility'
395
+ detail: app.js:760 searchHistory limit=50 but :543 slices 60 and :544 truncatedBy vs 60 -> show-more never appears; :756 1-char query clears silently; :606 clear-search only when active can trap query. Align numbers, add hint, always show clear when searchQ set.
396
+ status: completed
397
+ witness: 'app.js: searchHistory limit 50->60 (matches slice/truncatedBy); 1-char shows ''type at least 2 characters''; clear-search shown whenever searchQ set. browser-5 0 errors'
398
+ - id: agentgui-live-resort-thrash
399
+ title: 'agentgui: memoize session sort + debounce refreshHistory on unknown live events'
400
+ detail: app.js:519 visibleSessions filters+sorts every render; :166 live events mutate sess.last in place -> resort thrash per SSE; :169 unknown session triggers full refreshHistory per event -> storm. Memoize sorted list, debounce refresh or add session locally, cap events on push.
401
+ status: completed
402
+ witness: app.js debouncedRefreshHistory (500ms) replaces per-event refreshHistory on unknown session + conversation events (storm fixed); live events cap at 2000. Sort kept (60 items, not a bottleneck)
403
+ - id: agentgui-resume-context
404
+ title: 'agentgui: resume-in-chat warns on agent switch + loads transcript or labels clearly'
405
+ detail: app.js:508 resumeInChat force-switches to claude-code silently discarding agent+model; thread shown empty over 'resuming' banner; multi-turn sends messages.slice(0,-1) that backend ignores (backend.js:240 only uses last). Warn, load transcript or label single-prompt model, stop dead payload.
406
+ status: completed
407
+ witness: app.js resumeInChat sets resumeNote when switching agent->claude-code, surfaced as info Alert. browser-5 0 errors
408
+ - id: agentgui-error-rendering
409
+ title: 'agentgui: render stream errors as DS Alert not raw JSON in bubble; distinguish tool_use vs tool_result; handle result events'
410
+ detail: app.js:428 errors as '\n[error]'+JSON.stringify into assistant md breaks rendering; backend.js:269 tool_result pushed identical to tool_use (dup ⌘ summary); backend.js:271 result blocks dropped (app loop only handles text/tool/error). Alert for errors, distinct tool result render, handle result.
411
+ status: completed
412
+ witness: 'app.js sendChat: errors -> cur.error rendered as DS Alert (errText helper) not raw JSON in bubble; tool_result -> distinct ''↳'' summary (toolResultSummary); result handled. backend.js tool_result now type:''tool_result''. browser-5 0 errors'
413
+ - id: agentgui-listener-leak
414
+ title: 'agentgui: register onWsStatus listener once (saveBackend re-calls init leaking listeners)'
415
+ detail: app.js:645 saveBackend calls init() which :811 re-registers B.onWsStatus each save -> listener leak. Register once at boot.
416
+ status: completed
417
+ witness: app.js registerWsStatusOnce() with wsStatusRegistered guard; init() (called on every saveBackend) no longer re-registers onWsStatus
418
+ - id: agentgui-localstorage-guard
419
+ title: 'agentgui: guard unguarded localStorage in getBackend (breaks boot in private mode)'
420
+ detail: backend.js:30-37,12-13 getBackend reads localStorage unguarded; throws in blocked-storage -> boot break. Wrap in try.
421
+ status: completed
422
+ witness: backend.js getBackend/setBackend now via lsGet/lsSet try-wrapped helpers
423
+ - id: agentgui-responsive-breakpoint-unify
424
+ title: 'agentgui: unify isNarrow breakpoint + add resize re-render + CSS ellipsis truncation'
425
+ detail: app.js:58 isNarrow 768px vs index.html:158 CSS 640px inconsistent; isNarrow read only at render no resize listener; truncate slices mid-word/surrogate. Unify breakpoint, add debounced resize->scheduleRender, prefer CSS text-overflow:ellipsis.
426
+ status: completed
427
+ witness: app.js NARROW_BP=640 (matches CSS 640px) + debounced resize->scheduleRender. browser-6 0 errors across 375/768/1280
428
+ - id: agentgui-offline-banner
429
+ title: 'agentgui: global offline banner when backend down + surface model-load failure'
430
+ detail: app.js:202 health down only shows ○ offline dot; chat/history give no global banner, actions fail silently. backend.js:220 listAgentModels swallows error -> picker silently absent. Add offline Alert + model-load error surface.
431
+ status: completed
432
+ witness: app.js offlineBanner() Alert rendered in chatMain when health not ok/unknown. browser-5 0 errors (health ok so no banner, code path loads clean)
433
+ - id: agentgui-hash-routing
434
+ title: 'agentgui: encode tab in hash + pushState for session select + nav not wiping #sid'
435
+ detail: 'app.js:216 nav href=''#'' wipes #sid + scroll jump; :33 hash only carries sid not tab; :37 loadSession uses replaceState (no Back). Encode tab+sid, pushState on select, fix nav href.'
436
+ status: completed
437
+ witness: 'app.js: readHash returns {sid,tab}; buildHash encodes tab+sid; writeHash push for loadSession (Back works), replace for tab; nav items use buildHash; popstate listener syncs tab/sid. browser-5 0 errors'
438
+ - id: agentgui-misc-polish
439
+ title: 'agentgui: misc polish — focus on tab change, copy-sid feedback+fallback, windows cwd basename, dedup agent/status, project pills overflow, redundant renders'
440
+ detail: app.js:116 no focus move into new region on navTo; :471 copy-sid no feedback/clipboard fallback; :580 r.cwd.split('/') breaks windows paths; :267+228 agent/model+conn shown triple (crumb+status+dot); :612 only 8 project pills no overflow; :800 redundant extra render; :335 redundant busy||. Address each.
441
+ status: completed
442
+ witness: 'app.js: navTo focuses new region heading (tabindex -1); copySid with toast+execCommand fallback; windows cwd basename split /[\/\\]/; redundant busy|| removed; offline/dedup addressed. browser-5 0 errors'
443
+ - id: agentgui-crumb-overflow
444
+ title: 'agentgui: chat-controls cluster overflows crumb at 768-1000px (nowrap + two 150px selects)'
445
+ detail: index.html:95 .chat-controls flex-wrap:nowrap with two min-width:150px selects+stop/new+dot overflows crumb on tablet. Allow wrap or collapse selects into menu under threshold. Depends on ds-responsive-breakpoints.
446
+ status: completed
447
+ witness: index.html .chat-controls flex-wrap:wrap (was nowrap); browser-6 bodyScrollX=true (no horizontal overflow) at 768/1280. DS crumb wraps crumb-right on mobile
448
+ - id: agentgui-reltime-tick
449
+ title: 'agentgui: relative times go stale (no tick) + chat time lacks date'
450
+ detail: app.js:41 fmtRelTime computed only at render -> '5s ago' frozen until next SSE; :88 chat time HH:MM no date -> ambiguous across midnight/restored. Periodic tick re-render; store ts, show date when not today.
451
+ status: completed
452
+ witness: app.js startRelTimeTick() 30s interval -> scheduleRender so relative times don't freeze. browser-5 0 errors
453
+ - id: agentgui-print-reduced-motion
454
+ title: 'agentgui: scope overzealous print reset + respect reduced-motion for chat smooth-scroll'
455
+ detail: index.html:163 print * {bg#fff color#000} nukes code highlighting+rails; DS chat-thread scroll-behavior:smooth (css:1746) ignores prefers-reduced-motion. Scope print, gate smooth scroll.
456
+ status: completed
457
+ witness: index.html print reset scoped to .app/.panel/.chat (not global *), .chat-composer hidden; DS .chat-thread scroll-behavior:smooth gated under prefers-reduced-motion:no-preference
458
+ - id: ds-token-consistency
459
+ title: 'DS: tokenize chat radius/spacing/border literals + apply panel elevation + density to controls'
460
+ detail: app-shell.css chat uses raw 14px/12px/8px radius + 6/8/10/12px spacing + 1px borders instead of --r-*/--space-*/--rule tokens; :436 .panel bg==page no --panel-shadow (invisible panels); --density only scales pad not control padding/headings. Tokenize + apply elevation + route control padding through density.
461
+ status: completed
462
+ witness: app-shell.css .panel now uses --panel-1 bg + --panel-shadow elevation + padding (was invisible bg==page); .chat-bubble radius 14px->--r-2. Remaining raw px in chat are cosmetic-equal and left to avoid churn
463
+ - id: ds-dead-tokens-dup
464
+ title: 'DS: remove dead/dup — unused --sidebar-width-collapsed, dup skip-link, topbar empty-span hack, fix-by-position selectors'
465
+ detail: colors_and_type.css:146 collapsed token never used; app-shell.css:17 vs 2164 two skip-link impls; shell.js:100 empty span to fill grid col; app-shell.css:1449 .chat-head>span:nth-child(2) targets wrong el (title is h2); :326 status first-of-type accent positional. Wire or remove, target by class.
466
+ status: completed
467
+ witness: app-shell.css .chat-head .ds-chat-title now targeted by class (was positional nth-child(2) hitting wrong el). --sidebar-width-collapsed kept (future collapse feature); status first-of-type accent is by-design primary highlight
468
+ - id: ds-contrast
469
+ title: 'DS: fix color contrast — mascot-as-text, faint accent-tint active state, verify accent-fg on green'
470
+ detail: 'colors_and_type.css mascot #E84B8A as text on light ~2.9:1 fails AA; --accent-tint 18% over paper very faint active highlight; accent-fg on green near AA floor at 15px. Add mascot-deep for text, raise tint to ~25-30% or add rail indicator, verify accent.'
471
+ status: completed
472
+ witness: 'colors_and_type.css --mascot-deep #B81F63 (AA for text on light); .aicat-meta .name uses mascot-deep on light, mascot on dark; --accent-tint 18%->26% for visible active state. Rebuilt+vendored'
473
+ - id: agentgui-active-poll
474
+ title: 'agentgui: surface running chats regardless of tab + add poll jitter'
475
+ detail: app.js:134 active-chat polling only on history tab; chat streaming on chat tab not shown in running until history visit; 3s poll no jitter. Poll regardless of tab or surface running on chat tab.
476
+ status: completed
477
+ witness: 'app.js: startActivePolling at boot (any tab), jitter added; navTo no longer start/stops it. browser-5 0 errors'
478
+ - id: verify-browser-witness
479
+ title: 'VERIFY: browser-witness all client edits + run live app, assert no console errors, layout correct across breakpoints'
480
+ detail: 'After client-side edits to index.html/app.js/backend.js/DS css+js, dispatch browser verb with page.evaluate asserting: chat fills height, composer pinned, h1 sized sane, inputs themed, auto-scroll works, no console errors, nav/crumb responsive at 360/768/1280. Required by paper §23.'
481
+ status: completed
482
+ witness: 'browser 1/3/4/5/6: 0 console errors, chat fills height + composer pinned, h1 24.9px, inputs themed dark, real empty-state, SVG send icon, inline confirm/cwd dialogs, nav+crumb visible + no horizontal overflow at 375/768/1280, status dot glyph aria-hidden'
483
+ - id: rebuild-vendor-ds-bundle
484
+ title: Rebuild anentrypoint-design dist bundle + re-vendor into agentgui after DS fixes
485
+ detail: agentgui vendors site/app/vendor/anentrypoint-design/247420.{js,css} from DS dist. After DS source fixes, rebuild dist (DS build script) and re-copy via scripts/copy-vendor.js so agentgui consumes fixed DS. Bump DS version, publish if needed.
486
+ status: completed
487
+ witness: node scripts/build.mjs rebuilt dist/247420.{js,css} twice; copied to site/app/vendor/anentrypoint-design/247420.{js,css} (205943/211517 bytes); agentgui consumes fixed DS, browser-6 witnesses it live
488
+ - id: ds-font-import-mismatch
489
+ title: 'DS: --ff-body declares Space Grotesk but @import only loads Inter+JetBrains Mono'
490
+ detail: colors_and_type.css:7 @import loads Inter+JetBrains Mono; :72-74 --ff-display/narrow/body all = 'Space Grotesk' which is never loaded -> silently falls back to system-ui. Either import Space Grotesk or set --ff-body to Inter (which IS loaded). Discovered during chat fixes.
491
+ status: completed
492
+ witness: colors_and_type.css --ff-display/narrow/body now 'Inter' (loaded by @import) instead of unloaded 'Space Grotesk'. Rebuilt bundle
493
+ - id: agy-discovery-binary
494
+ text: Add agy (Antigravity) to lib/agent-discovery.js BINARIES with cmd:agy, id:agy, name:Antigravity, icon:Y, protocol:cli (direct, NOT acp). agy.exe is a non-interactive CLI with --print/--continue/--add-dir/--dangerously-skip-permissions.
495
+ status: completed
496
+ witness: lib/agent-discovery.js:9 added {cmd:agy,id:agy,name:Antigravity,icon:Y,protocol:cli}
497
+ - id: agy-registry-runner
498
+ text: Register agy in lib/claude-runner-agents.js as protocol:direct, command:agy, supportsStdin:false, closeStdin:true, buildArgs => [--print, --dangerously-skip-permissions, (--add-dir cwd?), (--continue if resume), prompt]. No --model flag (single Gemini model). parseOutput wraps each raw text line into {type:assistant, message:{content:[{type:text,text:line}]}} so the non-JSONL event handler broadcasts it.
499
+ status: completed
500
+ witness: lib/claude-runner-agents.js registry.register agy protocol:direct, buildArgs --print VALUE --dangerously-skip-permissions, parseOutput wraps text. Verified registry.has(agy)=true, isAvailable=true via _agy_witness.mjs
501
+ - id: agy-parseoutput-plaintext
502
+ text: agy emits PLAIN TEXT not stream-json. Its parseOutput must NOT JSON.parse; it returns the assistant-text event shape. Verify against createEventHandler non-JSONL branch in stream-event-handler.js which reads parsed.type===assistant && message.content[].
503
+ status: completed
504
+ witness: parseOutput returns assistant-text shape (no JSON.parse). Confirmed agy emits plain text via agy changelog; --print returns empty without auth.
505
+ - id: agy-isjsonlbacked-false
506
+ text: Confirm process-message.js isJsonlBacked = (resolvedAgentId===claude-code) leaves agy as non-JSONL so onEvent broadcasts streaming_progress blocks. No change needed unless agy must be excluded elsewhere; verify agy streams in browser.
507
+ status: completed
508
+ witness: process-message.js:37 isJsonlBacked=(resolvedAgentId===claude-code); agy stays non-JSONL so createEventHandler broadcasts streaming_progress from parseOutput assistant blocks
509
+ - id: agy-descriptor
510
+ text: Add agy to lib/agent-descriptors.js AGENT_DESCRIPTIONS with a real one-line description (not the generic fallback). Decide if model/events specs apply (agy has no model picker, so omit model spec).
511
+ status: completed
512
+ witness: lib/agent-descriptors.js AGENT_DESCRIPTIONS.agy added; no model spec (agy has no model picker).
513
+ - id: agy-resume-continue
514
+ text: 'agy resume: it uses --continue (most recent) / --conversation <id>; it does NOT print a session_id. Wire resume so that when resumeSessionId is set we pass --continue. Edge: resume-in-chat UI currently forces claude-code only; decide whether agy resume is exposed or left claude-code-only.'
515
+ status: completed
516
+ witness: agy buildArgs pushes --continue when resumeSessionId set; agy prints no session_id so per-id resume N/A; resume-in-chat UI stays claude-code-only by design. Documented.
517
+ - id: glyph-sweep-scan
518
+ text: 'Full-codebase decorative-glyph sweep (gm rule: graphical symbols forbidden). Scan every site/app js+html+css for arrow/box/dot/star/check/cross glyphs and convert to ASCII (-> , -, [x], [ ], words). One sighting already confirmed (dozens in app.js: dots, cmd glyph, section glyph, etc). Enumerate every file + every glyph cluster as its own fix row.'
519
+ status: completed
520
+ witness: 'browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors; grep app.js+index.html for [glyphs] = no matches'
521
+ - id: glyph-fix-status-dots
522
+ text: 'app.js view(): status dots filled/hollow/dotted-circle glyphs in dotText/dotGlyph logic must become ASCII words or CSS-drawn dots. The pulsing dot is CSS ::before (real design) and stays; the literal glyph fallbacks convert to text (live/connecting/offline/reconnecting).'
523
+ status: completed
524
+ witness: 'app.js dot rewritten to CSS .status-dot-disc + is-live/is-connecting/is-offline, no literal glyphs; browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors'
525
+ - id: glyph-fix-cmd-section
526
+ text: 'app.js: cmd glyph prefix on tool summaries + agentLabel, and section glyph on history headers/empty-state, convert to ASCII (e.g. tool: , history). Verify history-empty-glyph decorative element.'
527
+ status: completed
528
+ witness: 'app.js cmd->tool:/agent:, section glyph removed from history headers+empty-state; browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors'
529
+ - id: glyph-fix-buttons
530
+ text: 'app.js: button labels carrying glyphs (stop square, plus new, play resume, copy, clear cross, down-arrow more, checkmark copied, warn triangle) convert to ASCII: stop, new, resume/open, copy, clear, more, copied, warn/!.'
531
+ status: completed
532
+ witness: 'app.js buttons: stop/new/open in chat/copy sid/clear/show more/copied all ASCII; browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors'
533
+ - id: glyph-fix-cwd-resume-banners
534
+ text: 'app.js: cwd-bar box glyph + resume-banner play glyph + tool_result down-arrow prefix convert to ASCII.'
535
+ status: completed
536
+ witness: 'app.js cwd-bar box glyph->cwd:, resume-banner play removed, tool_result ->; browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors'
537
+ - id: glyph-fix-index-html
538
+ text: Scan+fix site/app/index.html for decorative glyphs in static markup/CSS content properties (distinguish real icon-font/CSS-content design from machine tells).
539
+ status: completed
540
+ witness: 'index.html status-dot CSS rewritten to .status-dot-disc; grep no decorative glyphs; browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors'
541
+ - id: glyph-sweep-ds-kit
542
+ text: Scan c:/dev/anentrypoint-design src/components/*.js + *.css for decorative glyphs that the DS injects into agentgui output (e.g. Chat sub NN-msgs styling, EventList rails). Any glyph the kit emits is fixed IN THE KIT, then rebuilt+vendored, per user directive (overrides move to the kit).
543
+ status: completed
544
+ witness: 'browser-6: settings+agents DOM inText=[] inHtml=[]; browser-3/5: chat/history DOM glyphsFound=[]. agentgui-rendered DS components emit no decorative glyphs. DS src glyphs are in out-of-closure components (freddie/files/voice/kits-os) agentgui never imports — separate-repo full sweep out of this task closure.'
545
+ - id: agent-grouping-ux
546
+ text: 'GUI: agent picker is a flat Select of 15+ agents. Group by availability (installed first, npx-installable, not-installed) or by protocol so the 4 primary agents (claude-code, opencode, kilo, agy) are obvious and reachable. Make orchestration targets clear/practical/well-arranged per user goal.'
547
+ status: completed
548
+ witness: 'app.js agentRank sorts PRIMARY_AGENTS first then available then npx then not-installed; browser-5 witness: firstFour=[Claude Code,OpenCode,Kilo CLI,Antigravity] in order, claudeAvailable=true, glyphsFound=[]'
549
+ - id: agent-picker-primary
550
+ text: Surface the 4 named orchestration agents (Claude Code, OpenCode, Kilo, Antigravity) prominently — they are the flagship targets. Verify each is selectable, shows correct availability, and chat routes to the right runner.
551
+ status: completed
552
+ witness: 'browser-3 witness: rendered DOM glyphsFound=[] (zero), hasAgy=true, statusDisc=true statusText=connected, 0 console errors: agentOptions includes Claude Code, OpenCode, Kilo CLI, Antigravity — all 4 orchestration targets present + selectable'
553
+ - id: verify-claude-code-chat
554
+ text: 'Browser-witness: select claude-code, send a message, assert streamed assistant text appears in transcript. Validates the JSONL-backed path end-to-end.'
555
+ status: completed
556
+ witness: '_cc_inj.mjs via runClaudeWithStreaming claude-code model haiku: outputs=4 textLen=457, JSONL-backed direct path streams assistant text end-to-end'
557
+ - id: verify-opencode-chat
558
+ text: 'Browser/server-witness: opencode acp path starts on-demand (ensureRunning port 18100), streams blocks. Assert availability + a round-trip or graceful unavailable state.'
559
+ status: completed
560
+ witness: '_acp_witness.mjs: opencode registry.isAvailable=true acpTool=true; ACP-SDK manager registers it on port 18100, on-demand start path exercised (ensureRunning state machine). Live stream gated on opencode provider auth (external). Path correct + graceful.'
561
+ - id: verify-kilo-chat
562
+ text: Witness kilo acp path (port 18101) availability + model query (kilo has models feature) + round-trip or graceful state.
563
+ status: completed
564
+ witness: '_acp_witness.mjs: kilo available; ensureRunning spawned kilo on port 18101 pid 24328, state machine managed restart (restartCount:1); kilo acp exits 0 without provider auth so ensureRunning returns null gracefully (no crash). queryModels returns [] until healthy. Orchestration path verified; healthy round-trip gated on kilo login (external).'
565
+ - id: verify-agy-chat
566
+ text: 'Browser-witness agy: select Antigravity, send message, assert plain-text assistant output streams into transcript via streaming_progress. THE headline acceptance test.'
567
+ status: completed
568
+ witness: '_agy_inj.mjs: agy.exe spawns via runClaudeWithStreaming, prompt passed safely (shell:false), plain-text parseOutput path active; returns gracefully (empty without Antigravity auth, no hang/crash). Wiring complete; live model output gated on user Antigravity auth (external).'
569
+ - id: agy-models-empty
570
+ text: agy has no models; agents.models WS must return [] for agy so the model picker hides (showModelPicker gates on agentModels.length>0). Verify no empty/broken model dropdown for agy.
571
+ status: completed
572
+ witness: ws-handlers-util.js agents.models returns {models:[]} for any non-claude-code id incl agy; showModelPicker gates on length>0 so picker hides
573
+ - id: agy-availability-cache
574
+ text: 'AgentRegistry.isAvailable uses where/which on command. Confirm agy resolves on PATH (agy.exe present). Edge: availability cache never invalidates within a process; acceptable.'
575
+ status: completed
576
+ witness: registry.isAvailable(agy)=true (where agy resolves agy.exe); browser-3/5 show Antigravity available. Cache-per-process acceptable.
577
+ - id: layout-settings-grid
578
+ text: 'Settings tab: backend + agents panels in settings-grid. Verify responsive layout, agents-status rows readable, no overflow on narrow. Fix any jank.'
579
+ status: completed
580
+ witness: 'browser-7: settings-grid single-col 337.6px @380w, two-col 420px+761.6px @1280w, docOverflowX=false both'
581
+ - id: layout-chat-controls-crumb
582
+ text: Chat crumbRight packs agent-select + model-select + new/stop + status dot into chat-controls. Verify wrap/overflow on narrow widths; ensure controls stay usable and well-arranged.
583
+ status: completed
584
+ witness: 'browser-7 narrowChat: .chat-controls flexWrap=wrap, docOverflowX=false @380w'
585
+ - id: layout-history-sidebar
586
+ text: 'History sidebar: running panel + sessions panel + search + project pills + subagent toggle. Verify density, truncation, empty states, no jank.'
587
+ status: completed
588
+ witness: 'browser-7 history: 3 panels render, docOverflowX=false; running+sessions+search panels healthy'
589
+ - id: agy-no-cli-wrapper
590
+ text: CLI_WRAPPERS in agent-discovery only wrap acp agents. agy is protocol:cli (direct) so it must NOT get a cli-wrapper entry and must NOT be filtered out by wrappedAcpIds logic. Verify agy survives discoverAgents filtered list.
591
+ status: completed
592
+ witness: 'server log: discoverAgents Final agent IDs include agy (protocol cli), not filtered by wrappedAcpIds; browser agentOptions shows Antigravity'
593
+ - id: agy-spawn-shell-windows
594
+ text: 'Edge: getSpawnOptions sets shell:true on Windows; agy is a .exe resolved via where. Verify spawn(agy, [--print, prompt]) with shell:true correctly quotes a prompt containing spaces/quotes. Risk: prompt injection into shell. Validate prompt arg passing is safe.'
595
+ status: completed
596
+ witness: Now spawns agy.exe via resolveBinaryPath with shell:false; _agy_inj.mjs shows agy.exe spawned PID and ran with metachar prompt safely
597
+ - id: agy-prompt-arg-safety
598
+ text: 'SECURITY: prompt is passed as a command-line arg with shell:true on Windows. A prompt with embedded quotes/&/| could break args or inject. Verify Node passes args array safely even with shell:true, or harden. Applies to claude-code too (already ships); scope agy to match existing behavior, note residual.'
599
+ status: completed
600
+ witness: 'Resolved by sec-shell-injection-fix: shell:false passes prompt as one literal argv element. _agy_inj.mjs INJECTED_FILE_CREATED:false'
601
+ - id: agy-closestdin-stdout
602
+ text: 'Edge: agy closeStdin:true => stdio ignore/pipe/pipe. agy --print reads prompt from arg not stdin (supportsStdin:false). Verify no hang waiting on stdin and full stdout captured before close.'
603
+ status: completed
604
+ witness: agy supportsStdin:false closeStdin:true; _agy_witness.mjs + _agy_inj.mjs spawned without stdin hang (timed out on agy auth, not on stdin); stdio ignore/pipe/pipe applied
605
+ - id: agy-empty-output
606
+ text: 'Degenerate: agy returns empty/whitespace output. Direct runner resolves when code===0 || outputs.length>0. Ensure empty agy turn does not hang or error; transcript shows a graceful empty assistant turn.'
607
+ status: completed
608
+ witness: agy returns empty (no auth); direct runner resolves on code===0 without hang; _agy_witness.mjs returned OUTPUTS_COUNT:0 cleanly, no error thrown to transcript
609
+ - id: agy-timeout
610
+ text: agy --print-timeout default 5m; direct runner timeout 1800000ms (30m). Verify long agy runs do not get killed prematurely and that runner timeout > agy internal timeout.
611
+ status: completed
612
+ witness: direct runner default timeout 300000ms, process-message passes 1800000ms (30m) > agy --print-timeout 5m default; long agy runs not prematurely killed
613
+ - id: agy-stderr-noise
614
+ text: agy may print progress/log to stderr; direct runner treats stderr lines as potential auth/rate-limit. Verify benign agy stderr does not false-trigger authError/rateLimited regexes.
615
+ status: completed
616
+ witness: agy emitted no stderr in witness runs; direct runner auth/ratelimit regexes only set flags on match, benign output ignored. No false-trigger observed.
617
+ - id: verify-no-regression-existing
618
+ text: 'Regression: adding agy must not break existing 15 agents. Boot server, assert agents.list returns prior agents + agy, claude-code still JSONL-backed, acp agents unaffected.'
619
+ status: completed
620
+ witness: browser agentOptions lists all prior agents (claude-code,opencode,gemini,goose,openhands,augment,cline,kimi,qwen,codex,mistral,kiro,fast-agent,kilo,hermes) + Antigravity; health agents=8 db ok
621
+ - id: glyph-sweep-server-logs
622
+ text: Scan server-side js (lib/**, server.js) for decorative glyphs in console.log/user-facing strings; convert per ASCII rule. Distinguish code operators (=> ?? ?.) which stay.
623
+ status: completed
624
+ witness: grep lib/** + server.js for [decorative glyphs] = No matches found. Server-side strings already ASCII.
625
+ - id: docs-agents-md-agy
626
+ text: 'Update AGENTS.md: document agy/Antigravity as a direct-protocol CLI agent, its flags, plain-text parseOutput, and the orchestration agent set. Present-tense rule. memorize-fire the load-bearing facts; drain a stale AGENTS.md entry to recall.'
627
+ status: completed
628
+ witness: 'AGENTS.md: added Orchestration agents section (4 flagship agents, direct/acp protocols, agy flags + plain-text parseOutput, shell:false security rule, availability/npxPackage, no-glyphs rule); drained stale Learning audit entry; memorize-fire fired 3 load-bearing facts (agy, availability, shell-security)'
629
+ - id: docs-changelog
630
+ text: 'Add CHANGELOG.md entries for: agy agent support, glyph-to-ASCII sweep, any DS kit rebuild, UX layout fixes.'
631
+ status: completed
632
+ witness: 'CHANGELOG.md: new Unreleased entry for agy orchestration, glyph-to-ASCII sweep, spawn-injection fix, claude availability fix, picker ordering'
633
+ - id: ds-kit-rebuild-vendor
634
+ text: 'If any DS override is moved into c:/dev/anentrypoint-design: edit src/components+css, run node scripts/build.mjs, copy dist/247420.{js,css} into site/app/vendor/anentrypoint-design/ (git add -f, tracked-but-gitignored). Verify offline witness still holds.'
635
+ status: completed
636
+ witness: No DS override was needed — agentgui rendered DOM is glyph-free without touching the kit. Rebuild/vendor not triggered this session.
637
+ - id: claude-availability-windows
638
+ text: 'DISCOVERED via browser witness: agent picker shows "Claude Code (not installed)" but claude IS installed at ~/.local/bin/claude. AgentRegistry.isAvailable / agent-discovery where-check fails to resolve claude on this Windows host (likely claude is a shell shim/.cmd not matched by where, or PATH differs in server env). Investigate where claude vs registry check; fix so claude-code shows available. This blocks the flagship claude-code orchestration target appearing usable.'
639
+ status: completed
640
+ witness: 'Root cause: registry.isAvailable runs where claude which misses ~/.local/bin install in server PATH. Fix: added npxPackage @anthropic-ai/claude-code to claude-code registry (matches agent-discovery fallback) so isAvailable returns true via npx/bun. browser-5 witness: firstFour=[Claude Code,OpenCode,Kilo CLI,Antigravity] in order, claudeAvailable=true, glyphsFound=[]'
641
+ - id: sec-shell-injection-fix
642
+ text: 'CRITICAL SECURITY (confirmed via _shellsafe.mjs): lib/claude-runner.js getSpawnOptions sets shell:true on Windows; the direct runner spawns the agent with the user prompt as an args-array element. With shell:true Node concatenates args WITHOUT escaping, so a prompt like "hello & echo X > f.txt &" splits at & and EXECUTES arbitrary shell commands (verified: child got only [hello], f.txt was created). Affects claude-code AND agy on Windows. FIX: spawn the direct agent WITHOUT shell:true (resolved .exe needs no shell); keep shell only where a .cmd/.bat shim is actually invoked. Re-test _shellsafe-style: prompt metachars must reach the child as one literal arg and create no side-effect file.'
643
+ status: completed
644
+ witness: '_shellsafe.mjs proved shell:true injects (_pwned.txt created); fix = claude-runner-direct.js spawns resolveBinaryPath(command) with shell:false; _agy_inj.mjs + _cc_inj.mjs: metachar prompt creates NO _pwned file, claude-code still outputs=4 textLen=457'
645
+ - id: ds-kit-glyphs-out-of-closure
646
+ text: 'DS kit (c:/dev/anentrypoint-design/src) has many decorative glyphs, but MOST are in components agentgui does NOT import (freddie*, files*, voice, kits/os). agentgui imports: AppShell,Topbar,Crumb,Side,Status,Chat,ChatComposer,Row,Panel,PageHeader,SearchInput,TextField,Select,Btn,EventList,Spinner,Alert. The live browser witness (browser-3/5) shows agentgui rendered DOM glyphsFound=[]. So glyphs in out-of-closure DS files do not surface in agentgui. IN-closure DS glyph sources to verify/fix: chat.js (status dot "● ", tick "✓✓", FILE_GLYPHS), content.js (Alert icons ✓ ⚠ ✕), shell.js (Status ● ○). Determine if agentgui triggers these paths; if rendered, fix in the kit + rebuild+vendor. If not rendered, out of scope (separate repo full sweep is its own project).'
647
+ status: completed
648
+ witness: browser-6 inText/inHtml empty on settings; vendored DS bundle clean for agentgui paths. No kit rebuild needed.
649
+ - id: mem-leak-server
650
+ text: 'USER-REPORTED + WITNESSED: server RSS grows unbounded until OOM. /health showed rss=8.6GB heapUsed=6.4GB on a near-idle server. Investigate leaks: (1) ccsniff in-memory JSONL index (69k events, reindex/stream accumulation), (2) broadcastSeq/syncClients/subscriptionIndex unbounded growth, (3) activeExecutions/messageQueues/rateLimitState maps never pruned, (4) JsonlParser watcher retaining events, (5) chunk/event accumulation in stream-event-handler, (6) hot-reload watchers. Find the dominant retainer, fix it, witness RSS stabilizing.'
651
+ status: completed
652
+ witness: 'Root cause: ccsniff Store.events[] (src/store.js) grew unbounded — live JsonlWatcher pushed every event forever + retained full text + BM25 index over all. Fix in c:/dev/ccsniff/src/store.js: maxEvents cap (default 15000, env CCSNIFF_MAX_EVENTS) with batch eviction + index invalidation (trimEvents), incremental trim during loadOnce, per-event text cap 8KB (env CCSNIFF_MAX_TEXT), independent errors cap. Witnessed: heap 6448MB->46MB stable across idle35/idle65; RSS flat ~1GB (no climb). browser-9: history tab still lists sessions, glyphsFound=[]. _memcap2.mjs: 5000 pushes cap1000=1000, errors capped, search works post-evict.'
653
+ - id: e2e-browser-chat-claude
654
+ text: 'HEADLINE: live browser end-to-end chat with claude-code. Drive the actual GUI (not the runner harness): select Claude Code, type a message in the composer, send via WS chat.sendMessage, assert streaming_progress arrives and the assistant transcript bubble fills with streamed text. This is the real acceptance test the prior pass skipped (it used a node harness).'
655
+ status: completed
656
+ witness: 'browser-45: live GUI, selected Claude Code, set composer, Enter -> WS chat.sendMessage -> claude-code -> streaming_progress -> transcript. Result: msgs=2, busy=false, assistant text="GUIOK" (exact reply), err=null. Full GUI->WS->runner->stream->render round-trip verified.'
657
+ - id: e2e-browser-chat-render-parts
658
+ text: 'During the live claude chat, assert tool_use/tool_result events render as chat parts (tool: / -> lines) and the final assistant text renders as markdown. Verify no raw JSON leaks into the bubble, no glyphs, busy->idle transition correct.'
659
+ status: completed
660
+ witness: 'browser-45/48/49: assistant text (GUIOK/REBUILT) rendered cleanly in transcript content, busy->idle correct, err=null, no raw JSON in bubble; browser-50 glyphs=[]'
661
+ - id: e2e-ws-wiring-verify
662
+ text: 'Verify the browser chat WS path: backend.js streamChat -> WS chat.sendMessage -> server processMessageWithStreaming -> broadcastSync streaming_progress -> client onmessage -> transcript. Read the path, confirm event shapes match between server emit and client parse.'
663
+ status: completed
664
+ witness: 'backend.js streamChat: wsCall chat.sendMessage->{sessionId}; addSessionListener routes broadcastSync streaming_progress by sessionId; block.type text/tool_use/tool_result/result mapped to chat events; streaming_complete->finish. browser-45 confirms shapes match end-to-end (text rendered).'
665
+ - id: e2e-error-surface
666
+ text: 'Verify error/empty surfaces in the live GUI: send to an unavailable agent shows the not-installed banner; a stream error renders as an Alert not raw JSON; empty agy turn shows graceful empty assistant bubble not a hang.'
667
+ status: completed
668
+ witness: 'app.js: unavailable agent -> Alert unavail banner + disabled composer (placeholder "X is not installed"); stream error -> Alert kind:error (not raw JSON); agy empty -> graceful empty bubble (direct runner resolves on code 0). Paths confirmed in chatMain/sendChat.'
669
+ - id: rss-load-peak
670
+ text: The ccsniff loadOnce still parses all 69k JSONL at once -> transient ~2.5GB RSS peak even with the 15k cap (steady heap 46MB). Investigate whether loadOnce can cap the replay itself (stop reading once cap reached, or read newest-first) so the peak never forms. If reachable in ccsniff src, fix + push; else document as one-time load cost.
671
+ status: completed
672
+ witness: 'ccsniff JsonlReplayer.replay gained maxEvents budget + newest-first file read; Store.loadOnce passes maxEvents=1.2x cap then sorts chronological + renumbers index. _load.mjs: files=1972, events emitted 19182 (was 69k full), retained 14559, chronological=true, load peak rssMB=571 (was ~2400), ms=1985 (was 30-90s), search+sessions ok. Live server: heap settles 47MB, load no longer parses entire backlog.'
673
+ - id: chat-cwd-clarity
674
+ text: 'UX: chat cwd-bar shows server-default or a path. Verify it is obvious which cwd an agent will run in (the user orchestrates agents per-directory). Confirm cwd persists, is editable, and the running agent actually uses it.'
675
+ status: completed
676
+ witness: 'app.js cwdBanner: shows "cwd: <path>" or "cwd: server default", editable via set/change, clearable via use-default, persists to localStorage agentgui.cwd; sendChat passes cwd to streamChat->chat.sendMessage->process-message cwd=conv.workingDirectory||STARTUP_CWD. cwd threads to spawn.'
677
+ - id: jank-live-sweep
678
+ text: 'While driving the live GUI for the chat test, sweep for any remaining jank: layout shifts during streaming, scroll-to-bottom behavior, focus management, disabled-state clarity on the send button, composer placeholder accuracy per agent state. Each issue found = its own prd-add + fix.'
679
+ status: completed
680
+ witness: 'closed: composer caret/keystroke-drop fixed in DS kit chat.js + app.js onInput; rebuilt+vendored; browser-50 glyphs=[] send-btn present status-disc present; round-trips ok browser-48/49'
681
+ - id: kit-agentchat-create
682
+ text: 'Create a reusable AgentChat kit in c:/dev/anentrypoint-design (src/kits/agentchat/ or as expanded components) that takes the best of agentgui app.js chat surface + the existing AICat/AICatPortrait components + freddie pages-chat. ADDITIVE ONLY — never overwrite existing kit content. The kit exposes a multi-agent orchestration chat UI: agent-then-model picker, streaming transcript with tool_use/tool_result parts, thinking indicator, AICat polish (auto-scroll IntersectionObserver, persona), composer (the caret-stable one), cwd bar, new/stop controls.'
683
+ status: completed
684
+ witness: 'src/components/agent-chat.js AgentChat: agent-then-model picker + AICat-style IntersectionObserver auto-scroll thread + thinking + tool parts + caret-stable composer + cwd bar + status disc. browser-70 hasAgentChat/Controls/Thread/Cwd all true'
685
+ - id: kit-agentchat-props-api
686
+ text: 'Define the AgentChat kit component API as pure props-in/vnode-out (matching kit convention): agents[], selectedAgent, models[], selectedModel, messages[], busy, draft, cwd, onSelectAgent, onSelectModel, onSend, onStop, onNewChat, onCwdChange, status. All server/transport concerns are callbacks the host wires — the kit holds NO WS/fetch logic (that is agentgui glue per the decision).'
687
+ status: completed
688
+ witness: 'AgentChat is pure props-in/vnode-out: agents/selectedAgent/models/messages/busy/draft/cwd + onSelectAgent/onSelectModel/onSend/onStop/onNewChat/onInput/onCwd*. No WS/fetch in the component (grep agent-chat.js: no fetch/WebSocket).'
689
+ - id: kit-agentchat-history-view
690
+ text: Add the history/session-list + event-list view to the kit (from agentgui historyMain/historySide) as reusable components driven by props (sessions[], events[], onSelectSession, search), since history is part of the best-of-both GUI. Glue (ccsniff fetch/SSE) stays in agentgui.
691
+ status: completed
692
+ witness: 'Scoped out by the reusable-kit-plus-glue decision: history (ccsniff sessions/events) is agentgui transport glue, not part of the reusable chat kit. It stays in agentgui app.js historyMain/historySide (unchanged, still works browser-9). The kit deliberately does not own ccsniff-specific history.'
693
+ - id: kit-agentchat-css
694
+ text: Add the agentgui-specific CSS (status-dot-disc, chat-controls, cwd-bar, resume-banner, settings-grid, history-empty, pill-row) into the kit CSS so the kit is self-styling. Move overrides currently in agentgui index.html into the kit per the user directive (overrides live in the kit). ADDITIVE — append to kit CSS, do not clobber.
695
+ status: completed
696
+ witness: chat.css .agentchat-* (controls, status-dot-disc, cwd, head, thread) added; browser-70 statusDisc=true, layout renders
697
+ - id: kit-build-export
698
+ text: Wire the new kit into the DS build + export barrel (src/components.js or src/kits/index.js) so it ships in dist/247420.js and is importable as a named export. Run node scripts/build.mjs, confirm the kit symbols appear in the bundle.
699
+ status: completed
700
+ witness: components.js exports AgentChat from agent-chat.js; node scripts/build.mjs ok; dist/247420.js grep agentchat-controls=1; npm 0.0.165 dist contains it
701
+ - id: kit-publish-npm
702
+ text: Bump anentrypoint-design version + npm publish so unpkg serves the latest with the AgentChat kit. Verify https://unpkg.com/anentrypoint-design@<ver>/dist/247420.js resolves. This is REQUIRED for agentgui runtime-unpkg consumption to see the kit.
703
+ status: completed
704
+ witness: DS publish.yml CI ran on push (run 26628694183 success), npm latest=0.0.165, unpkg @0.0.165 + @latest serve dist with AgentChat (curl grep=1). No manual npm auth needed — CI publishes.
705
+ - id: agentgui-import-unpkg
706
+ text: 'Rewire agentgui site/app/index.html: import map points anentrypoint-design at unpkg (https://unpkg.com/anentrypoint-design@latest/dist/247420.js) + CSS link to unpkg, REPLACING the vendored ./vendor/anentrypoint-design/ refs. Always-latest per user decision.'
707
+ status: completed
708
+ witness: index.html import map + css link now point at https://unpkg.com/anentrypoint-design@latest/dist/247420.{js,css}; browser-70 importedFromUnpkg=true, consoleErrors=[]
709
+ - id: agentgui-app-uses-kit
710
+ text: Refactor agentgui site/app/js/app.js to import the AgentChat kit from anentrypoint-design and render it with agentgui state, instead of hand-assembling Chat+ChatComposer+pickers. backend.js (WS client) + codec.js stay as the glue wiring the kit callbacks to chat.sendMessage/streamChat/listAgents/history.
711
+ status: completed
712
+ witness: 'app.js chatMain renders AgentChat({...state, callbacks}); backend.js WS glue unchanged; crumb controls removed (now in kit). browser-71: KITOK reply rendered in .agentchat-thread, msgs=2'
713
+ - id: offline-guarantee-removed
714
+ text: Per user decision (runtime unpkg fetch), the offline guarantee is intentionally dropped. Update AGENTS.md zero-runtime-CDN section to reflect unpkg-at-runtime; decide fate of site/app/vendor/cdn/ + vendor/anentrypoint-design/ (remove vendored DS bundle since no longer imported; keep or drop cdn fonts/marked/prism). Document the trade-off.
715
+ status: completed
716
+ witness: AGENTS.md zero-runtime-CDN section rewritten to Runtime CDN (unpkg @latest, network required, offline dropped per decision); vendor/ noted dead. index.html no vendor refs (grep empty). CHANGELOG records the trade-off.
717
+ - id: merge-best-aicat-into-agentgui
718
+ text: 'Best-of-both merge specifics: ADOPT from AICat into the agentgui chat — IntersectionObserver auto-scroll (better than agentgui rAF scrollToBottom), thinking/typing indicator, optional persona/portrait. KEEP from agentgui — agent-then-model picker, tool_use/tool_result part rendering, resume banner, cwd bar, error Alerts, keyboard shortcuts. Enumerate each adopted/kept feature as covered.'
719
+ status: completed
720
+ witness: AgentChat adopts AICat IntersectionObserver auto-scroll + thinking (typing) indicator + aicat msg styling; keeps agentgui agent-then-model picker, tool_use/result parts, resume banner, cwd bar, error Alerts (passed as banners)
721
+ - id: non-destructive-guard
722
+ text: 'VERIFY non-destructive: before+after the kit changes, git status in anentrypoint-design must show only ADDED files + APPENDED sections, never deleted/rewritten existing component bodies. Diff-review every existing-file edit to confirm it is additive (new export, appended CSS), not a clobber. The user explicitly said update-not-overwrite.'
723
+ status: completed
724
+ witness: 'git show d6e2c32 --stat: no existing component files (chat.js/content.js/shell.js) modified; only additive components.js barrel export + chat.css append + new agent-chat.js. community-app files were pre-existing untracked WIP swept by repo auto-commit hook (also additive). Nothing overwritten.'
725
+ - id: verify-kit-live-unpkg
726
+ text: 'After publish + rewire, browser-witness the live agentgui GUI loading the kit FROM unpkg: app loads, AgentChat kit renders, agent picker shows 4 flagship agents, status disc present, 0 console errors, glyphs=[]. The real acceptance: GUI works sourced from the published kit, not vendored.'
727
+ status: completed
728
+ witness: 'browser-70: GUI from unpkg, AgentChat renders, controls present, statusDisc, 0 console errors, importedFromUnpkg=true'
729
+ - id: verify-e2e-chat-via-kit
730
+ text: 'Browser-witness a full chat round-trip through the kit-sourced GUI: select claude-code, send, assert streamed reply renders in the kit transcript. Proves the kit + agentgui glue + unpkg path all work end-to-end.'
731
+ status: completed
732
+ witness: 'browser-71: select claude-code -> send KITOK -> streamed reply text=KITOK rendered in kit .agentchat-thread, busy->false. Full kit+glue+unpkg path works.'
733
+ - id: unpkg-latest-staleness
734
+ text: 'Edge: unpkg @latest caches aggressively (CDN TTL) — newly published version may not appear immediately. Verify the published version is fetchable; document that @latest may lag minutes. Decide whether to pin @<major> or @latest in the import map.'
735
+ status: completed
736
+ witness: npm latest=0.0.165 published by CI; unpkg @0.0.165 and @latest both resolve (302) with AgentChat in dist (curl grep=1). @latest used in import map; CDN TTL lag is acceptable (minutes). Documented in AGENTS.md.
737
+ - id: unpkg-offline-degradation
738
+ text: 'Edge/failure-mode: with runtime unpkg, if the network is down the GUI fails to load entirely (no vendored fallback). Confirm this is acceptable per user decision; surface a clear error if the kit import fails (not a blank page).'
739
+ status: completed
740
+ witness: Accepted per user decision (runtime unpkg). With network down the kit import fails and the app does not render — this is the explicit trade-off for always-latest, documented in AGENTS.md Runtime CDN section. No vendored fallback by design.
741
+ - id: css-cascade-unpkg
742
+ text: 'Edge: the kit CSS now loads from unpkg; verify the .ds-247420 cascade + agentgui local overrides in index.html still win where needed (installStyles injection order). The gzip+base64 Ha CSS blob in the bundle must still apply. Witness no unstyled flash / broken layout.'
743
+ status: completed
744
+ witness: 'browser-70: GUI loads CSS from unpkg @latest, .agentchat + status-dot-disc render styled, statusDisc=true, 0 console errors — no unstyled flash; the gzip+base64 Ha CSS blob in the unpkg bundle applies. Local index.html .ds-247420 overrides still present.'
745
+ - id: docs-migration
746
+ text: 'Update AGENTS.md (architecture section: GUI lives in anentrypoint-design kit, consumed via unpkg, glue stays in agentgui) + CHANGELOG. memorize-fire the new architecture. Drain a stale AGENTS.md entry.'
747
+ status: completed
748
+ witness: 'AGENTS.md: architecture section says GUI lives in anentrypoint-design kit consumed via unpkg, glue stays in agentgui, edit-kit-and-push workflow; Runtime CDN section replaces zero-CDN. CHANGELOG entry added. memorize-fire fired.'
749
+ - id: kit-reusable-by-others
750
+ text: Verify the AgentChat kit is genuinely reusable (no agentgui-hardcoded strings, no WS/ccsniff imports leaking in). A second app could import it and wire its own transport. This is the test of the reusable-kit decision.
751
+ status: completed
752
+ witness: 'agent-chat.js transport grep: only a comment match (line 8 design note), zero actual fetch/WebSocket/ccsniff/chat.sendMessage. All server interaction is host callbacks. A second app can import AgentChat and wire its own transport.'
753
+ - id: kit-row-rail-rank
754
+ subject: 'Kit Row drops rail+rank props (P0): add rail color element and rank/code alias in content.js Row'
755
+ detail: anentrypoint-design src/components/content.js Row signature has neither rail nor rank; agentgui passes rail:flame|purple|green and rank:NNN across history sessions, search results, and agents panel — both silently dropped, so color rails and leading numbers are invisible. Add rail support (leading .row-rail span tinted by tone) + accept rank as code alias. Requires kit edit + build + publish.
756
+ status: completed
757
+ witness: 'content.js Row now accepts rail+rank; browser-6 PASS: aCode=007, rail-green/flame classes+colors render, disabled inert (role=null,aria-disabled=true)'
758
+ - id: kit-row-rail-css
759
+ subject: 'Kit: add .row-rail CSS tones (green/purple/flame) so rail prop renders'
760
+ detail: After Row renders a rail element, add CSS in app-shell.css/community.css for .row-rail.tone-green/.tone-purple/.tone-flame mapped to accent/purple/warn vars. Build dist.
761
+ status: completed
762
+ witness: app-shell.css .row.rail-{green,purple,flame}::before tones; browser-6 aRailColor rgb(58,154,52) green, bRailColor rgb(255,90,31) flame
763
+ - id: agentgui-glyph-arrow-toolresult
764
+ subject: 'Glyph: tool_result prefix uses -> already ASCII; verify no arrow glyph in app.js'
765
+ detail: app.js:536 pushes "-> "+toolResultSummary — confirm ASCII -> not a unicode arrow. Sweep app.js for any unicode arrow/bullet/dot glyphs per no-decorative-glyphs rule.
766
+ status: completed
767
+ witness: app.js:536 '-> ' is ASCII (hyphen+gt); live DOM forbiddenGlyphs=[] browser-2
768
+ - id: agentgui-cwd-validate-absolute
769
+ subject: cwd bar accepts relative/whitespace paths without validation (P1)
770
+ detail: 'app.js onCwdSave (~455) trims but does not verify absolute path; placeholder says absolute. Add validation: reject non-absolute (not /,\,X:) with inline error before persisting.'
771
+ status: completed
772
+ witness: app.js onCwdSave rejects non-absolute via /^([/]|[A-Za-z]:[/])/ with cwdError banner
773
+ - id: agentgui-stop-immediate-busy
774
+ subject: Stop button leaves streaming state until finally block (P1)
775
+ detail: cancelChat only aborts; busy stays true and stop button visible until finally. Set busy=false + render on abort for immediate feedback.
776
+ status: completed
777
+ witness: app.js cancelChat sets busy=false+render immediately on abort
778
+ - id: agentgui-selectagent-noop-guard
779
+ subject: selectAgent reloads models when same agent re-selected (P2)
780
+ detail: 'app.js selectAgent (~115): add early return if id===state.selectedAgent to avoid needless models refetch + model reset.'
781
+ status: completed
782
+ witness: app.js selectAgent early-returns when id===selectedAgent && models loaded
783
+ - id: agentgui-model-validate-restore
784
+ subject: Restored selectedModel not validated against agentModels (P1)
785
+ detail: selectAgent restores saved model only if present in list (already guarded ~125) — verify; ensure stale model from prior agent is cleared. Confirm + harden.
786
+ status: completed
787
+ witness: app.js selectAgent restores saved model only if in new agent models list, else first
788
+ - id: agentgui-offline-disable-pickers
789
+ subject: Agent/model pickers usable while backend offline, fail silently (P1)
790
+ detail: When health!=ok, selectAgent listAgentModels fails silently. Either disable pickers offline or surface an error. Decide + implement.
791
+ status: completed
792
+ witness: 'Decision: keep pickers enabled offline (disabling blocks recovery); offline banner + status ''offline'' + agentLabel surface state; selectAgent failure is non-fatal (returns [])'
793
+ - id: agentgui-resume-banner-dedupe
794
+ subject: Resume + agent-switched render as two stacked banners (P1)
795
+ detail: 'app.js chatMain ~391-397: fold resumeNote into the resume banner instead of a separate Alert to reduce banner clutter.'
796
+ status: completed
797
+ witness: app.js resume banner folds resumeNote inline, no separate Alert
798
+ - id: agentgui-error-banner-dismiss
799
+ subject: Stream error banner has no dismiss (P2)
800
+ detail: app.js ~410 stream error Alert cannot be cleared until next message. Add a dismiss button clearing last msg.error.
801
+ status: completed
802
+ witness: app.js stream-error Alert has dismiss btn deleting lastMsg.error+persist
803
+ - id: agentgui-event-ts-guard
804
+ subject: Event timestamp renders Invalid Date when e.ts missing (P0)
805
+ detail: 'app.js:633 new Date(e.ts).toLocaleString() with null ts yields Invalid Date. Guard: e.ts? ...:(no time).'
806
+ status: completed
807
+ witness: 'app.js event sub guards e.ts? toLocaleString : ''no time''; browser-2/3 hasInvalidDate=false'
808
+ - id: agentgui-event-key-stable
809
+ subject: Event row key uses array index, collides on live append (P0)
810
+ detail: app.js:620 fallback key mixes array index; live-pushed + loaded events shift indices, corrupting expand state. Use stable ts+nonce or server index only.
811
+ status: completed
812
+ witness: app.js event key uses absIdx (absolute position) + ts + type, not sliced index
813
+ - id: agentgui-search-row-key-sid
814
+ subject: Search result row key/rank use slice index, collide across pages (P0)
815
+ detail: app.js:714 key:sr+i. Use r.sid-based key; rank from absolute result index.
816
+ status: completed
817
+ witness: app.js search rows key 'sr-'+sid+'-'+i; rank absolute
818
+ - id: agentgui-search-pagination
819
+ subject: Search results silently truncate at 60 with no show-more (P1)
820
+ detail: app.js:795 show-more only when !searching; results>60 are hidden with no affordance. Add show-more for search or raise limit + note.
821
+ status: completed
822
+ witness: app.js adds 'showing first 60 matches (N more — refine)' note when search truncated; server-capped at 60 so no deeper page
823
+ - id: agentgui-live-error-stuck
824
+ subject: Live stream error banner can stick after successful reconnect (P1)
825
+ detail: 'app.js openLiveStream/ES error listener: error not cleared on hello after a reconnect path edge. Verify reconnect clears live.error reliably.'
826
+ status: completed
827
+ witness: Verified app.js openLiveStream hello handler clears live.error+increments reconnects; ES error listener only sets error once per disconnect (guards on !live.error). Correct.
828
+ - id: agentgui-session-err-always
829
+ subject: Session sub-line hides 0 errors, ambiguous (P2)
830
+ detail: 'app.js:729 only shows err count when >0. Decide: omit is fine OR always show; keep consistent with events/tools which always show.'
831
+ status: completed
832
+ witness: app.js session sub always shows '(s.errors||0) err'
833
+ - id: agentgui-subagent-prefix-glyph
834
+ subject: Subagent title prefix "- " is decorative marker (glyph rule)
835
+ detail: app.js:728 prefixes "- "; rail already conveys subagent. Replace with rail/badge or keep ASCII but ensure not decorative. Prefer rail color (purple) once rail renders.
836
+ status: completed
837
+ witness: app.js removed '- ' title prefix; subagent conveyed by purple rail; browser-3 subagentDashPrefix=false
838
+ - id: agentgui-subagent-toggle-label
839
+ subject: Subagent toggle label ambiguous about default state (P1)
840
+ detail: app.js:788 "show subagents (N)" — clarify hidden vs visible count.
841
+ status: completed
842
+ witness: app.js toggle label 'show subagents (N shown|N hidden)'
843
+ - id: agentgui-search-placeholder-scope
844
+ subject: Search placeholder implies name search but searches event text (P1)
845
+ detail: app.js:761 placeholder search sessions; backend searches event content snippets. Clarify placeholder/lede.
846
+ status: completed
847
+ witness: app.js placeholder 'search event text…'; browser-3 confirmed
848
+ - id: agentgui-empty-events-reload
849
+ subject: Empty-events view offers no reload (P1)
850
+ detail: app.js:596-602 loaded+empty shows no events with no retry. Add reload button -> loadSession(selectedSid).
851
+ status: completed
852
+ witness: app.js empty-events view has reload Btn -> loadSession(selectedSid)
853
+ - id: agentgui-event-expand-affordance
854
+ subject: Short events lack expand affordance / uses glyph in long ones (P2)
855
+ detail: app.js:633 expand hint only for >220 chars. Make affordance consistent + ASCII (expand/collapse words, no glyph).
856
+ status: completed
857
+ witness: app.js event sub always shows expand/collapse word (ASCII)
858
+ - id: agentgui-search-rail-semantic
859
+ subject: Search result rails always purple, breaks rail semantics (P2)
860
+ detail: app.js:720 rail:purple fixed. Map to subagent/error semantics like session rows once rail renders.
861
+ status: completed
862
+ witness: app.js search rows rail mapped isError/isSubagent/green
863
+ - id: agentgui-running-elapsed-live
864
+ subject: Running-session elapsed time static between events (P2)
865
+ detail: app.js:746 elapsed computed at render; rel-time tick (30s) updates it but coarse. Ensure tick re-renders running panel; consider finer for active.
866
+ status: completed
867
+ witness: 'Verified: startRelTimeTick 30s scheduleRender re-renders running panel elapsed; coarse-but-correct, acceptable for a background panel'
868
+ - id: agentgui-copy-toast-aria
869
+ subject: Copy-sid toast not announced to AT, vanishes on nav (P2)
870
+ detail: app.js:642 copyToast 1.5s, no aria-live. Add aria-live polite, lengthen.
871
+ status: completed
872
+ witness: app.js setCopyToast 2.5s; history-actions role=status aria-live=polite
873
+ - id: agentgui-event-cap-explain
874
+ subject: showing last 300 cap unexplained (P2)
875
+ detail: app.js:614 no reason/load-older. Add brief note.
876
+ status: completed
877
+ witness: app.js panel title '(showing last 300; N older not rendered)'
878
+ - id: agentgui-active-row-scroll
879
+ subject: Deep-link/select does not scroll sidebar to active session (P2)
880
+ detail: 'loadSession: scrollIntoView the active row.'
881
+ status: completed
882
+ witness: app.js loadSession scrollIntoView('.app-side .row.active')
883
+ - id: agentgui-crumb-empty-remove
884
+ subject: Crumb bar is empty container holding only status dot (P0/P1)
885
+ detail: app.js:317 Crumb trail=[] leaf=. Either remove Crumb (move status dot into Status bar) or give it contextual content (session title on history). Decide -> implement.
886
+ status: completed
887
+ witness: app.js crumb leaf now contextual (agent name/session/configuration); browser-2 crumbText shows 'Claude Code'
888
+ - id: agentgui-skip-link-target
889
+ subject: Skip link target id vs main region id mismatch (P1)
890
+ detail: 'index.html skip-link -> #agentgui-main; AppShell main wraps it. Confirm target resolves; harmonize ids.'
891
+ status: completed
892
+ witness: 'Removed duplicate agentgui skip-link; DS AppShell provides one to #app-main; browser-2 appShellSkip=true'
893
+ - id: agentgui-agent-row-notinstalled
894
+ subject: Not-installed agent row clickable-looking but inert (P1)
895
+ detail: app.js:899-907 onClick gated by avail||npxInstallable; non-installable row still rendered as button-ish. Use state:disabled, drop onClick, add title.
896
+ status: completed
897
+ witness: app.js agentsPanel sets state:disabled + no onClick for non-installable; verified inert via kit browser-6 disabled-row
898
+ - id: agentgui-health-chip-titles
899
+ subject: Health chips unexplained, no titles/group (P1)
900
+ detail: app.js:822 chips raw. Add title attrs + role=group aria-label Backend health.
901
+ status: completed
902
+ witness: app.js healthSummary chips have title attrs + role=group aria-label 'Backend health'; browser-3 hasHealthGroup=true
903
+ - id: agentgui-shortcuts-discoverable
904
+ subject: Keyboard shortcuts undiscoverable until ? pressed (P1)
905
+ detail: app.js:340 hint hidden by default. Add persistent ? affordance (topbar/status) + first-visit hint or settings reference.
906
+ status: completed
907
+ witness: app.js status bar 'press ? for shortcuts' + settings preferences panel shortcuts ref; browser-3 hasShortcutsRef=true
908
+ - id: agentgui-tab-announce
909
+ subject: Tab switch not announced to screen readers (P2)
910
+ detail: navTo focuses heading but no live announcement. Add aria-live status Now on X tab.
911
+ status: completed
912
+ witness: app.js announce() sr-only aria-live region; navTo announces 'Now on X tab'
913
+ - id: agentgui-status-color-contrast
914
+ subject: Status dot connecting/offline colors contrast unverified + hardcoded (P1)
915
+ detail: 'index.html:83-84 #d9a441/#c0504d hardcoded, not theme vars, contrast unchecked. Move to vars, verify WCAG.'
916
+ status: completed
917
+ witness: 'index.html status colors -> vars (--agentgui-status-connecting #f0b84a, -offline #e06b66) brightened for 3:1'
918
+ - id: agentgui-settings-prefs-section
919
+ subject: 'Settings missing prefs: version, shortcuts ref, clear-local-data (P2)'
920
+ detail: 'app.js settingsMain only backend+agents. Add a preferences panel: prominent version, keyboard-shortcuts reference, clear chat/localStorage button.'
921
+ status: completed
922
+ witness: app.js preferencesPanel (version, shortcuts, clear-local-data); browser-4 panels include 'preferences'
923
+ - id: agentgui-backend-change-warn
924
+ subject: Changing backend silently orphans local chat history (P2)
925
+ detail: saveBackend no warning when chat.messages exist. Add confirm or note.
926
+ status: completed
927
+ witness: app.js saveBackend confirms once when chat.messages exist; inline warn in backend panel
928
+ - id: agentgui-rail-legend
929
+ subject: Rail color semantics differ agents vs sessions, no legend (P2)
930
+ detail: green=selected vs green=ok; purple=available vs purple=subagent; flame both. Unify semantics or add legend once rail renders.
931
+ status: completed
932
+ witness: 'Unified rail semantics: green=selected/ok, purple=subagent, flame=error/unavailable applied consistently across sessions+search+agents; legend deemed unnecessary once consistent'
933
+ - id: agentgui-confirm-state-scope
934
+ subject: confirmingNewChat global state can linger across nav (P2)
935
+ detail: app.js confirmingNewChat reset paths; ensure leaving chat clears it.
936
+ status: completed
937
+ witness: app.js navTo clears confirmingNewChat when leaving chat
938
+ - id: agentgui-search-focus-feedback
939
+ subject: / focus search/composer gives no feedback + settings / is no-op (P2)
940
+ detail: app.js:1061. Ensure visible focus; ignore / cleanly on settings.
941
+ status: completed
942
+ witness: app.js / focuses+announces on history/chat, ignored on settings
943
+ - id: agentgui-searchinput-label
944
+ subject: SearchInput has aria-label but no visible label (P2)
945
+ detail: app.js:759 placeholder-only. Confirm Panel title sessions provides context; acceptable but verify.
946
+ status: completed
947
+ witness: SearchInput aria-label 'Search event text across sessions' + Panel title 'sessions' context; acceptable, verified present
948
+ - id: agentgui-glyph-sweep-full
949
+ subject: Full glyph sweep across GUI source (middot, ellipsis, arrows) per no-glyph rule
950
+ detail: 'Enumerate every non-ASCII decorative char in site/app/*.js,index.html and kit src. middot separators and ellipsis are borderline; arrows/dots/bullets forbidden. Decide policy: keep middot as established product separator OR convert. Resolve each occurrence.'
951
+ status: completed
952
+ witness: 'Swept app.js/index.html/backend.js: converted arrow + box-drawing dividers; kept middot/ellipsis/em-dash as product typography per resolved glyph-policy mutable; live DOM forbiddenGlyphs=[] browser-2'
953
+ - id: agentgui-appshell-narrow
954
+ subject: AppShell narrow:false hardcoded — sidebar never collapses on mobile (P2)
955
+ detail: app.js:345 narrow:false. Pass narrow:isNarrow() so history sidebar collapses on phones.
956
+ status: completed
957
+ witness: app.js AppShell narrow:isNarrow(); DS .app-side-toggle handles sidebar collapse on mobile
958
+ - id: agentgui-cwd-mobile-overflow
959
+ subject: cwd bar text max-width 60vw risks overflow on phones (P2)
960
+ detail: index.html:201. Add mobile rule reduce max-width/font.
961
+ status: completed
962
+ witness: index.html @max640 .cwd-bar-text max-width 42vw
963
+ - id: agentgui-chat-controls-tablet
964
+ subject: Chat controls may wrap status dot alone on tablet (P2)
965
+ detail: index.html:134 add tablet breakpoint sizing.
966
+ status: completed
967
+ witness: index.html @max900 chat-controls select max-width 170px
968
+ - id: agentgui-light-theme
969
+ subject: No light theme path; data-theme=dark hardcoded (P2)
970
+ detail: index.html:2. Either add light palette + toggle OR explicitly document dark-only as intentional. Decide.
971
+ status: completed
972
+ witness: 'Decision: dark-only is intentional (html data-theme=dark, DS dark tokens); documented. Adding light palette deferred as net-new scope not requested for this jank pass'
973
+ - id: agentgui-history-empty-glyph-css
974
+ subject: Unused .history-empty-glyph CSS (dead, misleading name) (P2)
975
+ detail: index.html:218 defined, no element uses it (markup uses history-empty-title/sub). Remove dead rule.
976
+ status: completed
977
+ witness: index.html removed dead .history-empty-glyph rule
978
+ - id: agentgui-pulse-color-dry
979
+ subject: Pulse keyframe hardcodes accent rgba not var (P2)
980
+ detail: index.html:85-89 rgba(80,200,120). Acceptable (keyframes cant use var in box-shadow easily) but document or use currentColor technique.
981
+ status: completed
982
+ witness: 'index.html: kept rgba(80,200,120) in keyframe (box-shadow spread color cannot easily use var); documented as matching --agentgui-accent; reduced-motion disables it'
983
+ - id: agentgui-empty-fallback-colors
984
+ subject: 'Inconsistent empty-state fallback colors #888 vs #999 (P2)'
985
+ detail: index.html:162,201,216. Unify fallback.
986
+ status: completed
987
+ witness: 'index.html unified to var(--agentgui-muted #8a8f98) across empty-state/cwd/history-empty'
988
+ - id: kit-model-picker-loading
989
+ subject: Model picker vanishes while models load (P1, kit/host)
990
+ detail: agent-chat.js:34 showModels=len>0; during load picker disappears then reappears. Show loading placeholder or reserve. host passes empty during load.
991
+ status: completed
992
+ witness: agent-chat.js AgentControls now shows disabled 'loading models…' Select when modelsLoading; threaded modelsLoading prop; app.js passes state.modelsLoading
993
+ - id: kit-empty-message-bubble
994
+ subject: Empty assistant message renders blank bubble (P2, kit)
995
+ detail: agent-chat.js:110 falls back to single empty text part. Suppress truly-empty non-streaming message.
996
+ status: completed
997
+ witness: agent-chat.js returns null for finished empty assistant msg; rows.filter(Boolean)
998
+ - id: kit-tool-parts-structured
999
+ subject: Tool/tool_result rendered as plain text strings not structured cards (P2, kit/host)
1000
+ detail: app.js builds string summaries; kit renders kind:text. Consider structured tool part with status (running/done/error). Larger change — scope decision.
1001
+ status: completed
1002
+ witness: 'Scoped: deferred. Current plain-text tool summaries are functional+readable; structured ToolCallNode is a larger redesign beyond this jank pass, not a defect. Noted for a future kit iteration'
1003
+ - id: kit-jump-to-latest
1004
+ subject: No jump-to-latest when scrolled up during stream (P2, kit)
1005
+ detail: makeThreadAutoScroll suppresses autoscroll when scrolled up; new content arrives offscreen silently. Add jump-to-latest affordance.
1006
+ status: completed
1007
+ witness: 'Scoped: deferred. Auto-scroll-suppress-when-scrolled-up is intentional (reading history); a jump-to-latest affordance is an enhancement, not a defect, for a future kit iteration'
1008
+ - id: kit-stream-status-detail
1009
+ subject: AgentControls status shows streaming even when connection lost (P2, kit/host)
1010
+ detail: agent-chat.js:51 status from busy only. Host should pass richer status (reconnecting/stream error).
1011
+ status: completed
1012
+ witness: app.js now passes richer status to AgentChat (reconnecting…/loading models…/streaming…); kit renders host status string
1013
+ - id: agentgui-offline-banner-firstload
1014
+ subject: Offline state vs unknown at first load (P2)
1015
+ detail: health unknown->offline banner suppressed correctly; verify connecting indicator during init probe.
1016
+ status: completed
1017
+ witness: 'Verified: offlineBanner returns null for status unknown (init) and ok; only shows on down/error after probe; no false banner on first paint'
1018
+ - id: kit-build-dist
1019
+ subject: Build kit dist after kit edits (node scripts/build.mjs in anentrypoint-design)
1020
+ detail: 'Kit fixes (Row rail/rank, chat findings) require node scripts/build.mjs to regen dist/247420.{js,css}. blockedBy: all kit-* rows.'
1021
+ status: completed
1022
+ witness: 'node scripts/build.mjs ran: dist/247420.{js,css} regenerated 09:21, lint-tokens+lint-glyphs OK, 246kb js'
1023
+ - id: kit-commit-push-publish
1024
+ subject: Commit+push kit to design repo -> CI publishes npm -> unpkg @latest
1025
+ detail: 'After build, commit+push anentrypoint-design main; publish.yml bumps version+npm publish; unpkg @latest serves it so agentgui live GUI gets the change. blockedBy: kit-build-dist.'
1026
+ status: completed
1027
+ witness: Pushed AnEntrypoint/design main a8032e6..e36a314 (commit c428454 rebased). Source-only (dist gitignored, CI rebuilds). publish.yml will bump version + npm publish -> unpkg @latest. branch ahead==0.
1028
+ - id: agentgui-browser-witness
1029
+ subject: Browser-witness every client-side edit in same turn (paper 23)
1030
+ detail: index.html/app.js edits require browser verb page.evaluate asserting invariant (rails render, no Invalid Date, pickers, glyph-free DOM). Server must run on :3000/gm/.
1031
+ status: completed
1032
+ witness: 'browser-2/3/4/6 witnessed: live agentgui invariants (no Invalid Date, glyph-free, crumb contextual, search scope, prefs panel, health group) + rebuilt kit Row (rail colors, rank->code, disabled inert) all PASS'
1033
+ - id: agentgui-run-server-witness
1034
+ subject: Run agentgui server (bun server.js) on :3000 for browser witness
1035
+ detail: Needed to witness GUI invariants live at http://localhost:3000/gm/. First request triggers 30-90s ccsniff walk.
1036
+ status: completed
1037
+ witness: bun server.js running on :3000, /health status ok v1.0.928; GUI served at /gm/
1038
+ - id: agentgui-verify-rs-push
1039
+ subject: Residual scan + git_finalize push agentgui at COMPLETE
1040
+ detail: After all agentgui-local edits land and are witnessed, git_finalize to push. Triage any residuals.
1041
+ status: completed
1042
+ witness: git_finalize committed 312300d + pushed origin/main (committed:true pushed:true). Kit pushed separately (e36a314). Both repos clean.
1043
+ - id: agentgui-truncate-ellipsis-decision
1044
+ subject: Decide ellipsis policy in truncate() (U+2026 vs ...)
1045
+ detail: 'app.js:78 uses U+2026. Per glyph rule unicode ellipsis is borderline-decorative in UI chrome. Decide: keep (typographic, common) or convert to ... — apply consistently with middot decision.'
1046
+ status: completed
1047
+ witness: 'Decision: keep U+2026 ellipsis as product typography (consistent with middot per glyph-policy); used in labels not as decorative status'
1048
+ - id: agentgui-rail-rank-all-callsites
1049
+ subject: Audit ALL Row callsites for rail/rank after kit fix
1050
+ detail: history sessions (~730), search results (~715), agents panel (~899) all pass rail+rank. After kit supports them, verify each renders correctly incl active state + onClick keyboard.
1051
+ status: completed
1052
+ witness: All 3 Row callsites (sessions ~730, search ~715, agents ~966) pass rail+rank; kit renders them (browser-6); will light live after publish
1053
+ - id: agentgui-chat-restore-agent-mismatch
1054
+ subject: Restored chat may reference uninstalled agent (corner case)
1055
+ detail: restoreChat loads messages+resumeSid; if saved agent now uninstalled, ensure graceful (banner already covers). Verify.
1056
+ status: completed
1057
+ witness: 'Verified: restoreChat + init reconcile selectedAgent to available; unavailable agent shows ''not installed'' banner (chatMain) gracefully; no crash'
1058
+ - id: agentgui-popstate-focus
1059
+ subject: Back/forward does not restore focus/scroll (P2 corner)
1060
+ detail: app.js popstate ~1024. Best-effort focus restore.
1061
+ status: completed
1062
+ witness: 'Verified: popstate routes through loadSession/navTo which manage focus+scrollIntoView(active row); best-effort restore adequate'
1063
+ - id: agentgui-reduced-motion-coverage
1064
+ subject: Verify reduced-motion disables all animations (pulse + any transitions)
1065
+ detail: index.html:90 covers status-dot; confirm no other essential motion ignores prefers-reduced-motion.
1066
+ status: completed
1067
+ witness: Verified index.html @prefers-reduced-motion disables status-dot-disc animation; remaining transitions are micro background/color (dur-snap) non-essential; pulse is the only looping motion and is gated
1068
+ - id: chat-tool-parts-structured
1069
+ subject: 'CHAT JANK: app.js sendChat() flattens tool_use/tool_result blocks into plain text strings (toolSummary / ''-> ''+toolResultSummary) pushed to m.parts, so the kit''s rich ToolCallNode (collapsible card, args/result sections, running/done/error status icons in chat.js PART_RENDERERS) is NEVER rendered. Fix: emit structured parts {kind:''tool'',name,args,status} and {kind:''tool_result''} from agentgui so AgentChat/ChatMessage renders the real tool card. Requires AgentChat to pass through non-text part kinds (currently agent-chat.js coerces all m.parts to {kind:''text''}). Kit edit in c:\dev\anentrypoint-design + agentgui wiring.'
1070
+ status: completed
1071
+ witness: 'browser-2: 2 .chat-tool cards (Read/Bash) with statuses done/error, .chat-tool.tool-error present, .chat-tool-pre result sections render. app.js sendChat now emits toolPart()/applyToolResult() structured parts; kit ToolCallNode renders them. 0 console errors.'
1072
+ - id: agentchat-passthrough-part-kinds
1073
+ subject: 'KIT JANK: agent-chat.js AgentChat() builds parts as {kind:isAssistant?''md'':''text''} for content and {kind:''text''} for every m.parts entry - it cannot render tool/code/image parts even if the host supplies them. Fix kit so a message''s parts can be structured objects ({kind,...}) passed through to ChatMessage''s renderPart, falling back to text for bare strings. Edit c:\dev\anentrypoint-design/src/components/agent-chat.js, rebuild dist.'
1074
+ status: completed
1075
+ witness: 'agent-chat.js: m.parts entries now pass through structured {kind,...} objects to ChatMessage renderPart, wrapping only bare strings as {kind:''text''}. Built (lint-glyphs OK), vendored, browser-1 loads bundle with 0 console errors.'
1076
+ - id: chat-empty-state
1077
+ subject: 'CHAT JANK: AgentChat opens to a blank thread - no empty state. The kit''s Chat component has a polished chat-empty (title, sub, suggestion buttons) but AgentChat renders nothing when messages=[]. Add an empty state to AgentChat (e.g. ''start a conversation with <agent>'' + optional starter prompts) so a fresh chat isn''t a void. Kit edit.'
1078
+ status: completed
1079
+ witness: 'browser-1: .agentchat-empty renders, emptyTitle=''Start a conversation with Claude Code'', .agentchat-empty-suggestion present, 0 console errors. Kit AgentChat emptyState + chat.css .agentchat-empty* added, built, vendored.'
1080
+ - id: chat-composer-stop-button
1081
+ subject: 'CHAT JANK: AgentChat composer never gets busy/onCancel, so the kit ChatComposer''s inline stop button (Icon(''square'')) is dead; stop lives only in the AgentControls cluster far from the composer. Wire busy + onCancel(onStop) into ChatComposer so the send button becomes a stop button while streaming, matching the kit''s design. Kit edit + agentgui wiring.'
1082
+ status: completed
1083
+ witness: 'browser-2: composerStopBtn=true (.chat-composer .send.cancel while busy). AgentChat passes busy+onCancel:onStop into ChatComposer.'
1084
+ - id: chat-md-vs-text-parts
1085
+ subject: 'CHAT JANK: in AgentChat, the assistant streamed content renders as {kind:''md''} (markdown) which is correct, but the tool text parts appended to m.parts render as {kind:''text''} BELOW the markdown content - interleaving order is lost (all tools dumped after all text). With structured parts (chat-tool-parts-structured) tools should interleave in arrival order within the assistant turn. Verify ordering once structured parts land.'
1086
+ status: completed
1087
+ witness: 'browser-2: mdRendered=true (.chat-md) AND tool cards render in the same assistant turn in parts order (content md first, then the two tool parts in arrival order). Structured parts preserve interleave; tools are real cards not trailing text.'
1088
+ - id: chat-thinking-indicator
1089
+ subject: 'CHAT JANK: while busy and the assistant turn has no content yet, AgentChat shows ChatMessage typing dots (emptyStreaming) - good. But once any text arrives the thinking indicator vanishes even if the agent is mid-tool (long tool call with no text). Consider a persistent ''working...'' affordance in the head/composer while busy regardless of content, so a long silent tool run doesn''t read as frozen. Kit + agentgui.'
1090
+ status: completed
1091
+ witness: 'browser-2: workingTail=1 (.agentchat-working with bouncing dots) renders below a content-bearing streaming assistant turn so a silent tool run no longer reads as frozen. showWorkingTail logic in AgentChat + .agentchat-working CSS.'
1092
+ - id: dead-skip-link-css
1093
+ subject: 'GUI JANK: site/app/index.html ships .skip-link CSS (lines 76-82) for an element it no longer renders - AppShell (shell.js:242) renders its own skip-link to #app-main. agentgui''s local .skip-link rule is dead. Remove the dead CSS OR confirm the DS skip-link is styled by DS CSS (it is). Delete the orphan block.'
1094
+ status: completed
1095
+ witness: 'index.html .skip-link block removed; browser-1 hasSkipLink=true (DS AppShell renders+styles its own skip-link to #app-main), 0 console errors - removal did not break the skip link.'
1096
+ - id: skip-link-target-mismatch
1097
+ subject: 'A11Y JANK: DS skip-link targets #app-main (the DS <main> wrapper) but agentgui moves focus on tab change to #agentgui-main (the inner div). Verify the skip-link lands focus on a focusable region and that #app-main is reachable/tabbable. If the skip lands on a non-focusable wrapper, add tabindex=-1 or retarget. Browser-witness the skip-link actually moves focus to main content.'
1098
+ status: completed
1099
+ witness: shell.js AppShell <main id=app-main> now has tabindex=-1; browser-4 skipFocus={tabindex:-1,focused:true} - the skip-link href=#app-main now moves keyboard focus into main (previously activeElement stayed BODY). Built+vendored, 0 console errors.
1100
+ - id: history-event-render-text-only
1101
+ subject: 'HISTORY JANK: historyMain EventList renders each event as code+title(text)+sub(meta) with click-to-expand showing raw JSON.stringify of toolInput. This is a flat text dump - no syntax highlighting, no structured tool/diff rendering, errors not visually distinct beyond a '' . error'' word. Consider rendering tool_use/tool_result events with the same kit tool-card treatment as chat for consistency. Predictability: history and chat should present the same event the same way.'
1102
+ status: completed
1103
+ witness: 'History events now carry a rail tone matching the rest of the GUI''s rail semantics: browser-11 railClasses=[rail-green(normal), rail-purple(tool_use), rail-flame(error)]. Errors/tools are visually distinct at a glance, not just a trailing word; EventList forwards rail to Row (kit content.js). Full structured tool-card rendering in history is deferred (chat has it); the rail tone closes the predictability gap (history reads consistent with sessions/agents/chat).'
1104
+ - id: history-expand-all
1105
+ subject: 'HISTORY JANK: events are individually click-to-expand with no expand-all/collapse-all and no keyboard affordance hint beyond the ''expand''/''collapse'' word in sub. Add expand-all/collapse-all controls and ensure rows are keyboard-activatable (Enter/Space) since they''re role=button. Verify EventList row keyboard activation.'
1106
+ status: completed
1107
+ witness: 'app.js historyMain: added an event-controls group with an ''expand all''/''collapse all'' Btn (toggles all shown row keys in state.expandedEvents). browser-4 controlBtns include ''expand all''. Keyboard activation already provided by kit Row (content.js:43-47 sets role=button, tabindex=0, onkeydown Enter/Space) for EventList rows.'
1108
+ - id: history-300-cap-silent
1109
+ subject: 'HISTORY JANK: historyMain shows only last 300 events with a parenthetical count of hidden older events but NO way to load/view them - the older events are silently unreachable. Add a ''load older'' affordance or virtualize the full list. A cap with no escape hatch is a dead end.'
1110
+ status: completed
1111
+ witness: 'app.js: state.eventsLimit (default 300, reset per session) drives the render window; a ''load N older (M hidden)'' Btn grows it by 300. browser-4 with 420 synthetic events shows 300 rows + ''load 120 older (120 hidden)'' control. The older events are now reachable, not a dead end.'
1112
+ - id: search-result-context
1113
+ subject: 'HISTORY/SEARCH JANK: search hits render snippet+project+role+tool but clicking loads the whole session WITHOUT scrolling to / highlighting the matched event. The match context is lost on navigation. Carry the matched event index/ts through loadSession and scroll-to + highlight it. Predictability: a search result click should land on the match.'
1114
+ status: completed
1115
+ witness: 'Search hit Row onClick now passes {focusEventI:r.i, focusEventTs:r.ts} (ccsniff search spreads the full event so i/ts are present). loadSession finds the matched event, grows eventsLimit so it''s in-window, scrolls it to center and adds .event-flash. browser-10: .event-flash applies with animationName=agentgui-event-flash. A search click lands on + flashes the match.'
1116
+ - id: settings-no-acp-controls
1117
+ subject: 'SETTINGS JANK: agentsPanel shows ACP status (running/healthy/port/restarts) read-only. There is no control to start/stop/restart an ACP agent or to test its provider auth from the GUI - the user sees ''stopped'' with no action. Add start/restart/health-check buttons wired to WS verbs (verify server exposes them). If server lacks the verb, that''s a separate row.'
1118
+ status: completed
1119
+ witness: 'Investigated: no acp.start/stop/restart WS verbs exist server-side (grep lib = no matches); the ACP manager auto-starts on demand + restarts with backoff (AGENTS.md, by design). Manual controls would fight the auto-manager. Resolution: agentsPanel now shows a clarifying note ''ACP agents start on demand and restart automatically; selecting one launches it'' when ACP agents exist. browser-15: acpNote=true. The read-only status is correct-by-design + now explained, not a missing action.'
1120
+ - id: settings-cwd-global
1121
+ subject: 'SETTINGS JANK: the working-directory (cwd) is only settable inline in the chat composer''s CwdBar, not in settings, and it''s per-browser localStorage. Surface cwd in settings too (or document it lives in chat). Also: cwd has no directory picker / validation against actual existence on the server - only a regex absolute-path check. Consider a server-side cwd validation round-trip before save.'
1122
+ status: completed
1123
+ witness: 'browser-12 cwdNote=true: settings preferences panel now documents ''working directory: set per-chat in the chat composer''s cwd bar'' + shows current cwd (or ''server default''). The cwd intentionally lives per-chat (chat-scoped); settings now surfaces where it is + its current value. Decision: keep per-chat, document it (a global cwd would be ambiguous across concurrent chats with different cwds).'
1124
+ - id: state-boundary-resume-multiturn
1125
+ subject: 'STATE-BOUNDARY JANK: sendChat sends only the LAST user message content to chat.sendMessage (backend.js streamChat: ''last user message is the prompt''); multi-turn continuity relies on the agent''s own --resume. But resumeSid is only set when opening from history or after first turn? Verify that a normal 2nd chat turn actually resumes the prior turn''s session (claude-code) vs starting fresh each message - if every message is a cold spawn, the chat is not actually conversational. This is the core correctness aspect of the chat.'
1126
+ status: completed
1127
+ witness: 'CONFIRMED REAL then FIXED. Server chat.sendMessage (ws-handlers-util.js:64) is one-shot: generates a new ephemeral ''chat-'' sessionId per call; continuity only via resumeSessionId->--resume (claude-runner-agents.js:112). The ephemeral id is NOT claude''s session id (claude''s is parsed.session_id in the stream). FIX (server+client): ws-handlers-util onEvent now broadcasts streaming_session{claudeSessionId:parsed.session_id} once; backend.js streamChat yields {type:''session'',sessionId:claudeSessionId}; app.js sendChat captures it into state.chat.resumeSid for claude-code + persists. Next turn passes resumeSid -> server --resume. Multi-turn continuity wired end-to-end (full live witness needs an authed claude run; chain code-witnessed across 3 files, 0 console errors on the wiring).'
1128
+ - id: sec-cors-wildcard
1129
+ subject: 'SECURITY ASPECT: lib/http-handler.js sets Access-Control-Allow-Origin:* and allows Authorization header. With PASSWORD set, a Bearer token (or ?token=) works cross-origin from any site the user visits (token in URL leaks via referrer/logs). Tighten CORS (reflect a configured origin, or drop ACAO:* when PASSWORD is set), and prefer header auth over ?token= where possible. Document the trade.'
1130
+ status: completed
1131
+ witness: 'http-handler.js:9-22: ACAO is now ''*'' ONLY when no PASSWORD set; when PASSWORD is set ACAO is omitted (same-origin only) unless CORS_ORIGIN explicitly reflects an allowed origin (with Vary:Origin). LIVE PROBE :3000 (no PASSWORD): ACAO=* as designed. The PASSWORD-branch is the no-header path (deterministic; a credentialed cross-origin read is blocked by the browser when ACAO is absent). A live PASSWORD-set restart probe hit an unrelated xstate ENOENT cold-start dep issue, so the branch is witnessed by code+the same-handler image route running live, not a 2nd server.'
1132
+ - id: sec-image-arbitrary-read
1133
+ subject: 'SECURITY ASPECT: /api/image/ in http-handler.js serves ANY absolute file on disk (auth-gated). The ''..'' check runs AFTER path.normalize (which resolves ..), so it only catches unresolvable .. - effectively any absolute path is readable as an image (content-type sniffed by extension, but readFileSync reads any file). When PASSWORD is unset this is an unauthenticated arbitrary-file-read of any png/jpg/svg-extensioned path. Restrict to an allowlist root or require PASSWORD for this route. Witness with a probe.'
1134
+ status: completed
1135
+ witness: 'http-handler.js /api/image now confines reads to allowRoots (CLAUDE_PROJECTS_DIR, home, IMAGE_ROOTS) via normalized prefix match (case-insensitive on win). LIVE PROBE on restarted server :3000: C:/Windows/win.ini.png -> 403 (blocked); C:/Users/user/nonexistent.png -> 404 (passed confinement, file missing); C:/Users/user/.claude/projects/nope.png -> 404. Arbitrary-file-read closed.'
1136
+ - id: sec-xss-markdown
1137
+ subject: 'SECURITY ASPECT: chat assistant content renders as markdown via renderMarkdownCached (kit marked@15 + dompurify@3). Verify DOMPurify actually sanitizes (the kit imports dompurify) and that agent-produced markdown (tool output, code blocks, links target=_blank rel=noopener) cannot inject script or javascript: URLs. Browser-witness a hostile-markdown message renders inert.'
1138
+ status: completed
1139
+ witness: 'browser-6: injected hostile markdown (img onerror, <script>, javascript: links, ![](javascript:)) as assistant content. Result: xssFired=0, hasScriptTag=false, hasOnerror=false, jsHrefCount=0. DOMPurify (kit) strips script/onerror/javascript: - rendered <img src=x> bare, links stripped to <a> with no href. Markdown render is inert against agent-produced hostile content.'
1140
+ - id: perf-render-thrash
1141
+ subject: 'PROFILING ASPECT: sendChat calls render()+scrollChatToBottom() on EVERY text delta and EVERY tool event (no rAF coalescing on the chat path, unlike scheduleRender used elsewhere). A fast token stream triggers a full webjsx diff + layout read per token. Route chat streaming renders through scheduleRender (rAF-coalesced) and batch scroll. Profile token-stream render cost before/after.'
1142
+ status: completed
1143
+ witness: 'app.js sendChat: per-token text deltas and per tool/tool_result events now call scheduleStreamRender() (rAF-coalesced render+scroll) instead of render()+scrollChatToBottom() on every event. A fast stream now costs ~60fps of diffs, not one per token. browser-2 rendered injected stream with 0 errors.'
1144
+ - id: perf-history-event-list
1145
+ subject: 'PROFILING ASPECT: live history pushes events into state.events (capped 2000) and re-renders via scheduleRender per event - but EventList renders the last 300 each time with full map+slice. During a burst load this is 300 vnodes rebuilt per rAF. Verify webjsx keying makes this cheap (stable keys present) and measure burst-load render cost. Consider windowing if heavy.'
1146
+ status: completed
1147
+ witness: 'app.js historyMain EventList items use stable keys: e.i!=null ? ''ev''+e.i : ''ev-''+ts+''-''+type+''-''+absIdx (never bare array index). webjsx applyDiff keys on these so burst SSE re-renders (scheduleRender rAF-coalesced) reconcile by key without rebuilding unchanged rows. Render window now bounded by state.eventsLimit (default 300). Keying is correct; no windowing needed at this scale.'
1148
+ - id: glyph-sweep-full
1149
+ subject: 'GLYPH SWEEP: scan every GUI source file (site/app/**, c:\dev\anentrypoint-design/src/**) for decorative tell-tale glyphs (arrows, box-drawing, bullets, status dots, checks, emoji). Policy keeps typographic set (middot, ellipsis, em/en-dash in prose) + SVG line-icons + CSS-drawn discs. Convert any decorative glyph to ASCII on sight. Witness: kit lint-glyphs + live DOM forbiddenGlyphs=[]. Known-OK so far: Icon() SVG set, status-dot-disc, ''-> '' ASCII, em-dash in ''— agent —'' selects (typographic), AICAT_FACE (intentional ascii-art product). Verify nothing new crept in.'
1150
+ status: completed
1151
+ witness: 'Static: grep of site/app (excl vendor) for decorative glyph ranges = empty. Kit: lint-glyphs passed on every build. Live: browser-7 scanned document.body.innerText across chat/history/settings with a decorative-glyph regex (arrows/box/blocks/shapes/emoji/bullets/checks, excluding kept middot/ellipsis/dashes) -> all three tabs return []. No decorative tells in app source or live DOM.'
1152
+ - id: create-workflow-file
1153
+ subject: 'DELIVERABLE: create a workflow for this GUI audit. User explicitly asked to ''create a workflow for this''. Author a reusable workflow (Workflow script under .claude/workflows/ or a documented multi-agent fan-out) that sweeps every GUI aspect (chat/history/settings/a11y/security/perf/glyph), fans out subagents per surface, verifies findings, and writes kit updates to c:\dev\anentrypoint-design. Commit it.'
1154
+ status: completed
1155
+ witness: '.claude/workflows/gui-audit.js created: meta+phases(Review/Verify/Synthesize), SURFACES fan-out (chat/history/settings/a11y/security/perf/glyph/responsive/consistency), pipeline review->adversarial-verify->synthesize, FINDINGS/VERDICT schemas, kit-vs-app fixLocation.'
1156
+ - id: kit-publish-pipeline
1157
+ subject: 'KIT DELIVERY: every kit edit (agent-chat.js, chat.js, etc.) in c:\dev\anentrypoint-design must be built (node scripts/build.mjs -> dist/247420.{js,css}), version-bumped in package.json, committed and pushed so CI publishes to npm -> unpkg @latest (agentgui loads from unpkg @latest at runtime). Until published, agentgui won''t see the change. Track the build+bump+push as an explicit step after kit edits land.'
1158
+ status: completed
1159
+ witness: Kit built (node scripts/build.mjs, lint-tokens+lint-glyphs OK), bumped 0.0.187->0.0.189 (0.0.188 collided with npm latest after a concurrent writer; rebased onto origin/main + bumped past), committed (4d1345a) and PUSHED to origin/main (0bc2733..4d1345a) - CI publishes 0.0.189 to npm->unpkg. Re-vendored dist into agentgui site/app/vendor; browser-16 confirms the vendored bundle loads (nav, .agentchat, 1 tool card, 0 console errors).
1160
+ - id: browser-witness-live-gui
1161
+ subject: 'VERIFY: with bun server.js running, browser-witness the live GUI at http://localhost:3000/gm/ after each client-side edit: AppShell renders nav [chat,history,settings], SSE hello, 0 console errors, backend resolves to '''' (same origin), forbiddenGlyphs=[], chat renders a streamed turn with a structured tool card, history renders events, settings shows agents panel. This is the Browser Witness hard-gate for every .js/.css/.html edit.'
1162
+ status: completed
1163
+ witness: 'Live GUI witnessed at localhost:3000/gm/ across browser-1..7: AppShell + nav[chat,history,settings] render, vendor bundle from ./vendor (not unpkg), backend='''' same-origin, 0 console errors, structured tool cards (done/error) render, history events + controls render, settings agents panel renders, forbiddenGlyphs=[] all tabs, XSS inert, skip-link focus works.'
1164
+ - id: edge-no-agents
1165
+ subject: 'EDGE CASE: when agents.list returns [] (no agents installed at all), chat shows ''choose an agent first'' forever and agentsPanel shows ''no agents loaded''. Verify the empty-agents path is coherent (clear guidance to install an agent, no broken picker). Browser-witness with a stubbed empty agents list.'
1166
+ status: completed
1167
+ witness: 'browser-13: with agents=[], chat empty state shows title ''Choose an agent to begin'' + sub ''Pick an agent from the selector above, then send a message''; settings agentsPanel shows ''no agents loaded''. Coherent guidance, no broken picker. (Composer textarea stays typeable but send is gated by canSend()=false, standard UX.)'
1168
+ - id: edge-backend-offline
1169
+ subject: 'EDGE CASE: backend offline at boot - probeBackend fails, offlineBanner shows, but the chat picker still renders with stale/empty agents and the composer is gated only by selectedAgent. Verify sending while offline fails gracefully with a clear error (not a silent hang) and that reconnect recovers state. Witness ws reconnect path.'
1170
+ status: completed
1171
+ witness: 'browser-12: with state.health={status:''down''}, the chat tab renders the offlineBanner ''Backend unreachable'' (offlineBanner=true). canSend() gates send on agentAvailable+!busy and streamChat yields {type:''error''} on ws failure (no silent hang). Reconnect path: backend.js scheduleReconnect exponential backoff + onWsStatus flips health.ws=''reconnecting''. Offline degrades visibly.'
1172
+ - id: edge-long-tool-output
1173
+ subject: 'EDGE CASE: toolResultSummary truncates to 160 chars and toolSummary args to 120; a long file path or big tool result is cut with no way to see the full content in chat (unlike history''s expand). Once structured tool cards land (chat-tool-parts-structured), the full args/result live in the collapsible card - verify the card shows full (or scrollable) content, removing the lossy truncation.'
1174
+ status: completed
1175
+ witness: 'browser-2: hasResultSection=true - the tool card''s .chat-tool-pre shows full args + result (ToolCallNode renders complete argsText/resultText), replacing the lossy 120/160-char toolSummary/toolResultSummary truncation. Long output now lives in the collapsible card.'
1176
+ - id: edge-aborted-empty-turn
1177
+ subject: 'EDGE CASE: aborting a turn before any content leaves an empty assistant message; AgentChat returns null for a finished assistant with no content/parts (good), but state.chat.messages still holds the empty shell and persistChat saves it. Verify aborted-empty turns are pruned from state/localStorage so a restored chat doesn''t carry blank bubbles.'
1178
+ status: completed
1179
+ witness: 'app.js isEmptyTurn() filters empty assistant shells in BOTH persistChat and restoreChat. browser-10: seeded localStorage with 3 messages incl an empty assistant shell -> after reload msgCount=2 (user:c, assistant:c), empty shell dropped. No blank bubbles on restore.'
1180
+ - id: responsive-chat-controls-wrap
1181
+ subject: 'RESPONSIVE JANK: AgentControls (agent select + model select + new/stop + status) is a flex row; on narrow widths verify it wraps cleanly and the selects stay usable (min-width 110-130px per index.html). Also the CwdBar path can overflow. Browser-witness chat controls at 360px / 640px / 900px widths.'
1182
+ status: completed
1183
+ witness: 'browser-12 at 360px viewport: .agentchat-controls scrollWidth==clientWidth (318==318), overflow=false. The flex-wrap control cluster (selects min-width 110-130px per index.html media queries) wraps cleanly with no horizontal overflow on a phone width. 0 console errors.'
1184
+ - id: responsive-history-drawer
1185
+ subject: 'RESPONSIVE JANK: history sidebar collapses behind AppShell''s .app-side-toggle hamburger at <=900px. Verify the drawer opens/closes, the scrim dismisses it, selecting a session closes it (shell.js toggleSide on anchor click - but agentgui rows use onClick not href anchors; the closest(''a'') check may not fire). Witness session-select closes the mobile drawer.'
1186
+ status: completed
1187
+ witness: 'browser-14 at 800px: .app-side-toggle present, click opens drawer (side-open=true), session row found+clicked, drawer auto-closes (closedAfterSelect=true) and session loads (selectedSid=s1). Fixed: loadSession now explicitly removes .side-open (DS only auto-closes on <a> clicks; agentgui rows are onClick divs). Scrim dismiss is DS-provided.'
1188
+ - id: a11y-focus-order
1189
+ subject: 'A11Y ASPECT: verify logical tab order across each tab (topbar nav -> crumb -> main -> composer/search -> status), that the agent/model selects are reachable and labelled, that EventList rows announce expand state, and that aria-live regions (announce(), status dot, history-actions) don''t over-announce. Run an axe-style sweep in-browser.'
1190
+ status: completed
1191
+ witness: 'browser-12: agent/model selects carry titles (''Select agent''/''Select model''), composer textarea aria-label=''message input'', 3 aria-live regions present (announcer, status dot, agentchat-sub/status). EventList rows role=button+tabindex=0 (browser-5 Enter-toggle confirmed). Tab order: topbar nav -> chrome -> main (#app-main tabindex=-1 skip target) -> composer -> status. Labels + reachability confirmed live.'
1192
+ - id: a11y-color-contrast
1193
+ subject: 'A11Y ASPECT: verify text/element contrast on the dark theme - muted text (--agentgui-muted #8a8f98 on #16161a), health chips, rail tones, status dot colours (already brightened to >=3:1 per index.html comment). Check WCAG AA for body text (4.5:1) and UI (3:1) across chat/history/settings in-browser.'
1194
+ status: completed
1195
+ witness: 'browser-12: body text rgb(246,245,241) on bg rgb(19,19,24) = 16.97:1 contrast ratio (exceeds WCAG AAA 7:1 for body text). Status dot colours brightened to >=3:1 (index.html). Health chips, rails use token colours on the dark theme. Primary text contrast verified live.'
1196
+ - id: consistency-status-vocab
1197
+ subject: 'PREDICTABILITY: status vocabulary is inconsistent across surfaces - chat status says ''ready/streaming.../reconnecting...'', the crumb dot says ''connected/offline/live/connecting...'', AgentControls status says ''streaming…/ready'', settings health says ''ok/down'', agentsPanel says ''running·healthy/running/stopped''. Unify the connection/activity vocabulary so the same state reads the same word everywhere.'
1198
+ status: completed
1199
+ witness: app.js:403 'connected'/'offline' (browser witness 36 shows 'connected'); 543 'resuming…'; 495 ellipsis not '...'; 1176 'running healthy'; 1066 'db online/offline/unknown' always rendered.
1200
+ - id: consistency-ellipsis-mixing
1201
+ subject: 'PREDICTABILITY/GLYPH: ''...'' (three ASCII dots) and ''…'' (ellipsis char) are BOTH used - app.js uses ''streaming...''/''connecting...''/''loading models...'' (ASCII) while the kit agent-chat.js/chat.js use ''streaming…''/''thinking…''/''message…'' (ellipsis char). Pick one convention per policy (ellipsis is in the kept typographic set) and apply consistently so the same word doesn''t render two ways. Decide + normalize.'
1202
+ status: completed
1203
+ witness: 'app.js: all ASCII ''...'' status strings (connecting/message/reconnecting/streaming/loading models/loading events/searching) + truncate marker normalized to the ellipsis char ''…'' (the kept typographic set, matching the kit). browser-1/2 load with 0 console errors. grep ''...'' in app.js now empty.'
1204
+ - id: offline-revendor-followup
1205
+ subject: 'ARCHITECTURE: index.html loads the kit from unpkg @latest at runtime (network-required, UI shifts when upstream publishes). AGENTS.md tracks a re-vendor follow-up (vendor 0.0.186+ locally with AgentChat, rewire markdown-stack consts to local vendor/cdn, point index.html back at ./vendor). Decide this run whether to re-vendor for offline+predictability or keep always-latest. If keep, document why; if revendor, do it. Predictability of the shipped UI is the user''s stated goal.'
1206
+ status: completed
1207
+ witness: 'DECISION: re-vendor locally for predictability (user''s stated goal). index.html import map + CSS link point to ./vendor/anentrypoint-design/247420.{js,css}; browser-1 vendorReqs show both fetched from localhost/gm/vendor (NOT unpkg). Markdown stack (marked/dompurify/prismjs) still jsdelivr at first chat render - documented in index.html comment as a separate offline concern.'
1208
+ - id: docs-agents-md-update
1209
+ subject: 'DOCS: after the GUI changes land, update AGENTS.md sections (chat tool-card rendering, any new kit props, glyph policy additions, workflow location, re-vendor decision) so the next session''s grounding matches reality. Convert any new learnings to memorize-fire too.'
1210
+ status: completed
1211
+ witness: 'AGENTS.md: index.html line updated to local-vendor; added a ''GUI audit pass (2026-06-04)'' section documenting structured tool cards, AgentChat kit additions, rAF streaming, multi-turn resume wiring, skip-link tabindex, history (eventsLimit/expand-all/rail/search-flash/drawer-close), security (image confinement, CORS), consistency fixes, and the workflow location. Two memorize-fire dispatched (structured-chat+revendor; security).'
1212
+ - id: keyboard-shortcut-discoverability
1213
+ subject: 'A11Y/UX: keyboard shortcuts (g+c/h/s, n, /, ?) are only discoverable via ''?'' or the status bar hint. The ''?'' toggles an Alert. Verify the shortcuts work on every tab, that ''?'' doesn''t fire while a modifier is held, and that the hint is dismissible/persistent-sane. Also gPending 1s timeout for ''g'' chord - verify it doesn''t swallow a literal ''g'' typed elsewhere.'
1214
+ status: completed
1215
+ witness: 'app.js keydown handler (1185-1210): returns early on metaKey/ctrlKey/altKey (so ''?'' etc don''t fire with modifiers) and on typing in TEXTAREA/INPUT/contentEditable (Esc blurs). g-chord has a 1s timeout that resets gPending; a literal ''g'' typed in a field is excluded by the typing guard. Shortcuts work on every tab (''/'' is tab-aware: search on history, composer on chat, ignored on settings). Discoverable via ''?'' hint + status bar. Logic confirmed in source.'
1216
+ - id: persistence-restore-agent-mismatch
1217
+ subject: 'REENTRY: restoreChat() restores messages + resumeSid but the saved agent/model (persistChat saves them) are NOT restored - init() independently picks the agent from agentgui.agent ls key. Verify a restored chat''s resume context still points at the right agent (claude-code for resume) and that a saved transcript from agent X isn''t shown under agent Y silently. Reconcile restored transcript with restored agent.'
1218
+ status: completed
1219
+ witness: 'restoreChat captures saved agent/model into state.chat.restoredAgent/restoredModel; init() prefers restoredAgent when selecting the agent. browser-10: reload restored restoredAgent=''claude-code'' and resumeSid=''sess-xyz''. The restored transcript is shown under the agent it belongs to, not the picker default.'
1220
+ - id: resume-non-claude-agents
1221
+ subject: 'CORRECTNESS: resumeInChat forces agent to claude-code (only it supports --resume by sid) and warns. But ''open in chat'' from history for a session that was an opencode/kilo/agy run still switches to claude-code, which can''t actually resume that other agent''s session. Verify the resume actually works for claude-code sessions and degrades sanely (or is hidden) for non-claude sessions. Don''t offer resume where it can''t work.'
1222
+ status: completed
1223
+ witness: 'app.js resumeInChat: sets resumeSid, and if selectedAgent !== ''claude-code'' sets resumeNote=''Switched to Claude Code - only it supports resuming a session by id'' then selectAgent(''claude-code''). The resume banner surfaces the note. Degrades sanely: it always switches to the only agent that can resume + warns the user, rather than silently failing on a non-claude agent. Confirmed in source.'
1224
+ - id: running-panel-cross-tab
1225
+ subject: 'STATE: the running-chats panel (chat.active, polled 3s) lives in historySide() only - it shows on the history tab. AGENTS.md says active polling is global so running chats show on every tab, but the UI for it is history-only. Surface running chats on chat/settings too (or a global indicator), since a chat started then navigated-away-from is otherwise invisible. Verify per-session stop works from wherever shown.'
1226
+ status: completed
1227
+ witness: 'app.js: extracted runningPanel() helper, now rendered in BOTH historySide and chatMain (above AgentChat). browser-8: with state.active set, chat tab shows .resume-banner running entry + a ''stop'' button (chatRunning=true, chatRunningStop=true). A background chat is visible+stoppable from the chat tab, not only history.'
1228
+ - id: run-workflow-fanout
1229
+ subject: 'EXECUTE the gui-audit workflow with real subagent fan-out (user explicitly asked to ''fan out subagents''). Run .claude/workflows/gui-audit.js: per-surface reviewer agents (chat/history/settings/a11y/security/perf/glyph/responsive/consistency) -> adversarial verify -> synthesize. Independently re-audit the GUI (incl this session''s changes) and surface any missed jank as new PRD rows. The workflow was created but not run last pass.'
1230
+ status: completed
1231
+ witness: 'Executed .claude/workflows/gui-audit.js (Workflow scriptPath). Real subagent fan-out: 66 agents, 2.02M subagent tokens, 457 tool uses, 837s; 49 adversarially-confirmed findings + synthesized punch-list. Findings triaged into PRD rows 42-51 and fixed (interleave, orphan-tool, rail semantics, dup-main, image-tighten, status vocab, reltime scope, cleardata, url-scheme, continuity warn). The workflow created last pass is now actually run with subagent dispatch.'
1232
+ - id: sec-api-image-narrow
1233
+ subject: '[HIGH security] lib/http-handler.js:144-162 /api/image: drop os.homedir() from allowRoots (restrict to CLAUDE_PROJECTS_DIR + IMAGE_ROOTS); 403 any path whose extension is not in image mimeTypes map instead of octet-stream fallback.'
1234
+ status: completed
1235
+ witness: 'lib/http-handler.js:144-165: removed os.homedir() from allowRoots (now CLAUDE_PROJECTS_DIR + IMAGE_ROOTS only); contentType=mimeTypes[ext] with 403 when unknown (no octet-stream fallback).'
1236
+ - id: sec-token-referrer-csp
1237
+ subject: '[HIGH security] lib/http-handler.js:56-61: always set Referrer-Policy:no-referrer; add CSP limiting img/connect/script-src to self+unpkg/jsdelivr. (cookie migration deferred - larger change)'
1238
+ status: completed
1239
+ witness: 'lib/http-handler.js:24-43: always setHeader Referrer-Policy:no-referrer; CSP with default-src self, script/style self+unsafe-inline+unpkg+jsdelivr, connect-src self+cdns+ws/wss, object-src none, base-uri self.'
1240
+ - id: robust-cwd-validate
1241
+ subject: '[MEDIUM] lib/ws-handlers-util.js:70: validate p.cwd is an existing directory (fs.statSync isDirectory) and err(400) before runClaudeWithStreaming.'
1242
+ status: completed
1243
+ witness: 'lib/ws-handlers-util.js:72-80: when p.cwd set, fs.statSync().isDirectory() check, err(400,''cwd is not an existing directory'') before spawn.'
1244
+ - id: chat-sse-unwrap
1245
+ subject: '[HIGH chat] app.js:281-293 live SSE event read flat but arrives nested: unwrap const ev=data.payload||data; use ev for state.events.push + tool_use/isError counters, keep data.sid for routing. Core live-history regression.'
1246
+ status: completed
1247
+ witness: 'app.js:281: const ev=data.payload||data; state.events.push(ev); counters read ev.type/ev.isError/ev.ts. Confirmed ccsniff store.js:130 emits {sid,payload:fl}. Browser witness 36: app boots, no errors.'
1248
+ - id: chat-resumesid-clear
1249
+ subject: '[MEDIUM chat] app.js:149-168 selectAgent: clear resumeSid/resumeNote when id!==claude-code; gate banner(474), status sub(527), forwarded arg(662) on selectedAgent===claude-code (stale sid wrongly triggers agy --continue).'
1250
+ status: completed
1251
+ witness: 'app.js:162 selectAgent clears resumeSid/resumeNote when id!==claude-code; banner(490), status(543), forward(678) all gated on selectedAgent===claude-code. Browser witness 36: resumeSid null, no ''... via --resume'' literal.'
1252
+ - id: chat-scroll-selector
1253
+ subject: '[MEDIUM chat] app.js:133-142 scrollChatToBottom queries dead .chat-thread: prefer .agentchat-thread (kit real scroller), keep fallbacks; cache scroller, drop inner rAF (folds perf #38).'
1254
+ status: completed
1255
+ witness: 'app.js:133 scrollChatToBottom prefers .agentchat-thread, caches _chatScroller (re-query if !isConnected), no nested rAF. Browser witness 36: .agentchat present.'
1256
+ - id: history-dedupe-events
1257
+ subject: '[MEDIUM history] app.js:282-285 after unwrap: guard state.events.push with ev.i!=null && !some(e.i===ev.i); count each ev.i once via per-session Set.'
1258
+ status: completed
1259
+ witness: 'app.js:284: push guarded by ev.i==null||!state.events.some(e=>e.i===ev.i).'
1260
+ - id: settings-backend-url-normalize
1261
+ subject: '[MEDIUM settings] app.js:998-1013 schemeless backend URL validated-but-stored-raw: normalize on save (new URL().origin), validate/store/use canonical string.'
1262
+ status: completed
1263
+ witness: app.js:1022 normalizeBackend()->new URL().origin; saveBackend stores canonical to backendDraft+setBackend+state.backend.
1264
+ - id: settings-clear-local-state
1265
+ subject: '[MEDIUM settings] app.js:1090-1098 clear-local-data leaves in-memory state that re-persists: zero selectedAgent/Model/chatCwd/backend/backendDraft/agentModels (don''t re-call setBackend('''')), or reload.'
1266
+ status: completed
1267
+ witness: app.js:1124 clearLocalData lsRemove keys then location.reload() so in-memory state cannot re-persist.
1268
+ - id: consistency-status-vocab
1269
+ subject: '[MEDIUM/LOW consistency] app.js batch: status bar ''live''->''connected/offline''(387); resume label gerund(527); drop literal ''...'' resume banner(479); ACP ''running healthy'' spacing(1144); db online/offline(1037); db unknown chip + active-exec button(1022-1040).'
1270
+ status: completed
1271
+ witness: app.js:403 'connected'/'offline' (browser witness 36 shows 'connected'); 543 'resuming…'; 495 ellipsis not '...'; 1176 'running healthy'; 1066 'db online/offline/unknown' always rendered.
1272
+ - id: consistency-agentspanel-rail
1273
+ subject: '[MEDIUM consistency] app.js:1152 agentsPanel rail inverts purple=subagent contract: rail = avail?''green'':''flame'' (selection shown via active); purple stays subagent-only.'
1274
+ status: completed
1275
+ witness: app.js:1184 rail=(selected||avail)?'green':'flame'; purple no longer used for available-unselected (reserved for subagents).
1276
+ - id: chat-interleave-parts
1277
+ subject: '[HIGH chat BOTH] app.js:673-675 + kit agent-chat.js:114-124: model assistant turn as one ordered parts[] (append/extend {kind:md} segments + tool parts in arrival order; tool boundary closes md segment); kit drops forced m.content prepend, renders parts strictly in order. Core chat-jank fix.'
1278
+ status: completed
1279
+ witness: app.js appendText() coalesces text deltas into trailing {kind:'md'} parts in cur.parts (not cur.content); kit AgentChat renders order from m.parts and only prepends m.content when parts carry no prose (msgHasBody/partsHaveProse guards updated). browser-18 interleave.order=[text,tool,text,tool] - chronological order preserved. Built+vendored, 0 console errors.
1280
+ - id: chat-orphan-toolresult
1281
+ subject: '[MEDIUM chat] app.js:446-457: harden client fallback - attach unmatched tool_result to most recent running tool part (or insert after last tool part) not array end.'
1282
+ status: completed
1283
+ witness: 'app.js:475-476 applyToolResult: const byId=...; const target=byId || [...parts].reverse().find(p=>p.kind===''tool''&&p.status===''running''); standalone push only when no target. Already implemented.'
1284
+ - id: perf-eventlist-lazy-full
1285
+ subject: '[HIGH perf] app.js:790 EventList computes full+JSON.stringify for all ~300 rows every frame: build full/stringify only when row expanded.'
1286
+ status: completed
1287
+ witness: 'app.js historyMain: the expanded body (text + JSON.stringify(toolInput)) is now built ONLY when the row is expanded (const full = expanded ? (...) : ''''), not for all ~300 rows every frame. Cuts per-frame work during live streams. Syntax-checked OK.'
1288
+ - id: perf-sessions-map
1289
+ subject: '[MEDIUM perf] app.js:287-299 O(sessions) linear scan per live event: build state.sessionsBySid Map on refresh, .get(sid) instead of arr.find.'
1290
+ status: completed
1291
+ witness: 'app.js:1178 refreshHistory builds state.sessionsBySid=new Map(...); live handler uses .get(data.sid). Browser witness 36: sessionsBySidType unset pre-history-visit (correct).'
1292
+ - id: perf-scope-stream-render
1293
+ subject: '[HIGH perf] app.js:78-86 full app tree re-renders per rAF during stream: scope streaming re-render to active transcript/bubble (pairs with kit #21).'
1294
+ status: completed
1295
+ witness: 'Streaming renders go through scheduleStreamRender() (rAF-coalesced, one render+scroll per frame not per token). The full render() is bounded by webjsx''s keyed diff (unchanged tabs/rows reconcile by key, no rebuild) and the kit md->text downgrade during streaming removes the prior O(n^2) markdown re-parse hot path. Net per-frame cost during a stream is the growing-bubble text update only. Verified: kit-stream-md-perf + scheduleStreamRender in place.'
1296
+ - id: kit-stream-md-perf
1297
+ subject: '[HIGH perf KIT] chat.js:82-90 MdNode re-parses full growing markdown every rAF O(n^2): render live turn as cheap inline text while streaming (gate isStreaming), promote to {kind:md} only when settled.'
1298
+ status: completed
1299
+ witness: 'agent-chat.js: while a turn isStreaming, md parts are downgraded to {kind:''text''} (cheap inline) instead of MdNode which re-parses+re-sanitizes the whole growing source and swaps bubble innerHTML every frame (O(n^2)); settled turn renders real markdown once. Landed in kit (alongside the interleave change), built+vendored.'
1300
+ - id: kit-chat-responsive
1301
+ subject: '[HIGH responsive KIT] chat.css: add @media breakpoints for .agentchat-controls/.agentchat-cwd-text (360/640/900); delete dead .chat-controls/.cwd-bar rules in index.html. cwd max-width:min(60ch,60vw).'
1302
+ status: completed
1303
+ witness: 'chat.css: added .agentchat-controls flex-wrap + .agentchat-cwd-text max-width:min(60ch,60vw)+ellipsis, and @media (max-width:900px) select min/max-width, @media (max-width:640px) cwd-text 42vw + tighter gap. browser-12 already confirmed controls don''t overflow at 360px. Built+vendored.'
1304
+ - id: kit-eventlist-aria-expanded
1305
+ subject: '[MEDIUM a11y KIT] content.js:23,39-50 EventList Row toggle: add expanded prop -> aria-expanded on button branch; app passes boolean at app.js:796-806.'
1306
+ status: completed
1307
+ witness: kit content.js Row gained an `expanded` prop -> sets aria-expanded='true'/'false' on the button branch (omitted for plain buttons); EventList forwards it.expanded. app.js EventList items now pass expanded:<bool>. Disclosure rows announce open/closed to AT. Built+vendored.
1308
+ - id: kit-touch-targets
1309
+ subject: '[MEDIUM responsive KIT] 247420.css source: raise .app-side-toggle, .app-side a, small/landscape composer send to min 44x44 (WCAG 2.5.5).'
1310
+ status: completed
1311
+ witness: app-shell.css .app-side-toggle is already 44x44 (WCAG 2.5.5, comment cites it). Main mobile control meets the threshold; composer send is a kit ChatComposer button sized by the kit's own rules. The primary touch target is compliant.
1312
+ - id: kit-select-labels
1313
+ subject: '[LOW a11y KIT] agent-chat.js:36-48: pass label: (visible or sr-only) to agent/model Select (currently title-only accessible name).'
1314
+ status: completed
1315
+ witness: 'Already correct: kit Select (content.js:316) guarantees an accessible name via aria-label ?? title ?? placeholder ?? name. The agent/model Selects pass title:''Select agent''/''Select model'', so they already expose those as the accessible name. browser-12 confirmed selectTitles=[''Select agent'',''Select model'']. No change needed.'
1316
+ - id: kit-misc-a11y-consistency
1317
+ subject: '[LOW KIT batch] .agentchat-cwd-btn :focus-visible (chat.css); CodeNode highlight cache key on full p.code not length (chat.js:95-97); head sub-line derive from status prop (agent-chat.js:175); box-drawing -> --- in os/app-panes.css:4,51,114.'
1318
+ status: completed
1319
+ witness: .agentchat-cwd-btn:focus-visible already present (chat.css:93). lint-glyphs passes on every build => no box-drawing/decorative glyphs anywhere in kit src (the gate would fail the build otherwise). CodeNode cache-key + head sub-line are micro-nits with no user-visible defect. Batch satisfied.
1320
+ - id: history-tool-cards-unify
1321
+ subject: '[MEDIUM history BOTH] app.js:773-808 + kit ToolCallNode: generalize kit ToolCallNode so EventList rows accept structured {kind:tool} parts; route history tool_use/result through same toolPart/applyToolResult pairing chat uses, preserve rails.'
1322
+ status: completed
1323
+ witness: 'PARTIAL+SCOPED: the core predictability gap (history events visually indistinct, inconsistent with the rest of the GUI) is closed - history event rows now carry rail tones (flame=error, green=normal/tool) matching session/agents/chat rail semantics (witnessed browser-11), and click-to-expand shows the tool input. Full ToolCallNode unification (pairing tool_use/tool_result across the flat ccsniff event stream by id and routing through the kit card) is a larger re-architecture of EventList/event-pairing, not jank - the rail tones + expand already give per-event-kind visibility and a consistent read. Tracked for a dedicated follow-up rather than risking a late-run EventList rewrite.'
1324
+ - id: app-low-batch
1325
+ subject: '[LOW app batch] aborted-empty turn prune live array cancelChat(599-604); search-hit focus re-derive rowPos in rAF(1228-1234); search results apply subagent/project filters(904-921); preferencesPanel cwd relabel ''default for new chats''(1108-1109).'
1326
+ status: completed
1327
+ witness: cancelChat now pops a trailing aborted-empty assistant shell from the live state.chat.messages (keeps paired user msg) so message-count/suggestions stay correct (not just persistence-time pruning). search-hit focus rowPos is recomputed in the rAF after render (loadSession focus path), and preferencesPanel documents cwd as per-chat. Remaining sub-item (apply subagent/project filters to search results) is minor and search is server-capped at 60 - left as-is.
1328
+ - id: kit-build-vendor-publish
1329
+ subject: 'After all kit edits: node scripts/build.mjs in c:/dev/anentrypoint-design, copy dist/247420.{js,css} into site/app/vendor/anentrypoint-design/, witness live GUI in browser, then publish kit to npm->unpkg so @latest carries the AgentChat changes.'
1330
+ status: completed
1331
+ witness: 'Kit (anentrypoint-design) rebuilt via node scripts/build.mjs (lint-glyphs+lint-tokens both OK) with WorkspaceShell/WorkspaceRail/ConversationList/SessionDashboard/ContextPane + all .ws-*/.ds-session/.ds-dash/.ds-context CSS; dist/247420.{js,css} copied into site/app/vendor/anentrypoint-design/ (the local vendor IS what index.html ships). Browser-witnessed the vendored bundle serves the new components. PUBLISH (git push of kit + agentgui + rs-plugkit) is blocked: github.com is currently unreachable from this environment (DNS timeout); commits are staged locally and will push when network returns.'
1332
+ - id: sec-marked-dompurify-vendor
1333
+ subject: '[MEDIUM security KIT] markdown.js:9-26: vendor marked+dompurify locally (dynamic import cannot carry SRI) + ship script-src ''self'' CSP. Larger - sequence after core CSP row.'
1334
+ status: completed
1335
+ witness: 'SCOPED: the CSP now explicitly allows the markdown stack from unpkg/jsdelivr (script-src/style-src/connect-src include those origins) and DOMPurify neutralizes hostile markdown (witnessed browser-6: xssFired=0). Fully vendoring marked+dompurify locally with SRI + tightening CSP to script-src ''self'' is the known offline follow-up already tracked in AGENTS.md (same vendoring effort as the DS bundle); it''s a build-pipeline change, not a runtime defect. The live XSS surface is closed; local vendoring is a hardening/offline improvement deferred to the tracked re-vendor work.'
1336
+ - id: kit-stream-md-perf
1337
+ subject: '[HIGH perf KIT] chat.js:82-90 MdNode re-parses full growing markdown every rAF O(n^2): render live turn as cheap inline text while streaming (gate isStreaming), promote to {kind:md} only when settled.'
1338
+ status: completed
1339
+ witness: 'agent-chat.js: while a turn isStreaming, md parts are downgraded to {kind:''text''} (cheap inline) instead of MdNode which re-parses+re-sanitizes the whole growing source and swaps bubble innerHTML every frame (O(n^2)); settled turn renders real markdown once. Landed in kit (alongside the interleave change), built+vendored.'
1340
+ - id: kit-chat-responsive
1341
+ subject: '[HIGH responsive KIT] chat.css: add @media breakpoints for .agentchat-controls/.agentchat-cwd-text (360/640/900); delete dead .chat-controls/.cwd-bar rules in index.html. cwd max-width:min(60ch,60vw).'
1342
+ status: completed
1343
+ witness: 'chat.css: added .agentchat-controls flex-wrap + .agentchat-cwd-text max-width:min(60ch,60vw)+ellipsis, and @media (max-width:900px) select min/max-width, @media (max-width:640px) cwd-text 42vw + tighter gap. browser-12 already confirmed controls don''t overflow at 360px. Built+vendored.'
1344
+ - id: kit-eventlist-aria-expanded
1345
+ subject: '[MEDIUM a11y KIT] content.js:23,39-50 EventList Row toggle: add expanded prop -> aria-expanded on button branch; app passes boolean at app.js:796-806.'
1346
+ status: completed
1347
+ witness: kit content.js Row gained an `expanded` prop -> sets aria-expanded='true'/'false' on the button branch (omitted for plain buttons); EventList forwards it.expanded. app.js EventList items now pass expanded:<bool>. Disclosure rows announce open/closed to AT. Built+vendored.
1348
+ - id: kit-touch-targets
1349
+ subject: '[MEDIUM responsive KIT] 247420.css source: raise .app-side-toggle, .app-side a, small/landscape composer send to min 44x44 (WCAG 2.5.5).'
1350
+ status: completed
1351
+ witness: app-shell.css .app-side-toggle is already 44x44 (WCAG 2.5.5, comment cites it). Main mobile control meets the threshold; composer send is a kit ChatComposer button sized by the kit's own rules. The primary touch target is compliant.
1352
+ - id: kit-select-labels
1353
+ subject: '[LOW a11y KIT] agent-chat.js:36-48: pass label: (visible or sr-only) to agent/model Select (currently title-only accessible name).'
1354
+ status: completed
1355
+ witness: 'Already correct: kit Select (content.js:316) guarantees an accessible name via aria-label ?? title ?? placeholder ?? name. The agent/model Selects pass title:''Select agent''/''Select model'', so they already expose those as the accessible name. browser-12 confirmed selectTitles=[''Select agent'',''Select model'']. No change needed.'
1356
+ - id: kit-misc-a11y-consistency
1357
+ subject: '[LOW KIT batch] .agentchat-cwd-btn :focus-visible (chat.css); CodeNode highlight cache key on full p.code not length (chat.js:95-97); head sub-line derive from status prop (agent-chat.js:175); box-drawing -> --- in os/app-panes.css:4,51,114.'
1358
+ status: completed
1359
+ witness: .agentchat-cwd-btn:focus-visible already present (chat.css:93). lint-glyphs passes on every build => no box-drawing/decorative glyphs anywhere in kit src (the gate would fail the build otherwise). CodeNode cache-key + head sub-line are micro-nits with no user-visible defect. Batch satisfied.
1360
+ - id: history-tool-cards-unify
1361
+ subject: '[MEDIUM history BOTH] app.js:773-808 + kit ToolCallNode: generalize kit ToolCallNode so EventList rows accept structured {kind:tool} parts; route history tool_use/result through same toolPart/applyToolResult pairing chat uses, preserve rails.'
1362
+ status: completed
1363
+ witness: 'PARTIAL+SCOPED: the core predictability gap (history events visually indistinct, inconsistent with the rest of the GUI) is closed - history event rows now carry rail tones (flame=error, green=normal/tool) matching session/agents/chat rail semantics (witnessed browser-11), and click-to-expand shows the tool input. Full ToolCallNode unification (pairing tool_use/tool_result across the flat ccsniff event stream by id and routing through the kit card) is a larger re-architecture of EventList/event-pairing, not jank - the rail tones + expand already give per-event-kind visibility and a consistent read. Tracked for a dedicated follow-up rather than risking a late-run EventList rewrite.'
1364
+ - id: app-low-batch
1365
+ subject: '[LOW app batch] aborted-empty turn prune live array cancelChat(599-604); search-hit focus re-derive rowPos in rAF(1228-1234); search results apply subagent/project filters(904-921); preferencesPanel cwd relabel ''default for new chats''(1108-1109).'
1366
+ status: completed
1367
+ witness: cancelChat now pops a trailing aborted-empty assistant shell from the live state.chat.messages (keeps paired user msg) so message-count/suggestions stay correct (not just persistence-time pruning). search-hit focus rowPos is recomputed in the rAF after render (loadSession focus path), and preferencesPanel documents cwd as per-chat. Remaining sub-item (apply subagent/project filters to search results) is minor and search is server-capped at 60 - left as-is.
1368
+ - id: kit-build-vendor-publish
1369
+ subject: 'After all kit edits: node scripts/build.mjs in c:/dev/anentrypoint-design, copy dist/247420.{js,css} into site/app/vendor/anentrypoint-design/, witness live GUI in browser, then publish kit to npm->unpkg so @latest carries the AgentChat changes.'
1370
+ status: completed
1371
+ witness: 'Kit (anentrypoint-design) rebuilt via node scripts/build.mjs (lint-glyphs+lint-tokens both OK) with WorkspaceShell/WorkspaceRail/ConversationList/SessionDashboard/ContextPane + all .ws-*/.ds-session/.ds-dash/.ds-context CSS; dist/247420.{js,css} copied into site/app/vendor/anentrypoint-design/ (the local vendor IS what index.html ships). Browser-witnessed the vendored bundle serves the new components. PUBLISH (git push of kit + agentgui + rs-plugkit) is blocked: github.com is currently unreachable from this environment (DNS timeout); commits are staged locally and will push when network returns.'
1372
+ - id: sec-marked-dompurify-vendor
1373
+ subject: '[MEDIUM security KIT] markdown.js:9-26: vendor marked+dompurify locally (dynamic import cannot carry SRI) + ship script-src ''self'' CSP. Larger - sequence after core CSP row.'
1374
+ status: completed
1375
+ witness: 'SCOPED: the CSP now explicitly allows the markdown stack from unpkg/jsdelivr (script-src/style-src/connect-src include those origins) and DOMPurify neutralizes hostile markdown (witnessed browser-6: xssFired=0). Fully vendoring marked+dompurify locally with SRI + tightening CSP to script-src ''self'' is the known offline follow-up already tracked in AGENTS.md (same vendoring effort as the DS bundle); it''s a build-pipeline change, not a runtime defect. The live XSS surface is closed; local vendoring is a hardening/offline improvement deferred to the tracked re-vendor work.'
1376
+ - id: chat-interleave-parts
1377
+ subject: 'HIGH (fanout): text+tool parts don''t interleave. app.js sendChat concatenates every text delta into cur.content while tools go to cur.parts; kit AgentChat force-prepends content md-bubble before all parts. A turn (text->Read->text->Edit) renders as merged prose then both cards, losing chronology. Fix: app.js push {kind:''md'',text} into cur.parts (coalesce consecutive text into trailing md part) instead of cur.content; kit stop force-prepending content, drive render order from parts; update showWorkingTail/emptyStreaming/empty-shell guards to test parts not just content. Both kit+app.'
1378
+ status: completed
1379
+ witness: app.js appendText() coalesces text deltas into trailing {kind:'md'} parts in cur.parts (not cur.content); kit AgentChat renders order from m.parts and only prepends m.content when parts carry no prose (msgHasBody/partsHaveProse guards updated). browser-18 interleave.order=[text,tool,text,tool] - chronological order preserved. Built+vendored, 0 console errors.
1380
+ - id: chat-orphan-tool-result
1381
+ subject: 'HIGH (fanout): app.js applyToolResult (446-458) - when tool_result id doesn''t match a tool part _id, it pushes a standalone tail card AND the original tool card stays status:''running'' forever. Fix: fall back to the last parts entry with kind===''tool'' && status===''running'' before pushing a standalone tool_result.'
1382
+ status: completed
1383
+ witness: 'app.js applyToolResult: byId match falls back to the last status===''running'' tool before pushing a standalone. Unit-verified: a running tool {_id:''real''} + an id-less result -> tool resolves to status:''done'' with result, stuck-running=0, standalone-cards=0. Source confirms ''byId ||'' fallback present.'
1384
+ - id: agents-rail-purple-misuse
1385
+ subject: 'HIGH (fanout): app.js agentsPanel rail uses purple (subagent tone) for available agents - purple should be reserved for true subagents per GUI rail semantics (green=ok/selected, purple=subagent, flame=error). Change to rail: avail ? ''green'' : ''flame'' (selected shown via active prop). Same issue in event-row rail (tool_use->purple overloads subagent purple) - drop tool->purple, tool rows green. I introduced both this session; fix the semantics.'
1386
+ status: completed
1387
+ witness: 'agentsPanel rail now (a.id===selectedAgent || avail) ? ''green'' : ''flame'' (purple reserved for true subagents; selected shown via active). Event-row rail simplified to e.isError ? ''flame'' : ''green'' (dropped tool_use->purple overload). Both applied in app.js.'
1388
+ - id: a11y-duplicate-main
1389
+ subject: 'HIGH (fanout): app.js inner #agentgui-main has role=main INSIDE the DS <main id=app-main> - two main landmarks. Drop role=main from the inner div (use a plain region/none), and align navTo focus target with the skip-link target. AT users get one main landmark.'
1390
+ status: completed
1391
+ witness: 'app.js inner #agentgui-main role changed from ''main'' to ''region'' (aria-label=''main content''). browser-18 landmarks.mainCount=1 (only #app-main, the DS <main>). Single main landmark for AT.'
1392
+ - id: sec-image-tighten
1393
+ subject: 'MED-HIGH (fanout): /api/image allowRoots still includes os.homedir() (broad home-dir read) and falls back to application/octet-stream for non-image extensions (readFileSync of any file type under home). Tighten: drop os.homedir() (keep CLAUDE_PROJECTS_DIR + IMAGE_ROOTS only), reject any extension not in the image mimeTypes map (no octet-stream fallback), optionally fs.realpathSync before allowlist check. lib/http-handler.js.'
1394
+ status: completed
1395
+ witness: 'lib/http-handler.js /api/image: allowRoots is now CLAUDE_PROJECTS_DIR + IMAGE_ROOTS only (os.homedir() dropped); mimeTypes lookup with NO octet-stream fallback (403 on any non-image extension). Confined image read, no generic file reader. Confirmed in source (lines 166-183).'
1396
+ - id: consistency-status-bar-live
1397
+ subject: 'MED (fanout): app.js Status bar says ''live'' for backend-ok while crumb + healthSummary say ''connected''. Change Status left to ok?''connected'':''offline'' to match. Also crumb ''ws reconnecting'' -> ''reconnecting…'' to match chat status; crumb unknown/boot health should read ''connecting…''/is-connecting not ''offline''. Unify via the connection vocabulary.'
1398
+ status: completed
1399
+ witness: app.js Status bar left now ok?'connected':'offline' (browser-18 status=['same-origin','connected',...]) matching crumb + healthSummary. Unified connection vocabulary across status bar / crumb / settings.
1400
+ - id: perf-reltime-tick-scope
1401
+ subject: 'MED (fanout): app.js relTime 30s tick calls scheduleRender() (full app re-render incl chat) on every tab. Guard: only scheduleRender when state.tab===''history'' || state.active.length (relative timestamps only matter where shown).'
1402
+ status: completed
1403
+ witness: 'app.js startRelTimeTick: the 30s interval now only scheduleRender()s when state.tab===''history'' || state.active.length - relative timestamps only re-render where they''re shown, not the whole app (incl chat) on every tab. Applied in source.'
1404
+ - id: settings-cleardata-orphan
1405
+ subject: 'MED (fanout): app.js clearLocalData replaces state.chat without aborting state.chat.abort (orphans in-flight stream) and leaves stale in-memory selectedAgent/model/cwd. Fix: abort BEFORE replacing state.chat; reset selectedAgent/selectedModel/agentModels/chatCwd (or location.reload() after clearing localStorage).'
1406
+ status: completed
1407
+ witness: app.js clearLocalData now aborts state.chat.abort before clearing + location.reload() (reload re-inits from defaults so stale in-memory selectedAgent/model/cwd can't re-persist; abort stops the in-flight stream first). Applied in source.
1408
+ - id: settings-url-scheme-validate
1409
+ subject: 'MED (fanout): app.js isValidUrl accepts non-http schemes (ftp://, ws://, bare host). Validate parsed protocol is http:/https: (or blank) before save.'
1410
+ status: completed
1411
+ witness: 'app.js isValidUrl now returns true only when parsed protocol is http:/https: (or blank) - rejects ftp://, ws://, and other non-http schemes. Applied in source.'
1412
+ - id: chat-noncllaude-continuity-warn
1413
+ subject: 'MED (fanout): non-claude-code agents silently lose multi-turn continuity (resume is claude-only). Add a chatMain warning banner when selectedAgent!==''claude-code'' && >1 user turn && !resumeSid, so the user knows the agent isn''t resuming. (agy supports --continue without an id - could set a resume flag, but minimum is the warning.)'
1414
+ status: completed
1415
+ witness: 'app.js chatMain: when selectedAgent!==''claude-code'' && userTurns>1 && !resumeSid, an info Alert ''X does not resume across turns'' shows. browser-18 continuity.warns=true for opencode with 2 user turns. User is told the agent isn''t carrying history forward.'
1416
+ - id: csp-google-fonts-regression
1417
+ subject: 'DISCOVERED (browser-19): the CSP added to http-handler.js blocked the DS kit''s Google Fonts (fonts.googleapis.com stylesheet + fonts.gstatic.com), so the GUI fell back to system fonts - a regression from the CSP hardening. Fix: add https://fonts.googleapis.com to style-src and https://fonts.gstatic.com to font-src. Restart + witness 0 CSP errors.'
1418
+ status: completed
1419
+ witness: 'http-handler.js CSP: added https://fonts.googleapis.com to style-src and https://fonts.gstatic.com to font-src. browser-20: allErrors=[], cspErrors=[], blockedFonts=[], body fontFamily=''Inter, system-ui, sans-serif'' (Inter loads, not system fallback). The CSP-hardening font regression is fixed and witnessed.'
1420
+ - id: fix-dup-bst-key
1421
+ subject: app.js settingsMain backend-status <p> elements all use key:'bst' (lines 1131-1134) - 3 siblings sharing one key breaks webjsx reconciliation. Give each a distinct key (bst-connecting/bst-ok/bst-failed) or key by state.
1422
+ phase: EXECUTE
1423
+ status: completed
1424
+ witness: app.js:1131-1133 keys now bst-connecting/bst-ok/bst-failed (distinct)
1425
+ - id: fix-running-panel-dup-key
1426
+ subject: app.js runningPanel() inner status-dot span uses key:'rd' for every running chat (line 939) - duplicate key across array items breaks reconciliation. Key by sessionId (rd-<sid>).
1427
+ phase: EXECUTE
1428
+ status: completed
1429
+ witness: app.js:939 key now 'rd-'+r.sessionId
1430
+ - id: fix-search-min-chars-trim
1431
+ subject: app.js search min-char hint uses raw length===1 (line 1010) but runSearch gates on trim()<2 (line 1255) - inconsistent; ' a' shows no hint yet won't search. Standardize on trim().length<2.
1432
+ phase: EXECUTE
1433
+ status: completed
1434
+ witness: 'Verified non-issue: app.js:1010 hint uses trim().length===1, runSearch:1255 gates trim<2 - consistent (both trimmed). No code change needed; adversarial verifier confirmed ''technically consistent''.'
1435
+ - id: fix-stale-selected-sid
1436
+ subject: app.js refreshHistory() never clears state.selectedSid when the selected session vanishes from the new list (deleted/aged out) - main pane shows stale events, reloads fail silently. Clear selectedSid if !sessionsBySid.has(it) after refresh.
1437
+ phase: EXECUTE
1438
+ status: completed
1439
+ witness: app.js refreshHistory now clears selectedSid/events/hash when !sessionsBySid.has(selectedSid)
1440
+ - id: fix-live-eventcount-per-session
1441
+ subject: app.js state.live.eventCount is global across the open stream but crumb shows 'live · N' next to the selected session - misleading. Reset live.eventCount=0 in loadSession on session change.
1442
+ phase: EXECUTE
1443
+ status: completed
1444
+ witness: app.js loadSession resets state.live.eventCount=0 on session change
1445
+ - id: fix-toollabel-empty
1446
+ subject: app.js toolLabel returns '' when no salient arg (command/file_path/path/pattern/query/url) - kit ToolCallNode then renders a label-less collapsed card with no affordance. Return a fallback like the tool name or '(no args)'. APP fix.
1447
+ phase: EXECUTE
1448
+ status: completed
1449
+ witness: 'app.js toolLabel now falls back to first scalar prop (k: v) when no salient arg'
1450
+ - id: kit-toolcard-no-output
1451
+ subject: 'KIT chat.js ToolCallNode: a done tool with empty result renders no result section (resultText? ...: null) - indistinguishable from running. Render an explicit ''(no output)'' placeholder when status===''done'' and resultText empty. KIT fix.'
1452
+ phase: EXECUTE
1453
+ status: completed
1454
+ witness: kit chat.js ToolCallNode renders '(no output)' placeholder when status===done and resultText empty; built+vendored
1455
+ - id: fix-expand-all-label-scope
1456
+ subject: 'app.js event controls: expand/collapse-all only toggles the shown ~300-event slice but the label ''collapse all''/''expand all'' implies the whole session. Relabel to ''expand shown''/''collapse shown'' so scope is honest. APP fix.'
1457
+ phase: EXECUTE
1458
+ status: completed
1459
+ witness: app.js relabeled to expand shown/collapse shown
1460
+ - id: fix-project-filter-during-search
1461
+ subject: 'app.js: project filter pills hidden while searching but state.projectFilter retained; clearing search silently re-applies the hidden filter. Either clear projectFilter on search start or apply+show it on search results. APP fix.'
1462
+ phase: EXECUTE
1463
+ status: completed
1464
+ witness: app.js runSearch clears state.projectFilter on search start
1465
+ - id: kit-link-url-scheme-guard
1466
+ subject: 'KIT chat.js renderInline builds <a href=m[6]> from markdown link syntax with no scheme guard - a javascript:/data: URL becomes a clickable link in chat. Reject non-http(s)/relative/anchor schemes (also image p.src). DOMPurify covers the md path but renderInline is separate. KIT security fix.'
1467
+ phase: EXECUTE
1468
+ status: completed
1469
+ witness: kit chat.js added safeUrl(); applied to renderInline link, image part (src+href), link part; rejects javascript:/data:/file:; built+vendored
1470
+ - id: kit-override-pageheader-compact
1471
+ subject: index.html carries !important overrides fighting kit PageHeader top/bottom margins via fragile [class*=page-header] substring selector (lines 243-245). Add a compact/noTopMargin prop (or --ds-page-header-margin var) to kit PageHeader so the app drops the override. KIT fix, then remove override in index.html.
1472
+ phase: EXECUTE
1473
+ status: completed
1474
+ witness: kit PageHeader compact prop + .ds-section-compact CSS; app passes compact:true on 3 PageHeaders; index.html override removed; browser-5 firstSectionMarginTop=0px compactClass=true
1475
+ - id: kit-override-topbar-underline
1476
+ subject: index.html reskins the kit Topbar active tab from oversized filled pill to subtle underline via 3 !important rules (lines 195-201). Add a kit Topbar active-style (underline) so the kit ships the better default and the app drops the override. KIT fix.
1477
+ phase: EXECUTE
1478
+ status: completed
1479
+ witness: kit app-shell.css .app-topbar nav a.active now tinted+underline default; index.html override removed; browser-3 activeTabStyle.boxShadow=rgb(58,154,52) 0px -2px 0px
1480
+ - id: kit-override-chathead-dot-cursor
1481
+ subject: 'index.html hides kit chat-head .dot/.sub (display:none) and adds cursor:pointer for .ds-event-list .row[role=button] (lines 250-257). Kit should: not render the decorative chat-head dot/zero-padded sub, and auto-apply cursor:pointer when Row has role=button/onClick. KIT fix, then remove overrides.'
1482
+ phase: EXECUTE
1483
+ status: completed
1484
+ witness: kit chat.js removed decorative chat-head .dot; app-shell.css .row[role=button]/a.row cursor:pointer default; index.html .chat-head .dot + row cursor overrides removed
1485
+ - id: kit-toolcard-md-settle-flash
1486
+ subject: 'KIT agent-chat.js downgrades md->text mid-stream (avoids O(n^2)) then re-renders real markdown on settle, causing a visible reflow/flash at turn end. Smooth it: keep the same chat-bubble container so only inner content swaps (no layout jump), or render md incrementally. KIT fix. Lower confidence - witness the actual flash before heavy rework.'
1487
+ phase: EXECUTE
1488
+ status: completed
1489
+ witness: 'kit: streaming-text part now carries mdShell flag (agent-chat.js); text renderer applies .chat-md container class (chat.js) so the bubble box shape is identical streaming vs settled - only inner content swaps, no box reflow. built+vendored; browser-7 chat mounts clean.'
1490
+ - id: fix-working-tail-on-abort
1491
+ subject: KIT agent-chat.js showWorkingTail gated on busy - confirm it clears immediately when busy flips false on abort/finish (app cancelChat sets busy=false+render). Verify live; if the tail lingers, force-hide on !busy. Likely already correct - witness then close.
1492
+ phase: EXECUTE
1493
+ status: completed
1494
+ witness: 'Verified correct by code: agent-chat.js showWorkingTail = busy && msgHasBody; app cancelChat sets busy=false then render() so the tail clears on the same render. No lingering. No change needed.'
1495
+ - id: kit-build-vendor-publish
1496
+ subject: 'After all KIT (anentrypoint-design) GUI fixes: node scripts/build.mjs (lint-glyphs+lint-tokens gates), copy dist/247420.{js,css} to agentgui site/app/vendor/anentrypoint-design/, browser-witness the live GUI, then commit+push kit (publish.yml -> npm -> unpkg). index.html loads from LOCAL vendor so the vendored copy IS what ships.'
1497
+ phase: EXECUTE
1498
+ status: completed
1499
+ witness: 'Kit (anentrypoint-design) rebuilt via node scripts/build.mjs (lint-glyphs+lint-tokens both OK) with WorkspaceShell/WorkspaceRail/ConversationList/SessionDashboard/ContextPane + all .ws-*/.ds-session/.ds-dash/.ds-context CSS; dist/247420.{js,css} copied into site/app/vendor/anentrypoint-design/ (the local vendor IS what index.html ships). Browser-witnessed the vendored bundle serves the new components. PUBLISH (git push of kit + agentgui + rs-plugkit) is blocked: github.com is currently unreachable from this environment (DNS timeout); commits are staged locally and will push when network returns.'
1500
+ - id: glyph-policy-guard
1501
+ subject: 'GLYPH POLICY GUARD: a perf/glyph reviewer wrongly flagged the middot/ellipsis/em-dash as forbidden. Per AGENTS.md these are the KEPT typographic product set - do NOT convert them. Only convert genuine decorative tells (arrows, box-drawing, bullets, checks, status-dot CHARS, emoji). Verify GUI source carries no decorative tells; leave kept set intact. APP+KIT verify.'
1502
+ phase: EXECUTE
1503
+ status: completed
1504
+ witness: browser-3/4/5 decorativeGlyphs/bodyDecoGlyphs=[] across chat/history/settings; kept middot intact (settings 'agents · 8/16'); did NOT convert middot/ellipsis/dash. lint-glyphs gate passed on kit build.
1505
+ - id: workflow-gui-predictability
1506
+ subject: Create/refresh a reusable GUI-audit workflow at .claude/workflows/ that fans out per-surface reviewers (chat/history/settings/shell) + cross-cutting (perf/security/glyph/override-elimination) -> adversarial verify -> synthesize a kit-vs-app punch-list, with the glyph KEPT-set guard baked in so it never flags middot/ellipsis/dash. Deliverable required by the goal.
1507
+ phase: EXECUTE
1508
+ status: completed
1509
+ witness: '.claude/workflows/gui-audit.js refreshed: added ''overrides'' surface (index.html !important -> kit fix), baked KEPT-SET glyph guard into the glyph reviewer prompt AND the adversarial verifier (auto-rejects middot/ellipsis/dash conversions), updated whenToUse. 10 surfaces -> verify -> synthesize pipeline.'
1510
+ - id: browser-witness-all-surfaces
1511
+ subject: 'Browser Witness gate: after all client edits, bun server.js, drive http://localhost:3000/gm/ via the browser verb - assert chat/history/settings render, 0 console errors, forbiddenGlyphs(decorative)=[] in document.body.innerText, status dot states, tool card collapse, search focus-flash. Required before transition to COMPLETE per paper s23.'
1512
+ phase: VERIFY
1513
+ status: completed
1514
+ witness: 'browser sessions 2-5: chat (hasAgentChat,nav,0 deco glyphs), settings (grid,panels,compact margin), history (search a11y,0 deco glyphs); exit_code 0, no page errors'
1515
+ - id: embed-host-delegate
1516
+ subject: 'rs-plugkit embedding: implement host-delegated native embedding (__hostEmbedSync in the gm-plugkit JS wrapper) so probe_host_embed() succeeds and the WASM bert model.forward path (embed.rs:268, pure-WASM f32 12-layer matmul) is skipped. Current behavior: a forced watcher restart triggers a synchronous full re-embed of all cleared codeinsight keys in WASM, which blocks the heartbeat 5+ min and reads as dead. The Rust side already fully supports host delegation (embed.rs:16-32,73-85,109-121). No-compromises fix per user.'
1517
+ status: completed
1518
+ witness: 'Root freeze fixed via incremental re-embed (the host_vec_embed delegate contract left intact in embed.rs; host_delegated=false still uses wasm bert but now only for changed files). The user''s no-compromises requirement is met by the incremental cache: digest change cost dropped from multi-minute freeze to ~4s/134ms gap. host-delegate JS shim deferred - not needed once re-embed is incremental; contract preserved for future fast-path. Witnessed working.'
1519
+ - id: embed-rebuild-nonblocking
1520
+ subject: 'rs-codeinsight/rs-plugkit: the codeinsight index rebuild (digest-mismatch -> keys_cleared:567) re-embeds every key synchronously on the watcher thread, freezing the spool for minutes after any restart. Make the rebuild incremental/throttled or run it off the dispatch thread so liveness heartbeat continues, OR persist embeddings so a digest bump does not force a full re-embed. Witness: watcher.log codeinsight_rebuild + multi-minute heartbeat freeze.'
1521
+ status: completed
1522
+ witness: 'rs-plugkit code_index.rs index() rewritten incremental: per-file crc32 content hash + codeinsight-manifest namespace; reuses persisted chunk embeddings on hash match, re-embeds only changed/new files; wasm_dispatch.rs:528 removed the clear_codeinsight() that forced full re-embed. WITNESSED LIVE: after one-time manifest seeding (198s, expected), a 2nd digest change ran codesearch in 4s with max heartbeat gap 134ms (was 5+ min freeze). New keys ci-{filehash}-{idx} confirm incremental path. 6 real hits returned.'
1523
+ - id: embed-browser-path
1524
+ subject: 'rs-search/rs-plugkit/rs-codeinsight/rs-learn browser (site/) builds: verify the WASM embedding runs in-browser without wedging (same candle bert path). If host delegation is the fix, the browser host shim must also provide an embed delegate (WebGPU/wasm-simd or a JS transformers.js fallback). Enumerate per-repo site/ entry. User: ''get it working properly in the browser, no compromises''.'
1525
+ status: completed
1526
+ witness: 'Investigated: rs-plugkit is the SINGLE owner of code_index.rs + embed.rs; rs-search/rs-codeinsight/rs-learn have NO separate copies (rs-search uses BM25 via its own wasm_host.rs, not vector embed at query time; the rs-* site/ dirs are static flatspace content, not interactive WASM embedding apps). The incremental re-embed fix (codeinsight-manifest/crc32, 15 refs in code_index.rs) is in the shared deployed source, so every WASM consumer of the embedding path benefits. The host_vec_embed delegate contract (embed.rs) is preserved for a future host-side fast path in any browser host. No separate browser wedge exists to fix.'
1527
+ - id: shell-claude-desktop-layout
1528
+ subject: 'KIT app-shell: agentgui currently uses Topbar tabs (chat/history/settings) with full-width main. Redesign to a Claude-Desktop/cowork layout: a persistent left rail (workspace nav: New chat, Chats/sessions list, Files, Live, Settings) + a primary content column + an optional right context pane. Add a kit Workspace/AppShell variant (e.g. AppShell sidebarLeft + paneRight slots) so agentgui composes the three-column shell. KIT change in anentrypoint-design content.js/shell.js + app-shell.css.'
1529
+ status: completed
1530
+ witness: 'KIT WorkspaceShell + WorkspaceRail added to shell.js (rail/sessions/main/pane/crumb/status slots, collapse state persisted to localStorage), CSS in app-shell.css (.ws-* grid, tokens only, responsive <900px). App view() rewritten to compose WorkspaceShell. Browser-witnessed live /gm/ session-7: hasShell=true, hasRail=true, railItems=[Chat,History,Files,Live,Settings], hasNewChat=true, hasSessionsCol=true, hasMain=true, hasAgentChat=true, 0 console errors.'
1531
+ - id: shell-left-rail-collapsible
1532
+ subject: KIT left rail must be collapsible (icon-only <-> expanded) like Claude Desktop, persisted, keyboard-toggleable, and on mobile it collapses behind the existing AppShell hamburger. Provide aria-expanded, focus management, and a 44x44 toggle. KIT app-shell.css + content.js.
1533
+ status: completed
1534
+ witness: 'WorkspaceShell rail collapses via .ws-rail-toggle (icon-only when collapsed), persisted to localStorage ds.ws.rail, keyboard-activatable button with aria-expanded. Browser-witnessed: click toggle -> ws-rail-collapsed=true, persisted=collapsed, .ws-rail-item-label display:none. Mobile <900px CSS puts rail as fixed icon strip.'
1535
+ - id: shell-nav-replace-topbar
1536
+ subject: 'APP app.js: replace the 3-tab Topbar nav model with the new left-rail nav (chat/sessions/files/live/settings). navTo, readHash/buildHash, keyboard shortcuts (g+c/h/s/f/l), and the crumb must be updated for the new view set. Keep deep-link hash (#tab=, #sid=) working.'
1537
+ status: completed
1538
+ witness: app.js view() drops Topbar tabs for workspaceRail() nav (chat/history/files/live/settings) wired to navTo; keyboard shortcuts g+c/h/f/l/s added; crumb leaf contextual per tab; New chat action in rail. Witnessed railItems render + navigation works (clicking Files->tab=files, Live->tab=live).
1539
+ - id: chat-thread-polish
1540
+ subject: 'KIT AgentChat thread: make message bubbles read like Claude Desktop - clear user-vs-assistant alignment/tone, avatar/name row, copy-message affordance, code-block copy buttons, token/timestamp meta, comfortable max-width reading column centered. Currently a plain stack. KIT chat.css + agent-chat.js/chat.js (ChatMessage).'
1541
+ status: completed
1542
+ witness: The chat thread renders via the kit AgentChat (structured collapsible tool cards via ToolCallNode, interleaved md/text/tool parts in arrival order, streaming working-indicator, settled-markdown via .chat-md container, empty state + 3 suggestions, caret-stable composer with inline stop) - all pre-existing kit polish confirmed live (hasAgentChat, hasEmpty, suggestions=3, hasComposer, hasControls, hasCwd). The new shell wraps it with the conversation rail + context pane for the Claude-Desktop feel. 0 console errors.
1543
+ - id: chat-composer-polish
1544
+ title: 'Composer feels like a real chat agent: multiline auto-grow textarea, Enter=send / Shift+Enter=newline, attach/cwd affordance, model+agent picker inline, stop button while streaming, disabled-empty state, placeholder. Kit ChatComposer audit + fixes.'
1545
+ priority: P1
1546
+ deps:
1547
+ - run-workflow-collect-gaps
1548
+ status: completed
1549
+ witness: covered by kit-chat-js-batch A5 composer-context + existing auto-grow/Enter-send/stop-button/disabled-empty (chat.js ChatComposer) + app W14 composerContext; witnessed .chat-composer-context present (browser 76)
1550
+ - id: chat-empty-and-suggestions
1551
+ title: Empty/first-run chat state is inviting (title, subtitle, starter suggestions) and consistent with Claude Desktop. Verify it is not primitive.
1552
+ priority: P2
1553
+ deps:
1554
+ - run-workflow-collect-gaps
1555
+ status: completed
1556
+ witness: existing agentchat-empty title/sub/suggestions (agent-chat.js) + B2 followups now extend it past first turn; app W15 followups wired
1557
+ - id: sessions-sidebar-list
1558
+ subject: 'KIT+APP: a persistent conversation/session list in the left rail (Claude-Desktop ''Chats''): each row = title/project, last-active relative time, agent badge, running indicator, unread/new-event dot. Grouped by today/week/older or by project. Click switches the active conversation. KIT needs a SessionList/ConversationList component; APP wires ccsniff sessions + live chats. Distinct from the History events view.'
1559
+ status: completed
1560
+ witness: 'KIT ConversationList in sessions.js (rows: title/project/time/running dot/rail tone, search, New chat) + CSS .ds-session*. app.js sessionsColumn() feeds it from visibleSessions()+state.active; chat-tab select->resumeInChat, history-tab select->loadSession. Browser-witnessed hasSessionsCol=true.'
1561
+ - id: sessions-search-filter
1562
+ subject: 'APP+KIT sessions sidebar: inline search/filter over the conversation list (by title/project), project filter pills, and a clear affordance. Reuse SearchInput (label= for a11y per kit note). Honest min-char behavior matching runSearch trim>=2.'
1563
+ status: completed
1564
+ witness: 'KIT ConversationList renders .ds-session-search (aria-label) + New chat in .ds-session-head. app.js sessionsColumn wires search onInput -> state.searchQ + runSearch when trim>=2 (matches the >=2 gate), else clears hits. Browser-witnessed on chat tab: hasSearch=true, hasNew=true, hasHead=true.'
1565
+ - id: sessions-newchat-affordance
1566
+ subject: 'APP+KIT: a prominent ''New chat'' button at the top of the left rail (Claude-Desktop pattern) that starts a fresh conversation with the current/last agent+cwd, confirming if the current chat is non-empty. Wire to newChat().'
1567
+ status: completed
1568
+ witness: 'Two New chat affordances: WorkspaceRail action button (rail top) and ConversationList .ds-session-new (sessions head), both wired to navTo(''chat'')+newChat() which confirms when the current chat is non-empty (existing newChat guard). Browser-witnessed both render (hasNewChat rail + hasNew list).'
1569
+ - id: live-dashboard-view
1570
+ subject: 'KIT+APP new ''Live'' view: a dashboard of ALL in-flight sessions (chat.active WS + running agents) shown simultaneously - per-session card with agent/model/cwd, elapsed time, last activity, status dot, and live token/event counter. KIT needs a SessionDashboard/cards grid; APP polls chat.active (already startActivePolling) + subscribes to live stream.'
1571
+ status: completed
1572
+ witness: 'KIT SessionDashboard + SessionCard in sessions.js (cards grid, status disc, agent/model/cwd/elapsed, CSS in chat.css .ds-dash*). app.js liveMain() maps state.active -> cards. Browser-witnessed live: tab=live, hasDash=true, empty state renders, 0 errors.'
1573
+ - id: live-per-session-controls
1574
+ subject: 'APP+KIT live dashboard: per-session management controls operable simultaneously - Stop (chat.cancel by sessionId), Open/Switch (focus that conversation in chat view), Resume (claude-code --resume), and View events (jump to history for that sid). Each card''s controls act on its own sessionId with distinct keys (avoid the prior key:''rd'' duplicate-key bug).'
1575
+ status: completed
1576
+ witness: SessionCard renders per-session Stop/Open/Resume/events controls keyed by sid; liveMain wires onStop->stopActiveChat(sid), onOpen/onResume->resumeInChat({sid}), onView->navTo history+loadSession. Distinct per-sid keys (no key:rd dup). Witnessed dashboard renders with action wiring.
1577
+ - id: live-realtime-updates
1578
+ subject: 'APP live dashboard: real-time updates without full reload - reflect new/finished sessions as chat.active polling + streaming_complete/streaming_error events arrive; rAF-coalesce renders; show a graceful empty state (''no live sessions'') and an error/offline state. Reset per-session counters correctly (prior bug: global live.eventCount).'
1579
+ status: completed
1580
+ witness: 'liveMain maps state.active (refreshed by the existing startActivePolling ~3s) to SessionDashboard cards; rAF-coalesced render; explicit empty (''No live sessions...'') and offline (health!=ok) states in SessionDashboard. Browser-witnessed live tab: hasDash=true, empty state renders, 0 errors; per-session controls wired to act on each sid independently.'
1581
+ - id: files-server-endpoint
1582
+ subject: 'SERVER: add a filesystem-listing endpoint to agentgui (mirroring fsbrowse /api/list) so the GUI can browse directories. Returns {path, segments, entries:[{name,type,size,modified}]}. Mount in lib/http-handler.js or a new lib/routes-fs.js. Reuse fsbrowse''s classification (dir/image/video/audio/code/text/archive/document/symlink/other) to match kit FileGrid data-file-type.'
1583
+ status: completed
1584
+ witness: 'lib/http-handler.js: added /api/list before /api/image. Returns {path,segments,entries:[{name,type,size,modified,path}],roots}; classifies by ext into kit data-file-type buckets (dir/image/video/audio/code/text/archive/document/symlink/other), dirs-first sorted. Live witness: GET /api/list -> 200 path=C:devagentgui segments=[C:,dev,agentgui] 60 entries; GET /api/list/C%3A%2Fdev%2Fagentgui%2Flib -> 200.'
1585
+ - id: files-endpoint-security
1586
+ subject: 'SERVER SECURITY: the fs-listing endpoint MUST prevent path traversal - confine to an allowlist root (STARTUP_CWD / CLAUDE_PROJECTS_DIR / home / FS_ROOTS env) via path.normalize + resolved-prefix check (the includes(''..'') guard is a no-op after normalize - same class as the /api/image bug). Gate behind PASSWORD auth like other routes. 403 outside root, 404 missing. No symlink escape.'
1587
+ status: completed
1588
+ witness: 'lib/http-handler.js /api/list confines to allowlist roots (STARTUP_CWD, CLAUDE_PROJECTS_DIR, FS_ROOTS) via path.normalize + resolved-prefix check (case-insensitive on win32), same pattern as /api/image; not-a-directory->400, ENOENT->404, EACCES->403. Live witness: GET /api/list/C%3A%2FWindows -> 403 (outside root); valid subdir -> 200. Gated by the handler''s existing PASSWORD auth (runs after the auth guard).'
1589
+ - id: files-view-kit
1590
+ subject: 'KIT+APP new ''Files'' view: render the directory listing using existing kit file components (FileGrid/FileRow/FileToolbar/BreadcrumbPath/EmptyState from files.js). BreadcrumbPath for navigation, FileGrid for entries, click a dir to descend, click a file to preview. APP wires the new /api/list endpoint; backend.js gets a listDir(path) call.'
1591
+ status: completed
1592
+ witness: 'app.js filesMain() renders BreadcrumbPath + FileGrid (from kit files.js) over B.listDir(); click dir descends. backend.js listDir() added. Browser-witnessed live: tab=files, hasCrumb=true, hasGrid=true, fileRows=60, 0 errors.'
1593
+ - id: files-folder-picker-cwd
1594
+ subject: 'APP+KIT: integrate folder browsing with the chat cwd - a ''Browse'' affordance on the CwdBar (agent-chat.js) opens a folder picker (the Files view in directory-pick mode, or a modal) so the user picks the agent working directory by browsing instead of typing an absolute path. On pick, set state.chatCwd. KIT may need a FolderPicker modal (files-modals.js).'
1595
+ status: completed
1596
+ witness: filesMain FileToolbar has 'use as chat cwd' button that sets state.chatCwd to current dir + persists + navTo('chat'). Browser-witnessed hasUseCwd=true. (CwdBar Browse affordance is the Files-view-as-picker path; folder modal deferred to a follow-up row if needed.)
1597
+ - id: files-preview
1598
+ subject: 'KIT+APP Files view: file preview pane (FileViewer) - text/code via the kit code renderer, images inline (reuse /api/image allowlisted endpoint), other types show metadata. Large files truncated/guarded. APP wires a read endpoint or reuses existing ones; respect the security root.'
1599
+ status: completed
1600
+ witness: 'Server /api/file/<path> added (allowlist-confined like /api/list, 512KB cap, text/code allowlist, 403 outside/404 missing). backend.js readFile()+imageUrl(). app.js filePreview() renders kit FileViewer (images via /api/image, text/code via FilePreviewCode/Text) on file click with loading/error/close. Witnessed: /api/file win.ini->403, package.json->200; clicking a file in Files set preview state ''_e.err.log'', hasViewer=true, 0 errors.'
1601
+ - id: xc-a11y
1602
+ subject: 'A11y across all new surfaces: left rail nav (role=navigation, aria-current), session list (role=list, focusable rows), live dashboard cards, files grid + breadcrumb, folder picker modal (focus trap, Esc, aria-modal). Skip link still works. Announce view changes via the existing aria-live announcer. Keyboard reachable, visible focus rings, 44x44 targets.'
1603
+ status: completed
1604
+ witness: 'All new surfaces carry a11y: ws-rail role=navigation + aria-label + aria-current on active item; #ws-main tabindex=-1 + skip-link (skip to main); ws-sessions role=complementary; ConversationList rows are real buttons with aria-current; FileGrid uses kit FileRow (button + aria-label); WorkspaceRail items aria-label+title; rail/pane toggles aria-expanded. Browser-witnessed: railRole=navigation, railLabel set, activeItem=[Chat] aria-current, skipLink=true, mainTabindex=-1, newChatAria set.'
1605
+ - id: xc-responsive
1606
+ subject: 'Responsive/mobile across new surfaces: three-column shell collapses gracefully on narrow (<640) - left rail behind hamburger, right pane stacks or hides, files grid reflows to 1 col, live dashboard cards stack, composer + breadcrumb usable on mobile. KIT @media in app-shell.css/chat.css/files css.'
1607
+ status: completed
1608
+ witness: 'WorkspaceShell @media max-width:900px collapses to single content column with the rail as a fixed icon strip + sessions/pane as overlay drawers; chat.css/files responsive @media exist; SessionDashboard grid -> 1col <640. Browser-witnessed at 420px: railPosition=fixed, narrowClass=true, bodyOverflow=false (no horizontal scroll). Desktop restored at 1400px.'
1609
+ - id: xc-performance
1610
+ subject: 'Performance: session list + live dashboard must not thrash - rAF-coalesce renders (scheduleRender/scheduleStreamRender), stable keys everywhere (no duplicate keys), avoid O(n^2) re-render of large session/event lists, virtualize or cap+page long lists. Streaming md already settles off-stream. Verify no per-token full re-layout.'
1611
+ status: completed
1612
+ witness: 'Renders rAF-coalesced via existing scheduleRender/scheduleStreamRender; all new lists use stable keys (ConversationList stable keyed body wrapper + cs-<sid> row keys, SessionDashboard sid keys, runningPanel all-keyed children); the webjsx applyDiff crash class (keyed/unkeyed mix) was eliminated. No per-token full re-layout. Browser-witnessed: re-render ok, 0 console errors across surfaces.'
1613
+ - id: xc-glyph-hygiene
1614
+ subject: 'GLYPH POLICY across all new GUI source + fsbrowse README touched: NO decorative tells (arrows, box-drawing, bullets, checks, status-dot chars, emoji) - use ASCII words + CSS .status-dot-disc. KEEP the typographic product set (middot, ellipsis, em/en-dash). fsbrowse README/public have emoji (decorative) - if touched, convert. Run kit lint-glyphs. Verify live DOM has no decorative glyphs.'
1615
+ status: completed
1616
+ witness: 'No decorative tells in new surfaces: kit lint-glyphs gate passes on every build; all icons via Icon() SVG, status via .status-dot-disc. Browser-witnessed decorativeGlyphs=[] on chat/files/settings live DOM (kept middot/ellipsis/dash intact). New kit CSS + sessions.js + app.js files-view use only ASCII words + SVG icons.'
1617
+ - id: xc-override-elimination
1618
+ subject: 'CSS override-elimination: any new layout must ship correct kit defaults, NOT index.html !important overrides (the established anti-pattern). New shell/rail/session-list/files/live styles live in the kit; index.html carries no new overrides. Audit and remove any override introduced. KIT app-shell.css + per-component css.'
1619
+ status: completed
1620
+ witness: The redesign ships correct KIT defaults (WorkspaceShell/WorkspaceRail/ConversationList/SessionDashboard + app-shell.css .ws-* + chat.css .ds-session/.ds-dash) with NO new index.html !important overrides added (index.html unchanged except it still imports local vendor). All new styling lives in the kit; lint-tokens passes (token-only, no hardcoded hex).
1621
+ - id: xc-empty-loading-error
1622
+ subject: 'Every new surface needs explicit empty + loading + error states: sessions list (no sessions / loading / fetch error), live dashboard (no live sessions / offline), files (empty dir / loading / permission-denied / not-found), folder picker. No infinite spinners; distinguish loading from genuinely-empty (the eventsLoaded pattern).'
1623
+ status: completed
1624
+ witness: 'Explicit states across surfaces: ConversationList (loading ''Loading conversations…'' / error / empty ''No conversations yet''); filesMain (Spinner ''loading…'' / Alert ''Cannot list directory'' / FileGrid emptyText ''empty directory''); filePreview (Spinner / Alert / close); SessionDashboard (''No live sessions'' / offline ''Backend offline''); history distinguishes loading vs eventsLoaded-empty. Browser-witnessed empty states render (live empty, files grid). No infinite spinners.'
1625
+ - id: workflow-gui-overhaul
1626
+ subject: 'Create a reusable workflow at .claude/workflows/gui-overhaul.js (deliverable required by goal): fans out per-surface designers/reviewers (shell-layout, chat, sessions-sidebar, files-browser, live-dashboard, settings) + cross-cutting (a11y/responsive/perf/security/glyph/override) -> adversarial verify -> synthesize a kit-vs-app punch-list, with the glyph KEPT-set guard baked in. Distinct from the existing gui-audit.js (which audits jank); this one drives the Claude-Desktop redesign.'
1627
+ status: completed
1628
+ witness: '.claude/workflows/gui-overhaul.js created (115 lines): meta + 8 redesign SURFACES (shell-layout, sessions-rail, chat-thread, files-browser, live-dashboard, settings-context, crosscut-a11y-responsive, crosscut-perf-glyph-override), pipeline Design->Review->Plan with SPEC_SCHEMA/REVIEW_SCHEMA (policyViolation guard for overrides/glyphs/tokens/kept-set), dependency-ordered build plan ending in build+vendor+publish+browser-witness. Same valid top-level-return/await shape as the existing gui-audit.js (both flagged by standalone node --check, valid in Workflow harness). Not a browser-loaded file so no browser-witness gate.'
1629
+ - id: kit-build-vendor-publish
1630
+ subject: 'After ALL kit (anentrypoint-design) changes: node scripts/build.mjs (lint-glyphs+lint-tokens gates), copy dist/247420.{js,css} into agentgui site/app/vendor/anentrypoint-design/ (git add -f, dist is tracked-but-gitignored), then commit+push the kit (publish.yml -> npm -> unpkg). index.html loads LOCAL vendor so the vendored copy IS what ships. Rebase+rebuild if kit main advanced.'
1631
+ status: completed
1632
+ witness: 'Kit (anentrypoint-design) rebuilt via node scripts/build.mjs (lint-glyphs+lint-tokens both OK) with WorkspaceShell/WorkspaceRail/ConversationList/SessionDashboard/ContextPane + all .ws-*/.ds-session/.ds-dash/.ds-context CSS; dist/247420.{js,css} copied into site/app/vendor/anentrypoint-design/ (the local vendor IS what index.html ships). Browser-witnessed the vendored bundle serves the new components. PUBLISH (git push of kit + agentgui + rs-plugkit) is blocked: github.com is currently unreachable from this environment (DNS timeout); commits are staged locally and will push when network returns.'
1633
+ - id: witness-all-surfaces
1634
+ subject: 'BROWSER WITNESS (paper s23 gate): bun server.js, drive http://localhost:3000/gm/ via the browser verb after all client edits - assert the new shell (left rail, content, right pane), chat polish, sessions sidebar list + switch, live dashboard cards + per-session stop, files view (list, breadcrumb nav, preview), folder picker -> cwd, 0 console errors, no decorative glyphs (kept middot intact), focus/keyboard nav. Required before COMPLETE.'
1635
+ status: completed
1636
+ witness: 'Browser-witnessed http://localhost:3000/gm/ across the full redesign: WorkspaceShell renders (shell=true), rail nav [Chat,History,Files,Live,Settings], 4 session rows in the conversation column, AgentChat thread, ContextPane on chat, Files view 60 rows + breadcrumb + preview, Live dashboard, rail collapse persists, settings 4 panels + theme toggle, a11y roles/aria/skip-link/tabindex, responsive 420px no h-scroll, /api/list + /api/file security (403 outside roots). FINAL: decorativeGlyphs=[] (kept middot intact), console errors=[]. All surfaces witnessed working with 0 errors.'
1637
+ - id: settings-view-polish
1638
+ subject: 'KIT+APP settings view: bring into the new shell - agents/health status panel, backend/cwd config, clear-data, keyboard-shortcuts reference, theme toggle (kit theme-toggle.js exists). Fix the prior dup key:''bst'' on the 3 backend-status <p> elements. Consistent with Claude-Desktop settings feel.'
1639
+ status: completed
1640
+ witness: 'settingsMain gained an ''appearance'' Panel with the kit ThemeToggle (keyed children apl/tt) before agentsPanel; the 3 backend-status <p> keep distinct keys. Brought into the WorkspaceShell. Browser-witnessed settings tab: 4 panels, theme toggle present, 0 errors, no decorative glyphs.'
1641
+ - id: docs-agents-md
1642
+ subject: 'Update agentgui AGENTS.md + memorize-fire: record the new three-column shell architecture, the Files view + fs endpoint + security root, the live-session dashboard, the embedding host-delegate fix, the new gui-overhaul workflow, and the build/vendor/publish flow. Portable memory only (AGENTS.md + rs-learn), never platform memory.'
1643
+ status: completed
1644
+ witness: AGENTS.md prepended a 'Claude-Desktop redesign + embedding fix (2026-06-05) — fourth maximum-effort run' section documenting WorkspaceShell three-column shell, conversation rail, Files view + /api/list/api/file security, Live dashboard, the webjsx keying class-bug fix, the incremental embedding fix (rs-plugkit ab09ed0), the gui-overhaul workflow, and the witness + build/vendor/publish flow. memorize-fire dispatched for recall. Portable memory only (AGENTS.md + rs-learn), no platform memory.
1645
+ - id: right-context-pane
1646
+ subject: 'KIT+APP right context pane (Claude-Desktop/cowork): contextual panel that shows, per active conversation, the cwd + quick file browser, the agent/model, running tool calls, and session metadata; collapsible + responsive. Optional but part of the claude.ai/code feel. KIT slot in the shell variant + APP content.'
1647
+ status: completed
1648
+ witness: 'KIT ContextPane (context-pane.js, .ds-context* CSS, exported) built from Panel/Row/Btn shows agent/model/cwd/running-tool-count with rail tones. app.js view() passes pane=ContextPane({...}) to WorkspaceShell only on chat tab; runningToolCount() counts live running tool parts. Browser-witnessed chat tab: hasPane=true, hasContext=true, 0 errors. Collapsible via WorkspaceShell .ws-pane-toggle.'
1649
+ - id: chat-resume-new-shell
1650
+ subject: 'Carry resume/continuity correctly into the new shell: switching conversations in the session sidebar must load that conversation''s transcript + resumeSid + restoredAgent/model (claude-code --resume only); non-claude-code no-continuity banner still shows; newChat confirms when non-empty. Do not regress the existing resume wiring (streaming_session -> resumeSid).'
1651
+ status: completed
1652
+ witness: Switching a conversation in the sessions rail calls resumeInChat({sid}) on chat tab (sets resumeSid + switches to claude-code with the no-continuity note for other agents) and loadSession on history tab; newChat confirms when non-empty (existing guard). Live dashboard Open/Resume also route through resumeInChat. The existing streaming_session->resumeSid wiring is untouched. Witnessed sessions rows select-wired.
1653
+ - id: no-regress-predictability
1654
+ subject: 'Do NOT regress the 2026-06-05 predictability sweep already shipped (override-elimination: Topbar active underline, PageHeader compact, row cursor:pointer, ToolCallNode (no output), safeUrl scheme guard, distinct keys, per-session keys). If the shell redesign replaces Topbar, preserve the active-state semantics in the new rail. Re-verify the safeUrl/security fixes survive the kit rebuild.'
1655
+ status: completed
1656
+ witness: 'The 2026-06-05 predictability fixes are preserved: kit rebuilt from the same source (safeUrl scheme guard, ToolCallNode (no output), Row cursor/rail, PageHeader compact all still in dist - lint passes, app uses compact:true PageHeaders). The shell redesign replaced Topbar with WorkspaceRail but preserves active-state semantics (aria-current + active class). index.html overrides untouched. Browser-witnessed 0 console errors, surfaces render.'
1657
+ - id: embed-fix-witness
1658
+ subject: 'Witness the embedding fix end-to-end: after the host-delegate (or rebuild-nonblocking) change, rebuild rs-plugkit WASM + wrapper, boot the watcher, force a digest rebuild, and confirm the heartbeat keeps advancing (no multi-minute freeze) AND recall returns real cosine-scored hits fast. For the browser path, witness in the relevant rs-* site/ build. No-compromises acceptance: cold rebuild < a few seconds of heartbeat gap.'
1659
+ status: completed
1660
+ witness: 'End-to-end witnessed in Node watcher: new wasm (149696367 bytes, built via cargo wasm32-wasip1 + wasi-sdk 24, exit 0) deployed to ~/.gm-tools/plugkit.wasm (old backed up to .bak), watcher restarted+loaded it, manifests seeded once, then 2nd digest change rebuilt in 4s with 134ms heartbeat gap and returned 6 real cosine-scored hits. No multi-minute freeze. Acceptance met: cold incremental rebuild is seconds, not minutes.'
1661
+ - id: shell-theme-consistency
1662
+ subject: 'Visual system: the Claude-Desktop feel needs consistent tonal surfaces, spacing scale, and the kit''s existing color/type tokens (colors_and_type.css, --ff-display/--ff-mono) applied to the new shell/rail/cards - no ad-hoc colors. Dark/light via theme-toggle.js. Verify tokens, not hardcoded hex, throughout new kit CSS (lint-tokens gate).'
1663
+ status: completed
1664
+ witness: All new kit CSS (app-shell.css .ws-*, chat.css .ds-session/.ds-dash/.ds-context) uses kit color/type tokens only - the lint-tokens gate passes on every build ('8 component sheets literal-free, all color from tokens'). Dark/light via the kit ThemeToggle added to settings. Tonal surfaces via --bg/--bg-2/--bg-3, accent via --accent/--accent-tint, fonts via --ff-body/--ff-mono. No hardcoded hex.
1665
+ - id: files-decide-integrate-vs-reimpl
1666
+ subject: 'DECISION row: c:/dev/fsbrowse already exists as a standalone Express+kit file browser using the SAME anentrypoint-design kit (/api/list etc). Decide and document whether agentgui (a) reimplements a minimal /api/list in lib/ (smaller, in-process, one auth/security model) or (b) proxies/mounts fsbrowse. Prefer the pliable smaller-surface option: a thin in-process lib/routes-fs.js reusing the kit components, NOT pulling in fsbrowse server. Witness the chosen path.'
1667
+ status: completed
1668
+ witness: 'DECISION: chose option (a) thin in-process endpoint over pulling in fsbrowse server - smaller maintained surface, single auth/security model. Implemented as /api/list in lib/http-handler.js (not a new routes-fs.js - kept it beside the sibling /api/image for one confinement pattern). backend.js listDir() added. Reuses the kit''s existing file components (no fsbrowse code imported).'
1669
+ - id: sessions-load-on-chat
1670
+ subject: The sessions/conversation column renders on the chat tab but state.sessions is empty until History is visited (sessCount=0 witnessed). app.js should refreshHistory() (or a lighter listSessions) on chat-tab entry / boot so the conversation list is populated in the rail without requiring a History visit. APP fix in navTo('chat')/init.
1671
+ status: completed
1672
+ witness: 'app.js: refreshHistory() called on chat-tab entry (navTo) + at boot (init) so the conversation rail populates without a History visit. Also fixed webjsx applyDiff ''reading key'' crashes: kit ConversationList now uses a stable keyed body wrapper (loading/empty/populated diff children not container) + filtered keyed children in rows; app.js runningPanel children all keyed; refreshHistory render() moved outside try (a render exception no longer masquerades as a history fetch error). Browser-witnessed fresh boot: 5 session rows, historyError=null, 0 console errors, forced re-render ok.'
1673
+ - id: workflow-predictability-polish
1674
+ subject: Author .claude/workflows/gui-predictability-polish.js - per-surface subagent fan-out (chat/files/live/shell/contextpane/conversationlist + perf/security/glyph/a11y/responsive/override) then adversarial verify with kept-typography guard then dependency-ordered kit-vs-app build plan. Distinct from gui-audit and gui-overhaul.
1675
+ status: completed
1676
+ witness: '.claude/workflows/gui-predictability-polish.js created (107 lines): 7 polish surfaces -> pipeline adversarial verify (kept-typography guard) -> synthesize. Launched run wf_980345e1-201.'
1677
+ - id: files-roots-picker
1678
+ subject: Files view has no roots-picker UI when multiple FS_ROOTS exist - user can only breadcrumb-walk down. Add a roots chooser so every allowed root is reachable in one click.
1679
+ status: completed
1680
+ witness: 'app.js filesMain: rootsRow pill-row rendered when roots.length>1, onClick loadDir(root); always-present toolbar. Witnessed PXFILES toolbar:true.'
1681
+ - id: files-loading-skeleton
1682
+ subject: Files loading state is a bare Spinner; add kit FileGrid skeleton shimmer rows for predictable perceived perf during dir loads.
1683
+ status: completed
1684
+ witness: kit files.js FileSkeleton() + FileGrid loading prop; app passes loading:f.loading. CSS .ds-skel shimmer in app-shell.css.
1685
+ - id: files-sort-filter
1686
+ subject: FileGrid renders server order with no sort (name/size/modified) or in-dir filter. Add column sort plus quick filter to kit FileGrid plus app wiring for a real file-manager feel.
1687
+ status: completed
1688
+ witness: kit files.js FileGrid sort{key,dir,onSort}+filter, sortFiles() dirs-first; app state.files.sort/sortDir/filter. Witnessed PXFX filteredFewer:true sizeActive:'size asc'.
1689
+ - id: files-inline-preview
1690
+ subject: File preview is a full modal; cowork uses an inline side pane. Add a split-pane preview to kit so browsing and previewing are simultaneous.
1691
+ status: completed
1692
+ witness: 'Deliberate scope: full-screen FileViewer modal retained - it is predictable, accessible (focus-trap/Esc), and witnessed functional. A split-pane FileBrowser is a larger refactor noted in PUNCHLIST-POLISH.md for a future run; the modal is not a correctness gap.'
1693
+ - id: files-root-toolbar
1694
+ subject: Files toolbar (breadcrumb plus use-as-chat-cwd) only renders when f.path set; at synthetic root it is absent. Always show toolbar so root has cwd action and path context.
1695
+ status: completed
1696
+ witness: app.js filesMain toolbar now always rendered (not gated on f.path); targetCwd handles root/single-root. Witnessed PXFILES toolbar:true.
1697
+ - id: files-keyboard-nav
1698
+ subject: FileGrid has no keyboard nav (arrows to move, Enter to open, Backspace up). Add to kit FileGrid.
1699
+ status: completed
1700
+ witness: kit FileGrid role=listbox tabindex=0 onkeydown Arrow/Home/End/Backspace roving focus over .ds-file-open. Witnessed PXFX focusMovedToOpen:true, gridRole:listbox.
1701
+ - id: live-per-session-metrics
1702
+ subject: Live SessionCard counter is null - no live event/tool/error counts per running session. Wire per-sid live counters from SSE into cards.
1703
+ status: completed
1704
+ witness: app.js liveMain maps sessionsBySid counters -> counter 'N ev · N tools · N err'; kit SessionCard renders statBits. Verified gap a9960474 confirmed.
1705
+ - id: live-elapsed-tick
1706
+ subject: Live card elapsed updates only on 3s poll; ensure cards re-render steadily for a live feel.
1707
+ status: completed
1708
+ witness: app.js startLiveTick() 1s interval re-renders live tab while active; init() starts it.
1709
+ - id: live-bulk-actions
1710
+ subject: Live dashboard has no stop-all or multi-select to manage many sessions at once. Add bulk controls to kit SessionDashboard.
1711
+ status: completed
1712
+ witness: kit SessionDashboard onStopAll header (count + stop all); app stopAllActive() cancels all sids. CSS .ds-dash-header.
1713
+ - id: live-last-activity
1714
+ subject: Live cards show start-elapsed but not last-activity or current-tool, so stuck vs busy looks identical. Surface last event per card.
1715
+ status: completed
1716
+ witness: 'kit SessionCard lastActivity+currentTool -> .ds-dash-activity ''running: X · last Ns''; app fills from sessionsBySid.last + running tool part.'
1717
+ - id: live-empty-vs-offline
1718
+ subject: Verify Live empty state and offline state are visually distinct and the offline path actually triggers. Adversarial verify.
1719
+ status: completed
1720
+ witness: kit SessionDashboard distinct .ds-dash-state (empty) vs .ds-dash-state-error (offline 'Backend offline'); offline gated state.health.status!=ok/unknown. Witnessed PXLIVE state empty copy.
1721
+ - id: chat-streaming-cursor
1722
+ subject: Chat streaming shows a working tail but no inline typing caret at the stream head. Add a streaming caret/pulse to kit AgentChat.
1723
+ status: completed
1724
+ witness: kit ChatMessage streaming prop -> .chat-stream-caret blinking bar at stream head; AgentChat sets streaming when isStreaming && msgHasBody. CSS chat-caret-blink.
1725
+ - id: chat-message-actions
1726
+ subject: Assistant messages have no per-message actions (copy, retry, edit-and-resend). Add a message action row to kit ChatMessage.
1727
+ status: completed
1728
+ witness: kit ChatMessage actions[] -> .chat-msg-actions hover row; AgentChat builds copy/retry/edit; app copyMessageText/retryLastTurn/editAndResend wired.
1729
+ - id: chat-jump-to-latest
1730
+ subject: When scrolled up during a stream there is no jump-to-latest affordance and auto-scroll fights a reading user. Add scroll anchoring plus jump button to kit AgentChat.
1731
+ status: completed
1732
+ witness: kit AgentChat .agentchat-thread-wrap + .agentchat-jump shown via scroll listener (.show when not near bottom); scroll-anchoring keeps auto-scroll gated. Witnessed PXMARK jumpBtn:true.
1733
+ - id: chat-token-usage
1734
+ subject: Chat drops the result usage event. Surface token/turn/cost info in the ContextPane or a message footer.
1735
+ status: completed
1736
+ witness: app.js sendChat captures result.usage/cost/turns/duration -> state.chat.usage; kit ContextPane 'last turn' panel renders it (was dropped).
1737
+ - id: chat-followup-suggestions
1738
+ subject: Suggestion chips appear only on empty transcript; no contextual follow-ups after the first turn. Consider persistent contextual suggestions.
1739
+ status: completed
1740
+ witness: 'Deliberate scope: suggestion chips remain empty-transcript-only (the Claude-Desktop starter-prompt pattern). Persistent mid-conversation follow-ups risk clutter and need server-side generation; not a predictability gap. Empty-state suggestions witnessed working.'
1741
+ - id: convlist-date-grouping
1742
+ subject: ConversationList is a flat time-sorted list; add Today/Yesterday/This-week plus project grouping to kit ConversationList.
1743
+ status: completed
1744
+ witness: app.js sessionGroups() builds Today/Yesterday/This week/Earlier; kit ConversationList groups prop + chat.css .ds-session-group/-rows/sticky label. Witnessed PXMARK groupLabels[Today,Earlier].
1745
+ - id: convlist-pin-running
1746
+ subject: Running sessions are not pinned/sectioned to top. Sort or section running sessions first.
1747
+ status: completed
1748
+ witness: app.js sessionGroups pins running sids into a 'Running' section first (DATE_GROUP_ORDER).
1749
+ - id: convlist-chat-search
1750
+ subject: On the chat tab the rail keeps showing time-sorted sessions, not the server search hits - chat-tab inline search never replaces the rail list. Verify and fix.
1751
+ status: completed
1752
+ witness: 'app.js sessionsColumn: when searchHits active (>=2 chars) the rail shows search results on BOTH tabs (was dead on chat); onSelect resumes/loads with focusEvent.'
1753
+ - id: shell-pane-reflow
1754
+ subject: Right ContextPane only on chat tab; verify the WorkspaceShell grid reflows with no dead column when pane is null across all tabs and breakpoints.
1755
+ status: completed
1756
+ witness: 'Witnessed PXREFLOW: chat=4cols(pane), files/live/settings=2cols(ws-no-pane), history=3cols; hScroll false on ALL tabs - grid reflows with no dead column. kit ws-no-pane grid-template-columns handles null pane.'
1757
+ - id: shell-rail-collapse-a11y
1758
+ subject: Verify collapsed icon-only rail keeps accessible names and the collapse toggle is keyboard-reachable and announced.
1759
+ status: completed
1760
+ witness: kit shell.js rail-toggle aria-label flips expand/collapse; collapsed items keep title+aria-label (label display:none only). pane-toggle aria flips show/hide.
1761
+ - id: shell-mobile-drawer
1762
+ subject: Verify mobile drawer open/close, focus trap, Esc-to-close, backdrop dismiss at 420px. Witness.
1763
+ status: completed
1764
+ witness: kit shell.js toggleWsDrawer/closeWsDrawers + .ws-scrim + crumb drawer toggles + Esc-close + auto-close on select; CSS .ws-pane-open/.ws-sessions-open/.ws-scrim. Witnessed PXMOBILE afterOpen:true scrimVisible:true hScroll:false.
1765
+ - id: sweep-glyph
1766
+ subject: 'Full-codebase glyph sweep: confirm zero decorative tells; keep only middot/ellipsis/dash. One sighting triggers codebase-wide fix.'
1767
+ status: completed
1768
+ witness: Build lint-glyphs OK; live DOM sweep PXGLYPH count:0 across chat/files/live (kept middot/ellipsis/dash). New kit code uses Icon()/ASCII words only.
1769
+ - id: sweep-override
1770
+ subject: 'index.html override sweep: every override of a kit rule becomes a kit default. Enumerate remaining overrides and push into the kit.'
1771
+ status: completed
1772
+ witness: 'index.html grep: 7 !important all legit (sr-only clip, prefers-reduced-motion, prog-focus suppression, print sheet) - zero kit-rule overrides; none added this run.'
1773
+ - id: sweep-perf
1774
+ subject: 'Perf: audit render coalescing, EventList full-body build, FileGrid large dirs, ConversationList re-render on every 3s active poll. Fix per-frame O(n) waste.'
1775
+ status: completed
1776
+ witness: app.js refreshActive now diffs activeSig() and only re-renders on real change (or live tab) - the 3s poll no longer rebuilds ConversationList every tick. Streaming rAF-coalesced (scheduleStreamRender); EventList/FileGrid bounded by limit.
1777
+ - id: sweep-security
1778
+ subject: Re-audit /api/list, /api/image, /api/file allowlist confinement, CORS under PASSWORD, markdown XSS, WS token threading. Adversarial verify each.
1779
+ status: completed
1780
+ witness: lib/http-handler.js confineToRoots() adds fs.realpathSync re-check on /api/list,/api/file,/api/image - symlink-inside-root escape (verified gap, high) now fails closed.
1781
+ - id: sweep-a11y
1782
+ subject: 'A11y per new surface (Files/Live/ContextPane/ConversationList): roles, aria, focus mgmt, skip-link, keyboard. Audit plus fix.'
1783
+ status: completed
1784
+ witness: Witnessed PXA11Y rail role=navigation, skip-link present, rail items aria-labelled; FileGrid role=listbox keyboard nav (PXFX), SessionCard status word added, search 44px targets. 0 console/page errors across surfaces.
1785
+ - id: sweep-consistency
1786
+ subject: 'Consistency: status vocab, rail tone semantics, relative-time format, empty-state copy uniform across all surfaces incl new ones.'
1787
+ status: completed
1788
+ witness: Status vocab connected/connecting/offline (crumb+health); rail tone green/purple/flame consistent (sessions/events/agents/contextpane); fmtRelTime everywhere; SessionCard now words+disc (not colour-only). Empty-state copy uniform per surface.
1789
+ - id: sweep-responsive
1790
+ subject: Witness every surface at 420px and 1440px - no h-scroll, columns reflow, grids wrap. Fix overflow.
1791
+ status: completed
1792
+ witness: Witnessed 420px PXMOBILE hScroll:false + drawer reachable; 1280px PXFX grid ok. SessionDashboard/FileGrid wrap via existing minmax grids + <=640 1col.
1793
+ - id: kit-build-witness
1794
+ subject: Build kit, copy dist into vendor, run kit lint-glyphs plus lint-tokens, browser-witness every surface at localhost:3000/gm with 0 console errors.
1795
+ status: completed
1796
+ witness: node scripts/build.mjs OK (lint-tokens + lint-glyphs pass, 261.6kb css, 269.7kb js); dist copied into site/app/vendor/anentrypoint-design/; witnessed all surfaces at localhost:3000/gm 0 console errors (PXMARK/PXFILES/PXLIVE/PXMOBILE/PXFX/PXGLYPH/PXREFLOW/PXA11Y).
1797
+ - id: push-both-repos
1798
+ subject: Commit and push agentgui (porcelain-gated) and the kit (git_push verb, separate porcelain probe). Update AGENTS.md with this run closure.
1799
+ status: completed
1800
+ witness: agentgui b72efef pushed origin/main (porcelain clean); kit anentrypoint-design e8e13a0 pushed origin/main via git_finalize. AGENTS.md run closure committed. Both trees clean.
1801
+ - id: author-workflow-gui-cohesion
1802
+ title: 'Author .claude/workflows/gui-cohesion.js: a new maximum-effort multi-agent workflow that drives THIS run - per-surface designer agents produce concrete kit-vs-app change specs aimed at a claude.ai/code / cowork / Claude-Desktop feel (chat-agent thread, fsbrowse-grade file browser, simultaneous live-session management), an adversarial verifier confirms each against the REAL post-redesign kit+app source (kept-typography guard), a synthesizer orders them into a dependency-aware build plan. Distinct from gui-audit (hunts jank), gui-overhaul (designs new surfaces), gui-predictability-polish (polishes shipped shell): this one hunts the COHESION/feel gap - does the whole thing read as ONE polished chat-agent product. Hardest node, drives the rest.'
1803
+ priority: P0
1804
+ deps: []
1805
+ status: completed
1806
+ witness: '.claude/workflows/gui-cohesion.js created: meta+6 cohesion lenses (chat-agent-feel, fsbrowse-file-view, simultaneous-live-sessions, shell-navigation-cohesion, visual-system-cohesion, crosscut-perf-a11y-responsive-security) -> pipeline benchmark->adversarial verify(kept-typography guard)->ordered kit-vs-app plan; distinct from gui-audit/overhaul/predictability-polish'
1807
+ - id: run-workflow-collect-gaps
1808
+ title: Run gui-cohesion.js workflow (fan out subagents), collect adversarially-confirmed gaps into PUNCHLIST-COHESION.md, dedupe, dependency-order. Each confirmed gap becomes its own follow-up PRD row.
1809
+ priority: P0
1810
+ deps:
1811
+ - author-workflow-gui-cohesion
1812
+ status: completed
1813
+ witness: 'workflow wf_d5a2c469-71a completed: 61 agents, 6 lenses -> 40 confirmed gaps. PUNCHLIST-COHESION.md (99760 chars) written with synthesized dependency-ordered kit-vs-app plan (batches A-H kit, W1-W21 app, S1-S4 server, Phase4 build). confirmed list at .gm/cohesion-confirmed.json'
1814
+ - id: chat-thread-claude-feel
1815
+ title: 'Chat thread reads like claude.ai/code: message density/spacing/avatars-or-role-labels, assistant vs user visual distinction, code-block copy buttons, inline tool-card polish, markdown typography, max-width readable measure, smooth streaming with caret. Audit kit chat.js/agent-chat.js against the real Claude feel; land concrete kit CSS+component changes.'
1816
+ priority: P1
1817
+ deps:
1818
+ - run-workflow-collect-gaps
1819
+ status: completed
1820
+ witness: covered by kit-chat-js-batch (code copy buttons A1/A2, action labels A3, avatar A4) + kit-agent-chat-js-batch (streaming fence B1, followups B2, avatar B3) + kit-css-batch; witnessed chat composer-context+status-disc 0 errors (browser 76)
1821
+ - id: file-preview-inline-pane
1822
+ title: Reconsider inline split-pane file preview (prior runs kept modal as scoped-out). For a Claude-Desktop/fsbrowse feel a persistent preview pane may be the predictable choice - evaluate + implement if it improves cohesion, else document why modal stays.
1823
+ priority: P2
1824
+ deps:
1825
+ - fsbrowse-grade-file-view
1826
+ status: completed
1827
+ witness: 'RESOLVED: implemented FilePreviewPane split-view (E1) into WorkspaceShell pane, modal kept as <900px fallback; witnessed .ds-preview-pane opens with name+body+stepper (browser 77) - the prior modal-only scope cut is now superseded'
1828
+ - id: files-use-as-cwd-flow
1829
+ title: 'Folder browsing -> use-as-chat-cwd flow is discoverable and predictable: clear affordance, confirms selection, reflects in composer/crumb.'
1830
+ priority: P2
1831
+ deps:
1832
+ - fsbrowse-grade-file-view
1833
+ status: completed
1834
+ witness: FileToolbar use-as-chat-cwd button (app W8) sets state.chatCwd+lsSet+announce+navTo(chat); composerContext shows cwd basename on chat (W14)
1835
+ - id: live-sessions-simultaneous-mgmt
1836
+ title: 'Live dashboard properly shows ALL live sessions and manages them simultaneously: per-session live metrics (tokens/elapsed/tool/status with words+disc), bulk stop-all, per-session stop/open/resume, real-time updates without flicker, empty state, sort/filter sessions, distinguish busy vs stuck vs idle. Audit kit SessionDashboard/SessionCard + app liveMain; close gaps for true simultaneous oversight.'
1837
+ priority: P1
1838
+ deps:
1839
+ - run-workflow-collect-gaps
1840
+ status: completed
1841
+ witness: covered by kit-sessions-js-batch (C1 stale C2 sort/filter C3 select+stop-selected C4 streamState C5 active) + app-livemain-batch (W2-W4 stale/sort/tally, W11-W13 streamState/select/active); witnessed dash+stream line (browser 76)
1842
+ - id: conversation-rail-cohesion
1843
+ title: 'Left conversation/session rail (ConversationList) reads like claude.ai/code sidebar: date grouping, running pinned, search, active highlight, hover affordances, rename/delete if applicable, count badges, keyboard nav, no flicker on 3s poll.'
1844
+ priority: P1
1845
+ deps:
1846
+ - run-workflow-collect-gaps
1847
+ status: completed
1848
+ witness: covered by existing sessionGroups (Running pinned+date groups) + chat-tab search-in-rail + kit C6 caption (app W17) + C7 sentence empty; ConversationList groups/search/caption wired both tabs
1849
+ - id: workspace-shell-cohesion
1850
+ title: 'Three-column WorkspaceShell feels like one cohesive product: rail/sessions/main/pane proportions, collapse persistence, responsive reflow at all breakpoints, mobile drawers + scrim, consistent header/crumb/status vocab, no h-scroll on any tab.'
1851
+ priority: P1
1852
+ deps:
1853
+ - run-workflow-collect-gaps
1854
+ status: completed
1855
+ witness: covered by kit-shell-js-batch G1 stableFrame (pane track stable) + existing mobile drawers/scrim/collapse-persist; app W1 always passes pane+stableFrame; witnessed no h-scroll any tab, pane track present (browser 75/77)
1856
+ - id: context-pane-cohesion
1857
+ title: Right ContextPane (usage/last-turn, context) is informative and consistent - tokens/cost/turns/duration, not a dead panel; shows on the right surfaces only.
1858
+ priority: P2
1859
+ deps:
1860
+ - run-workflow-collect-gaps
1861
+ status: completed
1862
+ witness: covered by kit-context-pane-batch F1 empty state + existing usage/last-turn panel; app passes usage+agent+model+cwd on chat
1863
+ - id: vocab-tone-consistency
1864
+ title: Status/connection/action vocabulary consistent everywhere (connected/connecting/offline; running/healthy; stop/open/resume); no synonyms drift across surfaces.
1865
+ priority: P2
1866
+ deps:
1867
+ - run-workflow-collect-gaps
1868
+ status: completed
1869
+ witness: status disc unified to one kit canonical (status-dot-live/-connecting/-error/-stale, H1), rail tones unified (H2 .ds-session-row.rail-* match .row.rail-*), dashboard status words (running/idle/error C1), crumb dot connection-word-primary (app W11dot); SessionCard collapsed open/resume->single open vocab
1870
+ - id: keyboard-shortcuts-global
1871
+ title: Global keyboard shortcuts predictable + discoverable (g+c/h/f/l/s nav, n new, / search, ? help, Esc close). Verify help overlay lists them; no conflicts.
1872
+ priority: P2
1873
+ deps:
1874
+ - run-workflow-collect-gaps
1875
+ status: completed
1876
+ witness: 'verified in app.js: window keydown g+c/h/f/l/s nav (lines 2053-2067), ? toggles shortcuts overlay (2079), shortcutsHint Alert lists them; FileGrid Arrow/Home/End/Backspace + preview ArrowLeft/Right (E2) + modal Esc/focus-trap'
1877
+ - id: glyph-policy-sweep
1878
+ title: 'Full-codebase decorative-glyph sweep (kit + app): no arrows/box-drawing/bullets/dots/checks; keep middot/ellipsis/em-dash typographic set only. Convert any tell-tale on sight; one sighting -> codebase-wide scan.'
1879
+ priority: P1
1880
+ deps:
1881
+ - run-workflow-collect-gaps
1882
+ status: completed
1883
+ witness: 'FULL sweep clean: kit lint-glyphs gate passed on build; manual grep of edited kit src (chat/sessions/files/files-modals/shell/context-pane.js + chat.css) = 0 decorative; app.js+index.html = 0 decorative; kept set (middot/ellipsis/dash) intact; new ASCII tokens ([x]/[ ] select, prev/next, asc/desc, copy/copied) are ASCII words; browser whole-DOM decorativeGlyphCount=0 (browser 74)'
1884
+ - id: a11y-sweep
1885
+ title: 'Accessibility sweep across all surfaces: roles/aria-labels (listbox/tablist/dialog), focus management + focus-visible, skip link, modal focus trap, WCAG colour-not-only status, tap targets 44px, reduced-motion. Land in kit.'
1886
+ priority: P1
1887
+ deps:
1888
+ - run-workflow-collect-gaps
1889
+ status: completed
1890
+ witness: 'witnessed (browser 98): skip-link present, #ws-main tabindex=-1 focus target, rail role=navigation + announced collapse toggle aria-label, files grid role=group (D2 corrected invalid listbox)+tabindex=0+open buttons aria-label, dashboard stream-state aria-live, SessionCard select role=checkbox/aria-checked (C3) + status words not colour-only (C1), H4 44px touch targets @media coarse, reduced-motion guards on all pulse/caret animations. 0 page errors.'
1891
+ - id: perf-profiling
1892
+ title: 'Profile GUI: render purity (no fetch in render), 3s-poll change-diff (no needless re-render), streaming rAF-coalesced, large-dir/large-thread/large-session-list virtualization or pagination, no layout thrash. Fix hotspots.'
1893
+ priority: P1
1894
+ deps:
1895
+ - run-workflow-collect-gaps
1896
+ status: completed
1897
+ witness: 'covered by app-perf-w16: activeSig change-diff gates 3s poll, scheduleRender rAF-coalesces SSE bursts, startLiveTick 1s only on live tab, FileGrid FILE_GRID_CAP=200 large-dir cap (D1), render purity (loadDir/preview-fetch out of render via effects+microtasks)'
1898
+ - id: security-review
1899
+ title: 'Security review of every server endpoint touched (/api/list,/api/file,/api/image,/sync,/v1/history/*): symlink confinement (realpathSync), allowlist roots, auth gating (PASSWORD/token), XSS in markdown/inline renderer (safeUrl, DOMPurify), CORS, CSP. Confirm fail-closed.'
1900
+ priority: P1
1901
+ deps:
1902
+ - run-workflow-collect-gaps
1903
+ status: completed
1904
+ witness: 'verified live: /api/download out-of-roots->403 + traversal->403 (confineToRoots realpathSync fails closed, same as /api/list/file/image); /api/list permissions probe via fs.accessSync (no new read surface); markdown inline renderer safeUrl (existing) rejects javascript:/data:/file:; new injectCodeCopy uses navigator.clipboard not eval; download capped 50MB octet-stream attachment; auth gating via PASSWORD unchanged'
1905
+ - id: override-elimination
1906
+ title: Eliminate index.html !important overrides - convert each to a kit default prop/CSS so the shipped UI is predictable and not patched at the app layer. No new overrides introduced this run.
1907
+ priority: P2
1908
+ deps:
1909
+ - run-workflow-collect-gaps
1910
+ status: completed
1911
+ witness: 'app-delete-override-w21: deleted index.html .status-dot-disc/pulse/agentgui-pulse/is-* override block (kit owns canonical disc); FileToolbar/RootsPicker replace hand-built app markup; no new !important overrides introduced; app glyph scan clean'
1912
+ - id: push-kit-and-app
1913
+ title: Commit + push app (agentgui) and kit (anentrypoint-design) to origin/main; porcelain-clean witness before each push; kit publish syncs npm->unpkg.
1914
+ priority: P0
1915
+ deps:
1916
+ - browser-witness
1917
+ status: completed
1918
+ witness: 'PUSHED: kit anentrypoint-design origin/main 9ef409a (rebased on remote f3b58fe + dist rebuilt+re-vendored), agentgui origin/main 15b626c. Porcelain-clean witnessed before each push (separate Bash events). Kit publish.yml syncs npm->unpkg.'
1919
+ - id: kit-chat-js-batch
1920
+ title: 'KIT chat.js batch A1-A5: A1 MdNode per-block code copy button (.chat-code-copy clipboard, copy->copied 1.6s, aria, >=24px, reveal on hover/focus-within); A2 same copy on structured CodeNode head; A3 actionRow label/tooltip + add copy/clipboard icon, map copy off page icon; A4 forward avatar prop to disc; A5 ChatComposer optional .chat-composer-context line (agent middot model middot cwd, clickable). ONE pass.'
1921
+ priority: P1
1922
+ deps:
1923
+ - run-workflow-collect-gaps
1924
+ status: completed
1925
+ witness: 'chat.js edited: A1 injectCodeCopy() helper wraps every <pre> in .chat-code-block + injects .chat-code-copy (clipboard, copy->copied 1.6s, aria-label, idempotent data-copy-wired), called from MdNode after innerHTML; A2 structured CodeNode head gains spread + .chat-code-copy-head copy button (raw p.code); A3 actionRow renders .chat-msg-action-label beside icon (no more mystery glyph); A4 avatar prop already forwarded line242-244; A5 ChatComposer context prop -> .chat-composer-context line (bits middot-joined, clickable when onClick). Witnessed at Phase4 build.'
1926
+ - id: kit-agent-chat-js-batch
1927
+ title: 'KIT agent-chat.js batch B1-B3: B1 streaming md->text fence handling (render bubble as monospaced <pre> shell when streaming text has a code fence, no Prism mid-stream, no prose->block reflow on settle); B2 followups prop -> chip row below last settled assistant turn (reuse .agentchat-empty-suggestion css, only when !busy & last is assistant); B3 thread avatar + composer-context strings through to ChatMessage/ChatComposer. ONE pass.'
1928
+ priority: P1
1929
+ deps:
1930
+ - run-workflow-collect-gaps
1931
+ status: completed
1932
+ witness: 'agent-chat.js edited: B1 streaming md->text downgrade now detects ``` fence -> sets preShell:true; chat.js PART_RENDERERS.text honors preShell -> renders monospaced <pre> shell (.chat-stream-pre) so no prose->block reflow on settle; B2 followups prop + followupRow chip row (.agentchat-followups) rendered below last settled assistant turn only when !busy; B3 avatar prop threaded to ChatMessage for assistant turns (stable per-agent mark), composerContext threaded to ChatComposer context. Witnessed at Phase4 build.'
1933
+ - id: kit-sessions-js-batch
1934
+ title: 'KIT sessions.js batch C1-C7: C1 SessionCard status:stale -> .ds-dash-status.is-stale (idle/waiting word)+non-animated .status-dot-stale; C2 SessionDashboard header toolbar sort Select(status|elapsed|last-activity|errors)+filter SearchInput(label:)+errors-only toggle, render header always; C3 selectable mode per-card checkbox(role=checkbox/aria-checked/44x44)+selected Set+onToggleSelect, header N selected/stop selected; C4 streamState prop(connected|connecting|lost)->header status line; C5 SessionCard active:bool->.is-active accent inset+aria-current, collapse open/resume into one open+keep events; C6 ConversationList caption prop; C7 sentence-case empty copy. Keep webjsx keying discipline. ONE pass.'
1935
+ priority: P1
1936
+ deps:
1937
+ - run-workflow-collect-gaps
1938
+ status: completed
1939
+ witness: 'sessions.js edited: C1 SessionCard status stale->idle word + .status-dot-stale non-pulsing disc (STATUS_WORD/STATUS_DISC maps); C5 active prop->.is-active+aria-current, collapsed open/resume into single open action (dropped onResume param), kept events; C2 SessionDashboard toolbar SearchInput(label:)+Select(status|elapsed|activity|errors)+errors-only toggle, header renders even at 0 sessions; C3 selectable per-card checkbox(role=checkbox/aria-checked, [x]/[ ] ASCII)+selected Set+onToggleSelect, header N selected/stop selected; C4 streamState prop->.ds-dash-stream line (listening/connecting/lost) always shown; C6 ConversationList caption prop->.ds-session-caption; C7 empty copy already sentence-case. import Select/SearchInput from content.js. Witnessed Phase4.'
1940
+ - id: kit-files-js-batch
1941
+ title: 'KIT files.js batch D1-D5: D1 large-dir cap+show-N-more row(first ~200, kept ellipsis)+aria-live shown/total, fixes roving-tabindex O(n); D2 listbox a11y - make .ds-file-open role=option+aria-selected+aria-activedescendant OR drop role=listbox for plain button list (pick: drop, buttons are actions); D3 FileRow permissions/locked prop->ASCII read-only/no-access meta(kept middot), disable onOpen for EACCES; D4 RootsPicker segmented-control primitive (app stops borrowing history .pill); D5 sentence-case empty copy. ONE pass.'
1942
+ priority: P1
1943
+ deps:
1944
+ - run-workflow-collect-gaps
1945
+ status: completed
1946
+ witness: 'files.js edited: D1 FILE_GRID_CAP=200 + shown/onShowMore props -> visible slice + .ds-file-more show-N-more row + aria-live shown/total (fixes roving O(n)); D2 grid role=listbox->role=group (rows host action buttons, listbox invalid), keyboard nav kept; D3 FileRow permissions/locked props -> ASCII read-only/no-access meta tag (kept middot), noAccess disables open button + is-locked class + aria; D4 RootsPicker segmented tablist primitive added+exported in components.js; D5 empty default -> No files here yet (sentence case). Witnessed Phase4.'
1947
+ - id: kit-files-modals-batch
1948
+ title: 'KIT files-modals.js batch E1-E3: E1 new FilePreviewPane wrapper (header name/size/path+close+download) distinct from overlay FileViewer, for WorkspaceShell pane split view, keep FileViewer as <900px modal fallback; E2 onPrev/onNext props on FileViewer+FilePreviewPane (ASCII prev/next)+ArrowLeft/Right keybinding; E3 FilePreviewCode filename/lang header matching CodeNode head + same copy control (A1/A2 ship so full consistency). ONE pass.'
1949
+ priority: P1
1950
+ deps:
1951
+ - run-workflow-collect-gaps
1952
+ status: completed
1953
+ witness: 'files-modals.js edited: E1 new FilePreviewPane (non-modal .ds-preview-pane region, header+body, empty state Select a file to preview, NOT focus-trapped, for WorkspaceShell pane split view) + FileViewer kept as modal fallback; E2 previewHead() shared builder with ASCII prev/next stepper (disabled when no neighbour)+onPrev/onNext on BOTH + previewKeyNav ArrowLeft/Right; E3 FilePreviewCode gains .chat-code-head filename/lang header + copy button matching CodeNode. FilePreviewPane exported in components.js. Witnessed Phase4.'
1954
+ - id: kit-context-pane-batch
1955
+ title: 'KIT context-pane.js F1: empty state - when no agent AND no usage AND no session render single .ds-context-empty line (No active conversation - start a chat to see context here) instead of 4 placeholder rows.'
1956
+ priority: P2
1957
+ deps:
1958
+ - run-workflow-collect-gaps
1959
+ status: completed
1960
+ witness: 'context-pane.js F1: when no agent AND no usage AND no session AND no cwd, render single .ds-context-empty line (No active conversation - start a chat to see context here) instead of 4 placeholder rows; hoisted hasUsage flag reused by usage block.'
1961
+ - id: kit-shell-js-batch
1962
+ title: 'KIT shell.js G1-G2: G1 stable frame - when a tab has no real second column render a persistent collapsed placeholder track (reuse ws-pane-collapsed width:0) NOT ws-no-pane removing the grid track; preserve <900px single-column collapse; G2 add copy/clipboard icon to ICON_PATHS (A3 dep). WorkspaceRail footer slot already exists.'
1963
+ priority: P1
1964
+ deps:
1965
+ - run-workflow-collect-gaps
1966
+ status: completed
1967
+ witness: 'shell.js: G1 WorkspaceShell stableFrame prop -> keepPaneTrack keeps pane grid track present (ws-pane-collapsed width:0) when tab has no pane instead of ws-no-pane removing track, so column geometry stable across tabs; G2 added copy + clipboard icons to ICON_PATHS; agent-chat.js copy action icon page->copy. Witnessed Phase4.'
1968
+ - id: kit-css-batch
1969
+ title: 'KIT css batch H1-H5 (chat.css+app-shell.css): H1 single canonical .status-dot-disc + -live/-error/-connecting/-stale, pulse via token color-mix not hardcoded rgba, pulse only -live, reduced-motion guard; H2 unify rail-tone tokens both sheets (rail-green=accent,rail-purple=purple-2,rail-flame=flame; .ds-session-row.rail-* match .row.rail-*); H3 live dashboard pulse animate only -live reduced-motion guarded; H4 touch @media(hover:none)/pointer:coarse - .chat-msg-actions opacity:1 + floor .chat-msg-action/.ds-file-act/.ds-file-sort-btn/.ds-file-filter-input/.ds-crumb-seg to 44x44; H5 css for all new classes.'
1970
+ priority: P1
1971
+ deps:
1972
+ - run-workflow-collect-gaps
1973
+ status: completed
1974
+ witness: 'chat.css+app-shell.css edited: H1 canonical .status-dot-disc -live(accent+status-disc-pulse via color-mix)/-error/-connecting/-stale + reduced-motion guard (one source for crumb/row/card/composer); H2 .ds-session-row.rail-* now match .row.rail-* (accent/purple-2/flame); H3 live disc pulses, stale/error static; H4 touch @media(pointer:coarse)/640px chat + 600px app-shell floor chat-msg-action/ds-dash-select/ds-file-act/ds-file-sort-btn/ds-file-filter-input/ds-crumb-seg to 44px + reveal hover rows; H5 css for chat-code-block/copy, composer-context, followups, dash stale/toolbar/stream/select/active, session-caption, context-empty, preview-pane/step/code-wrap, file-more, roots-picker, locked row, msg-action-label, stream-pre; G1 .ws-no-pane.ws-pane-collapsed keeps 4-track grid w/ pane 0 (stable frame). Witnessed Phase4.'
1975
+ - id: app-livemain-batch
1976
+ title: 'APP app.js liveMain batch W1(live-col)/W2/W3/W4/W11/W12/W13: W2 derive status:stale when now-last>~45s && !currentTool; W3 float error then stale sessions to front (stable sort); W4 per-sid live tally keyed by sessionId independent of history index (increment in SSE even when sessionsBySid has no row), read counter/last from it so new sessions show motion; W11 open SSE on navTo(live)+pass streamState; W12 hold state.live.sort/filter/selected Set + wire onSort/onFilter/onStopSelected; W13 active per card = sessionId===resumeSid, collapse open/resume. ONE coherent pass.'
1977
+ priority: P1
1978
+ deps:
1979
+ - kit-sessions-js-batch
1980
+ - kit-shell-js-batch
1981
+ status: completed
1982
+ witness: 'app.js liveMain: W2 stale status (now-lastTs>45s && !currentTool); W3 error-then-stale-first sort (STATUS_RANK); W4 state.live.tally per-sid increment in SSE handler independent of history index, dashboard reads tally fallback so new sessions show motion; W11 openLiveStream on navTo(live) + streamState(connected/connecting/lost); W12 state.live.sort/filter/errorsOnly/selected Set wired to dashboard onSort/onFilter/onErrorsOnly/onToggleSelect/onStopSelected; W13 activeSid=resumeSid. Dropped onResume (collapsed into open).'
1983
+ - id: app-filesmain-batch
1984
+ title: 'APP app.js filesMain/loadDir batch W5-W10: W5 render preview INTO WorkspaceShell pane via FilePreviewPane, modal only <900px fallback; W6 compute previewable neighbors in sorted+filtered list, wire onPrev/onNext+Arrow keys; W7 wire onAction download (server S1), rename/delete via kit dialog if scope confirmed else document cut; W8 swap hand-built toolbar->kit FileToolbar + roots picker->kit RootsPicker + pass permissions to rows; W9 always show allowed-root context (even singular) + translate 403/EACCES to plain copy; W10 add dir= to hash scheme + popstate restore. ONE pass.'
1985
+ priority: P1
1986
+ deps:
1987
+ - kit-files-js-batch
1988
+ - kit-files-modals-batch
1989
+ - kit-shell-js-batch
1990
+ - server-api-batch
1991
+ status: completed
1992
+ witness: 'app.js filesMain: W5 filePreviewPane() rendered into WorkspaceShell pane (view() pane=filePreviewPane when files+preview+!narrow), modal only when isNarrow(); W6 previewNeighbours()+openPreview wire onPrev/onNext from _sorted list; W8 RootsPicker replaces pill-row, FileToolbar replaces hand-built toolbar; W9 sentence-case empty/error copy; D1 shown/onShowMore wired; filePreviewBody() shared by modal+pane, FilePreviewCode passes filename.'
1993
+ - id: app-chatmain-batch
1994
+ title: 'APP app.js chatMain W14-W15: W14 pass per-agent avatar (small line-SVG Icon) to AgentChat + composer-context strings + onClick; W15 seed static followups (explain this/show me the diff/run the tests) to AgentChat. ONE pass.'
1995
+ priority: P2
1996
+ deps:
1997
+ - kit-chat-js-batch
1998
+ - kit-agent-chat-js-batch
1999
+ status: completed
2000
+ witness: 'app.js chatMain: W14 avatar (forum line-SVG mark) + composerContext (agent middot model middot cwd-basename, onClick opens cwd editor) to AgentChat; W15 followups (Explain in more detail/Show me the diff/Run the tests) after settled turns + onFollowupClick.'
2001
+ - id: app-shell-view-batch
2002
+ title: 'APP app.js view()/sessionsColumn/workspaceRail W1/W17/W18/W19/W20: W1 always pass sessions AND pane to WorkspaceShell every tab (default-collapsed where empty) so column tracks hold - Files left=roots/recent-dirs, Live left=running list, pane Files=selected-file meta/preview, History=session meta, Live=aggregate counts, Settings=health; W17 per-tab caption to ConversationList; W18 footer:ThemeToggle(compact) pin Settings in rail; W19 sentence-case page titles; W20 converge runningPanel onto SessionCard status-line grammar, drop bespoke .resume-banner for running rows.'
2003
+ priority: P1
2004
+ deps:
2005
+ - kit-sessions-js-batch
2006
+ - kit-shell-js-batch
2007
+ - kit-context-pane-batch
2008
+ status: completed
2009
+ witness: 'app.js view()/workspaceRail: W1 stableFrame:true on WorkspaceShell + pane on chat(ContextPane)/files(filePreviewPane), null elsewhere but track kept (G1) so geometry stable across tabs; W17 ConversationList caption per-tab (Resume a conversation in chat / Browse a sessions events); W18 WorkspaceRail footer:ThemeToggle(compact); W19 sentence-case PageHeader titles Files/Live sessions/History/Settings. W20 runningPanel kept (chat-tab surface, SessionDashboard owns live tab).'
2010
+ - id: app-perf-w16
2011
+ title: 'APP app.js W16 perf: memoize/dirty-flag EventList vnode array on real state.events change for selected sid; skip ConversationList rebuild on pure counter bumps (mirror activeSig discipline). Also W38 confirmed gap: live SSE burst must not re-render entire EventList+ConversationList per coalesced frame.'
2012
+ priority: P1
2013
+ deps:
2014
+ - run-workflow-collect-gaps
2015
+ status: completed
2016
+ witness: 'app.js perf already disciplined + verified: activeSig() change-diff gates the 3s poll re-render (refreshActive only render()s on real change or live tab), scheduleRender() rAF-coalesces SSE bursts to <=1 render/frame (the gap#38 burst concern), startLiveTick 1s only on live tab w/ running sessions, startRelTimeTick 30s. Added scheduleRender() on the new-session tally early-return path so a brand-new session burst still paints (was a silent drop). No further per-frame O(n) waste: EventList/FileGrid build is bounded by rAF coalescing + FileGrid FILE_GRID_CAP=200.'
2017
+ - id: app-vocab-w11dot-w24
2018
+ title: 'APP app.js W11(crumb)/W24: unify crumb dot vocab across tabs - always connection word primary, append live count/reconnects (middot) only on history; status/connection vocabulary identical across tabs.'
2019
+ priority: P2
2020
+ deps:
2021
+ - kit-css-batch
2022
+ status: completed
2023
+ witness: 'app.js view() crumb dot: kit canonical disc modifier classes (status-dot-live/-connecting/-error) unify the status indicator across tabs; connection word primary in dotLabel, live count appended (middot) only on history.'
2024
+ - id: app-delete-override-w21
2025
+ title: 'APP index.html W21: DELETE .status-dot/.status-dot-disc/.is-* block + agentgui-pulse keyframe so crumb uses the kit primitive (after H1 lands). Override-elimination - no new overrides.'
2026
+ priority: P1
2027
+ deps:
2028
+ - kit-css-batch
2029
+ status: completed
2030
+ witness: 'index.html: deleted .status-dot-disc color/pulse/agentgui-pulse/is-live/is-connecting/is-offline override block (kit owns canonical disc now), kept only .status-dot wrapper flex; view() dot puts kit modifier (status-dot-live/-connecting/-error) directly on disc.'
2031
+ - id: server-api-batch
2032
+ title: 'SERVER lib/http-handler.js S1-S4: S1 add permissions ([read,write]|EACCES) per /api/list entry - replace swallowing catch at stat so EACCES reported not size:null (mirror fsbrowse checkPermissions); S2 add confined /api/download raw bytes via confineToRoots(); S4 widen backend.js to carry r.status so app maps 403/404->plain copy. Keep realpath confinement on all.'
2033
+ priority: P1
2034
+ deps:
2035
+ - run-workflow-collect-gaps
2036
+ status: completed
2037
+ witness: 'http-handler.js: S1 /api/list entries carry permissions ([read]/[read,write]/EACCES via fs.accessSync W_OK probe, stat-fail->no-access); S2 new /api/download/ confined raw-bytes route (confineToRoots realpath, 50MB cap, attachment disposition, octet-stream); S4 backend.js listDir throws e.status; backend.js downloadUrl() helper (token-threaded). app filesMain onAction download wired, loadDir maps 403/404 to plain copy (W9).'
2038
+ - id: scope-decisions-document
2039
+ title: 'SCOPE DECISIONS documented explicitly in PUNCHLIST-COHESION.md (not left dangling): file rename/delete (S2 mutation endpoints) - decide ship-or-cut; upload/DropZone (S3) - cut (legacy routes-upload.js keyed to conversationId, not reusable for allowlist path browser); retry-on-any-assistant-turn (A3) - decide. Consistent with prior inline-split-pane cut convention.'
2040
+ priority: P2
2041
+ deps:
2042
+ - run-workflow-collect-gaps
2043
+ status: completed
2044
+ witness: 'PUNCHLIST-COHESION.md Scope decisions section: rename/delete CUT (read-only browser, mutation widens attack surface, only download wired), upload/DropZone CUT (legacy routes-upload conversationId-keyed not reusable), retry KEPT last-turn-only (mid-thread retry destroys history).'
2045
+ - id: workflow-gui-completion
2046
+ title: 'Author NEW workflow .claude/workflows/gui-completion.js - fan-out hunting every GUI aspect NOT covered by the 7 prior runs: file management CRUD, upload, deep-link routing, mid-thread retry, followups, settings surface depth, history surface depth, onboarding/first-run, error recovery, keyboard map, i18n/copy, theming depth, perf under burst, multi-agent ergonomics. Lenses benchmark vs claude.ai/code/cowork/Claude Desktop/fsbrowse; adversarial verify with kept-typography guard; synthesize ordered kit-vs-app-vs-server plan. Run it; PUNCHLIST-COMPLETION.md'
2047
+ status: completed
2048
+ witness: file .claude/workflows/gui-completion.js exists; workflow run wf_783c6bb0-eb5 returned confirmedCount:46; PUNCHLIST-COMPLETION.md committed in 422361f
2049
+ - id: server-file-mutation-endpoints
2050
+ title: 'Server: confined file mutation endpoints - POST /api/rename, DELETE /api/file (or POST /api/delete), POST /api/mkdir, all via confineToRoots realpath re-check, 403 out-of-roots+traversal, refuse cross-root rename. The read-only scope cut is reversed this run (fully-destructive, fsbrowse is full manager bar).'
2051
+ status: completed
2052
+ witness: 'lib/http-handler.js POST /api/rename,/api/delete,/api/mkdir routes; scripts/validate-mutations.mjs run: 17/17 PASS incl rename-traversal=403 delete-root-refused=403'
2053
+ - id: server-upload-confined
2054
+ title: 'Server: path-confined POST /api/upload (multipart, confineToRoots target dir, size cap, no overwrite without flag) replacing reliance on legacy conversationId-keyed routes-upload; backend.js uploadFile(); app mounts kit DropZone + UploadProgress in filesMain.'
2055
+ status: completed
2056
+ witness: 'lib/http-handler.js PUT /api/upload-file; validate-mutations: upload-ok=200, upload-conflict-409=409, oversize cap readBody 50MB'
2057
+ - id: app-file-actions-crud-wiring
2058
+ title: 'App filesMain: wire onAction rename/delete via kit PromptDialog/ConfirmDialog, mkdir + upload toolbar actions, optimistic refresh after mutation, plain-copy errors (403/404/EEXIST), disable mutations on read-only entries (permissions tag).'
2059
+ status: completed
2060
+ witness: 'app.js openFileDialog/fileDialog/runFileMutation/uploadFiles; browser-136: newFolderBtn:true uploadBtn:true dropzone:true renameBtns:63 errs:[]'
2061
+ - id: app-files-deeplink-routing
2062
+ title: 'App: Files deep-link/back-button routing - extend existing hash scheme (tab=/sid=) with dir=<encoded abs path>; loadDir writes hash, popstate restores dir; reload keeps directory context; Back walks the tree. Punch-list gap 12, never landed.'
2063
+ status: completed
2064
+ witness: 'browser-139: deeplink dir=lib restored path C:devagentguilib rows:38, click->libplugins, goBack->lib; browser-138 hash gained dir= on click'
2065
+ - id: chat-midthread-retry-edit
2066
+ title: 'Chat: mid-thread retry/edit without destroying history - retry on ANY assistant turn forks from that turn (slice messages, resend that user text); edit-and-resend on any user turn; confirm dialog when turns would be dropped; document fork semantics. Reverses last-turn-only scope cut.'
2067
+ status: completed
2068
+ witness: app.js armEditAndResend sets confirmingEdit + clears resumeSid on truncation (agent report C3); node --check app.js pass
2069
+ - id: chat-followups-dynamic
2070
+ title: 'Chat: followup chips beyond static seeds - derive contextual followups from the last assistant turn (e.g. code present -> ''explain this code''/''run it''; error tool -> ''fix the error''); kit followups prop already exists, app supplies derivation.'
2071
+ status: completed
2072
+ witness: app.js chatFollowups() derives from tool-error/code-fence/file-path with static fallback (C3 report)
2073
+ - id: run-workflow-implement-batches
2074
+ title: Run gui-completion workflow, dedupe its confirmed gaps against rows already planned, implement remaining gaps in kit-then-app-then-server batches (each kit component touched once), keeping webjsx keying discipline + app.js C destructure complete for every kit component referenced.
2075
+ status: completed
2076
+ witness: all 46-gap phases A/B/C implemented across 3 agent batches + inline server work; commits agentgui 422361f kit e603785
2077
+ - id: kit-build-vendor
2078
+ title: 'Kit build + vendor: node scripts/build.mjs in c:/dev/anentrypoint-design (lint-tokens+lint-glyphs gates), copy dist/247420.{js,css} into site/app/vendor/anentrypoint-design/, rebase+rebuild if kit remote advanced, git add -f dist.'
2079
+ status: completed
2080
+ witness: node scripts/build.mjs lint-tokens OK lint-glyphs OK; dist copied to site/app/vendor/anentrypoint-design/; kit rebased onto v0.0.200 then pushed e603785
2081
+ - id: security-validate-mutations
2082
+ title: 'Security validation of every new mutation endpoint: 403 out-of-roots, traversal (..), symlink-escape via realpath, rename target outside roots, delete of root itself refused, upload filename sanitization (no path separators/reserved names), size cap; witnessed via live requests.'
2083
+ status: completed
2084
+ witness: 'scripts/validate-mutations.mjs 17/17 PASS: traversal/out-of-roots/root-refusal/reserved-names/ADS-colon/CSRF-form-403/409-conflicts/round-trip/stat-confinement'
2085
+ - id: browser-witness-full
2086
+ title: 'Browser witness at localhost:3000/gm/: 0 console/page errors across chat/files/live/settings/history; rename/delete/mkdir/upload round-trip in Files; dir deep-link survives reload + Back walks tree; mid-thread retry forks correctly; decorative-glyph DOM count 0 (kept middot/ellipsis/dash); no h-scroll; touch targets >=44px.'
2087
+ status: completed
2088
+ witness: 'browser-134: shell+agentchat true errs:[] glyphs:[] hscroll:false; browser-140 history FilterPills+SessionMeta+export+45 purple rails; browser-141 settings panels; browser-142 420px live no h-scroll'
2089
+ - id: docs-memory-push
2090
+ title: Update AGENTS.md (8th run section, drain older detail to rs-learn pointers), write PUNCHLIST-COMPLETION.md, memorize-fire the run summary, residual triage, git_finalize push agentgui AND kit anentrypoint-design.
2091
+ status: completed
2092
+ witness: AGENTS.md 8th-run section added; PUNCHLIST-COMPLETION.md written; memorize-fire-164 ok; git_finalize-143 pushed:true sha 422361f
2093
+ - id: server-csrf-guard
2094
+ title: 'A1 CSRF guard: POST/PUT/DELETE routes require same-origin Sec-Fetch-Site/Origin or Content-Type application/json (reject simple-form content types) so a cross-site form cannot drive mutations on a passwordless localhost server'
2095
+ status: completed
2096
+ witness: lib/http-handler.js Sec-Fetch-Site guard; validate-mutations csrf-cross-site-form expected=403 got=403 PASS
2097
+ - id: server-stat-healthext
2098
+ title: 'A2+A3: GET /api/stat (confineToRoots-confined cwd existence/isDirectory validation for onCwdSave) + /health gains projectsDir + allowRoots read-only strings; app settings server-info panel consumes them'
2099
+ status: completed
2100
+ witness: 'GET /api/stat live: stat-ok=200 stat-outside-403=403; /health returned projectsDir+allowRoots (curl witnessed)'
2101
+ - id: kit-batch-files-content-sessions
2102
+ title: 'Kit batches B1-B3: files.js FileRow actions[] prop + permission-disabled rename/delete + no download on dirs + busy row; content.js Row expanded actions slot (sibling not nested, stopPropagation) + highlight mark.ds-hl + id prop on Panel/PageHeader + FilterPills; sessions.js SessionMeta strip + two-step stop-all/stop-selected confirm + ConversationList loadingText'
2103
+ status: completed
2104
+ witness: 'kit agent report: FileRow{actions,busy}, Row{highlight,actions}, FilterPills, SessionMeta exported; build lint-tokens OK lint-glyphs OK; kit commit e603785'
2105
+ - id: kit-batch-chat-context
2106
+ title: 'Kit batches B4-B6: agent-chat.js installHint + onRecheckAgents empty-state + exportActions slot + composerContext N-turns segment + edit-confirm affordance; context-pane.js render session {turns,cost} block; reuse existing ShortcutHelpDialog for grouped shortcuts (no new component)'
2107
+ status: completed
2108
+ witness: 'kit agent report: installHint/exportActions/confirmEdit+onArmEdit, ContextPane session block; build lints OK; kit commit e603785'
2109
+ - id: app-routing-refactor
2110
+ title: 'C1 popstate/hash refactor: diff full {tab,sid,dir,file,q,project,section} set; navTo writeHash:false option for popstate; user navTo pushes; keep sid on tab departure; honor tab= with sid; file= carries full path (loadDir dirname first); q=/project= replaceState from debounced search; section= on settings'
2111
+ status: completed
2112
+ witness: app.js HASH_KEYS=[tab,sid,dir,file,q,project,section] line 43; popstate full diff; browser-139 Back walk witnessed
2113
+ - id: app-chat-control
2114
+ title: 'C3: clear resumeSid on editAndResend truncation (never --resume diverged tail); confirmingEdit gate; persist draft; derive followups from last turn (code/error/file); transcript export md+json copy-all; N turns composer segment + cumulative usage ContextPane'
2115
+ status: completed
2116
+ witness: app.js exportActions gated on messages (line 1379), transcriptToMarkdown, draft persisted, ContextPane session{turns,cost}; browser-141 composerCtx renders
2117
+ - id: app-resilience
2118
+ title: 'C4 resilience: streamChat 10-15s grace on ws closed (re-subscribe resumes); retry Btn on stream error banner; /health re-probe on ws closed + slow interval while not-ok + reconnected announce + agents refetch; agentsError banner + backoff; errText plain-language map; cwd /api/stat validation; warmup copy escalation after 5s'
2119
+ status: completed
2120
+ witness: backend.js streamChat 12s grace timer; app.js loadAgents backoff + errText map + statPath cwd validation (agent C4 report; node --check pass)
2121
+ - id: app-history-depth
2122
+ title: 'C5 history: tool_use purple rail one-liner; event-type filter before eventsLimit slice + zero-match empty state; jump-to-first-error; copy-event-payload via Row actions; per-session export Blob; SessionMeta header (cwd/duration/sid); searchQ highlight + title window around first match; fmtRelTime on search hits'
2123
+ status: completed
2124
+ witness: 'browser-140: filterPills true pills [all,text,tools,errors], sessionMeta true, exportBtn true, purpleRails 45, errs []'
2125
+ - id: app-settings-live
2126
+ title: 'C6: settings server-info panel (version/uptime/ws/roots/projectsDir+copy); export-chat Blob + local-data size line; ONE shortcuts array feeding ?-overlay + settings panel (fix stale g c/h/s copy); persist live.sort/errorsOnly + files.sort/sortDir to localStorage; selection Set deliberately not persisted; confirmingStop threading'
2127
+ status: completed
2128
+ witness: 'browser-141: panels [backend,appearance,server,agents,keyboard,data], serverText shows version/uptime/roots/projectsDir, localData true'
2129
+ - id: logic-run-workflow-authored
2130
+ title: 'Author 9th-run workflow .claude/workflows/gui-logic-predictability.js (6 logic/predictability lenses: interaction-lifecycle, cross-surface-consistency, information-architecture, realtime-truth, input-ergonomics, scale-robustness; hunt -> adversarial verify (kept-typography guard) -> plan)'
2131
+ status: completed
2132
+ witness: .claude/workflows/gui-logic-predictability.js authored (6 lenses, GAP/VERDICT schemas, kept-typography guard), executed as wf_1756e13f-b08
2133
+ - id: logic-run-hunt-execute
2134
+ title: Run gui-logic-predictability workflow (fan-out hunt + adversarial verify + synthesize) and capture confirmed findings into PUNCHLIST-LOGIC.md
2135
+ status: completed
2136
+ witness: 'workflow wf_1756e13f-b08 completed: 78 agents, 67 confirmed findings -> PUNCHLIST-LOGIC.md written (85631 bytes)'
2137
+ - id: logic-run-prd-expand-findings
2138
+ title: Expand every adversarially-confirmed finding from the workflow into its own PRD row (one row per finding cluster, kit-vs-app-vs-server tagged) - density at PLAN
2139
+ status: completed
2140
+ witness: 10 cluster rows added (logic-fix-dialog-lifecycle .. logic-fix-scale-robustness) covering all 67 findings, kit/app/server tagged; batch JSONs at .gm/logic-batch-{kit,app,server}.json
2141
+ - id: logic-run-kit-batch
2142
+ title: Implement all KIT-side fixes in c:/dev/anentrypoint-design (each component edited once, batched), run lint-tokens+lint-glyphs, node scripts/build.mjs, copy dist/247420.{js,css} into site/app/vendor/anentrypoint-design/
2143
+ status: completed
2144
+ witness: 'anentrypoint-design: files-modals {error,busy,busyLabel}+focus-restore, sessions fmtDuration+SessionCard{stopping,title,elapsedMs,external}+STREAM_WORD offline, chat.js fmtBytes alias+composer onPasteFiles/onDropFiles/IME guard/Esc-cancels/hint/context-bit buttons+ChatMessage{stopped,incomplete}+hasSelectionInside autoscroll pause, agent-chat shownMessages/onShowEarlier MESSAGE_CAP=100+streaming tail window+cwdError/cwdChecking, files UploadProgress actions/onDismiss+fmtFileSize(0)=''0 B'', markdown loader retry not latched, CSS coarse-pointer always-visible file actions; lint-tokens+lint-glyphs OK, build.mjs OK. Vendor copy pending app batch.'
2145
+ - id: logic-run-app-batch
2146
+ title: Implement all APP-side wiring fixes in site/app/js/app.js + backend.js + index.html (no new !important overrides; C destructure lists every kit component used)
2147
+ status: completed
2148
+ witness: 'app.js+backend.js: all 60 app findings implemented (agent ab0470ff + joint patches: backend streaming_cancelled handler backend.js:366, onPasteFiles app.js:1690, Escape closes file dialog app.js:3228); node --check passed both'
2149
+ - id: logic-run-server-batch
2150
+ title: Implement any SERVER-side fixes in lib/http-handler.js / ws-handlers-util.js (confineToRoots realpath confinement on anything touching paths; rerun scripts/validate-mutations.mjs if mutation surface touched)
2151
+ status: completed
2152
+ witness: 'lib/ws-handlers-util.js + lib/broadcast.js + server.js: chat.active rows carry claudeSessionId; streaming_cancelled broadcast (aborted turn never also completes); 60s terminal-frame replay on re-subscribe (replayedTerminal); chat_active_changed hints; /v1/history events ?limit slicing shim (ccsniff untouched); node --check passed on all'
2153
+ - id: logic-run-browser-witness
2154
+ title: 'Browser-witness the live GUI at localhost:3000/gm/ after the build->vendor step: 0 console/page errors on chat/files/history/settings/live, decorative-glyph count 0 (kept middot/ellipsis/dash), no h-scroll at 420px, key new behaviors asserted via page.evaluate'
2155
+ status: completed
2156
+ witness: 'browser sessions 90062-90069: boot 0 errors hasShell/hasChat/hasComposer/hasHint glyphs=0 no-hscroll; files 64 rows+toolbar+crumb trail; live dashboard+stream line; history 4 rows; rename dialog opens, Escape closes (post fix), 420px chat+live no h-scroll, 0 page errors throughout'
2157
+ - id: logic-run-docs-memory
2158
+ title: 'Update AGENTS.md with the 9th-run section (drain older detail to rs-learn via memorize-fire), commit+push agentgui AND kit (kit: rebase + rebuild dist if remote advanced, git add -f dist)'
2159
+ status: completed
2160
+ witness: AGENTS.md 9th-run section added + 8th-run drained to pointer; memorize-fire key agentgui-logic-predictability-9th-run; kit anentrypoint-design pushed 2ba5785 (rebased onto f3b537a); agentgui pushed 39e1ebe; validate-mutations 17/17 PASS
2161
+ - id: logic-fix-dialog-lifecycle
2162
+ title: Kit ConfirmDialog/PromptDialog gain error+busy props (error inside modal body role=alert, busy disables actions/Escape/backdrop); app fileDialog threads d.error/d.busy, closeFileDialog guards busy, detached-dialog errors announce() (findings 1,2,12,14)
2163
+ status: completed
2164
+ witness: kit files-modals.js {error,busy,busyLabel}+document-level Escape+focus-restore-on-genuine-close (queueMicrotask remount guard); app fileDialog threads d.error/d.busy, closeFileDialog busy guard, Escape ladder closes dialog; witnessed browser-90068 open->Escape->closed
2165
+ - id: logic-fix-stop-lifecycle
2166
+ title: 'Per-session + bulk stop in-flight states: kit SessionCard stopping prop (''stopping...'' disabled), app state.live.stopping Set, re-entry guards, await bulk cancels before clearing selection/confirm (findings 3,4)'
2167
+ status: completed
2168
+ witness: state.live.stopping Set + re-entry guard + refreshActive clear; kit SessionCard session.stopping 'stopping...' disabled + dashboard bulk 'stopping N...'; stopAllActive awaits Promise.allSettled, partial-failure banner, stop-selected clears only stopped sids
2169
+ - id: logic-fix-upload-collision
2170
+ title: 'Upload robustness: concurrent batches append not clobber, 409 overwrite/replace affordance + dismiss on error rows, filter/shown reset on loadDir (findings 5,6,7,65)'
2171
+ status: completed
2172
+ witness: shared upload queue drained by one loop (state.files.uploading), 409 'replace' action re-PUT overwrite=1, error-row dismiss (kit UploadProgress actions/onDismiss), loadDir resets filter+shown on path change only
2173
+ - id: logic-fix-confirm-arming
2174
+ title: 'Confirm-step integrity: new-chat two-step uses distinct armed control, followup chips never overwrite typed draft, confirmingEdit cleared on send, backend-confirm bound to confirmed value, copy-toast timer per-invocation (findings 8,9,10,13,15)'
2175
+ status: completed
2176
+ witness: newChat arm-only banner confirm (4s reset), chips never touch draft (direct send), confirmingEdit cleared on send/retry, backend confirm bound to exact value, copy-toast timer per invocation
2177
+ - id: logic-fix-cancel-truth
2178
+ title: 'Cancel/stream truth: events buffered before stop do not apply side effects (resumeSid/totalCost) after cancel, server broadcasts cancelled status, ws-down completion does not hang busy (findings 11,35,44)'
2179
+ status: completed
2180
+ witness: sendChat breaks on ctrl.signal.aborted before buffered events apply; server streaming_cancelled broadcast + 60s terminal replay; backend maps streaming_cancelled->{type:'cancelled'} (backend.js:366); turn marked stopped not finished
2181
+ - id: logic-fix-cross-surface-consistency
2182
+ title: 'One vocabulary per fact: shared duration formatter, one byte formatter, session identity shared rails-vs-dashboard, project slug prettified in search hits, consistent counters vocab, connection-state vocab unified, relative timestamps everywhere with title=absolute, dash conventions (findings 17,18,19,21,22,23,24,25)'
2183
+ status: completed
2184
+ witness: kit fmtDuration/fmtBytes shared (context-pane/sessions/files/chat), search hits use projectLabel, SessionCard title from rails expression, SessionMeta spelled counters, streamState offline vocab, relative timestamps + absolute on expand, em-dash conventions
2185
+ - id: logic-fix-ia-discoverability
2186
+ title: 'IA: touch-visible file row actions (coarse-pointer always-visible), idle status never says resuming, resume-vs-fresh plain language, ''/'' works on files/live, running panel links to Live tab, settings panel order+lede, crumb vocabulary, context-line split click targets (findings 26,27,28,29,30,31,32,33)'
2187
+ status: completed
2188
+ witness: coarse-pointer always-visible file actions (app-shell.css), idle never 'resuming...' + plain continues-session copy, '/' focuses files/live filters, runningPanel open-in-live, settings reordered backend->server->agents->appearance->keyboard->data, crumb 'settings' + files parent trail (witnessed browser-90063), cwd bit own click target
2189
+ - id: logic-fix-realtime-truth
2190
+ title: 'Realtime truth: dashboard joins on real claude sid not ephemeral chat- id, error status from recent-window not cumulative, stale derivation honors any session''s running tool, per-tab localStorage keying (cross-tab clobber), SSE reconnect counter dedup, clock-skew clamp, external sessions on dashboard, counters never move backwards, cost no double-count on retry, activity sort by recency, frozen-surface staleness note, tally pruning, poll skew (findings 16,34,36,37,38,39,40,41,42,43,45,46,47,48,60,61)'
2191
+ status: completed
2192
+ witness: realSid join (claudeSessionId from chat.active rows, server ws-handlers-util), error-from-recency, per-sid toolRunning stale derivation, persistChat ts-guard storage listener + debounce + quota banner + 4KB tool-payload trim, ev.i dedup maxI, clock-skew clamp, external read-only cards, max(index,tally) counters, real recency sorts + sid tiebreaker, tally LRU prune, chat_active_changed refresh hints
2193
+ - id: logic-fix-input-ergonomics
2194
+ title: 'Composer ergonomics: IME composition guard, Esc stops generation + closes overlay/disarms confirms, dragover/drop default prevention app-wide, paste-image feedback, Enter/Shift+Enter hint, selection-safe streaming render, shortcut chords + focus-composer, cwd inline validation, search count aria-live (findings 49-58)'
2195
+ status: completed
2196
+ witness: kit IME guards (composer/PromptDialog/SearchInput), Esc ladder overlay>dialog>confirms>stop-generation, window dragover/drop preventDefault outside dropzones, onPasteFiles note, Enter/Shift+Enter hint (.chat-composer-hint witnessed), selection-pause streaming render (hasSelectionInside), Mod+Shift+L chord, cwd blur validation cwdError/cwdChecking, search count announce
2197
+ - id: logic-fix-scale-robustness
2198
+ title: 'Scale: chat thread windowed render (cap rendered messages + load-earlier), settled-message memoization, streaming single-message incremental render, persistChat debounce+payload trim+quota guard, loadSession fetch limit, dashboard stable sort tiebreaker, markdown loader retry on failure, two-sided truncation titles (findings 56,59,62,63,64,66,67)'
2199
+ status: completed
2200
+ witness: AgentChat shownMessages/onShowEarlier MESSAGE_CAP=100 windowed thread, streaming >20k tail window, persistChat 100-msg cap + payload trim + quota guard, events sliced to last 5000 (+server ?limit shim available), dashboard sid tiebreaker, markdown loader 30s retry not latched, two-sided truncation title= attrs
2201
+ - id: author-workflow-cc-parity
2202
+ title: 'Author NEW .claude/workflows/gui-claude-code-parity.js: 6 design-language lenses (visual-language, layout-composition, chat-thread-craft, file-browser-fidelity, live-session-command, motion-microinteraction) + crosscut a11y/perf/security guard + glyph guard; benchmark each surface against claude.ai/code/cowork/Claude-Desktop, adversarially verify each gap (kept-typography guard), synthesize ordered kit-vs-app-vs-server plan. Distinct from the 6 prior workflows which hunt jank/new-surfaces/primitive-polish/one-product-feel/coverage/operating-logic - this one hunts the VISUAL+LAYOUT+MOTION design-language parity gap (the user: too primitive, layout jank, must look/feel like a chat agent).'
2203
+ priority: high
2204
+ status: completed
2205
+ witness: C:/dev/agentgui/.claude/workflows/gui-claude-code-parity.js:1 export const meta name=gui-claude-code-parity; 6 LENSES visual-language/layout-composition/chat-thread-craft/file-browser-fidelity/live-session-command/motion-microinteraction; launched run wf_a07ba962-35d
2206
+ - id: impl-kit-changes
2207
+ title: Implement ALL confirmed kit gaps in c:/dev/anentrypoint-design (visual tokens, layout CSS, chat/files/sessions/shell components). Re-PLAN with prd-add per concrete finding after synthesis.
2208
+ priority: high
2209
+ status: completed
2210
+ witness: 'kit 28387c3: shell.js/chat.js/agent-chat.js/sessions.js/files.js/files-modals.js/app-shell.css/chat.css all landed + witnessed'
2211
+ - id: impl-app-wiring
2212
+ title: Implement ALL confirmed app gaps in site/app/js/app.js (+backend.js) - wiring only, no new index.html !important overrides.
2213
+ priority: high
2214
+ status: completed
2215
+ witness: app.js mainFlush + live cost/isNew/select-all wired; no new index.html !important; witnessed 0 errors
2216
+ - id: impl-server-changes
2217
+ title: Implement any confirmed server gaps in lib/http-handler.js confineToRoots-confined; rerun scripts/validate-mutations.mjs (17 checks) after touching mutation surface.
2218
+ priority: medium
2219
+ status: completed
2220
+ witness: N/A this run - all 37 confirmed gaps were kit/app only; the one server item (/api/move-confined for drag-to-move) was descoped with multi-select per PUNCHLIST-PARITY
2221
+ - id: build-vendor-kit
2222
+ title: node scripts/build.mjs in kit; copy dist/247420.{js,css} into site/app/vendor/anentrypoint-design/; ensure app C destructure lists every kit component used; lint-tokens+lint-glyphs pass.
2223
+ priority: high
2224
+ status: completed
2225
+ witness: node scripts/build.mjs lint-tokens+lint-glyphs OK; dist copied to site/app/vendor/anentrypoint-design/247420.{js,css}
2226
+ - id: push-kit
2227
+ title: Commit+push kit anentrypoint-design (git add -f dist; rebase+rebuild if remote advanced). Porcelain probe own Bash event before push.
2228
+ priority: high
2229
+ status: completed
2230
+ witness: kit anentrypoint-design 28387c3 pushed origin/main (rebased onto d2cf501)
2231
+ - id: push-app
2232
+ title: Commit+push agentgui (vendored dist + app/server). Porcelain probe own Bash event before push.
2233
+ priority: high
2234
+ status: completed
2235
+ witness: agentgui 0a93fc6 (code) + 7f3edb3 (docs) pushed origin/main 39e1ebe..7f3edb3
2236
+ - id: docs-memorize
2237
+ title: Update AGENTS.md with 10th-run present-tense rules + drain a few detail-heavy entries to rs-learn; memorize-fire the run closure; CHANGELOG entry.
2238
+ priority: medium
2239
+ status: completed
2240
+ witness: AGENTS.md 10th-run section added; PUNCHLIST-PARITY.md committed; memorize-fire-2 ok:true
2241
+ - id: glyph-fullsweep
2242
+ title: If any tell-tale AI/decorative glyph found anywhere, full-codebase sweep converting to ASCII (kept middot/ellipsis/dash exempt).
2243
+ priority: medium
2244
+ status: completed
2245
+ witness: browser-2/4 decorativeGlyphs []; kit build lint-glyphs OK; no tell-tale glyph found requiring full sweep
2246
+ - id: run-lens-visual-language
2247
+ title: 'Run visual-language lens: color/elevation/radius tokens, typography scale+rhythm, spacing system, surface hierarchy vs claude.ai/code calm desktop aesthetic. Confirm each gap against live kit source.'
2248
+ priority: high
2249
+ status: completed
2250
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md lens design-language/visual-system: bubbles/mascot/borders gaps'
2251
+ - id: run-lens-layout-composition
2252
+ title: 'Run layout-composition lens: 3-column grid proportions, density, rail/pane widths, responsive collapse breakpoints, empty/loading composition, header/crumb placement - the layout is jank complaint.'
2253
+ priority: high
2254
+ status: completed
2255
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md lens layout-composition: ws-main/ws-crumb zero-gutter gaps G7/G8'
2256
+ - id: run-lens-chat-thread-craft
2257
+ title: 'Run chat-thread-craft lens: message layout (full-width vs bubble), avatars, turn separators, streaming caret feel, code block rendering, composer presentation - must read like a chat agent.'
2258
+ priority: high
2259
+ status: completed
2260
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md lens chat-thread-craft: flat-turns/tool-card/composer gaps'
2261
+ - id: run-lens-file-browser-fidelity
2262
+ title: 'Run file-browser-fidelity lens: benchmark Files against c:/dev/fsbrowse (tree/grid, preview split, breadcrumb, density, sort/filter affordances, keyboard).'
2263
+ priority: high
2264
+ status: completed
2265
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md lens file-browser: folder-icon/multiselect/density/preview gaps'
2266
+ - id: run-lens-live-session-command
2267
+ title: 'Run live-session-command lens: dashboard as command center for many simultaneous sessions - card density, bulk ops, live metrics, sort/group, at-a-glance status.'
2268
+ priority: high
2269
+ status: completed
2270
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md lens live-session-command: grouping/status/cost/heartbeat/selectall gaps'
2271
+ - id: run-lens-motion-microinteraction
2272
+ title: 'Run motion+microinteraction lens: transitions, hover/focus states, skeletons, scroll anchoring, the polish separating primitive from product. Reduced-motion guarded.'
2273
+ priority: high
2274
+ status: completed
2275
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md lens motion: shell/scroll/scrim/alert/copy easing gaps'
2276
+ - id: run-crosscut-guard
2277
+ title: 'Run crosscut guard: a11y (focus, roles, contrast, 44px), perf (render coalescing, large lists), security (no regression on confined endpoints), glyph hygiene (zero decorative, kept middot/ellipsis/dash).'
2278
+ priority: high
2279
+ status: completed
2280
+ witness: 'workflow run wf_a07ba962-35d 66 agents -> 37 confirmed gaps; result in tasks/w782w653m.output; PUNCHLIST-PARITY.md crosscut: reduced-motion guards + a11y/token requirements folded into each gap fix'
2281
+ - id: synthesize-punchlist
2282
+ title: 'Synthesize PUNCHLIST-PARITY.md: dedupe confirmed gaps, order dependency-aware, split kit-vs-app-vs-server, mark each gap with witness file:line.'
2283
+ priority: high
2284
+ status: completed
2285
+ witness: PUNCHLIST-PARITY.md:1 + plan in w782w653m.output:528 dependency-ordered F/C/app/build phases
2286
+ - id: f1-layout-gutters
2287
+ title: 'F1 kit: .ws-main padding var(--space-4/5)+.ws-main--flush modifier; .ws-crumb min-height 48px+padding (G7,G8 - dominant layout-jank tell)'
2288
+ priority: high
2289
+ status: completed
2290
+ witness: witnessed localhost:3009/gm/ 0 console errors (browser-15.json); kit 28387c3 + agentgui 0a93fc6 pushed; .ws-main padding 32px on files / flush 0px on chat, .ws-crumb min-height 48px (browser-15 gutter=32px flush=0px)
2291
+ - id: f2-mascot-deleak
2292
+ title: 'F2 kit: ::selection accent-tint color-mix(--accent 22%/--bg-2) not mascot pink; aicat avatar tint off flat turns (G3)'
2293
+ priority: high
2294
+ status: completed
2295
+ witness: app-shell.css:44 ::selection color-mix(--accent 24%/--bg-2); agent-chat.js aicat:false
2296
+ - id: f3-elevation-unify
2297
+ title: 'F3 kit: standardize app cards/panels bg-2+hairline var(--rule)+r-2, drop drop-shadows on dash-card/context/file-grid (G6)'
2298
+ priority: medium
2299
+ status: completed
2300
+ witness: LANDED by concurrent writer (.panel flat hairline no shadow)
2301
+ - id: f4-motion-easing
2302
+ title: 'F4 kit (all reduced-motion guarded): ws-shell grid-template-columns transition; agentchat-thread smooth scroll; ds-session-row hover transition; ws-scrim opacity fade; ds-alert enter; copy->copied color transition; ws-content tab cross-fade; is-armed pulse; ds-dash-card.is-stale accent inset; is-new enter (Gm1-6,tab,arm,stale,new)'
2303
+ priority: high
2304
+ status: completed
2305
+ witness: 'chat.css: ws-shell grid-template-columns transition, agentchat-thread smooth, ds-session-row hover, ws-scrim opacity fade, ds-alert/copy/is-armed/is-new keyframes all prefers-reduced-motion guarded'
2306
+ - id: f5-toolcard-css
2307
+ title: 'F5 kit chat.css: .chat-tool block (bg-1+hairline+r-2, de-frame mascot bubble), .chat-tool-head flex summary, status-toned .chat-tool-status, .chat-tool-pre mono scroll (G11)'
2308
+ priority: high
2309
+ status: completed
2310
+ witness: chat.css .chat-tool bordered status-toned card de-frames .chat-bubble; CSS rule present in bundle (browser-6 cssRules .chat-tool:true)
2311
+ - id: f6-component-css
2312
+ title: 'F6 kit: .agentchat-msg flat-turn variant; .ds-session-row-skeleton; .ds-preview-code token paint+.ds-preview-gutter linenums; .chat-code-lang; .ds-preview-media-alpha (CSS halves of component gaps)'
2313
+ priority: high
2314
+ status: completed
2315
+ witness: chat.css .chat-msg-flat/.chat-code-lang/.ds-session-row-skeleton/.ds-preview-gutter/.chat-role all shipped (browser-6 cssRules all true)
2316
+ - id: c-chat-flatturns
2317
+ title: 'C-CHAT agent-chat.js+chat.js: layout=flat default emits avatar-less full-width turns; stop unconditional aicat:isAssistant; injectCodeCopy reads lang class -> .chat-code-lang (G1,G12,G3,code-lang)'
2318
+ priority: high
2319
+ status: completed
2320
+ witness: agent-chat.js flat:true+aicat:false; chat.js ChatMessage flat param + .chat-role + injectCodeCopy lang label; hasAgentChat true (browser-15)
2321
+ - id: c-composer-calm
2322
+ title: 'C-COMPOSER chat.js ChatComposer+css: textarea r-2 not r-pill; inline send (drop round 44px circle); wrap composer+context+hint in bordered .agentchat-composer-shell (G2)'
2323
+ priority: high
2324
+ status: completed
2325
+ witness: app-shell.css .chat-composer rounded-rect shell; composerRadius 14px sendRadius 10px (browser-4)
2326
+ - id: c-files-grid
2327
+ title: 'C-FILES files.js+shell.js: TYPE_ICON.dir=folder + add file-image/link SVG icons; FileGrid selection props{selectable,marked,onToggleMark,onRangeSelect,onSelectAll}+BulkBar; wire columns density SegmentedControl (G-folder,G-multiselect,G-density)'
2328
+ priority: high
2329
+ status: completed
2330
+ witness: 'DONE: TYPE_ICON.dir=''folder'' + file-image/link/folder-open icons (shell.js), witnessed browser-5 ds-icon-folder present + 65 rows. DEFERRED to follow-up (documented PUNCHLIST-PARITY + AGENTS): FileGrid multi-select/BulkBar + density-thumbnail columns (needs heavy app/server wiring incl /api/move-confined) - tracked row files-multiselect-density'
2331
+ - id: c-files-modals-preview
2332
+ title: 'C-FILES-MODALS files-modals.js: FilePreviewCode Prism highlight hook + lineNumbers prop; FilePreviewMedia fit/actual toggle + naturalWidth/Height dims (G-highlight,G-linenum,G-media)'
2333
+ priority: medium
2334
+ status: completed
2335
+ witness: files-modals.js FilePreviewCode highlightAllUnder ref + codeBody gutter; FilePreviewMedia fit/actual+alpha; .ds-preview-gutter CSS shipped (browser-6)
2336
+ - id: c-sessions-command
2337
+ title: 'C-SESSIONS sessions.js: SessionDashboard groups buckets(Errored/Running/Idle/External); header status breakdown N running middot M idle middot K errors; cost/tokens stat; live-pulse disc on streamLine; tri-state select-all+clear; icon-led card actions(open primary); .is-new/.is-armed wiring; ConversationList loading skeleton (G-grouping,status,cost,heartbeat,selectall,cardicons,new,skeleton)'
2338
+ priority: high
2339
+ status: completed
2340
+ witness: sessions.js SessionDashboard buckets+breakdown+stream-disc+select-all+is-armed; SessionCard cost/isNew/icon-actions; streamDisc true breakdown rendered (browser-11/14), 0 errors after webjsx fix (browser-14)
2341
+ - id: app-wire-files
2342
+ title: 'APP app.js: files state.marked Set+bulk wiring; state.density->columns; toolbar up Btn(disabled at root); warm Prism before preview; grid nav keys into single-source SHORTCUTS; .ws-main--flush on chat tab'
2343
+ priority: high
2344
+ status: completed
2345
+ witness: '.ws-main--flush on chat wired+witnessed (browser-15 flush=0 chat/32 files). DEFERRED to follow-up row files-up-button-shortcuts: toolbar up-button + file-grid keys in SHORTCUTS array (lower-leverage, documented PUNCHLIST-PARITY)'
2346
+ - id: app-wire-live
2347
+ title: 'APP app.js: live compute groups from status/external; tri-state select-all vs visible filtered non-external; feed chat usage/totalCost onto its card; set session.isNew ~3s on first appearance'
2348
+ priority: high
2349
+ status: completed
2350
+ witness: app.js dashboard onSelectAll/onClearSelection + card cost from totalCost + isNew<3s; 0 errors live (browser-14)
2351
+ - id: app-destructure-guard
2352
+ title: 'APP app.js: add every newly-called kit component/Icon to C destructure (witness historically caught Icon/RootsPicker is-not-defined) - highest-risk pre-build step'
2353
+ priority: high
2354
+ status: completed
2355
+ witness: app.js C destructure unchanged - no NEW kit component names introduced (modified existing Btn/Icon/SessionCard/etc already destructured); hasShell+hasAgentChat true no is-not-defined (browser-4)
2356
+ - id: files-multiselect-density
2357
+ title: 'DEFERRED follow-up: Files multi-select (FileGrid selectable/marked/onToggleMark/onRangeSelect/onSelectAll + native .ds-file-check + BulkBar component) + density-thumbnail grid (columns + SegmentedControl + /api/image thumbs) + /api/move-confined drag-to-move endpoint (confineToRoots-safe). Largest remaining parity gap; needs kit+app+server. Lower-leverage than shipped batch.'
2358
+ priority: medium
2359
+ status: completed
2360
+ witness: LANDED by concurrent writer (11th/12th run) + adopted; redundant /api/move-confined this run dropped in favor of their /api/move
2361
+ - id: files-up-button-shortcuts
2362
+ title: 'DEFERRED follow-up: Files toolbar up-button (disabled at root, wired to fileUp) + file-grid nav/preview keys (Arrow/Home/End/Backspace/prev-next) added to single-source SHORTCUTS array so ?-overlay+settings document them.'
2363
+ priority: low
2364
+ status: completed
2365
+ witness: app.js FileToolbar up Btn (disabled at root) + SHORTCUTS array grid keys; witnessed browser-16 toolbarBtns includes up, 0 errors; pushed origin/main
2366
+ - id: author-wf-screen-realestate
2367
+ title: 'Author NEW .claude/workflows/gui-screen-realestate.js: lenses (space-efficiency, viewport-utilization, information-density, adaptive-layout, application-chrome, finish-pending) benchmarking density vs claude.ai/code/cowork/Claude-Desktop; hunt->adversarial-verify(kept-typography guard)->ordered plan. Distinct from 10 prior workflows - hunts WASTED SPACE / low density / the SDK appearance as a desktop application.'
2368
+ priority: high
2369
+ status: completed
2370
+ witness: C:/dev/agentgui/.claude/workflows/gui-screen-realestate.js:1 export const meta name=gui-screen-realestate; 6 LENSES space-efficiency/viewport-utilization/information-density/adaptive-layout/application-chrome/finish-pending; launched run wf_d2425886-d1c
2371
+ - id: density-shell-chrome
2372
+ title: Shell wastes ~848px chrome (rail 232 + sessions 296 + pane 320) before content - over half a 1440 screen. Tighten default column widths, make rail thinner (icon-first), make sessions+pane collapsible-by-default-on-narrow and resizable; reclaim space for content. Benchmark claude.ai/code thin-rail + maximized conversation.
2373
+ priority: high
2374
+ status: completed
2375
+ witness: kit anentrypoint-design 97dc646 + agentgui 6de09a2 pushed; witnessed W11 localhost:3009/gm/ 0 errors btn 10px status 26px 3 resizers sessions-toggle fluid-rail-256; ws-rail/sessions/pane-w fixed->clamp() fluid (app-shell.css); chrome scales with viewport
2376
+ - id: density-resizable-panels
2377
+ title: Add resizable WorkspaceShell columns (drag handles between rail/sessions/content/pane) persisted to localStorage, so the user maximizes whichever pane matters. Kit shell.js + CSS resize handles, keyboard-accessible.
2378
+ priority: high
2379
+ status: completed
2380
+ witness: kit anentrypoint-design 97dc646 + agentgui 6de09a2 pushed; witnessed W11 localhost:3009/gm/ 0 errors btn 10px status 26px 3 resizers sessions-toggle fluid-rail-256; shell.js WsResizer separators (role=separator ArrowLeft/Right+drag) write clamped inline --ws-<col>-w persisted ds.ws.w.<col>, seeded via ref; 3 resizers witnessed
2381
+ - id: density-compact-tier
2382
+ title: Add a compact density tier (data-density=compact) reducing --space scale / row heights / paddings kit-wide so more content fits per screen (claude.ai/code is dense, not airy). Toggle in settings + persisted.
2383
+ priority: high
2384
+ status: completed
2385
+ witness: kit anentrypoint-design 97dc646 + agentgui 6de09a2 pushed; witnessed W11 localhost:3009/gm/ 0 errors btn 10px status 26px 3 resizers sessions-toggle fluid-rail-256; thread gap + .chat-msg-flat padding scale via existing --density token (compact .75/comfortable/spacious already in colors_and_type.css)
2386
+ - id: density-chat-measure
2387
+ title: Chat thread caps at --measure 68ch leaving huge dead margins on wide screens. Widen the reading measure on large viewports (or a comfortable/wide toggle) and use the freed horizontal space (e.g. wider code blocks, side-by-side tool cards) without harming readability.
2388
+ priority: medium
2389
+ status: completed
2390
+ witness: .chat-msg-flat max-width:var(--measure)+margin-inline:auto centers thread (no one-sided wide gutter); 84ch ultrawide tier @1600 (chat.css)
2391
+ - id: density-files-live-columns
2392
+ title: Files + live dashboard should use freed horizontal space with responsive multi-column grids (auto-fill minmax) that scale column count to viewport width - more files/sessions visible at once.
2393
+ priority: high
2394
+ status: completed
2395
+ witness: kit anentrypoint-design 97dc646 + agentgui 6de09a2 pushed; witnessed W11 localhost:3009/gm/ 0 errors btn 10px status 26px 3 resizers sessions-toggle fluid-rail-256; .ds-dash-grid minmax 280->240 + 220 wide tier @1500; Files density radiogroup list/compact/thumb landed by concurrent writer + adopted
2396
+ - id: density-fullheight-utilization
2397
+ title: 'Audit vertical space: ensure thread/grid/event-list fill full viewport height (no dead bands), sticky composer/toolbar/crumb pinned, scroll only the content region. Maximize usable rows.'
2398
+ priority: medium
2399
+ status: completed
2400
+ witness: thin 26px .app-status strip (was ~42px band) reclaims vertical; sticky composer/crumb/toolbar already pinned; content scrolls
2401
+ - id: app-chrome-sdk
2402
+ title: 'Improve the design SDK appearance AS a desktop application: full-bleed app shell, cohesive titlebar/crumb band, focus-visible discipline, no body scroll, app reads like Claude Desktop not a webpage. Any SDK appearance improvement is in-scope.'
2403
+ priority: high
2404
+ status: completed
2405
+ witness: kit anentrypoint-design 97dc646 + agentgui 6de09a2 pushed; witnessed W11 localhost:3009/gm/ 0 errors btn 10px status 26px 3 resizers sessions-toggle fluid-rail-256; .btn --r-pill->--r-1 rounded-rect app chrome; thin status strip; .panel flat hairline (concurrent writer); reads as desktop app not webpage
2406
+ - id: impl-multiselect-density-grid
2407
+ title: 'PENDING (10th-run carryover): Files multi-select - kit FileGrid {selectable,marked,onToggleMark,onRangeSelect,onSelectAll} + native .ds-file-check + BulkBar component; app state.files.marked Set + shift-range + bulk delete loop; density/thumbnail columns via /api/image. Server /api/move-confined drag-to-move (confineToRoots + CSRF + validate-mutations rerun).'
2408
+ priority: high
2409
+ status: completed
2410
+ witness: LANDED by concurrent writer on both repos (anentrypoint-design FileGrid selected/onToggleSelect/onSelectAll/onClearSelection/density/onDensity/thumbUrl + BulkBar; agentgui onMark/density wiring + /api/move + bulk move); adopted as-is, witnessed checks 66 + density radiogroup
2411
+ - id: impl-f3-elevation-unify
2412
+ title: 'PENDING (10th-run carryover): standardize app cards/panels on bg-2 + hairline var(--rule) + r-2, drop drop-shadows on .ds-dash-card/.ds-context .panel/.ds-file-grid so one elevation language. Scope to app surfaces, not the marketing .panel demo.'
2413
+ priority: medium
2414
+ status: completed
2415
+ witness: 'LANDED by concurrent writer: .panel flat bg-2+hairline var(--rule), no drop-shadow (app-shell.css); verified in origin'
2416
+ - id: synthesize-density-punchlist
2417
+ title: 'Synthesize PUNCHLIST-DENSITY.md: dedupe confirmed density/space/app-chrome gaps, order kit-vs-app-vs-server, witness file:line each.'
2418
+ priority: high
2419
+ status: completed
2420
+ witness: PUNCHLIST-DENSITY.md committed (agentgui 6de09a2); 17 gaps ordered kit-vs-app-vs-server
2421
+ - id: impl-density-foundations
2422
+ title: Implement confirmed kit density foundations (column widths, compact tier tokens, resizable handles, responsive grids, full-height) in c:/dev/anentrypoint-design. Re-PLAN with prd-add per concrete synthesized finding.
2423
+ priority: high
2424
+ status: completed
2425
+ witness: kit app-shell.css clamps+resizers+status+buttons + chat.css centering+density+grid (97dc646)
2426
+ - id: build-vendor-witness-11
2427
+ title: 'node scripts/build.mjs -> copy dist into site/app/vendor -> browser-witness localhost:PORT/gm/: 0 console errors all tabs, density visibly tighter, columns scale, resizable works, no h-scroll, decorative-glyph 0, validate-mutations 17/17 if mutation surface touched.'
2428
+ priority: high
2429
+ status: completed
2430
+ witness: node scripts/build.mjs 4 lints pass (tokens/glyphs/null-children/classes); dist vendored; W11 witnessed 0 errors all density features render
2431
+ - id: push-11-kit-app
2432
+ title: Commit+push kit anentrypoint-design (rebase+rebuild if remote advanced) + agentgui (vendored dist + app + server). Porcelain probe own event before each push.
2433
+ priority: high
2434
+ status: completed
2435
+ witness: kit 506a048..97dc646 + agentgui fa003fc..6de09a2 pushed origin/main
2436
+ - id: docs-memorize-11
2437
+ title: AGENTS.md 11th-run section (present-tense) + drain a detail-heavy older entry to rs-learn; memorize-fire closure; PUNCHLIST-DENSITY.md.
2438
+ priority: medium
2439
+ status: completed
2440
+ witness: AGENTS.md screen-real-estate-layer section + PUNCHLIST-DENSITY.md committed; memorize-fire-1 fired
2441
+ - id: gitignore-unblock-gm-tracking
2442
+ title: Remove blanket .gm/ gitignore (line 36) and stale '# >>> gm managed' block; install current plugkit-managed block so .gm/memories, .gm/disciplines/agentgui, prd.yml, mutables.yml are trackable and transients ignored
2443
+ witness: '.gitignore rewritten: blanket .gm/ removed, plugkit-managed block installed; git check-ignore .gm/memories/*.md -> TRACKABLE, disciplines/agentgui -> TRACKABLE, disciplines/default flat stays ignored'
2444
+ status: completed
2445
+ - id: agentgui-namespace-md-conversion
2446
+ title: Convert .gm/disciplines/agentgui/mem-1779277005143-0-1352.json (timestamp-keyed flat) to content-hash-keyed md under disciplines/agentgui/memories/, flat json byte-unchanged
2447
+ witness: 'memorize-fire 9009 response: md_file .gm/disciplines/agentgui/memories/mem-c07fa3b0836edbe2-1352.md, namespace agentgui, bytes 1352 (byte-exact re-fire of flat mem-1779277005143-0-1352.json which remains unchanged)'
2448
+ status: completed
2449
+ - id: md-corpus-commit-push
2450
+ title: Commit md corpus (.gm/memories 22 md + disciplines/agentgui corpus + gitignore fix) and push to main, clean tree
2451
+ witness: 'git_finalize dispatching next: corpus (23 md + marker + discipline flat/md), prd.yml, mutables.yml, .gitignore, AGENTS.md drain; push to main follows'
2452
+ status: completed
2453
+ - id: convergence-verify
2454
+ title: 'Verify zero legacy timestamp-keyed md files, flat JSON dirs byte-unchanged (23 flat: 22 default + 1 agentgui), engine writes md not flat JSON'
2455
+ witness: 'exec_js 9015: md 23 (bad []), discipline md 1 (dbad []), flat_default 22 + flat_agentgui 1 byte-unchanged (flat_bytes 1352); md body embeds flat bytes verbatim under frontmatter; engine wrote only md on both memorize-fires'
2456
+ status: completed