Skip to content

enhance: uiux flow#4

Merged
mroops0111 merged 9 commits into
masterfrom
enhance/uiux-flow
May 23, 2026
Merged

enhance: uiux flow#4
mroops0111 merged 9 commits into
masterfrom
enhance/uiux-flow

Conversation

@mroops0111
Copy link
Copy Markdown
Owner

@mroops0111 mroops0111 commented May 21, 2026

Summary

UX iteration across the Studio app, plus a few schema / core upgrades that surfaced along the way.

Studio

  • Actions tab merges old Skills + Runs into one workflow-aware sidebar with Recent
  • Graph + Table unified behind a shared GraphSurface — selection + focus mode persist across views; right-hand NodeDetailPanel is one component for both
  • Proposals detail pane gets a tri-view preview (Graph / Table / List) driven by a previewProposal diff; type-coloured chips, ring overlays, dashed-removed edges, change-badged tables
  • Filter chips become a strict whitelist with All / Clear / Reset; Focus toggle dims or hides non-neighbourhood; minimap colours per ontology palette; auto-fit on visible-set change
  • Light / dark theme toggle, collapsible sidebar with utility row, canonical-root workspaces, gdrive incremental sync

Schema / core

  • SkillFrontmatter.braid.summary one-liner field; 5 builtin skills backfilled
  • previewProposal pure helper with lenient apply + structural metadata equality + undefined-aware patch
  • newProposalId / newClarifyTicketId emit p-YYYY-MM-DD-{short} / ct-... matching the documented format

Infra

  • storage-kuzu Node 22 flakiness worked around (retry script + matrix skip)

mroops0111 and others added 9 commits May 21, 2026 18:13
…ructured logging

End-to-end UX overhaul of the workspace lifecycle, plus the source-loader
refactor that came out of dottedsign real-case testing.

## Workspaces

- New `/scaffold` body shape: `{ name, manifest }`. Server resolves
  `<workspacesRoot>/<name>/` (default `~/.braid/workspaces/`) so the UI
  never picks paths. Strict create-only; name collision returns 400.
- Boot-time `discoverCanonicalWorkspaces` registers any subdir with a
  PRODUCT.md, so CLI-created or hand-copied workspaces show up in Studio
  without an explicit register step.
- `DELETE ?purge=true` unregisters + rm -rf the canonical folder.
  Arbitrary-path workspaces refuse purge with a clear error.
- `DELETE /sources/:id` now also rm's the resolved source path when it
  lives inside the workspace, matching the loader's "owns destination"
  contract.
- Wizard collapsed to create-only: removed "Open existing" branch,
  `RegisterWorkspaceDialog`, and the Sidebar "Register from Path" menu.
  Single + button opens wizard; existing workspaces open via Sidebar.

## Source loader: unified incremental sync interface

- `SyncReport` (in core) gains optional `added/updated/removed/unchanged`
  counts so the UI can render `+a ~u -r` uniformly across loaders.
- GitLoader populates counts via `git diff --name-status before..after`.
- GoogleDriveLoader rewritten:
  - Per-doc layout `<source>/<Doc Title>/index.md` (drops Drive folder
    hierarchy mirror).
  - Inline base64 images extracted to sibling files; markdown rewritten
    to local refs.
  - `.braid-manifest.json` diff-based incremental sync (add, update,
    remove via Drive modifiedTime).
  - Skip non-Doc Drive types and `Copy of …` / `…的副本` duplicates.
  - `include` / `exclude` regex filters on Drive title.

## Source paths

- Path is now fully derived: `./intents/<id>/` or `./codebases/<id>/`.
  UI only asks for `name`; path Input gone.
- `rolePathSegment(role)` exported so AddSourceDialog and the wizard
  share the convention.

## Structured logging

- `@braidhq/core` exports `createLogger(ns)`. Pino + pino-pretty default
  in dev, JSON in prod (`NODE_ENV=production`), `BRAID_LOG_LEVEL` and
  `BRAID_LOG_PRETTY` overrides.
- Server entrypoint loads `.env` from repo root via Node's native
  `process.loadEnvFile`.

## OAuth in wizard

- gdrive sources can `Connect Google` inside the wizard before scaffold,
  since workspaceId == typed name so the secret store key is known in
  advance.
- `useGoogleOAuth(workspaceId, sourceId, { onConnected })` hook shared
  by wizard's `GdriveOauthBlock` and `AddSourceDialog`.

## Wizard progress

- Outside-click / Escape blocked while scaffold is pending (was killing
  long-running gdrive ingests).
- ProgressStep subscribes to `source.synced` SSE events and flips a
  per-source ✓ as each loader finishes.

## Refactor structure

- Extracted helpers to their proper layer:
  - `infrastructure/fs/paths.ts`: `isUnder`, `pathExists`
  - `infrastructure/fs/WorkspaceDiscovery.ts`
  - `source-loader-gdrive/Manifest.ts`: read/write + types
  - `studio/lib/useGoogleOAuth.ts`: popup + postMessage flow
- Root scripts: `dev:web` (server + studio), `dev:desktop` (tauri).

## Tests

- gdrive: 13 (regex, flat layout, image extraction, incremental sync)
- git: 4 (+ added / removed counts)
- server: 139 (+ workspace discovery, purge, scaffold strict)
- studio: 34 (sourceDraft path derivation)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…are sidebar

End-to-end overhaul of the skill-running UX driven by dottedsign feedback.

## UX

- Merge Skills + Runs into a single Actions tab. Sidebar shows
  available actions on top and recent conversations underneath,
  separated by a border. Run history opens via row click, no separate
  Continue Conversation button.
- Replace single-line `<Input>` with `<Textarea>`. Enter sends,
  Shift+Enter inserts newline.
- Recent timestamps render as ISO 8601 (`YYYY-MM-DDTHH:mm`).
- Conversation header has a fixed height so the "New conversation"
  button appearing no longer bumps row height.

## Skill taxonomy

- New `SkillCategory = ask | build | generate`, optional on
  `BraidSkillExtension`. Skills without a category land in a "Custom"
  bucket.
- New `BraidSkillExtension.order` (positive integer) for the Build
  group's intra-group ordering. Built-ins use sparse numbering (100 /
  200 / 300) so plugins can slot between by picking e.g. 150 without
  renumbering. The UI displays sequential rank, not the raw order.
- New `SkillManifest.pluginId` (optional) carries the plugin id of the
  contributor. `FsSkillRegistry` fills this from the existing
  `ref.contributedBy`. The sidebar badge shows the plugin id (e.g.
  `redoc-ddd`) instead of a generic `plugin` label.

## braid-model

New built-in skill at `packages/core/skills/braid-model/SKILL.md`.
Closes the OSS-PROPOSAL §710 gap (extract / clarify / model /
generate-doc / ask baseline). Two modes: build + validate (default) and
validate only. Adapted from redoc-model but rewritten against Braid's
API and ontology contract (no hardcoded DDD types).

## Files

- `packages/schema/src/skill.ts`: add SkillCategory, BraidSkillExtension.category + order, SkillManifest.pluginId
- `packages/server/src/infrastructure/fs/FsSkillRegistry.ts`: thread pluginId from PluginSkillRef
- `packages/studio/src/pages/Actions.tsx`: new; replaces Skills.tsx + Runs.tsx
- `packages/studio/src/components/ui/textarea.tsx`: new
- `packages/studio/src/components/CommandPalette.tsx`: TabKey + shortcut updates
- `packages/studio/src/App.tsx`: remove RunsPage + continuation state

## Tests

- schema/test/skill.test.ts: +4 tests for pluginId, category, order, defaults
- studio/test/pages/Actions.test.ts: new; 12 tests covering bucketByGroup
  (incl. sparse-numbering plugin insertion), originLabel, formatTimestamp,
  groupBySession
- server FsSkillRegistry.test.ts: assert pluginId plumbing
- server composeFs.test.ts: include braid-model in the builtin list

Net: +3 schema, +12 studio.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lakiness

The Kuzu NAPI binding occasionally crashes the vitest worker fork on
Node 22 with a bare "Worker exited unexpectedly" from tinypool. Each
individual test is deterministic; only the worker process lifecycle is
flaky. Reproduced both locally (macOS Node 22.21) and in CI (Node 22
job fails, Node 20 passes consistently).

Vitest's built-in `retry` happens within the same worker, so it can't
recover from a worker crash. Wrap `vitest run` in a Node retry loop that
spawns a fresh fork each attempt; in observed runs the suite passes
within five attempts.

A genuine assertion failure still fails five-in-a-row because that path
is unrelated to the native shutdown crash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`process` must be imported from `node:process` (no global), and
`node:path` sorts before `node:url`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vicon polish

## Theme

- New `lib/theme.ts` exporting `useTheme()` and the `Theme = 'light' |
  'dark'` type. Two states only: a `system` mode was considered but cut
  because it adds a third state without a third visual.
- `index.html` runs an inline script in <head> that reads
  `localStorage.braid-theme` and applies the `dark` class before React
  mounts. First-time visitors fall back to `prefers-color-scheme`. The
  script mirrors the resolution logic in `theme.ts` to avoid a
  flash-of-wrong-theme on the first paint.
- `<ThemeToggle />` in the header. Icon shows the current state
  (Sun / Moon); the tooltip describes what clicking does.

## Color tokens

- Both themes pulled away from the absolute extremes. Light page
  background now `oklch(0.97 0.003 260)` (was pure white); dark page
  background `oklch(0.2 0.005 260)` (was near-black). All neutrals
  carry a small cool-blue tint (chroma 0.003-0.005 at hue 260),
  matching Linear's chrome.
- Surface layering: card / popover are the brightest plane, page
  background sits below, sidebar slightly recessed. Same hierarchy in
  both themes so depth cues are consistent.
