Commit Graph

1550 Commits

Author SHA1 Message Date
saravanakumardb1
8354595d0f docs(tracker-web): tick UX-2 wave (2.1-2.4) with SHAs + test counts (UX-2)
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-29 06:39:58 -07:00
saravanakumardb1
aa36671e95 feat(tracker-web): migrate board + item detail to primitives (UX-2.3)
Board: type/priority pills → StatusDot/StatusBadge tones, count pill →
Badge, quick status-move buttons → Button, load errors → AlertBanner
(keeping the existing TooltipProvider + column accents).

Item detail: title/description editor → Input/Textarea/Button, the
status/priority/visibility selects → shared Select, the vote control →
Button, comment composer → Textarea/Button, errors → AlertBanner.
ActionMenu + Timeline (UX-12.2) are untouched.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-29 06:39:15 -07:00
saravanakumardb1
c9e65d435c feat(tracker-web): overview MetricCard + ConfirmDialog delete (UX-2.2)
Replace the bespoke total-count card chrome with the shared MetricCard on
the dashboard overview (breakdown cards stay until the UX-4 chart swap),
and surface load errors via AlertBanner. Wrap the items-list delete
confirm() in the accessible ConfirmDialog (focus-trapped AlertDialog)
instead of the native browser prompt.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-29 06:36:18 -07:00
saravanakumardb1
a7a6f191ca feat(tracker-web): migrate items page controls + create modal to primitives (UX-2.1)
Replace the raw search input, the three filter selects, the New Item
button, and the hand-rolled create modal with @bytelyst/ui Input/Select/
Field/Button/Modal (via the Primitives adapter). The shared Modal closes
the focus-trap/Esc/scroll-lock a11y gap. Swap the inline type/status/
priority cell pills to StatusBadge with token-driven tones.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-29 06:33:20 -07:00
saravanakumardb1
64e6cc11a1 docs(tracker-web): tick CC.1-CC.8 cross-cutting + Expand-wave status (UX-CC)
Mark the cross-cutting items complete with evidence: full suite green per wave
(CC.1), dark-mode parity via the --bl-* bridge (CC.2), zero new color literals
(CC.3), offline axe on /login + /roadmap (CC.4), master ROADMAP left untouched
per default (CC.5), gzip-verified bundle budgets (CC.7), enforced 80% coverage
thresholds at 94/87/94/97 (CC.8). Add an Expand-waves status table with commit
SHAs and document the data-gated deferrals (UX-12.3, UX-13.1).

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 22:12:25 -07:00
saravanakumardb1
3d22c3031f feat(tracker-web): system banners via @bytelyst/notifications-ui (UX-13.2)
Add @bytelyst/notifications-ui as a workspace dep (minimal link: lockfile entry,
avoiding the env-specific full re-normalization). New SystemBanners component
mounts at the top of the dashboard shell:
  - BannerStack renders dismissible system/maintenance notices from
    NEXT_PUBLIC_SYSTEM_NOTICE (nothing when unset)
  - Announcement shows a localStorage-dismissible "what's new" pill

Defer UX-13.1 (NotificationCenter): tracker has no notifications feed — the
/api/tracker proxy exposes only items/comments/votes/roadmap. The dep + an
import smoke test are in place so a future feed wiring starts from green.

All colors come from the bridged --bl-* tokens; no hardcoded literals.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 22:08:38 -07:00
saravanakumardb1
3a5196417d fix(errors): make ServiceError instanceof cross-instance-safe
When docker-prep packs @bytelyst/* as tarballs, a second physical copy of
@bytelyst/errors can be created, breaking prototype identity. The
fastify-core error handler's 'error instanceof ServiceError' then returned
false, mis-mapping ConflictError/BadRequestError/etc. to HTTP 500.

Fix: brand ServiceError instances with a global Symbol.for() key and add a
Symbol.hasInstance that recognizes any branded instance for the base class,
while preserving prototype-chain semantics for subclasses. Resilient to
duplicated module copies without touching call sites.

- errors: brand + custom hasInstance (+3 cross-instance unit tests)
- fastify-core: regression test (duplicated-copy ServiceError -> 409 not 500)
- bump @bytelyst/errors 0.1.11 -> 0.1.13, published to Gitea registry
2026-05-28 22:02:35 -07:00
saravanakumardb1
32dac7d466 feat(tracker-web): item-detail ActionMenu + Timeline comments (UX-12.2)
Move the Edit/Delete item actions into a shared ActionMenu in the page header
and render the comment history with the shared Timeline. Document UX-12.3 as
data-gated/deferred: descriptions and comments are plain text with no backend
HTML sanitization, so RichTextEditor is intentionally not adopted yet.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 21:06:09 -07:00
saravanakumardb1
ddf25cf501 feat(tracker-web): SegmentedControl view toggle + board Tooltips (UX-12.1)
Replace the bespoke roadmap board/list toggle buttons (hardcoded blue-600) with
the shared SegmentedControl, and wrap truncated board-card titles in Tooltip.
Update the e2e toggle selector from button to radio for SegmentedControl.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 21:01:58 -07:00
saravanakumardb1
328e307212 feat(tracker-web): adopt @bytelyst/auth-ui on the login surface (UX-11)
- add @bytelyst/auth-ui workspace dep (minimal link: lockfile entry) (11.1)
- replace the password form with LoginForm and the OTP step with MfaChallenge,
  wiring onSubmit to the existing auth-context login + /api/auth/mfa/verify;
  social login gated to Google via NEXT_PUBLIC_GOOGLE_CLIENT_ID (11.2)
- add aria-labels to the placeholder-only auth-ui inputs so the a11y gate and
  label-based selectors stay green
- add an auth-ui import smoke test; full render assertion stays in the e2e
  "shows login form with correct branding" test (11.3)

The /api/auth/* proxy routes and auth-context are unchanged.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 20:56:00 -07:00
saravanakumardb1
ccee7dfae2 feat(tracker-web): extend --bl-* token bridge for adopted components (UX-1.2 ext, CC.2)
Map the full @bytelyst/* --bl-* token surface (surfaces, text, danger, success/
warning/info, focus ring, radii, spacing, typography, overlay) onto tracker OKLCH
vars so auth-ui/notifications-ui/ui components inherit the theme in light and
dark. Adds semantic --success/--warning/--info token defs; all bridge values
reference tracker vars or color-mix of them (no standalone color literals).

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 20:50:15 -07:00
saravanakumardb1
3a9621f0c2 feat(tracker-web): page chrome via @bytelyst/dashboard-components (UX-10)
- error.tsx now renders ErrorPage (keeps trackEvent + reset wiring) (10.1)
- add PageHeader (title + breadcrumbs) to /dashboard, /dashboard/items,
  /dashboard/board and the item detail page for a consistent header band (10.2)
- replace ad-hoc loading text with LoadingSpinner on overview, items, detail (10.3)

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 20:45:00 -07:00
saravanakumardb1
73d2891d8e chore(tracker-web): add @bytelyst/ui UI-drift ratchet eslint rule (CC.6)
Forbid direct @bytelyst/ui imports outside the Primitives adapter via
no-restricted-imports, with the adapter file exempted. No pre-existing
violations; a probe confirms the rule fires.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 20:39:20 -07:00
saravanakumardb1
18a09b25b8 feat(tracker-web): complete Primitives adapter + export-presence test (UX-9)
Extend src/components/ui/Primitives.tsx to re-export the shared @bytelyst/ui
controls later waves need (Select, Textarea, Checkbox, Switch, RadioGroup,
Tooltip, Tabs, SegmentedControl, DropdownMenu, Drawer, ActionMenu, IconButton,
AlertBanner, Card, Panel, Separator, DataList, Timeline), and add a pure
export-presence test that lifts the adapter off 0% coverage without a DOM dep.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 20:37:28 -07:00
saravanakumardb1
3515fbddc5 docs(tracker-web): expand UX integration roadmap (UX-9..UX-13 + cross-cutting)
Re-audited tracker-web against the full showcase catalog (~60 entries) and added
delegate-ready waves beyond the core UX-1..8:
- UX-9  complete the Primitives adapter + export-presence test (closes the
        Primitives.tsx 0%-coverage gap without a jsdom dep)
- UX-10 page chrome via @bytelyst/dashboard-components (ErrorPage, PageHeader)
- UX-11 @bytelyst/auth-ui on the login + MFA surface (presentation only)
- UX-12 detail/board richness (Tabs/Tooltip/Drawer/Timeline; rich-text stretch)
- UX-13 @bytelyst/notifications-ui (stretch, data-gated)
- CC.6 UI-drift ESLint ratchet, CC.7 bundle budget, CC.8 coverage ratchet
Also refreshed the entry point (UX-1 done -> start at UX-2) and the gap matrix.
2026-05-28 20:22:56 -07:00
saravanakumardb1
b9bc2163f4 docs(tracker-web): finalize TEST_VALIDATION_LOG with results + SHAs 2026-05-28 20:08:24 -07:00
saravanakumardb1
772609c919 test(tracker-web): cover untested API routes + tracker-client, enforce coverage
Add unit tests for the previously-untested proxy handlers (auth/mfa/verify,
auth/oauth/[provider], telemetry/ingest) covering success, error-status
forwarding, default error messages, validation, and the 502 unreachable path.
Add tracker-client tests asserting every endpoint path/method/body contract
and x-product-id header injection, plus product-config request resolution.

Overall coverage rises from ~90% to 94% stmts / 87% branch / 94% funcs. Also
fix the vitest coverage thresholds: the legacy global nesting was silently
ignored by the v8 provider, so the 80% gate was never enforced.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 20:05:33 -07:00
saravanakumardb1
1c231d6659 test(tracker-web): make e2e deterministic + add axe a11y and console checks
Rewrite the Playwright suite to mock the platform-service at the Next.js proxy
boundary (/api/tracker, /api/auth) so no live backend is required, and provide
the env vars /api/health needs via webServer.env. Adds a login->dashboard happy
path, board/list toggle and vote-prompt coverage, axe-core accessibility
assertions (resolved from the workspace, no new dependency), and a
no-unexpected-console-errors check.

The axe gate surfaced a real bug: the roadmap type-filter and submit-modal
<select> elements had no accessible name. Fixed by adding aria-labels.

Also ignore coverage/test-results/playwright-report in eslint.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 19:53:36 -07:00
saravanakumardb1
f0911e65ed docs(cheatsheets): add Devin/Claude/Codex CLI cheat sheets
New AI.dev/CHEATSHEETS/ reference set for delegating to terminal AI agents:
- README.md: comparison matrix, 'which CLI?' decision guide, official-docs links,
  cross-CLI rules + ByteLyst environment facts
- devin-cli.md: sessions, --permission-mode dangerous vs --sandbox, resume, the
  sandbox-stall gotcha, delegation pattern + prompt preamble
- claude-code-cli.md: REPL/-p/-c/--resume, permission+plan modes, slash commands, MCP
- codex-cli.md: interactive vs codex exec for CI, sandbox x approval matrix, config.toml

Flags hedged with 'confirm via --help' since they drift between versions; durable
value is the ByteLyst workflow. Does not reference .devin/config.local.json contents.
2026-05-28 19:42:07 -07:00
saravanakumardb1
8738d07da7 fix(tracker-web): format markdown + ignore e2e artifacts in prettier/git
Run Prettier over README and roadmap docs to satisfy format:check, and add
.prettierignore plus .gitignore entries for playwright-report/test-results so
generated e2e artifacts no longer break the format gate.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 19:40:54 -07:00
saravanakumardb1
d0707f22a5 docs(tracker-web): add TEST_VALIDATION_LOG with baseline gate results
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 19:39:29 -07:00
saravanakumardb1
77b074f3c0 feat(gitea): docker-mode env hygiene + document containerized job migration
- add-host-runner.sh docker mode now strips host-specific envs (HOME, PATH,
  PNPM_HOME) that leak macOS paths into Linux containers and override workflow
  env (broke $HOME-relative writes)
- GITEA_VM_SETUP.md 11.5: reference pattern + 5 gotchas for migrating a real
  job (docker-lint) onto the docker runner: Actions secret (not token file),
  doctor.sh token-file requirement, host-env leakage, env_file token override,
  proxy bypass. Validated green on M-…-4.
2026-05-28 19:16:52 -07:00
saravanakumardb1
6429436935 docs(tracker-web): mark UX-1 as completed in roadmap
Update UX_INTEGRATION_BYTELYST.md to mark all UX-1 tasks as completed
with commit SHA reference and update progress section.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 19:11:24 -07:00
saravanakumardb1
dc01dd0285 feat(tracker-web): add token bridge + Primitives adapter (UX-1)
Add @bytelyst/ui and @bytelyst/design-tokens as workspace dependencies,
create token bridge layer mapping --bl-* to existing tracker vars, and
implement Primitives.tsx adapter for shared UI components. Includes smoke
test verifying component exports. All verification checks pass.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 19:11:03 -07:00
saravanakumardb1
6381cabe68 feat(gitea): docker-mode support in add-host-runner.sh + capacity guidance
- add-host-runner.sh: optional [mode] arg (host|docker); docker mode sets
  dedicated 'docker' label, container.docker_host/force_pull/options, and
  appends host.docker.internal to NO_PROXY so containerized jobs reach the
  host Gitea through the corp proxy (avoids HTTP 504)
- GITEA_VM_SETUP.md 11.5: docker-mode runner setup + proxy-bypass caveat;
  fleet now 3 host runners x capacity 3 + 1 docker runner

Validated: runs-on: docker job runs in Ubuntu 24.04 container and reaches
Gitea /api/v1/version.
2026-05-28 19:00:00 -07:00
saravanakumardb1
0e89dafa43 test(tracker-web): add unit tests for src/lib/utils.ts
- Add comprehensive tests for cn utility function
- Cover empty input, single/multiple classes, conflicting Tailwind classes
- Test conditional classes, arrays, objects, and complex mixed inputs
- 10 assertions total, exceeding minimum requirement of 4

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 18:49:04 -07:00
saravanakumardb1
5bd2b92493 feat(tracker-web): replace loading text with skeleton layout
- Replace bare text loading state with skeleton cards matching board/list layout
- Use Tailwind animate-pulse for smooth loading animation
- Skeleton dimensions approximate real cards to avoid layout shift
- Fixes B-015: loading state is a bare text flash

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 18:48:48 -07:00
saravanakumardb1
7cef6a918a feat(tracker-web): add A11y attributes to vote buttons in ItemCard and ItemRow
- Add type="button", aria-pressed, and aria-label to vote buttons in both components
- Add missing title attribute to ItemRow button to match ItemCard
- Add unit test to verify A11y attribute logic
- Fixes A11y issue: vote buttons not announced to assistive tech

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 18:48:04 -07:00
saravanakumardb1
5edc4c92f2 fix(tracker-web): refresh roadmap data after successful public submission
- Call fetchData() after successful submit in handleSubmit to update stats bar and columns
- Add unit tests to verify fetchData is called on success and not on failure
- Fixes B-016: public roadmap stats don't refresh after submit

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-28 18:47:33 -07:00
saravanakumardb1
0985969377 feat(tracker-web): adopt @bytelyst/data-table for items list (Wave 9.C.9)
Replaces the bespoke <table> in /dashboard/items with <DataTable> (TanStack-
powered sorting + client pagination), keeping the existing badge cells, title
link, labels and delete action via ColumnDef cell renderers. Server-side
type/status/priority/search filters retained (enableFilter=false on the table).

Verified: tsc --noEmit clean; vitest 31/31; next build --webpack succeeds
(/dashboard/items compiles).
2026-05-28 18:38:08 -07:00
saravanakumardb1
7e1a2ad660 feat(gitea): add-host-runner.sh for multi-runner CI parallelism
- scripts/gitea/add-host-runner.sh: stand up Nth independent host-mode runner
  as its own launchd service (separate config/.runner/workdir, shared
  runner.env token, admin-API registration token, idempotent reload)
- GITEA_VM_SETUP.md 11.5: document multi-runner setup, fleet list/prune,
  and removal; 3 runners x capacity 2 ~= 6 parallel slots (verified)

Live fleet: learning-ai-mac (brew) + 2 added runners, all online; stale
offline registrations pruned.
2026-05-28 18:31:57 -07:00
saravanakumardb1
d04a303f98 feat(charts): RadarChart + charts@0.1.1 (Wave 9.A.5)
Multi-axis polygon (spider) chart: token palette, concentric grid rings,
axis labels, NaN-safe. Pure SVG, role=img + title. 3 tests added (26/26 total);
tsc clean; published @bytelyst/charts@0.1.1 to Gitea.
2026-05-28 18:27:45 -07:00
saravanakumardb1
fc8502ac0c feat(rich-text): @bytelyst/rich-text@0.1.0 on Tiptap v3
RichTextEditor (toolbar + slash menu + async mentions, SSR-safe via
immediatelyRender:false) + RichTextViewer (generateHTML, server-renderable) +
standalone Toolbar. Pure filterSlashItems/filterUsers helpers. 12/12 vitest
(incl. live editor mount + bold toggle in happy-dom); tsc clean; published to
Gitea.
2026-05-28 18:20:34 -07:00
saravanakumardb1
3224199894 feat(gitea): reproducible Actions runner registration + harden runner config
- add scripts/gitea/register-runner.sh (idempotent register, host/docker modes,
  capacity arg, admin-API registration token, --force re-register)
- GITEA_VM_SETUP.md Step 11: runner install/register, host-vs-docker tradeoffs,
  token externalization (env_file), concurrency (capacity), token rotation,
  end-to-end CI verification
- document runner registration + secrets in persist/ephemeral table

Live runner hardened separately: capacity 1->2, GITEA_NPM_TOKEN moved from
inline config.yaml to chmod-600 runner.env via env_file.
2026-05-28 18:05:55 -07:00
saravanakumardb1
9970a68a35 test(data-table): cover column resize + reorder (9.C.3)
Adds drag-resize and header drag-reorder cases against the shipped 0.1.0
implementation. data-table suite now 12/12.
2026-05-28 17:39:59 -07:00
saravanakumardb1
a55ea16370 feat(data-table): @bytelyst/data-table@0.1.0 on TanStack Table v8 + Virtual
DataTable wrapper: sort, global filter, pagination, row selection + bulk
action bar, column resize/pin/reorder, compact/comfortable density, and a
virtualised mode for 10k+ rows (seeded initialRect for SSR/headless).

Note: roadmap 9.C says 'TanStack Table v9' but no stable v9 exists yet; built
on the current stable v8.21.3 + react-virtual 3.13.

Verified: vitest 10/10; tsc --noEmit clean; tsc build emits dist; published
@bytelyst/data-table@0.1.0 to local Gitea registry.
2026-05-28 17:32:53 -07:00
saravanakumardb1
6cd60e86e8 test(ui): cover Skeleton, SkeletonGroup, SearchInput, LoadingDots (TODO #2)
Adds 13 vitest cases for the Wave 9.D loading primitives; ui suite now 19/19.
Removes the resolved ROADMAP-EXEC-TODO #2 marker from Skeleton.tsx.

Verified: npx vitest run --pool forks (19 passed); npx tsc --noEmit (clean).
2026-05-28 17:19:04 -07:00
saravanakumardb1
e5061350a5 feat(ui): Combobox scroll-into-view, opt-in TagInput blur-commit, unit tests (V4 IMP-1/3, GAP-4)
@bytelyst/ui 0.2.0 -> 0.2.1

- Combobox: scroll the highlighted option into view during arrow-key nav so
  it never leaves the popover viewport (IMP-1)
- TagInput: new commitOnBlur prop (default false) — committing the buffer on
  blur surprised users clicking away to discard. BEHAVIOR CHANGE: blur no
  longer commits unless commitOnBlur is set (IMP-3)
- Add vitest + happy-dom + @testing-library/react devDeps, test script, and
  packages/ui/vitest.config.ts; 6 unit tests for Combobox + TagInput (GAP-4)
- Drop the stale 'vitest pending' ROADMAP-EXEC-TODO comments

6/6 tests pass; tsc clean.
2026-05-28 15:57:49 -07:00
saravanakumardb1
a473a45aae chore(packages): bump versions + manifest after publishing 21 packages to Gitea
Published in this run:
  NEW: @bytelyst/charts@0.1.0
  NEW: @bytelyst/customizable-workspace@0.1.0
  NEW: @bytelyst/generative-theme@0.1.0
  NEW: @bytelyst/media-ui@0.1.0
  NEW: @bytelyst/notifications-ui@0.1.0
  NEW: @bytelyst/motion@0.2.1
  NEW: @bytelyst/data-viz@0.1.0 (?)
  CHANGED: @bytelyst/ui @0.2.0
  CHANGED: @bytelyst/auth-ui, broadcast-client, dashboard-components,
           llm-router, survey-client, ai-ui, command-palette,
           dashboard-shell, design-tokens, feature-flag-client,
           kill-switch-client, mcp-client, platform-client

Manifest fingerprint updated for all 21 packages.
2026-05-27 19:13:44 -07:00
saravanakumardb1
8562711f49 fix(workspace+theme): sanitise corrupt spans, short-circuit re-reconcile, drop unreachable regex
──────────────────────────────────────────────────────────────────
customizable-workspace
──────────────────────────────────────────────────────────────────

Two issues caught in the audit pass:

1. **Corrupt persisted spans broke the grid layout.**
   localStorage entries from older versions (or hand-edited debug
   sessions) could contain span=NaN / 0 / -3 / 99. These flowed
   straight into `grid-column: span <bad>` which silently broke
   the whole row. The visual symptom was a tile rendering at zero
   width or pushing every sibling off-screen.
   Fix: `reconcile()` now clamps every span (including newly
   appended tiles' defaultSpan) to the legal `[1, 4]` range via
   `sanitiseSpan()`.

2. **Re-reconcile effect could loop when callers forget to memoise.**
   The `useEffect([tiles, hydrated])` always called `setLayout`
   with a fresh `{ entries: [...] }` object reference, even when
   the content was identical. If `tiles` itself was a fresh
   reference per parent render (e.g. `tiles=[{...}]` inline),
   every render \u2192 setLayout \u2192 save effect \u2192 (no loop because
   tiles ref same), but constant unnecessary writes to
   localStorage.
   Fix: added `sameLayout(a, b)` structural-equality check;
   setLayout now short-circuits to the previous state when the
   reconciled output is identical.

Tests: 10 \u2192 11
  reconcile  \u00b7 sanitises corrupt spans (NaN/0/negative/>4) \u2192 clamp

──────────────────────────────────────────────────────────────────
generative-theme
──────────────────────────────────────────────────────────────────

Cosmetic but worth fixing: the `rose` palette regex included
`warm` as a keyword, but the `citrus` palette \u2014 listed earlier in
the PALETTES table \u2014 also matched `warm`. Since first-match wins,
`warm` was unreachable in rose and the entry was misleading.

Dropped `warm` from the rose regex. Citrus retains it (was always
where it routed in practice). All 18 existing tests still pass.
2026-05-27 18:46:19 -07:00
saravanakumardb1
b2e45380ec fix(media-ui): plug AudioContext leak + guard <img> against undefined src
Two latent bugs caught in the audit pass:

1. **AudioContext leak in AudioWaveform.**
   The lazy WebAudio decoder constructed an `AudioContext` per
   mount-with-audioUrl and never called `.close()`. Browsers cap the
   total per page (Chrome ~6, Firefox ~6) so a long-lived session that
   remounted waveforms enough times eventually hit
   `InvalidStateError: cannot create AudioContext` and silently
   stopped decoding.
   Fix: wrap `decodeAudioData` in try/finally and `close()` the
   context in the finally. Errors from close() are swallowed (best-
   effort cleanup).

2. **Undefined img src in ImageGenStream.**
   When `status=streaming` arrived before the first
   `snapshot.partialUrl` (very common during the SSE handshake),
   the component rendered <img src={undefined}> which browsers treat
   as a broken-image icon. Same for status=complete + missing
   finalUrl.
   Fix: compute `visibleSrc` once; only mount <img> if a source
   exists, otherwise show 'Waiting for first frame\u2026' placeholder.
   Also removed the dead `revealedAt` state \u2014 the prior 0.95/1
   opacity dance was imperceptible and contributed nothing.

3. **Bonus: AudioWaveform `bars` clamp.**
   `bars={0}` (or negative / fractional) divided by zero on the
   canvas slot math and rendered an empty waveform. Now clamped to
   `Math.max(1, Math.floor(barsProp))`.

Tests: 10 \u2192 12
  AudioWaveform   \u00b7 bars=0 doesn't crash + canvas still renders
  ImageGenStream  \u00b7 streaming without partialUrl shows placeholder
                    instead of <img src={undefined}>
2026-05-27 18:44:50 -07:00
saravanakumardb1
da8d4ecb19 fix(charts): drop NaN/Infinity from series; DRY-up compactNumber
Audit pass found two latent issues:

1. **NaN/Infinity broke the SVG path.** `LineChart` mapped raw values
   through `yScale()` without sanitising, so any non-finite input
   emitted a literal 'NaN' in the path `d` attribute and silently
   broke the visible stroke for the whole series. Same shape in
   `AreaChart`.

2. **`compactNumber()` was duplicated.** Three identical copies
   lived in LineChart / BarChart / AreaChart.

Fixes (all in `utils.ts`):
  + `compactNumber(v)` now exported (returns '' for non-finite)
  + `filterFinite(values)` returns `[index, value]` pairs,
    keeping the original X-axis spacing for the surviving points

Behavioural changes:
  - LineChart `series` containing NaN/Infinity → path skips those
    points cleanly. Series of *entirely* non-finite values render
    nothing (was: a fully NaN-poisoned path).
  - AreaChart `values` containing NaN/Infinity → same.
  - BarChart unchanged (was already safe via `extent`).

Tests: 19 \u2192 23 (4 new regression cases)
  utils      \u00b7 compactNumber k-suffix + non-finite handling
  utils      \u00b7 filterFinite preserves original indices
  LineChart  \u00b7 NaN/Infinity never appear in path `d`
  LineChart  \u00b7 all-non-finite series renders zero <g>
2026-05-27 18:43:18 -07:00
saravanakumardb1
d57ed9b878 docs+chore: Wave 9.A + 13.E + 13.F + 13.G + CC.3 + CC.8 \u2014 18 boxes flipped
Roadmap closure pass for this round. CC.8 wires the auto-counter
into the pre-commit hook so future roadmap-touching commits stay
self-consistent.

Flipped:
  9.A.1   LineChart                charts@0.1.0
  9.A.2   BarChart                 (StackedBar deferred to 0.2.x)
  9.A.3   AreaChart
  9.A.4   Donut + Gauge
  9.A.6   Token-driven theming verified
  9.A.7   /showcase/charts/all + 5 deep dives
  13.E.1  Brand-prompt generator   generative-theme@0.1.0
  13.E.2  WCAG contrast utilities + AA/AAA enforcement
  13.E.3  /showcase/futurism/theme-studio (MAG.5)
  13.F.1  Drag-resize tiles        customizable-workspace@0.1.0
  13.F.2  LayoutPersistence + reconcile()
  13.F.3  /showcase/futurism/workspace (MAG.6)
  13.G.1  ImageGenStream           media-ui@0.1.0
  13.G.2  AudioWaveform
  13.G.4  VideoPlayer              (PdfPreview 13.G.3 deferred to 0.2.x)
  13.G.5  /showcase/futurism/multimodal (MAG.7)
  CC.3    axe-core gate (every showcase route covered by smoke.spec.ts)
  CC.8    counter wired into .husky/pre-commit

Customer-magnet status: 6 of 8 LIVE
  \u2728 MAG.1 spatial-hero
  \u2728 MAG.3 trust-surfaces
  \u2728 MAG.5 theme-studio
  \u2728 MAG.6 workspace
  \u2728 MAG.7 multimodal
  \u2728 MAG.8 debug-overlay

Remaining magnets:
  MAG.2 on-device-chat   (Wave 13.A \u2014 needs WebLLM / Apple Intelligence)
  MAG.4 crdt-notes       (Wave 13.B \u2014 needs Yjs + Automerge)

Pre-commit hook (.husky/pre-commit):
  When docs/UI_ROADMAP_2026_V3_CROSS_REPO.md is staged, the counter
  rewrites the §11.2 progress block + per-wave headings and re-stages
  the file. Silent no-op when the roadmap wasn't touched. Honours
  HUSKY_ENABLED=false.
2026-05-27 17:48:13 -07:00
saravanakumardb1
99e59597d1 feat(media-ui): @bytelyst/media-ui@0.1.0 — Wave 13.G.1/.2/.4
Media surface primitives — ImageGenStream, AudioWaveform, VideoPlayer.
Zero runtime deps (canvas + native <video> + native <img>); PdfPreview
deferred to 0.2.x where it'll lazy-load pdf.js.

──────────────────────────────────────────────────────────────────
<ImageGenStream>  ·  Wave 13.G.1
──────────────────────────────────────────────────────────────────
  - Driven entirely by props (status + snapshot + finalUrl)
  - 4 statuses: idle / streaming / complete / error
  - Streaming: blurred partial image + overlay progress bar +
    step label (e.g. 'denoising 24/50')
  - Complete: drops blur with fade-in
  - Error: muted error placeholder (token-tinted danger)
  - Host pipes any transport (SSE / WebSocket / polling) into
    the snapshot prop — pure presentation

<AudioWaveform>  ·  Wave 13.G.2
  - Canvas render with DPR-aware paint
  - Two sources: pre-computed peaks (cheapest) OR lazy WebAudio
    decode from audioUrl (falls back gracefully w/o AudioContext)
  - Click-to-seek (returns 0..1 position)
  - Progress overlay tints played bars with progressColor
  - Resampling helper handles arbitrary peak-count inputs

<VideoPlayer>  ·  Wave 13.G.4
  - Native <video controls> wrapper — accessible by default
  - Chapter buttons that seek + auto-play (silently swallows
    autoplay rejections)
  - Optional in-memory caption track (aria-live polite); hosts
    that prefer real WebVTT pass <track> via the slot prop
  - Token-tinted; rounded; muted poster background

──────────────────────────────────────────────────────────────────
Quality gates
──────────────────────────────────────────────────────────────────
  ✓ 10/10 tests passing
    - AudioWaveform: 3 cases (canvas size · click-seek math ·
      placeholder when no peaks)
    - ImageGenStream: 4 cases (idle / streaming / complete /
      error)
    - VideoPlayer: 3 cases (src · chapter button list · caption
      rail)
  ✓ tsc build clean
  ✓ Zero new runtime deps; all WebAudio access guarded for SSR

Showcase /futurism/multimodal (MAG.7) lands in the paired showcase
commit.
2026-05-27 17:44:13 -07:00
saravanakumardb1
2affd1aba0 feat(customizable-workspace): @bytelyst/customizable-workspace@0.1.0 — Wave 13.F
Drag-reorderable tile grid with per-tile width control + view-scoped
persistence. Native HTML5 drag — zero runtime deps (no @dnd-kit).

──────────────────────────────────────────────────────────────────
Module surface
──────────────────────────────────────────────────────────────────
  <Workspace tiles storageKey persistence? columns? readOnly?>
    Wave 13.F.1 + .2

    - Native HTML5 drag-and-drop (draggable + dragover + drop)
    - role=grid + per-cell role=gridcell + tabIndex=0
    - Keyboard shortcuts on focused tile:
        ← / →   resize span down/up (clamped 1..columns)
        ↑ / ↓   move tile up/down by one slot
    - Per-tile 1/2/3/4 size buttons (suppressed in readOnly)
    - Drop target gets dashed accent outline
    - Reset-layout CTA (suppressed in readOnly)

  useWorkspaceLayout(tiles, { storageKey, persistence })
    - move(from, to) / resize(id, span) / reset()
    - hydrated flag — true once initial load resolves
    - One-shot load on mount, save on every change (post-hydration)

  reconcile(layout, tiles)  ·  exported pure fn
    - Drops entries referencing removed tiles
    - Appends new tiles in registry order with defaultSpan fallback
    - Used by the hook on every tile-registry change
      (defensive against schema drift between releases)

  localStoragePersistence  ·  default; SSR-safe (returns null when
                                 window is absent)

──────────────────────────────────────────────────────────────────
Quality gates
──────────────────────────────────────────────────────────────────
  ✓ 10/10 tests passing
    - reconcile: 3 cases (drop unknown / append new / preserve order)
    - hook:      4 cases (hydrate defaults / move / resize / reset)
    - component: 3 cases (renders cells / keyboard resize / readOnly)
  ✓ tsc build clean

──────────────────────────────────────────────────────────────────
Roadmap (lands in subsequent commit)
──────────────────────────────────────────────────────────────────
  13.F.1  Drag-resize tiles
  13.F.2  Saved views per route + persistence layer

Showcase /futurism/workspace (MAG.6) lands in the paired showcase
commit.
2026-05-27 17:30:33 -07:00
saravanakumardb1
4af06f732b feat(generative-theme): @bytelyst/generative-theme@0.1.0 — Wave 13.E
Brand-prompt → Tier-2 token-override generator. Local deterministic
generator by default; pluggable async LLM hook. WCAG-correct
contrast enforcement with AA / AAA lock policies.

──────────────────────────────────────────────────────────────────
Module surface
──────────────────────────────────────────────────────────────────
  generateThemeFromPrompt(prompt, opts?)
    - opts.generate: optional async LLM hook (host wires real LLM)
    - opts.enforce:  'aa' (default) | 'aaa' | 'off'
    - Always runs the contrast pass when enforce !== 'off'
    - WAVE 13.E.1

  localGenerate(prompt)
    - Synchronous; 7 hand-curated palettes (midnight, citrus,
      forest, ocean, rose, graphite, violet) + default fallback
    - Keyword-matched via regex; deterministic for caching

  Contrast utilities (Wave 13.E.2):
    parseHex / toHex            — input + output round-trip safe
    relativeLuminance           — WCAG 2.x luminance
    contrast(a, b)              — pair contrast ratio
    report(fg, bg)              — { ratio, aa, aaa }
    auditTheme(theme)           — 4 canonical text/accent pairings
    adjustForContrast(fg,bg,t)  — iteratively darken/lighten until ≥ t
    enforceContrast(theme,'aa') — returns adjusted ThemeProposal

  applyTheme(theme, target?)
    - Writes 11 --bl-* custom properties onto the target element
    - Returns a tear-down fn (saved prior values, restored on call)

──────────────────────────────────────────────────────────────────
Quality gates
──────────────────────────────────────────────────────────────────
  ✓ 18/18 tests passing (hex / contrast math / generator /
    enforcement / applyTheme cleanup)
  ✓ tsc build clean
  ✓ Zero runtime deps, pure functions where possible

──────────────────────────────────────────────────────────────────
Roadmap (lands in subsequent commit)
──────────────────────────────────────────────────────────────────
  13.E.1  Deterministic prompt → palette generator
  13.E.2  Contrast checker + AA / AAA enforcement

Showcase route /futurism/theme-studio (MAG.5) lands in paired
showcase commit.
2026-05-27 17:23:06 -07:00
saravanakumardb1
2eaec32849 feat(charts): @bytelyst/charts@0.1.0 — Wave 9.A.1-4 LineChart / BarChart / AreaChart / Donut / Gauge
A new package — pure-SVG token-themed chart primitives, zero deps,
SSR-safe. Slots into the existing dataviz family next to
@bytelyst/data-viz (which owns Sparkline + KpiCard + Heatmap).

──────────────────────────────────────────────────────────────────
Components
──────────────────────────────────────────────────────────────────
<LineChart>   Wave 9.A.1
  - Multi-series with per-series colour override
  - Smooth (catmull-rom) or straight polyline
  - 3-tick subtle Y grid + per-tick label
  - SSR-safe title id via useId()

<BarChart>    Wave 9.A.2
  - Negative-value support via configurable baseline
  - Per-bar colour override; per-bar accessible label
  - Compact-K tick labels

<AreaChart>   Wave 9.A.3
  - Single-series with gradient fill + line stroke
  - Zero-baseline included automatically when data >= 0

<Donut>       Wave 9.A.4 (a)
  - Categorical share-of-total ring
  - Token palette walks 6 colours; per-slice override
  - Empty / all-zero data renders a muted ring (no NaN slices)
  - Single near-100% slice collapses to a closed ring
  - centerContent slot via <foreignObject>

<Gauge>       Wave 9.A.4 (b)
  - Half-circle 'fuel-tank' dial
  - NaN / out-of-range clamped safely
  - Caption slot below the dial

──────────────────────────────────────────────────────────────────
Shared utilities (src/utils.ts) — also exported
──────────────────────────────────────────────────────────────────
  - linearScale, extent (NaN-safe), smoothPath, formatNumber

──────────────────────────────────────────────────────────────────
Quality gates
──────────────────────────────────────────────────────────────────
  ✓ pnpm -F @bytelyst/charts test  →  19/19 passing
    - utils:   3 cases (extent / linearScale / smoothPath edge cases)
    - Line:    3 cases (series count, colour override, useId aria)
    - Bar:     3 cases (count, diverging negative, colour)
    - Area:    2 cases (gradient + line, single-point safety)
    - Donut:   4 cases (slice count, empty ring, full-ring collapse,
                        centerContent slot)
    - Gauge:   4 cases (in-domain, clamp >max, NaN→min, caption)
  ✓ pnpm -F @bytelyst/charts build  →  tsc clean
  ✓ No console.log / no Math.random for ids
  ✓ All primitives honour the design-system anti-patterns doc

──────────────────────────────────────────────────────────────────
Deferred to 0.2.x
──────────────────────────────────────────────────────────────────
  - <StackedBar>, <RadarChart> (see roadmap §9.A)

Showcase routes + roadmap flips land in the paired commit.
2026-05-27 17:16:46 -07:00
saravanakumardb1
839f3ff794 docs+chore: CC.6 ANTIPATTERNS.md + DebugOverlay dead-code cleanup + roadmap flips (37/202)
──────────────────────────────────────────────────────────────────
docs/design-system/ANTIPATTERNS.md  (CC.6 — new file)
──────────────────────────────────────────────────────────────────
Twelve anti-patterns codified, every product engineer + AI agent
should treat as a hard 'no':

  1.  Hard-coded colour / spacing / radius values
  2.  Bespoke skeleton / spinner / empty-state per surface
  3.  Bespoke tag editor / searchable select
  4.  Raw API responses inside React state
  5.  Hidden privacy / cost / refusal state
  6.  Motion without prefers-reduced-motion
  7.  SSR-unsafe ID generation (Math.random)
  8.  console.log / console.error in production
  9.  Cross-product imports
  10. `any` (especially in public API surfaces)
  11. Untested primitives in @bytelyst/*
  12. Animations that block keyboard focus

Each entry has Smell → Why it's wrong → Do this instead, with
canonical `@bytelyst/*` references.

──────────────────────────────────────────────────────────────────
packages/ai-ui/src/DebugOverlay.tsx  (audit cleanup)
──────────────────────────────────────────────────────────────────
Dropped dead `patchSingleChild` helper. It cloned the single child
unchanged, doing literally nothing — the click handler always lived
on the outer <span>. Replaced the call site with `{children}` and
removed 4 unused React imports (Children, cloneElement,
isValidElement, ReactElement).

Same 98/98 tests pass.

──────────────────────────────────────────────────────────────────
Roadmap flips (this commit + the prior unflag pass)
──────────────────────────────────────────────────────────────────
  9.E.1   <Markdown> + citation interop                ai-ui@0.6.0
  9.E.2   <CodeDiff> split + unified                   ai-ui@0.6.0
  9.E.3   <ExplainThis>                                ai-ui@0.6.0
  9.E.4   usePromptHistory                             ai-ui@0.6.0
  9.E.5   useTokenCount                                ai-ui@0.6.0
  9.E.6   /showcase/ai-ui/markdown                     showcase
  13.C.4  <ProvenanceDrawer>                           ai-ui@0.5.0+
  13.C.5  <DebugOverlay>                               ai-ui@0.5.0+
  13.C.6  <PrivacyBadge>                               ai-ui@0.5.0+
  13.D.1  <Parallax>                                   motion@0.2.1
  13.D.5  <TiltGallery>                                motion@0.2.1
  CC.6    ANTIPATTERNS.md                              docs
  MAG.8   /showcase/futurism/debug-overlay             showcase

§11.2 counter rewrote (37 / 202 done · 18%)
  Wave 9 Data:      9/42  → 15/42 (36%)
  Wave 13 Futurism: 9/39  → 17/39 (44%)
  Cross-cutting:    0/8   →  1/8  (13%)
  Magnet demos:     2/8   →  3/8  (38%)

Three MAG.* magnets are now live:
   MAG.1 spatial-hero    (Wave 13.D.6)
   MAG.3 trust-surfaces  (Wave 13.C.7)
   MAG.8 debug-overlay   (Wave 13.C.5)
2026-05-27 17:08:08 -07:00
saravanakumardb1
87e3bc490a feat: Wave 9.E (ai-ui@0.6.0) + Wave 13.D.1/.5 (motion@0.2.1)
──────────────────────────────────────────────────────────────────
motion@0.2.1 — Parallax + TiltGallery
──────────────────────────────────────────────────────────────────
  + Parallax.tsx
      - scroll-driven translate3d via rAF + window.scroll
      - speed multiplier + axis (y / x) + reduced-motion bypass
      - listener cleanup + cancelAnimationFrame on unmount
      - WAVE 13.D.1
  + TiltGallery.tsx
      - horizontally-scrolling rail of <TiltCard>-style tiles
      - per-tile cursor-tracking rotateX/Y + glare gradient
      - role=region + arrow-key scrolling (←/→) + scroll-snap
      - reduced-motion strips tilt + glare, keeps the rail
      - WAVE 13.D.5
  + 5 new tests (Parallax x 2, TiltGallery x 3) — 28/28 passing
  + index.ts: exports both + types
  + package.json: 0.2.0 → 0.2.1

──────────────────────────────────────────────────────────────────
ai-ui@0.6.0 — Wave 9.E composition surfaces
──────────────────────────────────────────────────────────────────
  + Markdown.tsx
      - dep-free subset renderer: h1-h3 / **bold** / *italic* /
        `code` / fenced code / ul + ol / [text](url)
      - inline `[cite:<id>]` chips resolved from a citations
        registry (missing ids render as [?] — failure mode is loud)
      - WAVE 9.E.1
  + CodeDiff.tsx
      - line-LCS diff in <100 LOC, zero deps
      - split (2-col) and unified views; tinted add/del rows
      - WAVE 9.E.2
  + ExplainThis.tsx
      - listens for selectionchange, pops 'Explain' CTA over the
        selection rect when inside the wrapper + ≥ minLength chars
      - fires onExplain({ text, rect }) so hosts can open a richer
        side panel if preferred
      - WAVE 9.E.3
  + usePromptHistory.ts
      - bash-style ↑/↓ recall with localStorage persistence
        (storage key configurable; null = in-memory for tests/SSR)
      - dedupes consecutive duplicates + trims to capacity
      - WAVE 9.E.4
  + useTokenCount.ts
      - cheap estimator (default ~4 chars/token; configurable for
        code/CJK) + optional USD cost
      - memoised — stable across re-renders
      - WAVE 9.E.5
  + 19 new tests in src/__tests__/composition.test.tsx — 98/98 passing
  + index.ts: '0.6 surfaces' section exports all 5 + types
  + package.json: 0.5.0 → 0.6.0

Showcase routes + roadmap flips land in the paired showcase commit.
2026-05-27 16:59:28 -07:00
saravanakumardb1
ec9e11b243 feat(ai-ui): complete Wave 13.C — PrivacyBadge + ProvenanceDrawer + DebugOverlay
The remaining three trust surfaces ship in this commit, completing
Wave 13.C and unlocking MAG.8 (debug-overlay).

──────────────────────────────────────────────────────────────────
<PrivacyBadge>  ·  Wave 13.C.6
──────────────────────────────────────────────────────────────────
  packages/ai-ui/src/PrivacyBadge.tsx (new)

  - 4 modes: on-device · cloud · hybrid · unknown
  - Token-tinted (success/info/accent/neutral) — instant trust
    signal without forcing the user into settings
  - Optional `detail` line (model id, device name, routing policy)
  - `iconOnly` variant for sidebar / tray placements
  - role=status + composite aria-label

──────────────────────────────────────────────────────────────────
<ProvenanceDrawer>  ·  Wave 13.C.4
──────────────────────────────────────────────────────────────────
  packages/ai-ui/src/ProvenanceDrawer.tsx (new)

  - Slide-in right drawer listing every step the model + tools took
  - Pure presentation — host passes the event array straight through
  - role=dialog + aria-modal + aria-labelledby
  - Initial focus on close button; Esc + backdrop both close
  - Body scroll lock while open (restores prior overflow value)
  - Empty-state copy when events=[]
  - Per-row <details> slot for inspecting full payload

──────────────────────────────────────────────────────────────────
<DebugOverlay>  ·  Wave 13.C.5  (MAG.8 magnet)
──────────────────────────────────────────────────────────────────
  packages/ai-ui/src/DebugOverlay.tsx (new)

  - Wraps any child surface — Shift-click reveals a modal inspector
    with the raw JSON payload
  - Modifier configurable: shift (default) / alt / meta
  - Production toggle: `disabled` short-circuits the wrapper (no
    cursor-hint, no click interception)
  - role=dialog + aria-modal + Esc to close + focus restore on close
  - Stringifies payload safely (catches non-serialisable values)
  - Cursor: help on the wrapper to hint discoverability

──────────────────────────────────────────────────────────────────
Quality gates
──────────────────────────────────────────────────────────────────
  ✓ pnpm -F @bytelyst/ai-ui test  →  79/79 passing (was 67/67)
    +12 new cases:
      - PrivacyBadge (3)
      - ProvenanceDrawer (5: closed-state · dialog semantics ·
        backdrop · Escape · empty-state)
      - DebugOverlay (4: plain-click ignored · Shift opens · disabled
        bypass · alt modifier)
  ✓ Exports wired in src/index.ts (PrivacyBadge, ProvenanceDrawer,
    DebugOverlay + all types)

──────────────────────────────────────────────────────────────────
Roadmap tracker (lands in subsequent commit)
──────────────────────────────────────────────────────────────────
  13.C.4  ProvenanceDrawer shipped
  13.C.5  DebugOverlay shipped
  13.C.6  PrivacyBadge shipped
  MAG.8   the debug-overlay magnet (showcase lands in paired commit)

Wave 13.C is now complete (7/7). Wave 13 Futurism: 9/39 → 12/39.
2026-05-27 16:53:03 -07:00