Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6e3dbf6
feat(library): Best AI Agents for Web Scraping and Market Research in…
icecrasher321 Oct 12, 2026
bdbb5b6
fix(workflows): hold the v2 sync run result until its execution log i…
waleedlatif1 Oct 12, 2026
5313021
chore(db): drop retired storage whose readers have shipped (#8933)
waleedlatif1 Oct 12, 2026
ba998b6
fix(enrichments): reduce cost, increase accuracy (#8936)
waleedlatif1 Oct 12, 2026
c55698a
chore(ci): fail closed on missing test Redis in CI and skip unused pg…
waleedlatif1 Oct 12, 2026
bf87c51
fix(tests): cross the large-array threshold with 64 wide rows instead…
waleedlatif1 Oct 12, 2026
140f1ae
fix(e2e): connect the mobile suite to realtime and give dev-server ro…
waleedlatif1 Oct 12, 2026
3bfd224
fix(desktop-e2e): remove root causes of four retry-masked desktop fla…
waleedlatif1 Oct 12, 2026
230a465
feat(library): 6 Best AI Agents for Invoice Processing and Finance Re…
icecrasher321 Oct 12, 2026
2c72391
improvement(changelog): add meaningful release coverage (#8942)
waleedlatif1 Oct 12, 2026
bc3673a
chore(changelog): document twice-weekly editorial reviews (#8947)
waleedlatif1 Oct 12, 2026
d3e97f4
ci: scope pull request checks to what the diff can affect (#8946)
waleedlatif1 Oct 12, 2026
bf285f0
improvement(db): disable embedding FTS fastupdate and index delete-ac…
waleedlatif1 Oct 12, 2026
1cdb8c1
improvement(perf): bound trash, file-path and attachment reads (#8945)
waleedlatif1 Oct 12, 2026
90ed227
improvement(db): cut hot-path connector count and rate-limit lock cos…
waleedlatif1 Oct 12, 2026
caa925a
ci: run the mobile e2e suite on a production build, one job per brows…
waleedlatif1 Oct 12, 2026
eef9d29
feat(projects): enforce Project membership and retire the connector (…
mzxchandra Oct 13, 2026
23b0561
ci(e2e): temporarily skip tablet workflow-controls mobile check (#8950)
TheodoreSpeaks Oct 13, 2026
0d0bab6
fix(e2e): read the visible workspace shell while a streamed page arri…
waleedlatif1 Oct 13, 2026
2008383
fix(realtime): end a failed socket-token attempt promptly and honor R…
waleedlatif1 Oct 13, 2026
9939762
chore(tests): remove quarantined integration suites (#8953)
waleedlatif1 Oct 13, 2026
da553ac
feat(knowledge): index Confluence image attachments (#8949)
mzxchandra Oct 13, 2026
9375c01
chore(storage): drop resetStorageMethod, whose last caller was a remo…
waleedlatif1 Oct 13, 2026
f03d06f
fix(emcn): keep ongoing status toasts behind actionable toasts (#8955)
waleedlatif1 Oct 13, 2026
028055c
test(knowledge): cover member access withdrawal on a lost provider sc…
waleedlatif1 Oct 13, 2026
4af0f26
fix(ci): remove Project image digest migration gate (#8961)
mzxchandra Oct 13, 2026
b3e929a
fix(auth): rate-limit socket tokens per user, with an IP backstop (#8…
waleedlatif1 Oct 13, 2026
18b1750
chore(tests): ratchet real wall-clock waits in unit tests (#8954)
waleedlatif1 Oct 13, 2026
7d090d1
ci: ratchet type errors in apps/sim test files (#8959)
waleedlatif1 Oct 13, 2026
824b5d3
fix(ui): simplify responsive layouts and compact workflow controls (#…
waleedlatif1 Oct 13, 2026
2b5dbf7
fix(recently-deleted): keep previous-criteria rows inert while the ne…
waleedlatif1 Oct 13, 2026
d7ec406
fix(db): optimize Project backfill and repair archived workflows (#8962)
mzxchandra Oct 13, 2026
03450b2
fix(recently-deleted): dim the list and disable Restore while a new v…
waleedlatif1 Oct 13, 2026
8c52d6b
fix(lifecycle): retain shared resources when creators leave (#8762)
mzxchandra Oct 13, 2026
831d11d
fix(knowledge): key vision checkpoints by title and keep excluded ima…
waleedlatif1 Oct 13, 2026
d193173
improvement(search): Confluence and Jira partial-match recall, passag…
waleedlatif1 Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 6 additions & 3 deletions .agents/skills/add-enrichment/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Because enrichments run on Sim's hosted keys by default, **every provider tool y

## Architecture (what you're plugging into)

- **`enrichments/types.ts`** — `EnrichmentConfig { id, name, description, icon, inputs, outputs, providers }` and `EnrichmentProvider { id, label, toolId, buildParams, projectFailure, mapOutput }` — `toolProvider` fills `projectFailure` with the standard HTTP projection; override it only for a provider whose failure shape is nonstandard (see `enrichments/provider-failures/`). Providers are **plain data** (no `@/tools` import) so the catalog stays client-safe.
- **`enrichments/types.ts`** — `EnrichmentConfig { id, name, description, icon, inputs, outputs, providers }` and `EnrichmentProvider { id, label, toolId, buildParams, projectFailure, mapOutput, isVerified? }` — `toolProvider` fills `projectFailure` with the standard HTTP projection; override it only for a provider whose failure shape is nonstandard (see `enrichments/provider-failures/`). Providers are **plain data** (no `@/tools` import) so the catalog stays client-safe.
- **`enrichments/providers.ts`** — `toolProvider(...)` (typed passthrough) plus shared input helpers: `str(v)`, `normalizeDomain(v)`, `firstNonEmpty(arr)`, `splitName(fullName)`, and `projectEnrichmentProviderFailure`.
- **`enrichments/run.ts`** — the server-only cascade runner. Calls `executeTool(provider.toolId, { ...params, _context: { workspaceId, userId } })`, accumulates hosted-key cost, returns the first non-empty mapped result. **You do not edit this** — it works for any registry entry.
- **`enrichments/registry.ts`** — `ENRICHMENT_REGISTRY` / `ALL_ENRICHMENTS` / `getEnrichment`. Register new entries here.
Expand All @@ -37,7 +37,9 @@ For each output the enrichment produces, decide which existing tool provides it.
- Its `params` accept what you can derive from table columns (read the tool's `params`).
- Its `outputs` / `transformResponse` actually expose the field you need (read the real output shape — don't assume).

Order providers **cheapest / most-likely-to-hit first**; the cascade stops at the first non-empty result. Apollo and LinkedIn APIs are not hosted-safe (ToS) — never call them as providers.
Order providers by **accuracy first, then price per hit**. Order decides which answer wins when two providers would disagree, so a cheaper but less accurate provider ahead of a better one trades quality for cost; reorder for price only with benchmark evidence. Among similarly accurate providers, put the ones that bill on a miss or charge a high price per hit after the cheaper, free-on-miss ones (check each vendor's billing docs and the tool's `hosting.pricing`); never move a more accurate provider behind a less accurate one to save cost. The cascade stops at the first non-empty, verified result. Apollo and LinkedIn APIs are not hosted-safe (ToS) — never call them as providers.

Prefer a vendor's own verified-only option (e.g. Prospeo `only_verified_email`) when it turns an unverified result into a free miss. Otherwise, when a provider can return an unconfirmed hit (a catch-all guess, a `risky` status), declare `isVerified(output)`: an unconfirmed hit is held while later providers look for a confirmed one, and fills the cell only if none does. `isVerified: false` marks a provider that never confirms its hits (a database record); it runs only while nothing is held.

## Step 2: Verify hosted-key support — chain to `/add-hosted-key` if missing

Expand Down Expand Up @@ -136,7 +138,8 @@ export const ENRICHMENT_REGISTRY: EnrichmentRegistry = {

- [ ] Each output mapped to a real tool field (verified against the tool's `params`/`outputs`)
- [ ] **Every provider tool has a `hosting` block — ran `/add-hosted-key` for any that didn't**
- [ ] Providers ordered cheapest / most-likely-first; Apollo/LinkedIn not used
- [ ] Providers ordered accuracy-first, then price per hit (billing on a miss checked against vendor docs); Apollo/LinkedIn not used
- [ ] Providers that can return an unconfirmed hit declare `isVerified`
- [ ] Enrichment file is client-safe (no `@/tools` import); uses `toolProvider` + shared helpers
- [ ] `buildParams` returns `null` on insufficient inputs; `mapOutput` returns `null` on a miss
- [ ] Registered in `enrichments/registry.ts`
Expand Down
7 changes: 4 additions & 3 deletions .claude/rules/sim-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contracts, and demonstrated regressions.
| Suffix | Needs | Run with | In CI |
|--------|-------|----------|-------|
| `*.test.ts(x)` | nothing; global mocks from `vitest.setup.ts` | `bun run --cwd <workspace> test` | `test` jobs |
| `*.integration.ts` | real PostgreSQL (`TEST_DATABASE_URL`), optionally Redis (`TEST_REDIS_URL`) | `bun run --cwd <workspace> test --mode integration` | `integration` jobs, by glob (sharded per provisioning path) |
| `*.integration.ts` | real PostgreSQL (`TEST_DATABASE_URL`), Redis (`TEST_REDIS_URL`; optional locally, required in CI) | `bun run --cwd <workspace> test --mode integration` | `integration` jobs, by glob (sharded per provisioning path) |
| `*.live.test.ts` | provider APIs, hosted sandboxes, local runtimes, or sibling checkouts | `bun run --cwd apps/sim test --mode live <file>` | never |
| `apps/desktop/e2e/*.spec.ts` | the packaged Electron app | Playwright | desktop E2E workflow |
| `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`) |
Expand All @@ -48,8 +48,8 @@ contracts, and demonstrated regressions.
those checks. Isolate with a unique schema or generated IDs, and clean up in `afterAll`.
- Integration files run one at a time against one shared database. A new `*.integration.ts` is
picked up by CI with no workflow change, and the run writes the JSON report configured in
`vitest.shared.ts`, which CI uploads. Never add a passing suite to the quarantine list in
`apps/sim/vitest.config.ts`.
`vitest.shared.ts`, which CI uploads. Never exclude a failing suite from the run in
`apps/sim/vitest.config.ts`: fix it or delete it.
- `bun run test:integration` starts disposable Postgres and Redis containers, provisions the schema,
and runs both workspaces; pass filenames to narrow the `apps/sim` run.

Expand Down Expand Up @@ -169,6 +169,7 @@ The suite's wall time is bound by the single Vite server thread that serves ever
3. Mock heavy graphs a test does not need and the setup does not already mock: `@/blocks`,
`@/triggers/registry`, `@/tools/generated/*`.
4. No real timers: `vi.useFakeTimers()`, `flushMicrotasks()`, or `flushMacrotask()`.
`check:test-patterns` (`real-wait`) fails on a new wall-clock wait in a unit test.
5. Absolute imports only.

### Running
Expand Down
7 changes: 4 additions & 3 deletions .cursor/rules/sim-testing.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ contracts, and demonstrated regressions.
| Suffix | Needs | Run with | In CI |
|--------|-------|----------|-------|
| `*.test.ts(x)` | nothing; global mocks from `vitest.setup.ts` | `bun run --cwd <workspace> test` | `test` jobs |
| `*.integration.ts` | real PostgreSQL (`TEST_DATABASE_URL`), optionally Redis (`TEST_REDIS_URL`) | `bun run --cwd <workspace> test --mode integration` | `integration` jobs, by glob (sharded per provisioning path) |
| `*.integration.ts` | real PostgreSQL (`TEST_DATABASE_URL`), Redis (`TEST_REDIS_URL`; optional locally, required in CI) | `bun run --cwd <workspace> test --mode integration` | `integration` jobs, by glob (sharded per provisioning path) |
| `*.live.test.ts` | provider APIs, hosted sandboxes, local runtimes, or sibling checkouts | `bun run --cwd apps/sim test --mode live <file>` | never |
| `apps/desktop/e2e/*.spec.ts` | the packaged Electron app | Playwright | desktop E2E workflow |
| `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`) |
Expand All @@ -46,8 +46,8 @@ contracts, and demonstrated regressions.
those checks. Isolate with a unique schema or generated IDs, and clean up in `afterAll`.
- Integration files run one at a time against one shared database. A new `*.integration.ts` is
picked up by CI with no workflow change, and the run writes the JSON report configured in
`vitest.shared.ts`, which CI uploads. Never add a passing suite to the quarantine list in
`apps/sim/vitest.config.ts`.
`vitest.shared.ts`, which CI uploads. Never exclude a failing suite from the run in
`apps/sim/vitest.config.ts`: fix it or delete it.
- `bun run test:integration` starts disposable Postgres and Redis containers, provisions the schema,
and runs both workspaces; pass filenames to narrow the `apps/sim` run.

Expand Down Expand Up @@ -167,6 +167,7 @@ The suite's wall time is bound by the single Vite server thread that serves ever
3. Mock heavy graphs a test does not need and the setup does not already mock: `@/blocks`,
`@/triggers/registry`, `@/tools/generated/*`.
4. No real timers: `vi.useFakeTimers()`, `flushMicrotasks()`, or `flushMacrotask()`.
`check:test-patterns` (`real-wait`) fails on a new wall-clock wait in a unit test.
5. Absolute imports only.

### Running
Expand Down
65 changes: 65 additions & 0 deletions .github/scripts/ci-changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Prints which checks a pull request's changes can affect, as `<flag>=true|false` lines for
# $GITHUB_OUTPUT. A flag is `false` only when every changed file is on an allowlist whose jobs
# fully cover it. A file on no allowlist sets every flag but `desktop_live` (which keeps its own
# skip list) to `true`; a change under .github/, an empty diff, or any failure to work out the
# diff sets all of them.
#
# full integration, e2e, e2e-build, e2e-mobile: any file not allowlisted below
# test the Vitest shards and test-types: content, docs app, and desktop changes,
# plus `full`
# build the app's `next build`: content changes, plus `full`
# desktop_live the live Electron suite: any file outside its own skip list
#
# lint always runs and is not gated.
#
# Usage: ci-changes.sh <base-sha>
set -u

# Posts, author profiles, and the public assets only posts reference. The test shards read them
# (lib/content/og-image.test.ts) and `next build` prerenders every post with its MDX components,
# which check:library-content compiles but never renders. Components under content/ are code.
content='^apps/sim/content/([^/]+/)*[^/]+\.mdx?$|^apps/sim/content/authors/[^/]+\.json$|^apps/sim/public/(authors|blog|changelog|library)/'
# Its own workspace tests, its type-check and docs-manifest audit in lint, and apps/sim tests
# that read its pages (tools/netsuite/netsuite.test.ts).
docs='^apps/docs/'
# Nothing outside the Electron app imports it; the desktop bridge and protocol packages are
# consumed by apps/sim and stay unlisted.
desktop='^apps/desktop/'
# Agent guidance, read only by the lint job's audits.
guidance='^(\.agents|\.claude|\.cursor)/|^([^/]+/)*(AGENTS|CLAUDE)\.md$|^README\.md$'
# The live desktop suite drives apps/desktop against the app, so it skips more than the rest.
desktop_live_skip="$guidance"'|^(apps/docs/|apps/pii/|apps/sim/content/|apps/sim/public/(authors|blog|changelog|library)/|packages/(python-sdk|ts-sdk)/)|\.mdx?$|(^|/)LICENSE$'

emit() {
printf 'full=%s\ntest=%s\nbuild=%s\ndesktop_live=%s\n' "$1" "$2" "$3" "$4"
}
run_all() {
emit true true true true
echo "Running every check: $1" >&2
exit 0
}
matches() { printf '%s\n' "$names" | grep -qE "$1"; }
outside() { printf '%s\n' "$names" | grep -qvE "$1"; }

base=${1:-}
[ -n "$base" ] || run_all 'no base commit'
git fetch --quiet --depth=1 origin "$base" || run_all "could not fetch $base"
names=$(git diff --no-renames --name-only "$base" HEAD) || run_all "could not diff against $base"
[ -n "$names" ] || run_all 'no changed files listed'
if matches '^\.github/'; then run_all 'a CI file changed'; fi

desktop_live=false
if outside "$desktop_live_skip"; then desktop_live=true; fi
if outside "$content|$docs|$desktop|$guidance"; then
emit true true true "$desktop_live"
unlisted=$(printf '%s\n' "$names" | grep -vE "$content|$docs|$desktop|$guidance" | head -n 1)
echo "Running every check (desktop_live=$desktop_live): $unlisted is not on an allowlist" >&2
exit 0
fi

test=false build=false
if matches "$content|$docs|$desktop"; then test=true; fi
if matches "$content"; then build=true; fi
emit false "$test" "$build" "$desktop_live"
echo "Scoped checks: test=$test build=$build desktop_live=$desktop_live" >&2
40 changes: 40 additions & 0 deletions .github/scripts/ci-gate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# The `ci` job's verdict: the single required check, so it must never pass a run whose checks did
# not all pass. Reads the job's `needs` context (`toJSON(needs)`) from $NEEDS and exits non-zero,
# naming each offending job, unless every job succeeded or was skipped as `changes` intended.
#
# A skip passes only for a gated job whose `changes` flag is exactly `false`:
#
# integration, e2e, e2e-build, e2e-mobile full
# test, test-types test
# build build
# desktop-live desktop_live
#
# Every other skip fails: an ungated job (lint, `changes` itself), a gated job whose flag is
# `true`, missing, or anything else (a failed or skipped `changes` leaves no outputs). A failure
# or a cancellation always fails.
#
# Usage: NEEDS='<toJSON(needs)>' ci-gate.sh
set -euo pipefail

needs=${NEEDS:-}
if ! jq -e 'type == "object"' <<< "$needs" > /dev/null 2>&1; then
echo "::error::The needs context is not a JSON object: '$needs'"
exit 1
fi

failed="$(jq -r '
{"integration": "full", "e2e": "full", "e2e-build": "full", "e2e-mobile": "full", "test": "test",
"test-types": "test", "build": "build", "desktop-live": "desktop_live"} as $gate
| (.changes.outputs // {}) as $flags
| to_entries[]
| select(.value.result != "success")
| select(.value.result != "skipped" or $gate[.key] == null or $flags[$gate[.key]] != "false")
| "\(.key): \(.value.result)"
' <<< "$needs")"
if [ -n "$failed" ]; then
echo "::error::Checks did not pass:"
echo "$failed"
exit 1
fi
echo "All checks passed."
24 changes: 0 additions & 24 deletions .github/scripts/desktop-live-changes.sh

This file was deleted.

Loading
Loading