Skip to content

Commit 670def1

Browse files
authored
v0.9.15: mship improvements, memory improvements, nextjs bump, plane and ramp integrations, outbox hardening
2 parents 546d4e7 + e3800f6 commit 670def1

1,188 files changed

Lines changed: 313405 additions & 16334 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-settings-page/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ Each grep lists candidates; review every match against the expected ones named b
6666
- Editable pages: confirm Save/Discard go through `saveDiscardActions()` and
6767
dirty is wired via `useSettingsUnsavedGuard` (called before early-return
6868
gates) — flag any hand-rolled Save button, `beforeunload`, or unsaved modal.
69-
`git grep -n "beforeunload" -- 'apps/sim/**/settings/**' 'apps/sim/ee/'`
70-
should only hit the centralized `use-settings-before-unload.ts`.
69+
`git grep -n "beforeunload" -- 'apps/sim/**/settings/**' 'apps/sim/ee/' 'apps/sim/components/settings/' ':(exclude,glob)**/*.test.*'`
70+
should only hit the centralized `use-settings-browser-navigation.ts`.
7171
5. Fix each finding with the smallest structural change that satisfies the checklist;
7272
do not touch handlers, state, queries, or gate returns. A pixel-size fix swaps
7373
only the size class for its exact-pixel token (`text-[12px]` → `text-caption`).

