Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions docs/architecture/16-build-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,27 @@ Both halves of the config stay as they are, and each now means one thing:
- **No hydration.** `solid-js@1.9.14` does ship `solid-js/web` — `render`, `hydrate`, `renderToString`, `generateHydrationScript` are all there — so this is **framework work, not an upstream blocker** `As of 2026-08`. What is missing is the compile step: `solid-js/jsx-runtime` exports types and no factory, because Solid compiles JSX to `template()` calls rather than runtime `jsx()` calls. Hydration therefore needs a second, Solid-compiled bundle graph for the client, distinct from the inert `h` the server renders through. `hydrate.ts` and `islands.ts` emit the markup conventions that bundle would read; nothing reads them yet.
- **No `<Suspense>` holes.** `renderStreamHtml` still splices `<x-hole>` chunks, but nothing marks a subtree as one, so a `stream` route flushes its whole body in the first chunk — correct output, no streaming benefit. Solid's own `<Suspense>` is **not** the missing piece and must not be reached for: it calls `getContextId()`, which throws `cannot be used under non-hydrating context` outside a Solid renderer. Async data needs no boundary here — `renderToHtml` awaits async components and promise children directly.
- **No per-module CSS graph.** `stylesFor(surface)` filters by the stylesheet's own path, which keeps `site/` and `app/` apart but does not attribute CSS to a single route.

## What reaches the document, in what order

`As of 2026-08`. `stylesFor(surface)` emits **globals first, then modules**, and includes the
surface's own sheets plus every `surface === null` (a package sheet) and `shared/` one.

Two bugs lived in the gap between that sentence and the code, and both were invisible until a page
was opened in a browser:

| Was | Cost |
|---|---|
| `packages/ui/src/global.scss` — "one import for an app document shell" — was imported by **nothing, anywhere** | no `:root` block reached any document, so every `var(--color-*)` and `var(--space-*)` resolved to nothing and the whole token layer was dead in every app ever deployed |
| `stylesFor` dropped `surface === 'shared'` | a stylesheet in the one directory both graphs import was filtered out of both documents |
| insertion order, not globals-first | the reset could lose a specificity tie to whichever page module happened to load first |

The global layer arrives through the **app's own module graph** — `apps/web/shared/global.scss`
(`@use '@ultimat3/ui/global.scss'`) plus a one-line `shared/global.ts` that imports it — because
`render` is tier 4 and `ui` is tier 5, so no framework package may pull it in. `x new` writes both,
and `x verify`'s `budgets` step fails with `X_STYLES_GLOBAL_MISSING` when a rendered document
carries CSS and defines no `:root` custom properties.

`shared/global.ts` must be reached by a **dynamic** import (`loadApp`'s glob already is one): Bun
resolves a file's entire static graph before evaluating the module that installs the `.scss`
plugin, so a static import would compile the stylesheet with no loader in place.
4 changes: 4 additions & 0 deletions dummy/social-media-clone/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export const config = defineConfig({
defaultLocale: 'en',
defaultTimeZone: 'UTC',
defaultCurrency: 'USD',
// Without `signInPath` a browser that opens /dashboard with no session is answered with the
// problem+json document — correct for an agent, and rendered as raw JSON text to a person.
// Naming the page turns that into a 303 carrying `?next=`, and the page sends them back.
auth: { signInPath: '/signin', afterSignInPath: '/dashboard' },
// Env KEYS, never the value: the same image deploys to every environment.
database: { urlEnv: 'DATABASE_URL', poolSize: 10 },
cache: { driver: 'memory', tiers: ['memo', 'lru'] },
Expand Down
24 changes: 22 additions & 2 deletions dummy/social-media-clone/apps/web/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { MAX_HANDLE } from '@social-media-clone/domain';
import { action, t } from '@ultimat3/action';
import { NEXT_PARAM, nextAfterSignIn, setRedirect, useRequestHeader } from '@ultimat3/http';
import { allow } from '@ultimat3/policy';
import type { IssuedSession } from '../app/auth/service';
import { signIn, signOut, signUp } from '../app/auth/service';
Expand Down Expand Up @@ -50,10 +51,28 @@ const attach = (ctx: unknown, issued: IssuedSession, now: Date): void => {
);
};

/**
* The form field the sign-in page carries, and the only reason these two actions differ from
* every other one. `nextAfterSignIn` refuses anything that is not a same-origin path.
*/
const nextInput = t.optional(t.string.max(2048));

/**
* A browser that posted the native form gets a 303 to where it was going; an agent that posted
* JSON gets the output schema, unchanged. Same operation, two audiences — `setRedirect` is the
* seam that exists for exactly this, so the action does not grow a second protocol.
*/
const landAfter = (raw: string | undefined, fallback: string): string => {
const next = nextAfterSignIn(raw, fallback);
if ((useRequestHeader('accept') ?? '').includes('text/html')) setRedirect(next);
return next;
};

export const createSession = action({
input: t.object({
handle: handleInput,
password: t.string.min(1),
[NEXT_PARAM]: nextInput,
[CAPTCHA_FIELD]: t.optional(t.string),
}),
output: sessionOutput,
Expand All @@ -72,7 +91,7 @@ export const createSession = action({
now,
);
attach(ctx, issued, now);
return { ok: true, next: '/feed', handle: input.handle };
return { ok: true, next: landAfter(input[NEXT_PARAM], '/feed'), handle: input.handle };
},
});

Expand All @@ -82,6 +101,7 @@ export const createAccount = action({
displayName: t.string.min(1).max(80),
email: t.email,
password: t.string.min(1),
[NEXT_PARAM]: nextInput,
[CAPTCHA_FIELD]: t.optional(t.string),
}),
output: sessionOutput,
Expand All @@ -100,7 +120,7 @@ export const createAccount = action({
now,
);
attach(ctx, issued, now);
return { ok: true, next: '/feed', handle: input.handle };
return { ok: true, next: landAfter(input[NEXT_PARAM], '/feed'), handle: input.handle };
},
});

