Skip to content

Commit 4b9d4a7

Browse files
committed
Merge remote-tracking branch 'origin/improvement/platform-core-chat' into test/chat-steering-e2e-final
The org view moved to use-organization-conversation and use-composer, which kept the queue: removed there, with Retry and Dismiss in the shared thread props and the queued-edit test gone. The acceptance suite split into checks/, so the steering scenarios moved to checks/chat.ts; its harness stubs the socket token itself, so the throttle allowance is gone with the file it was in.
2 parents d301af8 + 38d83e3 commit 4b9d4a7

1,502 files changed

Lines changed: 115555 additions & 17798 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/add-trigger/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ export const {service}PollingHandler: PollingProviderHandler = {
411411
await markWebhookSuccess(webhookId, logger)
412412
return 'success'
413413
} catch (error) {
414-
logger.error(`[${requestId}] Error processing {service} webhook ${webhookId}:`, error)
414+
logger.error('Error processing {service} webhook:', error)
415415
await markWebhookFailed(webhookId, logger)
416416
return 'failure'
417417
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
description: One pattern per job across the organization views (Settings, People, Integrations, Links, Home)
3+
paths:
4+
- "apps/sim/platform/embedded/app/(shell)/{settings,people,integrations,links,home}/**"
5+
- "apps/sim/platform/design-system/components/{status,modal,picker,settings-section,search-field,activity-row}/**"
6+
- "apps/sim/app/o/*/**"
7+
- "apps/sim/app/o/*/settings/**"
8+
---
9+
10+
# Organization Views
11+
12+
The organization views live in `apps/sim/platform/embedded/app/(shell)/` and take everything shared from
13+
`@/platform/design-system/components`. Each job below has one pattern. A view that differs needs a
14+
reason written where it differs; "it was already like that" is not one.
15+
16+
| Job | Use | Never |
17+
| --- | --- | --- |
18+
| First read of a view | `QueryBoundary` (`query`, `failure`, `loadingLabel`, optional `empty`, `loading`), which renders `LoadingRows`, then `FailureNotice`, then `empty`, then `children(data)` | Inline `isPending ? … : error ? …`, a raw `<p>Loading…</p>`, a loading `InlineNotice` |
19+
| Loading | `LoadingRows` by default. A placeholder shaped like its content (`WidgetSkeleton`, usage charts) only where rows would misrepresent it | A second generic skeleton |
20+
| Failed read | `FailureNotice`, labelled Retry. `intent='warning'` where the read partly answered | A hand-built retry button or `InlineNotice` + `Button` |
21+
| Failed background refresh | `FailureNotice` outside the boundary, over the content it keeps | Replacing content already on screen |
22+
| Empty | `EmptyState`, with no create button when the page already has one. A picker's no-match line inside a dialog stays `Muted`, since a block would take over the dialog | `Muted` or a raw `<p>` anywhere else |
23+
| Confirm | `ConfirmDialog` (`target`, `title`, `confirmLabel`, `pendingLabel`, `onConfirm`, `safe` when nothing is lost). Removing a person, revoking an invite and any destructive row action always ask first | `Modal variant='confirm'`, `window.confirm`, an unconfirmed destructive action |
24+
| Save | `SaveActions`, end-aligned in the page flow under the fields it commits: Discard while dirty, then Save. Save is visible and disabled while clean | Save in a header, Save before Discard, a Cancel beside Discard |
25+
| Editing a server value | `useServerDraft`. Adopts a newer value while clean; while dirty it keeps the edits and sets `conflict`. Show `DraftConflictNotice`, and block Save until the viewer chooses | Seeding `useState` from a query once |
26+
| Autosave vs Save | One or the other per page. A page with `SaveActions` holds every field in one draft and writes it in one request. List actions with their own dialog, such as adding or removing a member, are not field edits | A page mixing both |
27+
| Create | A primary compact button in `SectionToolbar`, or in the page header on a page with one | Create in both the toolbar and the empty state |
28+
| Search over a section's rows | `SectionSearch subject='members'` | `SearchField` with its own copy, unless the search covers more than the subject's name |
29+
| Row | `ActivityRow` for a row that opens something, `SettingsRow` for a row holding a control | A hand-built row |
30+
| List of rows | `RowList` (`dividers` for a list read as one block). `ActivityRow density='comfortable'` in settings | `calc(-1 * var(--ui-row-inset))` around a list; styling a row's height from outside |
31+
| Row actions | `ActionMenu` with icon `I108`, destructive item last with a separator | The same action in the row and its menu |
32+
| Section heading | `SettingsSection title` (h2), `DetailHeader` for a sub-view's way back | A hand-written heading |
33+
| Toolbar | `SectionToolbar grow={<SectionSearch />}` with its actions as children | A hand-built flex row |
34+
35+
## Failure feedback
36+
37+
- Form validation is inline, on the field (`error`), once a save has been attempted.
38+
- A dialog's own action failing stays in the dialog: `Modal` shows a rejected `onConfirm`'s message and keeps the draft.
39+
- Any other action failing is a toast. Nothing fails silently.
40+
41+
## Page gates
42+
43+
Every page under `app/o/[organizationId]` calls
44+
`requireOrganizationViewer(organizationId, callbackUrl)` from `@/app/o/[organizationId]/viewer`. It
45+
redirects a signed-out viewer to sign-in and back, 404s a viewer with no access, and returns
46+
`{ userId, principal, surface }`. Never write `{ kind: 'session', ... }` in a page.
47+
48+
## Dialogs
49+
50+
`Modal` holds itself open while `onConfirm`'s promise or its `pending` prop runs. Escape, the backdrop
51+
and Close do nothing until it settles, including when the browser closes the `<dialog>` itself: once
52+
user activation is spent, `cancel` cannot be prevented, so `OverlayDialog` reopens from `close`.
53+
54+
## Files
55+
56+
- A section with more than one job splits by job: `group-detail.tsx` is the shell, `group-general-tab.tsx`
57+
and `group-catalog-tabs.tsx` its bodies, `use-group-draft.ts` its write.
58+
- Merge logic worth a test is a pure module next to the view, such as `secrets-draft.ts`.
59+
- `section-parts/` holds what only settings needs: `SectionStack`, `FieldStack`, `SectionToolbar`,
60+
`DetailHeader`, `HeadingRow`, `Muted`, `SectionSkeleton`, `DraftConflictNotice`. `useServerDraft` is in `_components/`, since Integrations uses it too.
61+
62+
## Legacy workspace settings
63+
64+
`apps/sim/app/workspace/*/settings/**` keeps `SettingsPanel` from `@/components/settings/settings-panel`,
65+
which owns chrome through `settings/[section]/layout.tsx`. The `add-settings-page` skill describes it.
66+
New organization work never imports it.

‎.claude/rules/sim-settings-pages.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
description: Settings page layout and SettingsPanel design-system compliance
33
paths:
44
- "apps/sim/app/workspace/*/settings/**"
5-
- "apps/sim/app/o/*/settings/**"
65
- "apps/sim/app/workspace/*/{integrations,skills,upgrade}/**"
76
- "apps/sim/app/workspace/*/components/{resource-tile,credential-detail}/**"
87
- "apps/sim/components/{settings,permissions}/**"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
description: "One pattern per job across the organization views (Settings, People, Integrations, Links, Home)"
3+
globs: ["apps/sim/platform/embedded/app/(shell)/{settings,people,integrations,links,home}/**","apps/sim/platform/design-system/components/{status,modal,picker,settings-section,search-field,activity-row}/**","apps/sim/app/o/*/**","apps/sim/app/o/*/settings/**"]
4+
---
5+
6+
<!-- Generated from .claude/rules/sim-platform-views.md by `bun run skills:sync`. Edit the source, not this file. -->
7+
8+
# Organization Views
9+
10+
The organization views live in `apps/sim/platform/embedded/app/(shell)/` and take everything shared from
11+
`@/platform/design-system/components`. Each job below has one pattern. A view that differs needs a
12+
reason written where it differs; "it was already like that" is not one.
13+
14+
| Job | Use | Never |
15+
| --- | --- | --- |
16+
| First read of a view | `QueryBoundary` (`query`, `failure`, `loadingLabel`, optional `empty`, `loading`), which renders `LoadingRows`, then `FailureNotice`, then `empty`, then `children(data)` | Inline `isPending ? … : error ? …`, a raw `<p>Loading…</p>`, a loading `InlineNotice` |
17+
| Loading | `LoadingRows` by default. A placeholder shaped like its content (`WidgetSkeleton`, usage charts) only where rows would misrepresent it | A second generic skeleton |
18+
| Failed read | `FailureNotice`, labelled Retry. `intent='warning'` where the read partly answered | A hand-built retry button or `InlineNotice` + `Button` |
19+
| Failed background refresh | `FailureNotice` outside the boundary, over the content it keeps | Replacing content already on screen |
20+
| Empty | `EmptyState`, with no create button when the page already has one. A picker's no-match line inside a dialog stays `Muted`, since a block would take over the dialog | `Muted` or a raw `<p>` anywhere else |
21+
| Confirm | `ConfirmDialog` (`target`, `title`, `confirmLabel`, `pendingLabel`, `onConfirm`, `safe` when nothing is lost). Removing a person, revoking an invite and any destructive row action always ask first | `Modal variant='confirm'`, `window.confirm`, an unconfirmed destructive action |
22+
| Save | `SaveActions`, end-aligned in the page flow under the fields it commits: Discard while dirty, then Save. Save is visible and disabled while clean | Save in a header, Save before Discard, a Cancel beside Discard |
23+
| Editing a server value | `useServerDraft`. Adopts a newer value while clean; while dirty it keeps the edits and sets `conflict`. Show `DraftConflictNotice`, and block Save until the viewer chooses | Seeding `useState` from a query once |
24+
| Autosave vs Save | One or the other per page. A page with `SaveActions` holds every field in one draft and writes it in one request. List actions with their own dialog, such as adding or removing a member, are not field edits | A page mixing both |
25+
| Create | A primary compact button in `SectionToolbar`, or in the page header on a page with one | Create in both the toolbar and the empty state |
26+
| Search over a section's rows | `SectionSearch subject='members'` | `SearchField` with its own copy, unless the search covers more than the subject's name |
27+
| Row | `ActivityRow` for a row that opens something, `SettingsRow` for a row holding a control | A hand-built row |
28+
| List of rows | `RowList` (`dividers` for a list read as one block). `ActivityRow density='comfortable'` in settings | `calc(-1 * var(--ui-row-inset))` around a list; styling a row's height from outside |
29+
| Row actions | `ActionMenu` with icon `I108`, destructive item last with a separator | The same action in the row and its menu |
30+
| Section heading | `SettingsSection title` (h2), `DetailHeader` for a sub-view's way back | A hand-written heading |
31+
| Toolbar | `SectionToolbar grow={<SectionSearch />}` with its actions as children | A hand-built flex row |
32+
33+
## Failure feedback
34+
35+
- Form validation is inline, on the field (`error`), once a save has been attempted.
36+
- A dialog's own action failing stays in the dialog: `Modal` shows a rejected `onConfirm`'s message and keeps the draft.
37+
- Any other action failing is a toast. Nothing fails silently.
38+
39+
## Page gates
40+
41+
Every page under `app/o/[organizationId]` calls
42+
`requireOrganizationViewer(organizationId, callbackUrl)` from `@/app/o/[organizationId]/viewer`. It
43+
redirects a signed-out viewer to sign-in and back, 404s a viewer with no access, and returns
44+
`{ userId, principal, surface }`. Never write `{ kind: 'session', ... }` in a page.
45+
46+
## Dialogs
47+
48+
`Modal` holds itself open while `onConfirm`'s promise or its `pending` prop runs. Escape, the backdrop
49+
and Close do nothing until it settles, including when the browser closes the `<dialog>` itself: once
50+
user activation is spent, `cancel` cannot be prevented, so `OverlayDialog` reopens from `close`.
51+
52+
## Files
53+
54+
- A section with more than one job splits by job: `group-detail.tsx` is the shell, `group-general-tab.tsx`
55+
and `group-catalog-tabs.tsx` its bodies, `use-group-draft.ts` its write.
56+
- Merge logic worth a test is a pure module next to the view, such as `secrets-draft.ts`.
57+
- `section-parts/` holds what only settings needs: `SectionStack`, `FieldStack`, `SectionToolbar`,
58+
`DetailHeader`, `HeadingRow`, `Muted`, `SectionSkeleton`, `DraftConflictNotice`. `useServerDraft` is in `_components/`, since Integrations uses it too.
59+
60+
## Legacy workspace settings
61+
62+
`apps/sim/app/workspace/*/settings/**` keeps `SettingsPanel` from `@/components/settings/settings-panel`,
63+
which owns chrome through `settings/[section]/layout.tsx`. The `add-settings-page` skill describes it.
64+
New organization work never imports it.

‎.cursor/rules/sim-settings-pages.mdc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: "Settings page layout and SettingsPanel design-system compliance"
3-
globs: ["apps/sim/app/workspace/*/settings/**","apps/sim/app/o/*/settings/**","apps/sim/app/workspace/*/{integrations,skills,upgrade}/**","apps/sim/app/workspace/*/components/{resource-tile,credential-detail}/**","apps/sim/components/{settings,permissions}/**","apps/sim/ee/**/components/**"]
3+
globs: ["apps/sim/app/workspace/*/settings/**","apps/sim/app/workspace/*/{integrations,skills,upgrade}/**","apps/sim/app/workspace/*/components/{resource-tile,credential-detail}/**","apps/sim/components/{settings,permissions}/**","apps/sim/ee/**/components/**"]
44
---
55

66
<!-- Generated from .claude/rules/sim-settings-pages.md by `bun run skills:sync`. Edit the source, not this file. -->

‎.github/CI.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,12 @@ WebKit acceptance on Blacksmith uses the matching, digest-pinned Playwright brow
4040

4141
HTTP and docs browser installation first asks the pinned Playwright driver to simulate its OS dependency installation. When the simulation requires no package changes, the runner avoids an apt update; missing dependencies, proposed upgrades, or an unsuccessful simulation use the normal dependency installer. Browser selection and downloads still use the pinned driver, including full native Chromium for extension acceptance.
4242

43+
`app/o` bundles `platform/embedded` and `platform/design-system`, so both are build and `ci:shared` inputs: an edit to either changes the build hash and selects every boundary. The Platform zone (`platform/{app,lib,fixtures,checks,docs,preview,public}` and its own configs) is a separate Next app that the embedded tree may not import, reached only through the `/org` rewrite, and is excluded by name so that a new `platform/` folder stays an input until it is declared zone-only. A zone-only edit selects `platform-design.yml` and the app's unit tests, and no main-app build or acceptance job.
44+
4345
Every HTTP and browser group consumes the same production build in an isolated job/database. Electron also uses that build and skips its development-route warm-up. Local HTTP and Electron development runs retain their dev-server behavior. Build inputs exclude test-only files so a harness edit can reuse an unchanged production build.
4446

47+
The Platform's fixture and live suites stay in `platform-design.yml`, outside `checks / ci`. The HTTP and production matrices consume one `next-build` artifact, built without `SIM_PLATFORM_ORIGIN`, while the `zone` topology bakes that origin into its rewrite and also builds the Platform app, so it needs its own job, gate flag and plan output rather than a suite entry. A task that depends on `ci:shared` would select two 16-vCPU jobs of up to 75 minutes for nearly every app PR and hold every staging push's deployment gate on them. The cost is that a red run is advisory, and a path-filtered workflow cannot be a required check: one that never starts never reports, so the status does not block a merge until the suite joins the aggregate.
48+
4549
Desktop inbox protocol, assistant actions UI, and organization search UI run as three parallel HTTP jobs with isolated databases, apps, and reports. Each harness owns its acceptance task, so an edit selects its own job. The inbox protocol task depends on server and realtime code; the browser tasks also depend on workspace UI. Desktop inbox needs no browser. The UI jobs and browser extension install Chromium while their app starts, waiting for both before executing assertions; browser-install failures still fail the job and preserve the install log. No acceptance assertions are removed by the split.
4650

4751
Live Electron acceptance tracks its live-app suite, fixtures, and Playwright configuration separately from shell-only smoke tests. Changing a shell-only smoke case runs the dedicated desktop workflow without starting the app's production build and live lease checks.

‎.github/scripts/http-e2e.sh‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ start_platform() {
170170
# platform_live_environment: one hosted Enterprise organization, the deterministic seed its specs
171171
# read, and a scripted worker in place of the model. Hosted is decided by the app's hostname, which
172172
# the job maps to loopback. Google and Microsoft answer only to real credentials, so those clients
173-
# are fixtures, and what no fixture can reach is reported by the specs as a skip.
173+
# are fixtures, and what no fixture can reach is reported by the specs as a skip. Each group starts
174+
# the realtime server after its app, as org-redesign does, and holds the same tripwire and tool
175+
# permissions, so a route that passes one suite cannot fail on the same stack in the other.
174176
platform_live_environment() {
175177
app_server=start
176178
export REDIS_URL=redis://127.0.0.1:6379
@@ -186,6 +188,9 @@ platform_live_environment() {
186188
export NEXT_PUBLIC_ACCESS_CONTROL_ENABLED=true
187189
export SIM_LINKS=true PEOPLE=true
188190
export INTERNAL_API_SECRET=platform-live-ci-local-secret-at-least-32-characters
191+
export COPILOT_TOOL_PERMISSIONS_ENABLED=true
192+
export DB_TX_TRIPWIRE=throw
193+
export NEXT_PUBLIC_SOCKET_URL=http://127.0.0.1:3030 SOCKET_SERVER_URL=http://127.0.0.1:3030
189194
export COPILOT_API_KEY=platform-live-ci-local-copilot-key MOTHERSHIP_SIM_TRANSPORT=direct
190195
export SIM_AGENT_API_URL=http://127.0.0.1:3029
191196
export GOOGLE_CLIENT_ID=platform-live-google-client GOOGLE_CLIENT_SECRET=platform-live-google-secret
@@ -491,6 +496,7 @@ JS
491496
export SIM_PLATFORM_ORIGIN=http://127.0.0.1:3028
492497
start_browser_install platform-live --only-shell chromium
493498
PLATFORM_ORG_UI=true start_app platform-live 3027 'Platform main' record-http-status
499+
start_realtime platform-live 3030
494500
start_platform 3028
495501
bun --no-env-file platform/checks/live/support/scripted-agent.ts 3029 \
496502
> "$report_dir/platform-live-agent.log" 2>&1 &
@@ -510,6 +516,7 @@ JS
510516
export PLATFORM_ORG_UI=true
511517
start_browser_install platform-live --only-shell chromium
512518
start_app platform-live-flag-off 3027 'Main, no zone' record-http-status
519+
start_realtime platform-live-flag-off 3030
513520
wait_for_browsers
514521
run_platform_live flag-off platform-live-flag-off
515522
;;

‎.github/workflows/platform-design.yml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,35 @@ name: platform-design
22

33
on:
44
pull_request:
5+
# The zone and the seam it reads through: the main app that mounts and proxies it, the contracts,
6+
# hooks and enterprise domains its data layer wraps, and the chat modules it drives unchanged.
7+
# A pattern's order matters: the ledger follows the docs exclusion so an edit to it still meets
8+
# the report-based parity check. `*` stands for `[workspaceId]`, which a filter would read as a
9+
# character class.
510
paths:
611
- 'apps/sim/platform/**'
712
- '!apps/sim/platform/docs/**'
813
- '!apps/sim/platform/README.md'
14+
- 'apps/sim/platform/docs/org-parity.md'
15+
- 'apps/sim/app/o/**'
16+
- 'apps/sim/lib/platform/**'
17+
- 'apps/sim/proxy.ts'
18+
- 'apps/sim/next.config.ts'
19+
- 'apps/sim/lib/api/contracts/**'
20+
- 'apps/sim/hooks/queries/**'
21+
- 'apps/sim/ee/**'
22+
- 'apps/sim/app/workspace/*/home/hooks/**'
23+
- 'apps/sim/app/workspace/*/home/types.ts'
24+
- 'apps/sim/app/workspace/*/home/search-params.ts'
25+
- 'apps/sim/app/workspace/*/home/components/message-content/**'
26+
- 'apps/sim/app/workspace/*/home/components/knowledge-search-results/utils.ts'
27+
- 'apps/sim/app/workspace/*/w/*/components/panel/components/copilot/components/user-input/hooks/**'
28+
- 'apps/sim/scripts/platform-live-seed.ts'
929
- 'apps/sim/package.json'
30+
- 'apps/realtime/**'
1031
- 'scripts/check-platform-design*'
32+
- 'scripts/check-platform-parity*'
33+
- 'scripts/platform-search-source-catalog.ts'
1134
- 'scripts/platform-variant-usage.ts'
1235
- 'scripts/sync-platform-*.ts'
1336
- '.github/workflows/platform-design.yml'

0 commit comments

Comments
 (0)