‎.agents/skills/ship/SKILL.md‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ When the user runs `/ship`:
4747
- Run `/db-migrate` to review the migration for zero-downtime safety (expand/contract phasing, backward-compatibility with the deployed app version).
4848
- `(cd packages/db && bunx drizzle-kit generate && git status --porcelain ./migrations)` must print nothing (CI's schema/migration sync step).
4949
- `bun run check:migrations origin/staging` must pass (staging is the PR base). Do not silence a flagged statement with a `-- migration-safe:` annotation unless `/db-migrate` confirmed the old code no longer depends on it; otherwise split the destructive change into a later deploy.
50-
6. **Run pre-ship checks** from the repo root before staging. This has two phases: first **regenerate** every committed artifact so generated files never drift into a CI failure (this is what catches things like `agent-stream-docs` going stale after a `models.ts` edit), then run the **full audit suite** CI's `Lint and Test` job enforces. Both phases parallelize — but only across commands that write **disjoint** outputs — and a bare `wait` swallows child exit codes, so both phases below explicitly collect each job's status and abort ship if any failed.
50+
6. **Run pre-ship checks** from the repo root before staging. This has two phases: first **regenerate** every committed artifact so generated files never drift into a CI failure (this is what catches things like `agent-stream-docs` going stale after a `models.ts` edit), then run the **full audit suite** CI's `lint` job enforces. Both phases parallelize — but only across commands that write **disjoint** outputs — and a bare `wait` swallows child exit codes, so both phases below explicitly collect each job's status and abort ship if any failed.
5151
5252
**Phase A — regenerate the always-in-repo committed artifacts (parallel), then let step 7 stage whatever changed.** Regenerate only the generators whose inputs live entirely in this repo and that any ordinary code change can drift — `agent-stream-docs:generate` (derives from the provider model registry), `docs-manifest:generate` (derives from docs page paths), and `skills:sync` (derives from `.agents/skills/**`). They write disjoint outputs (`apps/docs/…/agent.mdx`, `apps/sim/lib/mothership/generated/docs-manifest.ts`, and `.claude/skills` links), so they parallelize safely, and each is idempotent (a no-op when already in sync):
5353
```bash
@@ -66,7 +66,7 @@ When the user runs `/ship`:
6666
6767
**Do NOT blanket-run the domain generators here.** `mship:generate` (`generate-mship-contracts.ts`) is an **umbrella** that drives all nine mothership contract generators (`mship-contracts`, `billing-protocol-contract`, `mship-tools`, the four `trace-*`, `metrics-contract`, `vfs-snapshot-contract`) and biome-formats `apps/sim/lib/mothership/generated/` — never run it *and* its constituents (they write the same files and corrupt each other in parallel), and never run it on an ordinary ship: it reads an **external** copilot-contract source that isn't checked out in most worktrees, so it hard-fails with `ENOENT` and would abort ship for an unrelated reason. `generate:pi-model-catalog` (under `apps/sim`) likewise regenerates from the installed Pi package, not repo source. `scripts/generate-docs.ts` rewrites the integration docs and client-safe catalog; run it when this PR changes their block/icon/landing-content inputs or when `integration-catalog:check` reports drift, then review its broad generated diff. Only when **this PR's diff actually touches** a domain generator's input do you regenerate it deliberately and run its matching `:check` (`bun run mship:check` / the individual `*:check`) — with the external source present.
6868
69-
**Phase B — run lint + every audit CI enforces, in parallel, and abort ship if any fails.** Before running the commands, compare this list with `.github/workflows/test-build.yml`; when CI adds an audit, run it and update this skill instead of trusting a stale snapshot. The env-flag audit is currently an inline workflow block rather than a package script: when `apps/sim/lib/core/config/env-flags.ts` changed, run that current workflow block verbatim instead of copying a second version into this skill. Run `bun run lint` first (it autofixes formatting and mutates files, so don't parallelize it with the read-only audits), then run the base-sensitive block-registry check, then fan the independent audits out and collect exit codes:
69+
**Phase B — run lint + every audit CI enforces, in parallel, and abort ship if any fails.** Before running the commands, compare this list with `.github/workflows/checks.yml`; when CI adds an audit, run it and update this skill instead of trusting a stale snapshot. The env-flag audit is currently an inline workflow block rather than a package script: when `apps/sim/lib/core/config/env-flags.ts` changed, run that current workflow block verbatim instead of copying a second version into this skill. Run `bun run lint` first (it autofixes formatting and mutates files, so don't parallelize it with the read-only audits), then run the base-sensitive block-registry check, then fan the independent audits out and collect exit codes:
7070
```bash
7171
# autofix formatting first (mutating; not parallel-safe with the audits). Gate its exit too —
7272
# a non-zero lint (unfixable errors) must abort before the audits run, not be ignored.
@@ -77,6 +77,9 @@ When the user runs `/ship`:
7777
}
7878
# Runs every audit CI runs, concurrently, and replays the output of any that fail.
7979
# The audit list is derived in scripts/run-audits.ts — do not hand-list audits here.
80+
# Install CI's pinned actionlint version for the host OS/architecture and verify its
81+
# artifact against the official release checksums in a local mktemp directory.
82+
# Preserve CI's -shellcheck= -pyflakes= flags; lint all workflows and abort ship if it fails.
8083
bun run check:audits || { echo "❌ audit(s) failed — do not ship"; exit 1; }
8184
bun run type-check || { echo "❌ type-check failed — do not ship"; exit 1; }
8285
# CI's "Verify docs manifest is in sync" step is not a `check:*` script, so the runner above

‎.claude/rules/landing-seo-geo.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ paths:
1717
- All copy is server-rendered text: no text baked into images, no content that exists only after a client effect runs.
1818
- Navbar is a Server Component (no `'use client'`) for immediate crawlability. Logo `<Image>` has `priority` (LCP element). The navbar `<nav>` carries `SiteNavigationElement` schema.org markup.
1919
- Structured data: emit JSON-LD (`Organization`, `WebSite`, `WebApplication` with `featureList`, `FAQPage` if an FAQ exists) from a server component rendered before visible content. Keep `featureList` in sync with the features the page shows (`apps/sim/app/(landing)/components/home-structured-data/`).
20+
- `/library` and `/blog` pagination stays server-rendered with real next/previous links. Each unfiltered page is indexable and uses its own canonical URL (`?page=2`, etc.); page 1 canonicalizes to the bare index. Tag-filtered variants remain `noindex,follow`. See [Google pagination guidance](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading).
2021
- After adding routes or anchors, verify `app/sitemap.ts` and `app/robots.ts` still reflect reality.
2122

