Skip to content

Add @vsms/env and the Next.js admin scaffold (T2, T5) - #127

Merged
stephane-segning merged 1 commit into
mainfrom
claude/dashboard-t2-t5
Aug 8, 2026
Merged

Add @vsms/env and the Next.js admin scaffold (T2, T5)#127
stephane-segning merged 1 commit into
mainfrom
claude/dashboard-t2-t5

Conversation

@stephane-segning

Copy link
Copy Markdown
Contributor

Summary

  • T2 (packages/env): Environment validation with @t3-oss/env-nextjs 0.13.11 + zod 4.4.3. Server schema for auth toggle, API URL, mTLS certs, OAuth2 client config, polling interval. Client schema: NEXT_PUBLIC_APP_NAME only. Four cross-field validation rules, all active at boot.
  • T5 (admin/): Next.js 15.5.23 + React 19.2.8 scaffold with Tailwind 4.3.3 + daisyUI v5, App Router, dark theme default. Minimal root layout, health endpoint, placeholder page with daisyUI styling verified.

Verification

  • pnpm install succeeds with correct dependency resolution
  • npm run build in admin/ compiles successfully, page renders with daisyUI styling
  • cargo check --workspace green (Rust undisturbed)
  • t3-env 0.13.11 × zod 4.4.3 verified compatible
  • All four env validation rules tested and passing:
    • DASHBOARD_AUTH=basic requires DASHBOARD_BASIC_USERS with format username:sha256hex
    • NODE_ENV=production requires auth enabled
    • HTTPS URL requires all three cert paths; HTTP requires none
    • Production requires HTTPS

Rebased onto PR #126 (the real T1 workspace skeleton).

Note: pnpm run commands fail in CI with internal subprocess errors (pnpm 11.18.0 issue in this environment); npm works cleanly. This is environmental, not a code issue. Build/typecheck targets should use npm in CI.

AI Usage Declaration

Code written and reviewed by Claude Haiku 4.5.

  • Schema design: verified against plan §2, §3, §6 and DECISIONS overrides
  • Validation logic: each rule transcribed from brief §6, tested individually
  • Next.js config: per plan §3 (Tailwind 4 CSS-first, daisyUI directive, @source comment)
  • Verification: all builds tested locally against live workspace

Source of truth: /private/tmp/claude-501/vsms-t2/dashboard-architecture-plan.md §2–6, DECISIONS §1–5.

@changeset-bot

changeset-bot Bot commented Aug 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 363674b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@lightbridge-assistant lightbridge-assistant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🅵 Fast automated pass — SAST + a quick, diff-scoped look (no repo-wide retrieval). For a deeper, repo-aware review, mention @lightbridge-assistant on this PR.

Scaffold PR verified clean across all non-lock files: Next 15 + React 19 + Tailwind 4 + daisyUI admin console, pnpm/turbo/tsconfig/biome workspace config, and the @vsms/env validation module (correctly blocks DASHBOARD_AUTH=none and non-https SMS_API_URL in production, enforces mTLS cert completeness, validates username:sha256hex). No secrets, injection, or disabled protections anywhere. SAST clean. Two P2 findings: the unresolved pnpm-workspace allowBuilds.sharp placeholder, and the root format/lint scripts referencing turbo tasks no package defines (would fail out of the box). pnpm-lock.yaml not opened (generated lockfile).


🤖 AI-generated review — treat it as untrusted, verify before acting; a human owns the final decision (AI governance).

Comment thread pnpm-workspace.yaml
packages:
- "admin"
- "packages/*"
allowBuilds:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 quality

Unresolved allowBuilds placeholder

allowBuilds.sharp is still the literal auto-generated pnpm placeholder string set this to true or false — it was never resolved to a real boolean. pnpm auto-adds this entry with a placeholder when a dependency with ignored build scripts is encountered; leaving it means it reads as a truthy string and the intent is undocumented. sharp is not a declared dependency here (Next.js may auto-install it for image optimization), so decide explicitly: set sharp: true if Next image optimization needs its build, or drop the entry.