Expand Down
24 changes: 24 additions & 0 deletions dummy/social-media-clone/apps/web/shared/global.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// The app document's global layer, and the only stylesheet in this app that emits top-level CSS:
// @ultimat3/ui's custom properties (`:root{--color-*;--space-*;…}`) and then its reset. Every rule
// a component module emits reads those properties through `var(--…)`, so without this file the
// browser drops every one of those declarations and the app renders unstyled.
//

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove empty // comments.

Stylelint reports scss/comment-no-empty at every listed site. Remove the empty separator so stylesheet lint can pass.

  • dummy/social-media-clone/apps/web/shared/global.scss#L5-L5: Remove the empty // separator.
  • dummy/social-media-clone/apps/web/shared/tokens.scss#L5-L5: Remove the empty // separator.
  • examples/dummy/apps/web/shared/global.scss#L5-L5: Remove the empty // separator.
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 5-5: Unexpected empty comment (scss/comment-no-empty)

(scss/comment-no-empty)

📍 Affects 3 files
  • dummy/social-media-clone/apps/web/shared/global.scss#L5-L5 (this comment)
  • dummy/social-media-clone/apps/web/shared/tokens.scss#L5-L5
  • examples/dummy/apps/web/shared/global.scss#L5-L5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dummy/social-media-clone/apps/web/shared/global.scss` at line 5, Remove the
empty // separator at dummy/social-media-clone/apps/web/shared/global.scss lines
5-5, dummy/social-media-clone/apps/web/shared/tokens.scss lines 5-5, and
examples/dummy/apps/web/shared/global.scss lines 5-5; no other stylesheet
changes are needed.

Source: Linters/SAST tools

// Exactly one file, imported for its side effect by `global.ts` — never `@use`d from a
// `*.module.scss`. Each module is a separate Sass compilation, so a `@use` that emits would
// duplicate the whole `:root` block once per module that reached for it. `shared/tokens.scss` is
// the authoring layer and stays emit-free for that reason.

@use '@ultimat3/ui/global.scss';
@use './tokens' as t;

// The two element rules the reset leaves to the app, both product decisions rather than
// normalization: code sits on a tinted ground, and a scrollable region keeps its scrollbar in the
// theme's own colours instead of the browser's.
:where(code, pre) {
background: t.role('bg-soft');
border-radius: t.radius('sm');
}

:where(html) {
scrollbar-color: t.role('line') t.role('bg');
}
42 changes: 42 additions & 0 deletions dummy/social-media-clone/apps/web/shared/global.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// The regression the deployed app shipped for its whole life: 64kB of component CSS in the
// document and not one `:root` custom property, so every `rgb(var(--color-bg)/1)` the UI kit
// emits was dropped by the browser. The page looked structurally correct and rendered unstyled.

import { stylesFor } from '@ultimat3/render';
import { expect, unitTest } from '@ultimat3/testing';

/**
* `await import`, not a static one, for the same reason `loadApp` uses a dynamic import: Bun loads
* a file's whole static graph before it evaluates the module that installs the `.scss` plugin, so
* a static `import './global'` here would resolve the stylesheet with no loader in place. The
* module cache makes repeated calls free, so each test states its own precondition.
*/
const loadGlobalLayer = (): Promise<unknown> => import('./global');