2223
## GEO (Generative Engine Optimisation)

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

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -339,34 +339,33 @@ rendered through `<SettingsActionChips actions={…} />` from
339339
`@/components/settings/settings-header` — that is the shared chip path, and it
340340
is what keeps tone/icon/variant/tooltip handling from drifting between the two
341341
shells. Reach for it before hand-rolling a `Chip`.
342-
- **`useSettingsUnsavedGuard({ isDirty })`** (`…/settings/hooks/use-settings-unsaved-guard`)
343-
— syncs the page's local `isDirty` into the shared `useSettingsDirtyStore` (so
344-
the sidebar's **section-switch** confirm + the centralized `beforeunload` both
345-
apply for free) and returns `{ showUnsavedModal, setShowUnsavedModal, guardBack,
346-
confirmDiscard }` for a detail view's **in-view back** chip.
347-
- **Top-level pages** (whitelabeling, sso): call it **unassigned** —
348-
`useSettingsUnsavedGuard({ isDirty: hasChanges })` — they only need the
349-
store-sync; the sidebar/`beforeunload` do the rest.
350-
- **Detail sub-views** (data-retention, access-control group-detail): route the
351-
back chip through `onClick={() => guard.guardBack(closeFn)}` and render the
352-
shared `<UnsavedChangesModal open={guard.showUnsavedModal}
353-
onOpenChange={guard.setShowUnsavedModal} onDiscard={guard.confirmDiscard} />`
354-
(from `@/app/workspace/[workspaceId]/components/credential-detail`). The
355-
in-view header **Discard** chip (via `saveDiscardActions({ onDiscard })`) is a
356-
*reset to original* — distinct from the back-confirm's discard, which leaves.
357-
- **`useSettingsBeforeUnload`** is mounted by the settings shells
358-
(`settings/layout.tsx` and `components/settings/standalone-settings-shell.tsx`) —
359-
never add a per-page `beforeunload`.
360-
- **Dirty *computation* stays local** (shapes differ: field-compare vs
361-
normalize+stringify) — only how dirty is *consumed* is shared. Derive it (a
362-
`const`/`useMemo`), never store it in `useState`.
363-
- **CRITICAL — rules of hooks:** call `useSettingsUnsavedGuard(...)`
364-
**unconditionally, before every early-return gate** (entitlement / loading /
365-
not-entitled `return <SettingsEmptyState>`). A hook placed after a gate is
366-
skipped on gated renders and crashes.
367-
- The route-based credential detail keeps its own `useUnsavedChangesGuard` (it
368-
guards real `router.push` navigation + browser Back via a history sentinel);
369-
it already shares `UnsavedChangesModal`, so copy stays unified.
342+
- **`useSettingsUnsavedGuard({ isDirty, navigationBlocked, onDiscard })`** from
343+
`@/components/settings/use-settings-unsaved-guard` registers one editor with the
344+
shared settings store. Register every full-page draft, including inline creation
345+
forms. Dirty computation stays local and derived; fetched defaults stay clean.
346+
- **`onDiscard` resets the registered draft.** The root `SettingsNavigationGuard`
347+
owns the discard dialog, internal link interception, browser Back/Forward, and
348+
refresh protection for registered editors. Do not add a per-page dialog or
349+
history listener for an editor using this hook. Skill create/detail pages retain
350+
their existing `useUnsavedChangesGuard` and local dialog until they migrate to
351+
the shared registration.
352+
- **Native history needs entry indexes.** The Navigation API supplies them for
353+
legacy entries. Without it, native traversal to an unindexed entry cannot be
354+
cancelled reliably; never guess a direction or rewrite the history stack.
355+
- **History confirmation authorizes traversal before discarding.** Drafts are
356+
discarded only when the browser reports the confirmed traversal, so a no-op
357+
Back or Forward retains edits. Known cross-document traversal uses native unload
358+
protection. An unknown cross-document target on a classic History browser can
359+
require a second native confirmation after the shared dialog.
360+
- **Detail back controls** call `guard.guardBack(closeFn)`. Other destructive view
361+
transitions, such as switching an editor's direction or environment, also use
362+
the shared `requestLeave` action before resetting their draft.
363+
- **`navigationBlocked` covers pending saves and uploads.** The shared guard blocks
364+
navigation while requests are pending. Attempts are not queued; retry after they
365+
settle. Disable or preserve edits made during requests;
366+
failed saves retain drafts, and successful saves only clear committed values.
367+
- **Call the hook unconditionally, before every early-return gate** (entitlement,
368+
loading, or empty-state return), so gated renders preserve hook order.
370369