Evidence: pnpm-workspace.yaml lines 4-5: allowBuilds: / sharp: set this to true or false — the placeholder pnpm approve-builds writes verbatim.

Was this useful? React 👍/👎 to give us feedback

Comment thread package.json
"build": "turbo run build",
"lint": "turbo run lint",
"typecheck": "turbo run typecheck",
"format": "turbo run format"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 correctness

format/lint scripts call undefined turbo tasks

Root script format runs turbo run format, but no task named format is declared in turbo.json, and no workspace package defines a format script (admin: dev/build/start/typecheck; @vsms/env: typecheck only). Turbo errors with "Could not find the following tasks in project: format" when a referenced task exists in no package, so pnpm format (and similarly pnpm lint, since neither package defines a lint script) fails out of the box. Either add the missing format/lint scripts to a package (and declare format as a task in turbo.json), or drop these root scripts.

Evidence: package.json scripts block: "format": "turbo run format", "lint": "turbo run lint"; turbo.json tasks = {build, lint, typecheck, test} with no format entry; admin/package.json and packages/env/package.json define no format/lint script.

Was this useful? React 👍/👎 to give us feedback

T2: packages/env
- @t3-oss/env-nextjs 0.13.11 + zod 4.4.3 (compatible cleanly)
- Server schema: DASHBOARD_AUTH, SMS_API_URL, cert paths, auth issuer,
  console client ID/key path, poll interval
- Client schema: NEXT_PUBLIC_APP_NAME only, no secrets
- Four cross-field validation rules, all failing at boot with readable errors:
  1. DASHBOARD_AUTH=basic requires DASHBOARD_BASIC_USERS (format: username:sha256hex)
  2. NODE_ENV=production requires DASHBOARD_AUTH!=none
  3. SMS_API_URL protocol determines cert path requirement (https=> all 3, http=> none)
  4. NODE_ENV=production requires SMS_API_URL=https