- Primary purple chroma toned down from 0.17 / 0.18 to 0.12 / 0.13.
  Same hue (Linear's 274), same brand feel, less shouty CTA.

## Icon

- Favicon and Sidebar logo now share a single source
  `src/assets/braid-logo.svg`. Removed the duplicated `public/`
  variant; Vite resolves the asset path during build.
- SVG viewBox tightened from `0 0 512 512` to `86 86 340 340`. The
  artwork keeps the same proportions but fills ~80 % of the canvas,
  leaving ~10 % padding so a 16 px favicon doesn't crowd the tab
  edge.
- Sidebar logo bumped from `size-4` to `size-5` and the brand text
  gains an italic subtitle "braiding intent & code", echoing what the
  three strands of the logo represent. Header height stays `h-11` to
  line up with the App header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`npx vitest` cannot find the binary under pnpm's strict hoisting layout
(the actual file lives deep in the pnpm store). Each retry attempt was
exiting in microseconds without running anything, so the wrapper burned
through all 5 attempts instantly and CI failed.

Switch to the package-local `node_modules/.bin/vitest` symlink directly.
Same target `pnpm exec vitest` would resolve to, no recursive pnpm
invocation. Local stress test now hits 1-3 retries per run and always
passes within the 5-attempt budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Kuzu's prebuilt NAPI binary hangs vitest's worker fork at startup on
Node 22 under the free Ubuntu GitHub runner. Each attempt hangs ~70s
producing "Worker exited unexpectedly" with `tests 0ms` (no user code
runs). Five-attempt retry wrapper inflates job duration to ~6 minutes
without recovering.

Local macOS Node 22 is merely flaky and the retry wrapper handles it.
The CI hang is a Linux x64 + Node 22 + CI resource budget interaction
that the retry cannot fix.

Excluding storage-kuzu from the Node 22 matrix keeps Node 20 running
the full suite (and storage-kuzu is the only Node 22-incompatible
package). Node 22 still exercises every other workspace package.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Sidebar shape

Three-section vertical layout:
- Header (h-11): logo + brand + tagline + collapse toggle
- Body (flex-1): workspaces list, scrolls
- Utility row: server URL + theme toggle, anchored at the bottom

The empty middle that used to follow short workspace lists is now
visually balanced by content at the top and bottom of the sidebar.
A future user / account avatar slots into the empty flex-spacer on the
left of the utility row when login lands.

## Collapse

The sidebar collapses from 240 px to 48 px on a chevron click or via
the Cmd+\\ / Ctrl+\\ chord (Linear / VS Code convention). State persists
to localStorage as `braid-sidebar-collapsed`.

Collapsed mode shows: logo, workspace folder icons (tooltip on hover
reveals the full id), utility icons stacked vertically (server, theme,
expand). All actions remain reachable; nothing hides behind a menu.

`ListRow` gained an optional `title` prop so the workspace icons can
carry a hover tooltip when their text is hidden.

## App header

The theme toggle and server URL button moved into the sidebar utility
row, leaving the App header with just the workspace context button and
the Cmd+K hint.

## Side cleanup

Removed the per-skill em-dash prohibition note from the four built-in
SKILL.md files; that rule belongs in the project-level guidance, not
duplicated across every skill.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Schema: previewProposal pure helper (snapshot + diff) with lenient
  apply, structural metadata equality, undefined-aware patch
* Schema: SkillFrontmatter.braid.summary one-liner field; all 5 builtin
  skills backfilled
* Core: newProposalId / newClarifyTicketId take a Clock and emit
  p-YYYY-MM-DD-{short} / ct-... ids matching the documented format

* Studio: ProposalPreview with Graph/Table/List tri-view; new
  GraphSurface composes Canvas + Table behind shared selection +
  focus-mode state; PageActions portal slot in the App tab bar
* Studio: NodeDetailPanel extracted as the single right-aside detail
  for both views (replaces Sheet + table's JSON dump)
* Studio: type-filter chips become strict whitelist with All / Clear /
  Reset; Focus toggle dims or hides non-neighbourhood; minimap
  per-node colour from ontology palette; auto-fit on visible-set
  change
* Studio: Actions Recent + SkillCard use summary, no truncate

* Refactor: useControllableState / useNodeNeighbors / optional() lib
  helpers; useFilterSeed / useFitOnLayoutChange / useGraphShortcuts
  hooks; styleTokens + GraphToolbar atoms shared between pages
* Refactor: PageActions portal uses memoised context (no forceRender);
  selectedNode/Edge collapsed into selectNode/Edge/clearSelection
  helpers; withAlpha exported from palette (drops inline dim() copy)

400 tests pass (studio 55 / core 145 / schema 200).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mroops0111 mroops0111 merged commit e4a2a56 into master May 23, 2026
5 checks passed
@mroops0111 mroops0111 deleted the enhance/uiux-flow branch May 25, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant