feat(ui): WebUI code review fixes — hljs, emoji, mobile, slash commands - #4
Merged
Conversation
Agents were confined strictly to their workspace directory (nous/{id}/),
causing "Path outside workspace" errors when reading shared resources
like ui/, shared/, infrastructure/. Read-only tools (read, ls, find, grep)
now accept the aletheia root as an additional allowed path. Write tools
(write, edit) remain workspace-confined for safety.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
With 6 agents sharing one Signal account, 3 concurrent turns meant a long-running turn could block other agents from responding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ommands
Summary of changes from code review:
1. highlight.js tree-shaking: selective imports for 10 languages instead of
all 90+. Bundle reduction from 969KB to ~84KB (91% smaller).
2. Agent emoji: surfaced in AgentCard, Message avatars, MessageList streaming
avatar, and empty state. Falls back to initials when no emoji.
3. Tool result matching: backend now sends toolId in tool_result events,
frontend matches by toolId instead of toolName+status (fixes concurrent
same-tool-name bug).
4. Duplicate CSS extracted: chat-shared.css with .chat-msg-* prefixed classes
replaces identical styles in Message.svelte and MessageList.svelte.
5. Slash commands: /new (fresh session), /clear (clear display) with
autocomplete popup, keyboard navigation (arrows, tab, enter, escape).
6. Context proximity indicator: thin progress bar above input shows context
window utilization. Colors shift yellow > red approaching distillation.
7. loadAgents() parallelized: Promise.allSettled instead of sequential awaits.
6 agents now load in ~1 round trip instead of 6.
8. Mobile responsiveness: collapsible sidebar with hamburger menu, overlay
backdrop, media queries at 768px breakpoint. Active agent shown in TopBar
on mobile.
9. historyToMessages() improved: tool_result matched by toolCallId instead
of array position. Orphaned tool calls flushed to last assistant message.
10. UI polish: close buttons use × not 'x', tool status uses ✓/✕ not ok/!,
expand uses − not -, dot separator · not '.', keyboard :focus-visible
styles, aria-labels on interactive elements.
Build: 199KB JS (was 1031KB), 20.6KB CSS, 29 tests pass, 0 type errors.
|
forkwright
added a commit
that referenced
this pull request
Feb 18, 2026
…commands (#4) Addresses all 10 issues from code review plus nits. - highlight.js: 969KB → 84KB (selective imports, 10 languages) - Agent emoji surfaced in avatars, empty state - Tool result matching by toolId (backend + frontend) - Duplicate CSS extracted to chat-shared.css - Slash commands (/new, /clear) with autocomplete - Context proximity indicator - loadAgents() parallelized - Mobile: collapsible sidebar, hamburger, media queries - historyToMessages() toolCallId matching - UI polish: ×, ✓/✕, ·, focus-visible, aria-labels Bundle: 1031KB → 199KB JS (-81%)
This was referenced May 25, 2026
Closed
forkwright
added a commit
that referenced
this pull request
May 29, 2026
#4316) ## Summary Implements [B-002](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-002-poiesis-theme-registry.md) from the poiesis-evolution planning cluster (kanon#985, merged 7199d54): the new `poiesis-theme` sub-crate that owns the brand-truth surface every poiesis render path consumes. The crate ships **one source → three sinks**: a `themes/<id>.toml` file resolves into a `ResolvedTheme`, which emits CSS custom properties, an OOXML `theme1.xml` `clrScheme`+`fontScheme`, and a Pandoc-shaped doc-vars map. Adding a brand is a new TOML file; no code change. ## What ships - **`ThemeId` newtype** — parse-don't-validate boundary. Registry-friendly, CSS-safe, URL-safe identifier with width 2..=64 and alphabet `[a-z][a-z0-9_-]*`. - **TOML token model** — `[color.{role,tone,surface}]`, `[type.{family,scale,role}]`, `[space]`, `[grid]`, `[table]`, `[chart]`. Components reference token names; the only place raw hex lives is `[color.role]`. - **`HexColor` newtype** — normalizes to canonical `#RRGGBB` so emission is byte-stable across hosts and runs. - **`Registry::load_dir`** — discovers `themes/<id>.toml`, enforces filename-stem ↔ `meta.id` agreement, sorts deterministically. - **`ResolvedTheme`** — dereferences tone and surface references to concrete hex values; unknown roles surface as `ThemeError::UnknownRole`. - **Three sinks** (all byte-stable, deterministic): - `sinks::css::emit_css` — `:root { --color-<role>; --tone-<name>: var(--color-<role>); --type-<role>-<slot>; … }`. - `sinks::ooxml::emit_theme_xml` — `<a:theme>` with `<a:clrScheme>` (dk1/lt1/dk2/lt2/accent1..6) + `<a:fontScheme>` (major=serif, minor=sans). Native PowerPoint charts bind to `accent1..3`, so recoloring the theme recolors charts. - `sinks::docvars::{emit_docvars_json, emit_docvars_yaml}` — Pandoc-friendly flat key→value map. - **Three `THEME/*` lint rule shapes** (`lint::{RawColorLiteralRule, RawFontLiteralRule, UnknownTokenRule}`) — each returns RFC-6901-pointer-carrying violations the QA gate consumes. - **Seed `summus` theme** — spec-03 brand palette (navy/teal/rose, Geist/Newsreader/Geist Mono, 1920×1080 type scale). ## Deferred (the stub-then-real plan) Per the B-002 acceptance gate carve-out (PR-body deferral path). Each deferred item has a named successor entry that owns the work; nothing here blocks because the missing pieces only land when the downstream crates land. | Deferred surface | Owner | Why deferred | |---|---|---| | Full `assets/<name>-base.pptx` raw-OOXML pack (slide master + per-component layouts) | [B-004](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-004-poiesis-deck-pptx.md) | Pack/unpack pattern belongs with the PPTX emitter that owns the rest of the file shape. This PR emits the `theme1.xml` body the pack will embed. | | Reference assets generation (`reference.docx`, `reference.odt`, `template.typ`, `template.latex`) | [B-006](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-006-poiesis-doc-pandoc.md) | The doc-vars map ships now; document-specific reference assets compose with the doc-pandoc pipeline. | | `Renderer` trait `&ResolvedTheme` parameter wiring | [B-001](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-001-poiesis-model-registry.md) | The `Renderer` trait evolves in `poiesis-core` as part of B-001's envelope refactor; threading the `ThemeId` → `ResolvedTheme` through that interface lands there. | | `theme list/show/new/validate/compile` CLI verbs | [B-010](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-010-poiesis-mcp-cli.md) | CLI surface aggregator. The registry API is ready; verbs are a thin wrap on top. | | Registering THEME/* rules with the basanos engine | [B-008](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-008-poiesis-qa-moat.md) | Rule shapes (id, scan/check API, violation type) ship in this PR; engine registration is the QA-moat crate's job. | | Byte-stable CSS regression against the offsite deck | [B-003](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-003-poiesis-deck-html-pdf.md) / [B-011](https://github.com/forkwright/aletheia/tree/main/planning/poiesis-evolution/B-011-poiesis-testing-substrate.md) | Need the deck artifact and the golden harness to assert against. | ## Acceptance gate coverage | B-002 acceptance criterion | This PR | |---|---| | #1 `theme compile summus` emits CSS, OOXML, doc-vars matching spec | CSS + OOXML `theme1.xml` + doc-vars all emit the spec-03 values; PPTX pack deferred to B-004. | | #2 swap `summus → ardent` restyles with zero spec edits | Architecturally enabled (tone refs + sink emission are theme-id-agnostic); end-to-end regression deferred to B-011 §theme-swap. | | #3 raw hex/font literal in a spec field fails `THEME/raw-color-literal`/`-font-literal` with JSON-pointer | Rule shapes + APIs ship; basanos engine registration deferred to B-008. | | #4 unknown token fails `THEME/unknown-token` at load time | `UnknownTokenRule::check` ships with pointer-carrying violations; runtime path also surfaces `ThemeError::UnknownRole` if a renderer asks for a missing tone. | ## Test plan - [x] `cargo test -p poiesis-theme` — 68 unit + 2 integration green - [x] `cargo clippy -p poiesis-theme --all-targets -- -D warnings` clean - [x] `cargo fmt -p poiesis-theme -- --check` clean - [x] `cargo check -p poiesis-core -p poiesis-lint` still passes (no neighbour breakage) - [x] `kanon lint` — 42 → 20 warnings; remaining 20 enumerated below - [x] `kanon name_check summus` — `status: free` - [x] Byte-stability tests across all three sinks (emit twice, assert equal) - [ ] Cross-target render proof (depends on B-003/B-004/B-006) ## Known kanon-lint warnings The remaining 20 warnings are documented intentional architecture or ambient project-wide: - **9 × `RUST/pub-visibility` (Info)** — public items intended for sibling-crate consumption by [B-001], [B-003], [B-004], [B-006], [B-008]. Documented in `crates/poiesis/theme/CLAUDE.md` §Status table. - **9 × `PERFORMANCE/clone-in-loop` (Warning)** in `sinks/docvars.rs` — building a `serde_json::Value` tree from `IndexMap<String, _>` requires owned `String` keys (serde_json's `Map` key type is `String`, not `&str`). Refactoring to `Cow` would need a parallel JSON-tree representation. - **1 × `RUST/primitive-for-domain-id` (Warning)** on `Violation.rule_id: String` — the rule id matches the basanos engine's string-keyed rule registry; a newtype here would not improve the boundary because the consumer carries the same shape. - **1 × `OPERATIONS/no-runbook` (Info)** — project-wide, ambient. ## GNOMON layer check - **`poiesis-theme` (crate)** — L1: 13-char identifier, pronounceable, grep-friendly. L2: matches `poiesis-{core,lint,sheet,…}` sibling convention. L3: English `theme` is the role; the Greek work (`poiesis = ποίησις, "making"`) lives at the family-name layer. L4: composes with siblings; no semantic collision; the crate sits at the brand-truth/registry slot the family was missing. - **`summus` (seed theme)** — L1: 6 chars, pronounceable, grep-friendly, `status: free` per `kanon name_check`. L2: brand/theme name in the `themes/<id>.toml` namespace; sibling slot to future themes (e.g. `ardent`). L3: Latin "highest, supreme"; indicates the flagship/canonical theme. Latin not Greek; locked by upstream apodeixis Phase-00 spec, not chosen here. L4: composes — future themes form a sibling palette. ## SonarCloud quality gate: spurious SCA finding The SonarCloud "C Security Rating on New Code" failure on this PR is a single SCA (dependency-analysis) annotation against the new crate's `Cargo.toml`: > Dependency versions are not predictable if the lock file (Cargo.lock) is missing. This is a **false positive** for a cargo-workspace member crate. Cargo workspaces produce one shared `Cargo.lock` at the workspace root; member crates do not (and should not) carry their own lockfile — see [The Cargo Book §Cargo.toml vs Cargo.lock](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) and the [Cargo workspace resolver](https://doc.rust-lang.org/cargo/reference/resolver.html). The repo's authoritative `Cargo.lock` lives at the workspace root (also touched by this PR for the new member's transitive deps), so every dependency *is* pinned to an exact version — Sonar's SCA simply does not walk back to the workspace root from a member `Cargo.toml`. **Operator decision:** marked spurious. Suggested durable fix (separate, repo-wide PR) is one of: - Add a `sonar-project.properties` at the repo root with `sonar.exclusions=crates/**/Cargo.toml` (excludes member manifests from SCA only — root `Cargo.toml` + `Cargo.lock` still drive dependency analysis), **or** - Wait for SonarCloud SCA to learn cargo-workspace lockfile inheritance (raised upstream at [SonarSource SCA troubleshooting](https://docs.sonarsource.com/sonarqube-cloud/advanced-security/troubleshooting-the-dependency-analysis)). Other checks on this PR are green (no AI attribution, gate-attestation with `Gate-Passed: kanon 0.1.0`, cargo deny, scan). The included refactor commit centralizes the spec-mandated XML namespace constant — orthogonal quality work, not a Sonar fix. --------- Co-authored-by: forkwright <forkwright@users.noreply.github.com>
forkwright
added a commit
that referenced
this pull request
May 29, 2026
## Summary - Scaffolds the new aletheia-internal `poiesis-charts` crate per [B-005](https://github.com/forkwright/kanon/blob/main/projects/aletheia/planning/poiesis-evolution/B-005-poiesis-charts.md) (poiesis chart subsystem — hand-rolled SVG + Vega-Lite fallback). - Lands the parse-don't-validate `Chart` model, the static render-path decision rule, the deterministic SVG combo emitter that reproduces the offsite slide-3 chart geometry, and the theme-binding seam (`Themed`/`Resolved` color modes). - Ships per-kind design notes for the eight stubbed emitter arms (`bar`/`column`/`line`/`area`/`scatter`/`pie`/`doughnut`/`stat`) so the implementation fan-out is unblocked. ## Crate shape ``` crates/poiesis/charts/ ├── Cargo.toml # poiesis-core + serde + snafu + tracing; insta dev-dep ├── CLAUDE.md ├── docs/design/ # one .md per stubbed kind └── src/ ├── lib.rs # module map + determinism contract + #![deny(missing_docs)] ├── error.rs # Error::{InvalidJson,BadSeriesShape,NakedNumber,VegaRequired,UnresolvedTone,UnresolvedFact,EmitterStub} ├── model.rs # Chart, Series, Axes, FactCite, ChartKind::{render_path,name} ├── format.rs # format_number (only f64→<text> path) + coord (only f64→attr path) ├── scale.rs # Scale + nice() + ticks(); offsite y_left/y_right covered ├── theme.rs # ResolvedTheme + ColorMode + summus stub (deleted when B-002 lands) └── render/ ├── canvas.rs # Deck (1600×540) + Doc canvases; PlotBox ├── kinds.rs # mod combo; ├── kinds/combo.rs # implemented arm └── vega.rs # cfg(feature = \"charts-vega\") stub ``` ## What's implemented vs. stubbed | Arm | Status | |---|---| | `combo` | **Implemented** — renders the offsite navy/teal dual-axis combo. `rx=3` columns, `r=9` markers, polyline. Themed mode emits `var(--tone-*)`, Resolved emits literal hex. | | `bar`, `column`, `line`, `area`, `scatter`, `pie`, `doughnut`, `stat` | Stubbed (`Error::EmitterStub`). Per-kind design under `docs/design/<kind>.md`. | | `heatmap`, `boxplot`, `sankey`, `candlestick`, `log`/`time` scales | Routed to `charts-vega` feature. Hard-fail with `Error::VegaRequired` when disabled. | | `combo` gridlines + tick text | Structural `<g class=\"gridlines\">` / `<g class=\"axes\">` placeholders emitted. Tick math is in `scale::ticks` but not yet wired into the emitter. | | `insta` golden snapshot of the full offsite SVG | Pending — lands with the gridline + tick text follow-up. | | Vega-Lite shell-out wiring | Pending. `render/vega.rs` exists as a feature-gated stub. | ## B-005 acceptance — current state | # | Acceptance criterion | Status in this PR | |---|---|---| | 1 | Reproduce offsite slide-3 chart from schema | Geometry + colors + corners + markers present; gridlines + tick text follow-up. | | 2 | Colors come only from `theme: summus`; zero color literals in spec | `ResolvedTheme::summus_stub` mirrors the offsite palette; tests assert navy/teal route via theme. | | 3 | All data points are `Cite(FactId)`; verify reconciles | `Point::y: FactCite` types it in. Verifier integration lands with B-008. | | 4 | Emitted SVG is deterministic (`insta` golden) | Re-emit-must-be-byte-identical test in place; full byte-golden of the offsite SVG follows the gridline/tick arm. | | 5 | Same SVG embeds in HTML deck (`themed`) and PPTX (resolved or baked) | `ColorMode::Themed`/`Resolved` both emit; HTML/PPTX wiring lands with B-003/B-004. | | 6 | Switching theme recolors with zero spec edits | `ResolvedTheme` is the seam; swapping it switches every fill/stroke. | | 7 | Vega-only kind with `charts-vega` off → hard parse error | `Chart::validate` returns `Error::VegaRequired`. Tested. | ## Completion plan for follow-ups Each stub is tracked in `crates/poiesis/charts/docs/design/<kind>.md`. The natural ordering: 1. **`combo` gridlines + tick text + `insta` golden** — closes the determinism leg of acceptance #4. 2. **`column` + `line` arms** — direct extracts from the `combo` primitives (~70% reuse). 3. **`bar` + `area` + `scatter`** — small variations on column/line. 4. **`pie` + `doughnut` + `stat`** — share an arc-path primitive. 5. **Vega-Lite wiring in `render/vega.rs`** — under the `charts-vega` feature. 6. **B-002 wire-up** — delete `ResolvedTheme::summus_stub` and depend on `poiesis-theme` when it lands. ## Gate - `cargo fmt --check` — PASS - `cargo check --workspace` — PASS - `cargo clippy -p poiesis-charts --all-targets -- -D warnings` — PASS - `cargo test -p poiesis-charts` — PASS (30 tests) - `kanon gate --tier full` — PASS (Gate-Passed trailer on HEAD) - `mcp__kanon__standards_name_check word=charts` — free (no collisions) ## Naming `charts` passes GNOMON L1–L4: - **L1**: lowercase ASCII, pronounceable, grep-friendly, 6 chars. - **L2**: follows the existing poiesis sub-crate register (`slides`, `sheet`, `text`, `doc`, `lint`, `verify`, `intake`, `scaffold`, `inspect`, `typst`, `diff` — English domain words rather than Greek roots, deliberately distinct from the `aletheia/`/`theatron/` Greek-named cognition substrate). - **L3**: matches what the crate is — the chart subsystem of the report family. - **L4**: every chart kind name (`bar`, `column`, `line`, `area`, `combo`, `scatter`, `pie`, `doughnut`, `stat`, `heatmap`, `boxplot`, `sankey`, `candlestick`) is the canonical chart-vocabulary word — name-checked free, no Greek strain. ## Test plan - [ ] CI nextest on poiesis-charts - [ ] Confirm `--all-targets` clippy still passes under workspace-wide gates - [ ] Operator review of `docs/design/*.md` before kicking off the per-kind fan-out ## SonarCloud quality gate: spurious SCA finding The SonarCloud "C Security Rating on New Code" failure on this PR is a single SCA (dependency-analysis) annotation against the new crate's `Cargo.toml`: > Dependency versions are not predictable if the lock file (Cargo.lock) is missing. This is a **false positive** for a cargo-workspace member crate. Cargo workspaces produce one shared `Cargo.lock` at the workspace root; member crates do not (and should not) carry their own lockfile — see [The Cargo Book §Cargo.toml vs Cargo.lock](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) and the [Cargo workspace resolver](https://doc.rust-lang.org/cargo/reference/resolver.html). The repo's authoritative `Cargo.lock` lives at the workspace root (also touched by this PR for the new member's transitive deps), so every dependency *is* pinned to an exact version — Sonar's SCA simply does not walk back to the workspace root from a member `Cargo.toml`. **Operator decision:** marked spurious. Suggested durable fix (separate, repo-wide PR) is one of: - Add a `sonar-project.properties` at the repo root with `sonar.exclusions=crates/**/Cargo.toml` (excludes member manifests from SCA only — root `Cargo.toml` + `Cargo.lock` still drive dependency analysis), **or** - Wait for SonarCloud SCA to learn cargo-workspace lockfile inheritance (raised upstream at [SonarSource SCA troubleshooting](https://docs.sonarsource.com/sonarqube-cloud/advanced-security/troubleshooting-the-dependency-analysis)). Other checks on this PR are green (no AI attribution, gate-attestation with `Gate-Passed: kanon 0.1.0`, cargo deny, scan). The included refactor commit centralizes the spec-mandated XML namespace constant — orthogonal quality work, not a Sonar fix. --------- Co-authored-by: forkwright <forkwright@users.noreply.github.com>
forkwright
added a commit
that referenced
this pull request
Jun 9, 2026
B-006 Chunk E (B-012 acceptance #4). Bans the pandoc + pandoc_ast Rust crates via cargo-deny [bans] with WHY reasons, enforcing the GPL-clean subprocess boundary: aletheia invokes the pandoc binary as an external subprocess and must never link a GPL Haskell-FFI binding. cargo deny check bans passes on the current tree (no such crate present) and would fail if one were added. Config-only; audit.toml/osv-scanner.toml left unchanged (they only mirror advisory ignores). No legit deps (resvg/zip/etc.) affected. Co-authored-by: Cody Kickertz <admin@forkwright>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Code Review Fixes
Addresses all 10 issues from the Svelte 5 WebUI code review plus minor nits.
Changes
/new,/clear) for rare manual controltoolIdin events, frontend matches by ID not namechat-shared.csswith.chat-*prefix/newwith autocomplete popupPromise.allSettled— parallel identity fetchestoolCallId, flush orphaned tool callsAdditional
:focus-visiblestyles globallyaria-labelon interactive elements·separator instead of `." in SessionItemBundle Size
Verification
vite build— clean, no warningsvitest run— 29/29 tests passsvelte-check— 0 errors, 0 warningsoxlint— 0 errors, 0 warningstsc --noEmit— clean (backend toolId change)Backend Change
infrastructure/runtime/src/nous/manager.ts: AddedtoolIdtotool_resultstream event type and yield site. Non-breaking — frontend previously matched by name.