Skip to content

Commit 3b69b67

Browse files
committed
Add live Platform checks, the parity ledger and the data seam contract
checks/live runs Playwright against a real app and database with a JSON report at PLATFORM_LIVE_REPORT_PATH, and stays out of the fixture suite. org-parity.md records the capabilities hidden or left legacy so far. AGENTS, README and the ownership map describe the seam, the modes and the zone.
1 parent 6ad6575 commit 3b69b67

16 files changed

Lines changed: 134 additions & 23 deletions

File tree

‎apps/sim/lib/platform/routes.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/**
22
* How the legacy organization surface maps onto the Platform zone. Dependency-free: the edge proxy,
3-
* `next.config.ts` (loaded before the `@/` alias resolves), and client code all read it.
3+
* server components, and client code all read it.
44
*/
55

6-
export const PLATFORM_BASE_PATH = '/platform'
6+
/** Where the main app's `/platform` rewrite in `next.config.ts` mounts the zone. */
7+
const PLATFORM_BASE_PATH = '/platform'
78

89
/**
910
* Carries the requested `/o/...` URL from the proxy to the organization layout, which Next gives no

‎apps/sim/next.config.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
} from './lib/core/security/csp'
1212
import { LANDING_ROUTES } from './lib/landing/routes'
1313
import { LIBRARY_MERGED_SLUGS, LIBRARY_MOVED_BLOG_SLUGS } from './lib/library/retired-slugs'
14-
import { PLATFORM_BASE_PATH } from './lib/platform/routes'
1514

1615
/**
1716
* `Accept` header patterns for Markdown negotiation. `text/markdown` must be one of the listed
@@ -729,8 +728,8 @@ const nextConfig: NextConfig = {
729728
...(PLATFORM_ORIGIN
730729
? [
731730
{
732-
source: `${PLATFORM_BASE_PATH}/:path*`,
733-
destination: `${PLATFORM_ORIGIN}${PLATFORM_BASE_PATH}/:path*`,
731+
source: '/platform/:path*',
732+
destination: `${PLATFORM_ORIGIN}/platform/:path*`,
734733
},
735734
]
736735
: []),

‎apps/sim/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"type-check:platform": "cd platform && next typegen && tsc --noEmit",
1515
"test:platform:desktop": "playwright test --config platform/checks/desktop/playwright.config.ts",
1616
"test:platform:e2e": "playwright test --config platform/checks/playwright.config.ts",
17+
"test:platform:live": "playwright test --config platform/checks/live/playwright.config.ts",
1718
"start:platform": "cd platform && next start --port 3100",
1819
"dev:capped": "bun run build:cli-runtime && bun run dev:cache:cap && NODE_OPTIONS='--max-old-space-size=4096' next dev --port 3000",
1920
"dev:cache:cap": "bun run ../../scripts/prune-turbopack-cache.ts",

‎apps/sim/platform/AGENTS.md‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Platform design contract
22

3-
Applies to this independent Next.js application. In this tree “EMCN” means the Sim Platform design system in `design-system/`. The existing app and `packages/emcn` remain isolated. For new work or design drift, run the repository's `$implement-platform` skill. Its Paper companion is `$sim-paper-design`. Never import the legacy app, styles, chrome, business logic or providers across this boundary.
3+
Applies to this independent Next.js application. In this tree “EMCN” means the Sim Platform design system in `design-system/`. The existing app and `packages/emcn` remain isolated. For new work or design drift, run the repository's `$implement-platform` skill. Its Paper companion is `$sim-paper-design`. Never import the legacy app, styles, chrome, business logic or providers across this boundary. Real data crosses only through the [data seam](#phase-3-data-seam).
44

55
## Ownership
66

@@ -11,6 +11,7 @@ Applies to this independent Next.js application. In this tree “EMCN” means t
1111
| Product: Platform Core, Chat, Workspaces | A thin `app/(shell)/<segment>/page.tsx`; unique markup, interaction and a local CSS Module under `<segment>/_components/<component>/` | Route composition |
1212
| Engineering workbench, outside Product | `preview/` | The separate /workbench route |
1313
| Temporary example data | `fixtures/` | Server-only, default-off loader |
14+
| Real data | `lib/data/` | The route composition that passes it to views |
1415

1516
Search existing EMCN and route owners first. For existing designs, follow Shared → Platform → Product and verify the stable Paper key, live Base and actual product Use (or a nested use rooted in one). Reference artwork, archive files, guides and specimen-only variants are not product uses. Do not use Marcus's files. Two systems that can share a recipe use Shared. If a second feature needs a page-owned component, promote the existing implementation, register it centrally and replace the original import in the same change. Do not copy it or import directly from another feature. Once product routes exist, remove unreached public variants from code and keep deferred states in Paper until a product or nested component needs them.
1617

@@ -30,7 +31,7 @@ Use `ActivityRow` for a first-line glyph or avatar, title/detail and centered tr
3031

3132
Measure the actual scroll viewport with `ScrollArea`, or attach `useScrollEdges` directly to a native or Base UI scroll owner. Use the shared 16px mask only on edges with more content, updating after scroll, resize, input, reset or filtering. Insets establish resting layout; they cannot substitute for an edge fade. Home widgets and notification panels use `--ui-widget-max-size` as a ceiling, not a forced height. Keep the dashboard free to scroll and let each overflowing list or schedule scroll independently. Plain tooltips preview at most three lines; retain the complete accessible text. A structured people popover owns its content and uses the dropdown’s popup padding and row insets.
3233

33-
Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labels and handlers. The shared borderless surface positions its title at the logical start and supplies one quiet Cancel/Back. Command search and previews use the same shadow and dismissal layer. Form controls cannot construct or style a form header, footer or Cancel button. Opening a dialog or advancing its step focuses the first available field. Confirmation dialogs focus Cancel, and read-only previews focus Close. A nested dialog handles Escape one layer at a time. While a form is open, the global search shortcut leaves its draft and keyboard focus in place. Closing returns focus to the invoking control. Textareas scroll or grow within their owner and never show a manual resize handle. Recipient fields use the textarea’s height, draft typography and inset. Committed addresses use compact caption text and an always-visible neutral ×, like a panel tab. The remove button sits inside the tag beside its label; long addresses yield space to the button. Comma or Enter commits a draft, pasting a list commits its addresses, and Backspace from an empty draft removes the last tag.
34+
Form dialogs use `Modal` or `MultiStepModal`. Pass a title, content, action labels and handlers. A confirm handler that returns a promise holds the dialog open as pending, closes it on success, and keeps the draft with an inline message on failure; nothing dismisses it while pending. The shared borderless surface positions its title at the logical start and supplies one quiet Cancel/Back. Command search and previews use the same shadow and dismissal layer. Form controls cannot construct or style a form header, footer or Cancel button. Opening a dialog or advancing its step focuses the first available field. Confirmation dialogs focus Cancel, and read-only previews focus Close. A nested dialog handles Escape one layer at a time. While a form is open, the global search shortcut leaves its draft and keyboard focus in place. Closing returns focus to the invoking control. Textareas scroll or grow within their owner and never show a manual resize handle. Recipient fields use the textarea’s height, draft typography and inset. Committed addresses use compact caption text and an always-visible neutral ×, like a panel tab. The remove button sits inside the tag beside its label; long addresses yield space to the button. Comma or Enter commits a draft, pasting a list commits its addresses, and Backspace from an empty draft removes the last tag.
3435

3536
Use `useClipboard` / `CopyButton` for successful copy feedback and `useFeedbackExpiry` for temporary status. Copied indicators and transient notices dwell for four seconds; repeated events restart the timer. Pause an actionable notice during hover or focus and keep its action available in the stack afterward. Icon color changes use `--ui-motion-color` (120ms, zero under reduced motion). Do not put literal timers or CSS durations in product components.
3637

@@ -53,6 +54,20 @@ Use [the shared Platform time formatters](lib/time.ts) for displayed ages, clock
5354
- The isolated project root owns its globals, fonts and PostCSS entry. Keep the legacy root layout, proxy, globals and routes intact. Do not add a new provider or styling package just to define a variant.
5455
- Only the server loader may import example data, and only when `SIM_PLATFORM_FIXTURES=1`. No fallbacks in components. Delete the fixture module/gate and workbench consumers together once real data takes over. Never write examples to storage, send an invite or imply successful backend work.
5556

57+
## Phase 3 data seam
58+
59+
Only `lib/data/**` may import from outside this project: query hooks under `@/hooks/queries`, contracts and clients under `@/lib/api`, and the named helpers listed in `scripts/check-platform-design.ts`. `@/app`, `@/components` and `@sim/emcn` stay banned there too. Add a helper to that list only after reading its runtime import closure.
60+
61+
Each `lib/data/<domain>.ts` wraps the existing hook and maps its contract shape to a Platform view model with a pure adapter. Never fork a hook, key factory, invalidation or contract; a hook that lacks a needed option gains it additively. A module's first import of a query hook that toasts resolves through `lib/data/emcn-shim.ts`, which webpack substitutes for `@sim/emcn` and which exports only `toast`, bridged to the notification tray. Any other name from that package fails the build, which is what proves no legacy UI entered.
62+
63+
Views stay prop-driven. A route composes `data` and a named `actions` interface, filled by session state on fixtures and by mutations on live data. No fixture branch inside a view. Hide what has no backend and record it in the [parity ledger](docs/org-parity.md); never fake it. Distinguish loading, empty and failure. A list reads `status` rather than treating empty as loading.
64+
65+
`resolvePlatformMode` decides each server entry once: `fixture` with `SIM_PLATFORM_FIXTURES=1`, `live` with `SIM_PLATFORM_API_ORIGIN`, otherwise `none`, today's empty rendering. Load the seam with a dynamic import behind the `live` check, so fixture servers never evaluate it. The viewer's organization comes from membership on the server, read once per request by `readOrganizationSurface`. Platform URLs carry no organization.
66+
67+
The legacy organization layout redirects flagged organizations here through `lib/platform/routes.ts`. Link out to a legacy route only with `legacyRoutes`, which builds an absolute URL, and only to a route that mapper leaves unmapped. Linking to a mapped route makes the two surfaces redirect to each other.
68+
69+
Gates live on the server and arrive as data. Hiding an entry is navigation, not authorization: the API behind every page enforces the same rule. A Settings section plugs in by filling its folder under `settings/_components/sections/`, which `section-registry.ts` loads lazily; its navigation entry and gate are in `settings-navigation/destinations.ts`.
70+
5671
## Product UI and desktop
5772

5873
- Keep a draft or interface edit in memory for the lifetime its user expects. A chat draft belongs to its chat; profile edits and inbox read marks belong to the product session. Navigating away and back must preserve them. A new fixture scenario starts a fresh session. Popups and cancelled edits stay local to the control.
@@ -68,7 +83,7 @@ When removing or consolidating a component, trace its real callers. Migrate thei
6883

6984
## Verification
7085

71-
Run `bun run check:platform-design`, build the isolated Next app, and run its browser checks (commands in README.md). Review actual Paper and browser rendering, wrapping, interactive states, keyboard and focus, both themes, content overflow and intermediate widths. Check disabled fixture output independently. A passing static audit is not a visual comparison. Record the affected keys, readback, rendered verdict, uncertainty and next action in the task checkpoint. Run the repository gates before shipping.
86+
Run `bun run check:platform-design`, build the isolated Next app, and run its browser checks (commands in README.md). Live data has its own suite in `checks/live/`, which runs against a real app and database. Record each organization capability's status and proving check in the [parity ledger](docs/org-parity.md). Review actual Paper and browser rendering, wrapping, interactive states, keyboard and focus, both themes, content overflow and intermediate widths. Check disabled fixture output independently. A passing static audit is not a visual comparison. Record the affected keys, readback, rendered verdict, uncertainty and next action in the task checkpoint. Run the repository gates before shipping.
7287

7388
<!-- BEGIN:nextjs-agent-rules -->
7489

0 commit comments

Comments
 (0)