371370
## Detail sub-views
372371

‎.claude/rules/sim-testing.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ contracts, and demonstrated regressions.
3232

3333
| Suffix | Needs | Run with | In CI |
3434
|--------|-------|----------|-------|
35-
| `*.test.ts(x)` | nothing; global mocks from `vitest.setup.ts` | `vitest run` | Lint and Test job |
36-
| `*.integration.ts` | real PostgreSQL (`TEST_DATABASE_URL`), optionally Redis (`TEST_REDIS_URL`) | `vitest run --mode integration` | `PostgreSQL integration` job, by glob |
35+
| `*.test.ts(x)` | nothing; global mocks from `vitest.setup.ts` | `vitest run` | `test` jobs (sharded) |
36+
| `*.integration.ts` | real PostgreSQL (`TEST_DATABASE_URL`), optionally Redis (`TEST_REDIS_URL`) | `vitest run --mode integration` | `integration` jobs, by glob (sharded per provisioning path) |
3737
| `*.live.test.ts` | provider APIs, hosted sandboxes, local runtimes, or sibling checkouts | `vitest run --mode live <file>` (apps/sim) | never |
3838
| `apps/desktop/e2e/*.spec.ts` | the packaged Electron app | Playwright | desktop E2E workflow |
39-
| `apps/sim/scripts/test-*-e2e.ts` | a running app over HTTP | its `package.json` script when one exists (`bun run test:scim:e2e`; `test:workflow-version-compare:e2e` adds `--no-env-file`), else `bun scripts/test-<suite>-e2e.ts` from apps/sim | End-to-end over real HTTP job |
39+
| `apps/sim/scripts/test-*-e2e.ts` | a running app over HTTP | its `package.json` script when one exists (`bun run test:scim:e2e`; `test:workflow-version-compare:e2e` adds `--no-env-file`), else `bun scripts/test-<suite>-e2e.ts` from apps/sim | `e2e` jobs (`.github/scripts/http-e2e.sh`) |
4040

4141
- A unit test lives next to its source: `feature.ts` → `feature.test.ts`. No network, no database,
4242
no real timers.

‎.cursor/rules/landing-seo-geo.mdc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ globs: ["apps/sim/app/(landing)/**/*.tsx","apps/sim/content/**/*.mdx"]
1717
- All copy is server-rendered text: no text baked into images, no content that exists only after a client effect runs.
1818
- Navbar is a Server Component (no `'use client'`) for immediate crawlability. Logo `<Image>` has `priority` (LCP element). The navbar `<nav>` carries `SiteNavigationElement` schema.org markup.
1919
- Structured data: emit JSON-LD (`Organization`, `WebSite`, `WebApplication` with `featureList`, `FAQPage` if an FAQ exists) from a server component rendered before visible content. Keep `featureList` in sync with the features the page shows (`apps/sim/app/(landing)/components/home-structured-data/`).
20+
- `/library` and `/blog` pagination stays server-rendered with real next/previous links. Each unfiltered page is indexable and uses its own canonical URL (`?page=2`, etc.); page 1 canonicalizes to the bare index. Tag-filtered variants remain `noindex,follow`. See [Google pagination guidance](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading).
2021
- After adding routes or anchors, verify `app/sitemap.ts` and `app/robots.ts` still reflect reality.
2122

2223
## GEO (Generative Engine Optimisation)

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

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -336,34 +336,33 @@ rendered through `<SettingsActionChips actions={…} />` from
336336
`@/components/settings/settings-header` — that is the shared chip path, and it
337337
is what keeps tone/icon/variant/tooltip handling from drifting between the two
338338
shells. Reach for it before hand-rolling a `Chip`.
339-
- **`useSettingsUnsavedGuard({ isDirty })`** (`…/settings/hooks/use-settings-unsaved-guard`)
340-
— syncs the page's local `isDirty` into the shared `useSettingsDirtyStore` (so
341-
the sidebar's **section-switch** confirm + the centralized `beforeunload` both
342-
apply for free) and returns `{ showUnsavedModal, setShowUnsavedModal, guardBack,
343-
confirmDiscard }` for a detail view's **in-view back** chip.
344-
- **Top-level pages** (whitelabeling, sso): call it **unassigned** —
345-
`useSettingsUnsavedGuard({ isDirty: hasChanges })` — they only need the
346-
store-sync; the sidebar/`beforeunload` do the rest.
347-
- **Detail sub-views** (data-retention, access-control group-detail): route the
348-
back chip through `onClick={() => guard.guardBack(closeFn)}` and render the
349-
shared `<UnsavedChangesModal open={guard.showUnsavedModal}
350-
onOpenChange={guard.setShowUnsavedModal} onDiscard={guard.confirmDiscard} />`
351-
(from `@/app/workspace/[workspaceId]/components/credential-detail`). The
352-
in-view header **Discard** chip (via `saveDiscardActions({ onDiscard })`) is a
353-
*reset to original* — distinct from the back-confirm's discard, which leaves.
354-
- **`useSettingsBeforeUnload`** is mounted by the settings shells
355-
(`settings/layout.tsx` and `components/settings/standalone-settings-shell.tsx`) —
356-
never add a per-page `beforeunload`.
357-
- **Dirty *computation* stays local** (shapes differ: field-compare vs
358-
normalize+stringify) — only how dirty is *consumed* is shared. Derive it (a
359-
`const`/`useMemo`), never store it in `useState`.
360-
- **CRITICAL — rules of hooks:** call `useSettingsUnsavedGuard(...)`
361-
**unconditionally, before every early-return gate** (entitlement / loading /
362-
not-entitled `return <SettingsEmptyState>`). A hook placed after a gate is
363-
skipped on gated renders and crashes.
364-
- The route-based credential detail keeps its own `useUnsavedChangesGuard` (it
365-
guards real `router.push` navigation + browser Back via a history sentinel);
366-
it already shares `UnsavedChangesModal`, so copy stays unified.
339+
- **`useSettingsUnsavedGuard({ isDirty, navigationBlocked, onDiscard })`** from
340+
`@/components/settings/use-settings-unsaved-guard` registers one editor with the
341+
shared settings store. Register every full-page draft, including inline creation
342+
forms. Dirty computation stays local and derived; fetched defaults stay clean.
343+
- **`onDiscard` resets the registered draft.** The root `SettingsNavigationGuard`
344+
owns the discard dialog, internal link interception, browser Back/Forward, and
345+
refresh protection for registered editors. Do not add a per-page dialog or
346+
history listener for an editor using this hook. Skill create/detail pages retain
347+
their existing `useUnsavedChangesGuard` and local dialog until they migrate to
348+
the shared registration.
349+
- **Native history needs entry indexes.** The Navigation API supplies them for
350+
legacy entries. Without it, native traversal to an unindexed entry cannot be
351+
cancelled reliably; never guess a direction or rewrite the history stack.
352+
- **History confirmation authorizes traversal before discarding.** Drafts are
353+
discarded only when the browser reports the confirmed traversal, so a no-op
354+
Back or Forward retains edits. Known cross-document traversal uses native unload
355+
protection. An unknown cross-document target on a classic History browser can
356+
require a second native confirmation after the shared dialog.
357+
- **Detail back controls** call `guard.guardBack(closeFn)`. Other destructive view
358+
transitions, such as switching an editor's direction or environment, also use
359+
the shared `requestLeave` action before resetting their draft.
360+
- **`navigationBlocked` covers pending saves and uploads.** The shared guard blocks
361+
navigation while requests are pending. Attempts are not queued; retry after they
362+
settle. Disable or preserve edits made during requests;
363+
failed saves retain drafts, and successful saves only clear committed values.
364+
- **Call the hook unconditionally, before every early-return gate** (entitlement,
365+
loading, or empty-state return), so gated renders preserve hook order.
367366

368367
## Detail sub-views
369368

0 commit comments

Comments
 (0)