build: desktop-tabbed-app - #435
Conversation
…wn-TDD tests red)
|
Advisory review (codex, read-only, one-shot — findings are input for the human):
|
|
stark-build run summary (run
Closing e2e: RED at Next moves are the operator's: (a) resume with a fresh run for T2+T5 remainders (small, well-named deltas) then T7–T14, (b) finish T2/T5 interactively, or (c) rescope. 🤖 Generated with Claude Code |
|
Round 2 close-out (operator-directed, scoped to the T2+T5 remainders):
Closing e2e: GREEN (exit 0) — PR stays draft on purpose: 6 of 14 spec tasks are delivered (T1–T6). The window shell exists with working tabs/IPC/bundling, but T7–T14 (brain-invoke, config-tab port + dropdowns + detection, YAML editor, stats/chat/ingest panels, docs close-out) are not built. Operator decides whether to continue the run. 🤖 Generated with Claude Code |
|
Round 3 summary (operator-directed: continue T7–T14):
Environment note: State: 12/14 green, suite 825 + 79 a11y at 🤖 Generated with Claude Code |
…ingest registered for real registerChatIpc/registerStatsIpc/registerIngestIpc had NO callers and no production InvokePort existed: green suites, dead tabs (T11/T12 latent deviation). app-shell gains engineInvoke + ingest deps with teardown; index.ts binds startInvoke on the launch runtime's entrypoint resolution, the native open panel (INGEST_EXTENSIONS filter) and nodePasteFilePort. Behavioral registration cases + static index.ts gate in app-shell.test.ts.
|
RUN COMPLETE — 14/14 tasks green. Final commit T13 (ingest tab) closed interactively: the inherited pipeline module was sound; a second operator session contributed the three test files ( T14 (docs + packaging): green by composition — non-desktop suites ✓, desktop suite minus one file 875 ✓, contract clean, a11y 86 ✓, full build ✓, packaged The one asterisk: Suite trajectory across the run: 565 → 875+ tests, 28 → 86 a11y. The human live-drive checklist from the spec's Verification section remains owner-run: Cmd+1..4, one chat message, one paste-ingest preview→apply, detect models, break + fix the YAML. 🤖 Generated with Claude Code |
|
CI green across the board — both 🤖 Generated with Claude Code |
…y CodeMirror, VS Code Dark+ palette Live-drive feedback: the raw **bold** blob becomes a read-only CodeMirror view with markdown grammar and a Dark+ token palette (all colors clear 4.5:1 on #1e1e1e). Injectable createAnswer seam keeps the fake-DOM suite honest; degraded path (no seam / non-DOM mount) is the old plain text. Adds @codemirror/lang-markdown + @codemirror/language + @lezer/highlight.
…injected style element was CSP-blocked style-src 'self' blocked style-mod's <style> injection, so both editors rendered with ZERO styling (no tokens, no theme) — shipped as 'the colors do not work'. Styles-only relaxation, this document only; scripts stay 'self'; popover keeps the strict policy; the app-tabs CSP gate now asserts per-directive. Probe-verified computed colors: bg #1e1e1e, tokens #569cd6/#c586c0/#ce9178/#6796e6/#9d9d9d. Fallback catch logs loudly.
…n button Live-drive round 3: the CodeMirror source view comes out; answers render as actual bold/headings/lists/code, built exclusively via createElement + textContent over the Lezer CommonMark tree (no markup sink; headings are styled paragraphs so the model cannot join the app's outline; [[wikilinks]] re-fused from the parser's bracket+Link reading). Copy button holds the RAW markdown behind an injected clipboard seam; outcome announced via the status line. Theme-aware light/dark via the app's own CSS vars.
…ing reached the screen
sections.ts escapes intra-heading slashes (%→%25, /→%2F) so breadcrumbs
stay splittable; citations rendered the wire form raw ('Role %2F
Relationship'). sectionPathText() is the display-side inverse per segment,
re-joined with ›.
… only above The 2026-07-27 vault migration (87f5ede) versioned the typed-related shape Atlas has required since #331 as schema_version 2, per the vault's shared Schema-versions doctrine (brain-cli reads 1..built-with). Atlas's reader pinned the literal 1 with an exact match and fail-closed the whole vault. SCHEMA_VERSION → 2 in contracts (writers emit 2); the reader accepts 1..2 and errors only on a NEWER note, naming the upgrade.
…ut that note Each citation row is now a button (accent-styled, aria-labelled 'Ask about <note-id>') that fires ask(noteId): the query identity layer short-circuits on the exact id, so the answer comes back grounded in that note. ask() gains an optional explicit question; a citation click never clears the composer's draft and respects the single-flight.
…ck asks about the note The rendered answer's [[wikilinks]] drop their brackets and become the same ask-about-this-note buttons the citations are (accent link styling, 'Ask about <id>' labels, composer draft untouched, single-flight held). renderMarkdownAnswer gains an optional onNoteClick; without it the wikilink stays a styled span.
…pport 'Cite each supporting note' read as per-claim stamping: a single-source answer arrived with the same [[citation]] on every sentence. citedNoteIds detects presence, not count, so once is enough.
The bundle was `LSUIElement` — menubar-only, no Dock tile, no Cmd-Tab entry. That was right when the app was a readiness glyph and nothing else. The tabbed main window (#435) made it a surface the operator works in, so the plist key goes and Atlas becomes a regular app. A Dock tile that does nothing when clicked is worse than none, so `activate` opens the main window: raise-only when one is up — `openMainWindow` with the tab already showing never navigates, so the chat transcript survives every Dock click — and a fresh window at Chat when the operator closed it. The popover is deliberately not what a Dock click opens; it is positioned under a menubar item the click did not come from. `activate` is GATED ON STARTUP HAVING FINISHED. Electron fires it on "launching the application for the first time" as well as on a reopen (its own typings say so), and Atlas is an operator login item — ungated, the main window would land on the screen at every login, which is the opposite of a menubar app. `started` flips as the last act of `start()`, so the discriminator is "startup finished" rather than a timing guess. Gates: - `test/dock-presence.test.ts` keeps `<key>LSUIElement</key>` out of the packaged plist, plus a companion assertion so the negative cannot pass against a script that stopped emitting a plist at all. - `test/main-window.test.ts` pins the login-item guard with a case that holds `session.init()` open, so the emit lands *after* the listener is registered. Emitting earlier passes against a handler with no guard whatsoever — the first version of that test did exactly that, and was verified to fail against the unguarded handler before being kept. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Stage-2 stark-build run. Spec: docs/specs/2026-07-27-desktop-tabbed-app-spec.md (spec PR #434, accepted-base 686807a). Run: /Users/aryeh/.claude/code-review/history/build/desktop-tabbed-app/20260727T082622Z