const occurrences = (haystack: string, needle: string): number => haystack.split(needle).length - 1;

unitTest('both surfaces carry the token layer, because shared/ is in both graphs', async () => {
await loadGlobalLayer();
for (const surface of ['site', 'app'] as const) {
expect(stylesFor(surface)).toContain('--color-fg:');
}
});

unitTest(
'the global layer leads the document, so the reset cannot lose a specificity tie',
async () => {
await loadGlobalLayer();
expect(stylesFor('site').startsWith(':root{')).toBe(true);
},
);

unitTest(
'it is emitted exactly once — `--space-8` is defined in one scope and only one',
async () => {
await loadGlobalLayer();
// Not `--color-fg`: the theme defines that one four times on purpose (light `:root`, the dark
// media query, and `[data-theme]` for each), so only a theme-invariant token counts copies.
expect(occurrences(stylesFor('site'), '--space-8:')).toBe(1);
expect(occurrences(stylesFor('app'), '--space-8:')).toBe(1);
},
);
6 changes: 6 additions & 0 deletions dummy/social-media-clone/apps/web/shared/global.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// The one edge that puts the global stylesheet in this app's module graph. `shared/` is loaded by
// both surfaces and by the framework's own boot scan, so the tokens reach every document without a
// page having to remember to import them — and `x verify` fails with X_STYLES_GLOBAL_MISSING if
// this edge is ever cut.

import './global.scss';
6 changes: 5 additions & 1 deletion dummy/social-media-clone/apps/web/shared/tokens.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// The app's one styling entry point: `@use '../../shared/tokens' as t;` and nothing else.
// It forwards @ultimat3/ui's token layer verbatim and adds the computed layer the framework
// deliberately leaves to the app — fluid sizing, the two breakpoint intents `respond-to` cannot
// express, and the motion guard. Emits no CSS; global custom properties come from ui's theme.scss.
// express, and the motion guard.
//
// Emits no CSS, and must not: every module is its own Sass compilation, so a `:root` block in here
// would be inlined once per stylesheet that uses it. The custom properties these functions REFER
// to are defined exactly once, by `shared/global.scss`.

@use 'sass:map';
@use 'sass:math';
Expand Down
68 changes: 64 additions & 4 deletions dummy/social-media-clone/apps/web/site/page.module.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,77 @@
@use '../shared/tokens' as t;

.hero {
.page {
display: grid;
gap: t.space(4);
padding: t.space(8);
gap: t.space(10);
padding: t.space(8) t.space(6);
background: t.role('bg');
color: t.role('fg');
}

.hero {
display: grid;
gap: t.space(4);
max-width: 62ch;

h1 {
// Wrapping a headline at the last two words reads as a mistake; `balance` evens the lines.
text-wrap: balance;
}
}

.lede {
color: t.role('fg-muted');
font-size: t.text('lg');
}

.actions {
display: flex;
flex-wrap: wrap;
gap: t.space(3);
}

.cta {
justify-self: start;
text-decoration: none;
padding: t.space(2) t.space(4);
border-radius: t.radius('md');
background: t.role('accent');
color: t.role('accent-fg');
}

.secondary {
text-decoration: none;
padding: t.space(2) t.space(4);
border: 1px solid t.role('line');
border-radius: t.radius('md');
color: t.role('fg');
}

.note {
color: t.role('fg-muted');
font-size: t.text('sm');
}

.list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Allow proof cards to shrink on narrow viewports.

minmax(18rem, 1fr) can overflow after .page applies horizontal padding. Cap the minimum track at the container width.

Proposed fix
-  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
+  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dummy/social-media-clone/apps/web/site/page.module.scss` at line 56, Update
the proof-card grid’s grid-template-columns declaration to cap the minimum track
at the available container width while preserving the existing 18rem preferred
minimum on wider viewports, so cards can shrink without overflowing the padded
.page container.

gap: t.space(4);
margin-block-start: t.space(4);
}

.proof {
display: grid;
gap: t.space(2);
padding: t.space(4);
border: 1px solid t.role('line');
border-radius: t.radius('lg');
background: t.role('surface');

h3 {
text-wrap: balance;
}

p {
color: t.role('fg-muted');
font-size: t.text('sm');
}
}
52 changes: 44 additions & 8 deletions dummy/social-media-clone/apps/web/site/page.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
import { expect, unitTest } from '@ultimat3/testing';
import { config } from './page';

unitTest('the landing page ships zero JS and declares metadata', async () => {
expect(config.render).toBe('static');
expect(config.hydrate).toBe('never');
expect(config.budget.js).toBe('0kb');
const meta = await config.meta({});
// The landing page: what it declares, and what it actually emits.
//
// The page module is imported DYNAMICALLY, for the reason `apps/admin/app/admin/page.test.ts`
// spells out: Bun loads a file's static imports before executing any of them, so a static
// `import './page'` compiles before `@ultimat3/render`'s module scope installs the `.tsx` loader,
// and every element comes out as `React.createElement` against a global that does not exist.

import { beforeAll, expect, test } from 'bun:test';
// Side-effect import: `defineCatalogs()` runs on the way through. Without it every `t()` in the
// tree renders `⟦key⟧` — which is exactly what the last assertion here is checking for.
import '@social-media-clone/i18n';
import { t } from '@ultimat3/i18n';
import { renderComponent } from '@ultimat3/render';

const FILE = 'apps/web/site/page.tsx';

let page: typeof import('./page');

beforeAll(async () => {
page = await import('./page');
});

test('unit · the landing page ships zero JS and declares metadata', async () => {
expect(page.config.render).toBe('static');
expect(page.config.hydrate).toBe('never');
expect(page.config.budget.js).toBe('0kb');
const meta = await page.config.meta({ data: {}, params: {}, url: 'https://demo.test/', t });
expect(meta.title ?? '').not.toBe('');
});

// This page shipped for weeks as the generator's placeholder — "Everything you need, one command
// from shippable" over a single link to `/dashboard`, which 401s a signed-out visitor. A title
// assertion alone passed the whole time, because the placeholder had a title too.
test('unit · it says what this deployment is, and links somewhere anonymous', async () => {
const html = await renderComponent(
page.HomePage,
{ data: {}, params: {}, url: '/', query: {} },
FILE,
);
expect(html).toContain('href="/feed"');
expect(html).toContain('href="/signin"');
expect(html.toLowerCase()).toContain('seeded');
// A missing catalog key renders ⟦key⟧, which is visible on the page and passes every other
// assertion here — so the absence of one is its own check.
expect(html).not.toContain('⟦');
});
38 changes: 32 additions & 6 deletions dummy/social-media-clone/apps/web/site/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// The landing page. site/ is 0kb JS: static render, hydrate never, no framework script tag.
// It says what this deployment IS — a framework stress test with seeded data — because an
// unlabelled demo reads as a real network, and the first thing anyone does is try to sign up.
Comment on lines +2 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make this a module header.

Move the header before imports. State the landing page's single responsibility and why it stays static. These lines describe deployment content instead.

As per coding guidelines: “Add a 1–4 line header comment to each file stating its single responsibility.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dummy/social-media-clone/apps/web/site/page.tsx` around lines 2 - 3, Move the
existing deployment-description comment in the landing page module before all
imports and rewrite it as a 1–4 line header stating the page’s single
responsibility and why it remains static. Keep the explanation focused on the
seeded framework stress-test deployment rather than implementation details.

Source: Coding guidelines


import { t } from '@ultimat3/i18n';
import { defineRoute } from '@ultimat3/render';
import styles from './page.module.scss';
Expand All @@ -14,14 +17,37 @@ export const config = defineRoute({
}),
});

/** Each row is one framework claim this deployment is the evidence for. */
const PROOFS = ['policy', 'realtime', 'offline', 'admin'] as const;

export function HomePage() {
return (
<main class={styles.hero}>
<h1>{t('site.home.title')}</h1>
<p>{t('site.home.description')}</p>
<a class={styles.cta} href="/dashboard">
{t('site.home.cta')}
</a>
<main class={styles.page}>
<section class={styles.hero}>
<h1>{t('site.home.title')}</h1>
<p class={styles.lede}>{t('site.home.description')}</p>
<nav class={styles.actions}>
<a class={styles.cta} href="/feed">
{t('site.home.cta')}
</a>
<a class={styles.secondary} href="/signin">
{t('site.home.signin')}
</a>
</nav>
<p class={styles.note}>{t('site.home.seeded')}</p>
</section>

<section class={styles.proofs}>
<h2>{t('site.home.proofs.title')}</h2>
<ul class={styles.list}>
{PROOFS.map((proof) => (
<li class={styles.proof}>
<h3>{t(`site.home.proofs.${proof}.title`)}</h3>
<p>{t(`site.home.proofs.${proof}.body`)}</p>
</li>
))}
</ul>
</section>
</main>
);
}
Expand Down
Loading
Loading