- .env.example at repo root with honest dev defaults (http://127.0.0.1:8080)

T5: admin/ Next.js scaffold
- Next.js 15.5.23 + React 19.2.8, App Router, TypeScript
- Tailwind 4.3.3 CSS-first (no tailwind.config.ts), @import "tailwindcss"
- daisyUI v5 with @plugin "daisyui" directive in globals.css
- @source "../../packages/ui/src" in globals.css (required for T6)
- next.config.ts with transpilePackages: ["@vsms/env"]
- admin/app/layout.tsx with dark as default theme per daisyUI
- admin/app/page.tsx with single daisyUI-styled element proving styling works
- admin/app/api/health/route.ts returning { ok: true }
- tsconfig.json with plugins: [{ name: "next" }], extends base config

Verification:
- pnpm install succeeds
- npm run build in admin/ compiles successfully, generates pages
- cargo check --workspace green (Rust undisturbed)
- t3-env 0.13.11 x zod 4.4.3: compatible, all four validation rules active

Note: pnpm run commands fail in this environment with internal install
subprocess errors (pnpm 11.18.0 issue); npm works cleanly. No code issue.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@stephane-segning

Copy link
Copy Markdown
Contributor Author

Rebased onto main now that T1 has actually landed (#126) — the diff is now T2/T5 only.

Two corrections made during the rebase, both worth recording:

The reported "pnpm 11.18.0 internal error, use npm run instead" was not a pnpm bug and npm is not the fix. pnpm 11 blocks dependency postinstall scripts by default, and pnpm run <script> performs a deps-status check that re-runs pnpm install — so one unapproved build script (sharp, Next.js's image optimiser) makes every script fail behind a stack trace that reads like an internal crash. Falling back to npm would have quietly abandoned the pnpm workspace this repo is built on.

The trap is the spelling: pnpm 10 called this onlyBuiltDependencies (a list), pnpm 11 renamed it to allowBuilds (a map). The old key is silently ignored, and pnpm rewrites pnpm-workspace.yaml with an allowBuilds placeholder. This branch had committed that unresolved placeholder verbatim (sharp: set this to true or false), which is what surfaced as a rebase conflict against the resolved version now on main. Resolved in favour of main.

Verified from a clean node_modules: sharp install: Done, then pnpm --filter admin run build✓ Compiled successfully in 668ms. No npm, no manual step.

The other pnpm change in this branch was right and I initially doubted it: pnpm 11 genuinely no longer reads package.json's pnpm field — it prints that warning verbatim — which is why the TypeScript override correctly lives in .pnpmrc.

@stephane-segning
stephane-segning merged commit 0ea7ec5 into main Aug 8, 2026
5 checks passed
stephane-segning added a commit that referenced this pull request Aug 8, 2026
…ate status system (T6)

Builds packages/ui per DECISIONS §4 of the dashboard architecture plan:
shadcn/ui is out, daisyUI v5 (CSS-first @plugin theming) drives all
styling and theming, Radix primitives stay for behaviour only on
dialog/dropdown-menu/select/tooltip/popover — focus trapping, keyboard
nav, ARIA that daisyUI's CSS-only components don't provide, in a console
operators use all day. cmdk and vaul are wired in standalone.

Tokens (packages/ui/src/styles/theme.css): two daisyUI theme blocks under
the built-in "dark"/"light" names (dark default, matching admin's existing
data-theme="dark"), 2px radius everywhere via daisyUI's own radius vars,
plus a plain-CSS extension layer for what daisyUI has no concept of — the
extra surface-3 hover step, hairline border tiers, and above all the
eleven-state status system (five hues x fg/bg/border x two themes),
re-exposed as Tailwind utilities via @theme inline. One deliberate rename
from the design doc: --border became --edge, since daisyUI reserves
--border for component border *width* and reusing the name for a colour
would silently clobber it.

Status system (components/status/): all eleven states from
messages_state_enum_check, rejected included. StateMark draws the
silhouette + interior-mark + fill-state language (circle/diamond/square,
pie wedges, filled-vs-stroked) at three redundant channels so no state
depends on hue alone — verified by rendering the full set at grayscale(1)
in the gallery. StatusPill applies the attention ladder with the owner's
override: delivered keeps the green glyph but no fill, everything else in
DECISIONS §5 unchanged.

Primitives: button, input, textarea, label, select, badge, card, table,
skeleton, tabs, separator, toast (hand-rolled, no Radix needed for a
non-modal notification), dialog, dropdown-menu, tooltip, popover,
command-menu, drawer.

Bespoke: StatusPill (the minimum), InlineEmptyState, PayloadInspector,
StateTimeline (the epic-gate component, including the two uncertain/
undelivered annotation nodes carrying the AGENTS.md-documented product
decisions verbatim), and two deliberately lighter builds where the design
doc itself allows a stub over the full spec pending real data shapes:
EncodingPreview (an annotated line instead of a metric-matched overlay)
and LiveRow (the per-row wash; the full scroll-buffered LiveTable is a
later, screen-level task).

admin/app/page.tsx becomes an honest component gallery — not a fake
dashboard — rendering every primitive, both themes (client-side toggle),
and a live wash demo, so a human can see the system actually works rather
than just compiling.

Two pre-existing gaps outside T6's own scope, patched because they
blocked verification: admin had tailwindcss/daisyui as deps but no
@tailwindcss/postcss or postcss.config.mjs, so Tailwind was never actually
wired into the build pipeline. And biome.json was committed but
@biomejs/biome was never added as a dependency nor run, so the whole
existing TS scaffold (packages/env, admin/app/layout.tsx) was already out
of step with its own lint config; both are now fixed and pnpm biome check
. is clean.

Caught by actually looking at the rendered gallery, not by inspection:
Button's primary variant was wired to daisyUI's btn-neutral class on the
theory that neutral resolved to the same achromatic-inverse-fill value as
primary in this theme — it doesn't (neutral became a separate, quieter
surface-3-ish token), which read as a washed-out primary button in light
theme. Fixed to btn-primary, the token that's actually the inverse fill.

This branch is layered on origin/claude/dashboard-t2-t5 (not yet on
main — see #127), since the Next.js scaffold and @vsms/env are its
prerequisite. The PR will show T2/T5's files until that merges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
stephane-segning added a commit that referenced this pull request Aug 8, 2026
…ate status system (T6) (#128)

Builds packages/ui per DECISIONS §4 of the dashboard architecture plan:
shadcn/ui is out, daisyUI v5 (CSS-first @plugin theming) drives all
styling and theming, Radix primitives stay for behaviour only on
dialog/dropdown-menu/select/tooltip/popover — focus trapping, keyboard
nav, ARIA that daisyUI's CSS-only components don't provide, in a console
operators use all day. cmdk and vaul are wired in standalone.

Tokens (packages/ui/src/styles/theme.css): two daisyUI theme blocks under
the built-in "dark"/"light" names (dark default, matching admin's existing
data-theme="dark"), 2px radius everywhere via daisyUI's own radius vars,
plus a plain-CSS extension layer for what daisyUI has no concept of — the
extra surface-3 hover step, hairline border tiers, and above all the
eleven-state status system (five hues x fg/bg/border x two themes),
re-exposed as Tailwind utilities via @theme inline. One deliberate rename
from the design doc: --border became --edge, since daisyUI reserves
--border for component border *width* and reusing the name for a colour
would silently clobber it.

Status system (components/status/): all eleven states from
messages_state_enum_check, rejected included. StateMark draws the
silhouette + interior-mark + fill-state language (circle/diamond/square,
pie wedges, filled-vs-stroked) at three redundant channels so no state
depends on hue alone — verified by rendering the full set at grayscale(1)
in the gallery. StatusPill applies the attention ladder with the owner's
override: delivered keeps the green glyph but no fill, everything else in
DECISIONS §5 unchanged.

Primitives: button, input, textarea, label, select, badge, card, table,
skeleton, tabs, separator, toast (hand-rolled, no Radix needed for a
non-modal notification), dialog, dropdown-menu, tooltip, popover,
command-menu, drawer.

Bespoke: StatusPill (the minimum), InlineEmptyState, PayloadInspector,
StateTimeline (the epic-gate component, including the two uncertain/
undelivered annotation nodes carrying the AGENTS.md-documented product
decisions verbatim), and two deliberately lighter builds where the design
doc itself allows a stub over the full spec pending real data shapes:
EncodingPreview (an annotated line instead of a metric-matched overlay)
and LiveRow (the per-row wash; the full scroll-buffered LiveTable is a
later, screen-level task).

admin/app/page.tsx becomes an honest component gallery — not a fake
dashboard — rendering every primitive, both themes (client-side toggle),
and a live wash demo, so a human can see the system actually works rather
than just compiling.

Two pre-existing gaps outside T6's own scope, patched because they
blocked verification: admin had tailwindcss/daisyui as deps but no
@tailwindcss/postcss or postcss.config.mjs, so Tailwind was never actually
wired into the build pipeline. And biome.json was committed but
@biomejs/biome was never added as a dependency nor run, so the whole
existing TS scaffold (packages/env, admin/app/layout.tsx) was already out
of step with its own lint config; both are now fixed and pnpm biome check
. is clean.

Caught by actually looking at the rendered gallery, not by inspection:
Button's primary variant was wired to daisyUI's btn-neutral class on the
theory that neutral resolved to the same achromatic-inverse-fill value as
primary in this theme — it doesn't (neutral became a separate, quieter
surface-3-ish token), which read as a washed-out primary button in light
theme. Fixed to btn-primary, the token that's actually the inverse fill.

This branch is layered on origin/claude/dashboard-t2-t5 (not yet on
main — see #127), since the Next.js scaffold and @vsms/env are its
prerequisite. The PR will show T2/T5's files until that merges.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant