mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
d895e0efee4018ea44319bb74c4af7aede79e3af
4550
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d895e0efee |
fix(security): authorize MCP subagent IDs, oauth workspace, credential admin demotion (#4551)
* fix(security): authorize MCP subagent IDs, oauth workspace, credential admin demotion
- handleSubagentToolCall and handleDirectToolCall now authorize user-supplied
workflowId/workspaceId via authorizeWorkflowByWorkspacePermission /
ensureWorkspaceAccess before forwarding downstream; resolvedWorkspaceId is
derived from the authorized workflow record instead of trusted from the body
- executeOAuthGetAuthLink verifies caller membership (write level) on the
target workspaceId before generating the OAuth link or writing
pendingCredentialDraft, closing the cross-workspace credential injection path
- POST /api/credentials/[id]/members wraps role updates in a transaction that
counts active admins and rejects demotion of the last admin (mirrors the
existing DELETE guard in the same file)
- GET /api/credentials/[id]/members returns uniform 404 for both missing and
inaccessible credentials to remove the existence oracle
* fix(security): address PR review — active-status guard, FOR UPDATE locks, workspaceId propagation
- credentials/members POST: add `current.status === 'active'` check to the
last-admin demotion guard so re-inviting a revoked admin as a non-admin role
no longer incorrectly hits the "Cannot demote the last admin" path
- credentials/members POST+DELETE: add `.for('update')` to the active-admin
count SELECT inside both transactions to serialize concurrent demotions and
eliminate the admin-count TOCTOU race under Postgres READ COMMITTED
- credentials/members POST: also lock the member row itself with `.for('update')`
so the role+status read and the subsequent UPDATE are atomic
- mcp/copilot handleDirectToolCall: thread the DB-verified workspaceId from the
authorization result into prepareExecutionContext instead of relying on
user-supplied args
- oauth handler: fix error message to mention both workspaceId and userId when
either is missing from the execution context
|
||
|
|
3adbde404b |
fix(oauth): persist rotated Microsoft refresh tokens (#4554)
* fix(oauth): persist rotated Microsoft refresh tokens Microsoft Entra rotates refresh tokens on every refresh and expects clients to replace the stored token with the new one. The Microsoft provider config was missing supportsRefreshTokenRotation, so the rotated refresh_token returned by Azure AD was silently discarded and the original token from initial OAuth connect was reused indefinitely — causing periodic 'Failed to refresh access token' errors for Excel, Teams, Outlook, OneDrive, SharePoint, Planner, AD, and Dataverse integrations. * test(oauth): cover hyphenated Microsoft service IDs in rotation test |
||
|
|
c47777fa33 |
fix(security): close cross-tenant IDOR gaps in OAuth credential and execution auth (#4549)
* fix(security): close IDOR gaps in OAuth credential and execution authorization Routes that called resolveOAuthAccountId followed by a conditional workspace permission check (only run when workspaceId was set) silently skipped all ownership validation on the legacy account-ID fallback path. Any authenticated user could supply a raw account.id to access another tenant's OAuth credentials. - Replace resolveOAuthAccountId + conditional perm check with authorizeCredentialUse in: auth/oauth/wealthbox/item, tools/gmail/label, tools/onedrive/files, tools/onedrive/folder, tools/outlook/folders, tools/wealthbox/item (routes 1, 3-7) - Add authorizeCredentialUse ownership gate before resolveVertexCredential in providers/route.ts (route 2) - Add verifyFileAccess check on the user-supplied file key before downloadFileFromStorage in tools/wordpress/upload (route 8) - Add workflowId param to PauseResumeManager methods (enqueueOrStartResume, beginPausedCancellation, completePausedCancellation, blockQueuedResumesForCancellation, clearPausedCancellationIntent, getPausedCancellationStatus, processQueuedResumes) and filter all pausedExecutions lookups by workflowId so callers cannot act on another tenant's paused execution by supplying a foreign executionId (route 9) - Update all call sites (cancel, resume, poll routes) to pass workflowId * fix(security): close verifyFileAccess bypass, thread workflowId to processQueuedResumes, fix log level - Fail closed in WordPress upload when userFile.key is present but authResult.userId is absent, preventing silent bypass of ownership check via JWT fallback path - Thread workflowId into processQueuedResumes in the async resume error-recovery path and in pause-persistence.ts to close residual cross-tenant gap - Change logger.error to logger.warn for credential access denial in OneDrive folder route to match all other routes in this PR * fix(security): thread workflowId through all processQueuedResumes call sites Closes residual cross-tenant IDOR gap where processQueuedResumes was called without a workflowId scope in persistPauseResult, startResumeExecution (success and error paths), and clearPausedCancellationIntent. workflowId was already in scope at each site — this wires it through to the existing optional parameter. * fix(security): remove any types, drop extraneous comments, normalize caught errors - catch (error: any) → catch (error) + toError(error).message in resume and cancel routes - Remove what-not-why inline comments from wordpress upload and onedrive/files routes - Remove redundant debug-only item breakdown log and the file-IDs log in onedrive/files - Trim extraneous DAG-edge comments from updateSnapshotAfterResume in HITL manager * fix: use logger.warn for credential access denial in outlook folders route * fix(security): make workflowId required in all HITL pause/resume methods All 7 method signatures (processQueuedResumes, enqueueOrStartResume, beginPausedCancellation, completePausedCancellation, blockQueuedResumesForCancellation, clearPausedCancellationIntent, getPausedCancellationStatus) previously accepted workflowId as optional. Every call site already supplies it — making it required closes the vulnerability at the type level so future callers cannot accidentally omit tenant scoping and silently fall back to an unscoped DB query. * fix(security): thread workflowId through internal HITL cancellation calls and remove dead branches in credential-access * fix(security): harden workflowId scoping and file key guard Replace falsy workflowId checks in PauseResumeManager (all methods now unconditionally apply the workflowId WHERE clause, preventing empty-string bypass). Flip WordPress upload file guard from truthy key check to explicit non-empty validation so key:"" fails closed with a 404 instead of silently skipping access control. |
||
|
|
badfeae432 |
fix(security): SSRF fixes (#4548)
* fix: address SSRF and token-leakage security vulnerabilities
- Azure TTS SSRF: validate region against /^[a-z][a-z0-9-]{1,30}[a-z0-9]$/
in both the contract (tts.ts) and runtime guard in synthesizeWithAzure,
preventing user-supplied region from redirecting requests to arbitrary hosts
- HubSpot token in logs: remove fullResponse from logger.info call;
log only non-sensitive metadata (hub_id, hub_domain, user_id) instead
of the full introspection response which included the access token
- Wealthbox account takeover: replace hardcoded email with per-user identity
by fetching /v1/users/me; fall back to token-derived stable identifier
so distinct Wealthbox users no longer share the same email address
- Shopify SSRF: apply shopifyShopDomainSchema (.myshopify.com allowlist)
to shopDomain from cookie before using it to build the fetch URL
* fix(wealthbox): correct getUserInfo endpoint, auth header, and stable identity
- Bug 1: Change API endpoint from /v1/users/me to /v1/me (correct Wealthbox API path)
- Bug 2: Replace ACCESS_TOKEN header with Authorization: Bearer <token> (standard OAuth 2.0)
- Bug 3: Remove generateId() from returned id (was non-deterministic, caused duplicate accounts);
use refresh token (stable, long-lived) instead of access token (rotates every ~2 hours)
as the hash source for the fallback identity; return null if no token is available
* fix(security): hash wealthbox fallback token identity, guard undefined userId
- Replace base64 encoding with SHA-256 hash for fallback token-derived identity
so raw token bytes are never stored in the DB
- Return null early when Wealthbox API response lacks an id field to prevent
all such users colliding on the wealthbox-undefined account
* fix(auth): replace stale wealthbox userInfoUrl placeholder with actual endpoint
The dummy URL comment was rendered obsolete when getUserInfo was updated
to fetch from api.crmworkspace.com/v1/me. Align userInfoUrl with the real
endpoint used in the getUserInfo implementation.
* fix(auth): append generateId() suffix to Wealthbox account IDs to match codebase pattern
All other providers use `${stableId}-${generateId()}` so the account.create.after
hook can strip the UUID suffix, find stale sibling rows, and migrate credential FKs.
Without the suffix the migration logic is skipped and reconnections would hit
duplicate key conflicts instead of gracefully updating credentials.
|
||
|
|
ae680afab2 |
improvement(data-drains): docs page, screenshots, and search/UI polish (#4547)
Adds the enterprise data-drains docs page with two screenshots, a search bar over the drains table, and a UI cleanup pass on the settings component (size-*, useMemo removal, text-sm fixes). The previously-proposed env-var reference feature has been dropped — drain credentials remain raw values, encrypted at rest by the existing pipeline. |
||
|
|
10f7d36f13 |
fix(settings): accurate View navigation after restore in recently deleted (#4546)
* fix(settings): accurate View navigation after restore in recently deleted
Files now deep-link to /files/{id} (was /files), and folders expand
the restored folder + its parent chain in the sidebar before navigating
to /w so the user actually lands on the item they restored.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: fall back to archived folders for parent-chain lookup
The restored folder may not be in the active folders cache yet when
View is clicked (the invalidation+refetch fires from onSettled, after
onSuccess surfaces the View button). Merge archived folder data — where
the restored item still lives — into the lookup map so the expansion
loop can always resolve the folder and walk its parent chain.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(styling): canonicalize size-* shorthand for equal height/width
Document the size-* shorthand as the canonical pattern across CLAUDE.md,
AGENTS.md, .claude rules, .cursor + .agents commands, and the emcn
design-review skill. Default icon size is size-[14px]. Treat
h-[Npx] w-[Npx] and h-N w-N pairs as refactor targets.
Also migrate the remaining occurrences in recently-deleted.tsx.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
3ed8615b5d |
chore(cleanup): react-doctor dead code elimination, landing + docs overhaul, component modernization (#4544)
* fix(react-doctor): remove unused export types, useEffect clearTimeout missing, a11y fixes * fix(react-doctor): strip unused export types from contracts, copilot, stores, and components Remove export keyword from type/interface declarations confirmed to have zero importers across lib/api/contracts/tools/aws/, lib/api/contracts/*.ts, lib/copilot/generated/, stores/workflows/workflow/types.ts, ee/access-control, ee/data-retention, lib/logs/types.ts, and app/workspace component files. TypeScript and API validation both pass clean. Reduces unused-types count from 394 → 181 and fully eliminates the ✗ critical dead-code categories (exports, types, files now show as ⚠ warnings not ✗ errors). * docs improvements * fix(react-doctor): delete 50 unused files (dead barrels, unreachable components, stale utilities) Remove confirmed-unused barrel index.ts files across stores/, connectors/, executor/, lib/, and app/workspace/ that had zero importers. Also delete unreachable components (chat-history-skeleton, trace-spans, logs-list, template-profile, enterprise landing sections), stale utilities (buffered-stream, blob-to-data-url, queued-workflow-execution, compute-edit-sequence), and obsolete generated/contract files. TypeScript passes clean. * remove dead code * cleanup * more * fix(blog): restore DiffControlsDemo for v0-5 blog post * fix: restore ContactButton for enterprise blog post, export WIKIPEDIA_PAGE_CONTENT_OUTPUT_PROPERTIES * fix(react-doc): restore stripped exports and remove server-only dependency * chore(deps): update lockfile after removing server-only * added back some exports * docs * more * fix type issues * tc * fix docs search route Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tag-dropdown): add missing isEqual import from es-toolkit --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
aae93f8228 |
fix(execution): cap isolate memory at 128MB and recycle workers every 200 executions (#4543)
* fix(execution): cap isolate memory at 128MB and recycle workers every 100 executions * fix(execution): set IVM_MAX_EXECUTIONS_PER_WORKER env default to 100 * fix(execution): raise MAX_EXECUTIONS_PER_WORKER from 100 to 200 * fix(execution): update memory limit error messages from 256 MB to 128 MB |
||
|
|
94f60e79b2 | improvement(deps): remove unused remark deps (#4542) | ||
|
|
a170255633 | fix(script): biome format wrap (#4541) | ||
|
|
13666b162d |
fix(zustand): v5 selector stability issues (#4539)
* fix(zustand): v5 selector stability issues * address comments |
||
|
|
6544e0a783 | fix(tables): inline editing center alignment in table cells (#4538) | ||
|
|
f122b686a4 |
fix(uploads): write workspaceFiles row when issuing presigned URL (#4537)
* fix(uploads): write workspaceFiles row when issuing presigned URL * test(uploads): cover insertFileMetadata failure path in presigned route |
||
|
|
02f381d08c |
feat(pptx): replace viewer with custom preview (#4536)
* feat(pptx): replace viewer with custom preview * address comments * address comments * address bugbot * address comment * rm bun lock * resolve conflict * update credit comment * address comments * rm bun lock * resolve conflict * make previews consistent for zoom and scroll * zoom fix |
||
|
|
c7130c6bf9 |
fix(tables): fix bulk ops truncation for tables larger than one page (#4532)
* fix(tables): fix bulk ops truncation for tables larger than one page Bulk operations (column-header delete, select-all copy/cut/delete/run) were silently truncated to the first 1000 rows because handlers only iterated the loaded pages from useInfiniteQuery. Fix: - Extract tableRowsInfiniteOptions factory (infiniteQueryOptions) so the hook and imperative drain share the same typed cache key - Add background drain via useEffect watching hasNextPage/isFetchingNextPage — chains fetchNextPage until getNextPageParam returns undefined - Add ensureAllRowsLoaded to use-table: reads cache via getQueryData + calls fetchNextPage in a while loop until the last page is partial - Await ensureAllRowsLoaded at every kind:'all' bulk-op entry point in table-grid (column delete, copy, cut, action-bar delete/run) - Add chunkBatchUpdates to send updates in MAX_BULK_OPERATION_SIZE=1000 chunks so server validation never rejects oversized batches - Fix undo-redo: make executeAction async and chunk clear-cells, update-cells, and delete-column cell-restore with mutateAsync loops Tests: 41 passing across use-table, tables queries, and use-table-undo * chore(tables): remove extraneous comments * fix(tables): add missing useEffect import; chunk range-selection delete and cut * fix(tables): add hasRunningGroupExecution and import it * fix(tables): define mergePagePreservingIdentity helper used in polling cache merge * fix(tables): define ROWS_POLL_INTERVAL_WHILE_RUNNING_MS constant used in polling loop * fix(tables): capture rowSel before await in delete handler; handle clipboard NotAllowedError * fix(tables): abort cut on clipboard NotAllowedError to prevent silent cell deletion * fix(tables): push undo before chunkBatchUpdates to survive partial chunk failures * fix(tables): use text input for number cells; idle poll backoff; csv error toast; column-cut drain * fix(tables): audit fixes — column-copy drain, polling scope, merge identity - Fix polling tick: move Promise.all inside else-branch so dirty[] stays in scope; keep hasDirty=true during active mutations so the short interval fires while chunked batch-updates are in flight - Add isColumnSelectionRef branch to handleCopy (mirrors handleCut fix): column-header Cmd+C now drains all pages before building clipboard content - Replace String(updatedAt) comparison in mergePagePreservingIdentity with Date.getTime() equality — handles ISO vs +00:00 timezone variants - Remove redundant batchUpdates.length > 0 guards at chunkBatchUpdates callsites (empty-array case is handled inside the function) - Export _mergePagePreservingIdentity for unit testing - Add 6 unit tests covering mergePagePreservingIdentity edge cases * improvement(tables): cleanup — extract components, stabilize callbacks, fix ref sync * improvement(tables): remove polling, eager drain, and parallelize batch updates - Drop the per-page polling loop — SSE stream already patches execution cell state in real time and invalidates on buffer prune; polling was redundant and burned CPU/network on every open table - Remove eager mount drain (fetchNextPage loop in use-table.ts); scroll handler and ensureAllRowsLoaded handle progressive/on-demand loading - Parallelize chunkBatchUpdates with a 3-worker pool instead of serial chunks, reducing bulk-op round-trips by ~3x - Delete mergePagePreservingIdentity and its tests (no longer called) * chore(tables): remove stale comments and dead defensive code - Fix chunkBatchUpdates JSDoc to reflect parallel dispatch (was "sequentially") - Inline CHUNK_CONCURRENCY=3, single-use constant needs no abstraction - Drop stale "Polls while any cell is in flight" from useTableRows JSDoc - Remove two generic "Validation errors surfaced by caller" comments - Remove ASCII separator line from workflow group mutations section - Remove dead `if (!variables) return` guard in useImportCsvIntoTable onSettled (TanStack v5 always provides variables to onSettled) * fix(tables): run-all selection sends all rows to server, not just loaded pages When rowSelection.kind === 'all', selectedRunScope now flags allRows: true. The action-bar run handlers pass rowIds: undefined to the server when allRows is set, matching the server contract (missing rowIds = run all eligible rows). Stop likewise routes through scope: 'all' instead of per-row cancels. Previously, selecting all rows and clicking Run would silently only run the rows loaded in the current infinite-query cache (potentially one page of 1000 on a 5000-row table). * test(tables): remove background-drain describe block (behavior intentionally removed) * fix(tables): surface CSV import error toast; remove dead hasRunningGroupExecution * fix(tables): make runWithoutRecording async-aware so undoRedoInProgress covers full undo execution * feat(tables): render URL cells with favicon and clickable link * feat(tables): clickable URL cells with favicons using tldts |
||
|
|
4cc00b2ef9 |
fix(security): enforce workspace scope on workflow middleware and validate shopify shop domain (#4535)
* fix(security): enforce workspace scope on workflow middleware and validate shopify shop domain
- validateWorkflowAccess now rejects workspace-scoped API keys whose
workspaceId doesn't match the workflow's workspace, closing a boundary
leak across /api/workflows/[id]/{log,paused,status} and
/api/resume/[workflowId]/[executionId]/[contextId]
- shopify authorize route now validates the resolved shop domain against
shopifyShopDomainSchema before proceeding
- adds middleware tests covering workspace/personal/session auth paths
* fix(shopify): disallow trailing hyphen in shop subdomain regex
* fix(shopify): align shop domain regex with shopify handle rules (3-60 lowercase, no edge hyphens)
* fix(security): widen shopify subdomain regex to allow up to 63 chars
Shopify and RFC 1123 allow labels up to 63 chars; the previous
{1,58} quantifier capped the subdomain at 60 chars and rejected
valid 61–63 char shops with a 400.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
773720ec7f |
fix(uploads): allow images/video/audio in mothership presigned route (#4534)
* fix(uploads): allow image/video/audio attachments in mothership presigned route The mothership branch of the presigned upload route called validateFileType, which only permits SUPPORTED_DOCUMENT_EXTENSIONS — rejecting PNG screenshots and other media users have always been able to attach via the legacy /api/files/upload mothership branch. Introduce validateAttachmentFileType, backed by the union of document, code, image, audio, and video extensions, and wire it into the mothership branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(uploads): allow non-document execution outputs in presigned route The execution branch of the presigned upload route called validateFileType, which only permits documents — but workflow execution outputs are arbitrary by design (images, audio, video, code). The legacy /api/files/upload execution branch had no docs-only gate, so the staging refactor to presigned PUTs regressed parity. Switch execution to validateAttachmentFileType to match prior behavior. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(uploads): add coverage for attachment validator and dedupe extensions - Dedupe SUPPORTED_ATTACHMENT_EXTENSIONS (webm appears in both audio and video lists) - Reuse SUPPORTED_ATTACHMENT_EXTENSIONS in /api/files/upload to avoid drift with the presigned route - Add unit tests for validateAttachmentFileType - Add presigned route tests covering mothership/execution/knowledge-base validator selection and permission gating * fix(uploads): restore SUPPORTED_IMAGE_EXTENSIONS import in upload route Build broke because biome auto-fix collapsed my multi-import edit and dropped SUPPORTED_IMAGE_EXTENSIONS, which is still referenced for the generic-MIME image fallback at line 298. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
1d3ca79779 |
chore(deps): audit and clean up dependencies (#4531)
* chore(deps): audit and clean up dependencies - Remove unused: chalk, chart.js, dotenv, encoding, entities, thread-stream, uuid, @opentelemetry/exporter-jaeger, critters, marked, redis, soap - Replace soap with hand-rolled Workday SOAP client - Migrate marked to unified pipeline for inbox responses - Bump zustand v5, @react-email/* - Align all @aws-sdk/* to 3.1032.0 - Move type-only deps to devDependencies - Remove duplicate drizzle-orm/postgres overrides * fix(workday): coerce SOAP scalar strings to typed booleans/numbers - XML parser returns leaf text as strings; `!"false"` evaluated to `false`, causing all organizations to report `isActive: false` - Add parseSoapBoolean and parseSoapNumber helpers and apply at consumer sites (Inactive, Total_Results) - Drop unused service/soapAction fields from WD_OPERATIONS map * fix(workday): coerce compensation amounts and guard Date marshaling - get-compensation returned Amount/Per_Unit_Amount/Individual_Target_Amount as strings (XML leaf text), violating the tool's number contract - Coerce via parseSoapNumber and widen plan type to number | string - Add defensive Date branch in marshal() so Date inputs serialize as ISO 8601 instead of String(date) |
||
|
|
cb3a876d3e |
feat(knowledge): include sourceUrl in KB search results (#4533)
* feat(knowledge): include sourceUrl in KB search results * improvement(kb-search): mark sourceUrl nullable and cover non-null happy path in tests |
||
|
|
d0b0ede39e |
fix(mothership): misc ui bugs (#4528)
* fix(vfs); compiled check tool call natural language tool desc * fix few more ui/ux bugs |
||
|
|
b74f8dac40 |
improvement(sandbox): expand document generation — style extraction, sandbox hardening, OOM errors, task guards (#4526)
* improvement(sandbox): expand document generation — style extraction, sandbox hardening, OOM errors, PPTX/DOCX/PDF task guards * fix(style): make pptx aspect-ratio regex attribute-order independent * fix(sandbox): clarify pptx null-guard message; fix bold=false inheritance sentinel in docx style extractor * chore(lint): suppress noTemplateCurlyInString in resolver tests — strings intentionally assert template literal preservation * fix(contracts): export ListWorkspaceFilesResponse type from workspace-files contract |
||
|
|
50d4afd4a8 |
fix(logs): include subfolders when filtering logs by folder (#4525)
* fix(logs): include subfolders when filtering logs by folder * fix(logs): use pop() for O(1) dequeue in folder BFS * fix(logs): move folder expansion to server-only module to fix client bundle build |
||
|
|
630db5cee0 |
improvement(mothership-chat): memoize message rows for long-transcript load speed (#4520)
* improvement(mothership-chat): memoize message rows for long-transcript load speed * improvement(mothership-chat): hoist EMPTY_BLOCKS constant to keep MessageContent memoized for block-free messages |
||
|
|
3c33c20378 | improvement(peopledatalabs): add titlecase, placeholders, and 404 handling on search (#4519) | ||
|
|
29affdac58 |
improvement(search-replace): dedupe double indexed segments (#4517)
* improvement(search-replace): dedupe double indexed segments * cleanup error message * address comments |
||
|
|
9e9ddaa1aa |
feat(peopledatalabs): add People Data Labs integration (#4513)
* feat(peopledatalabs): add People Data Labs integration Add 11 PDL operations: person enrich/identify/search/bulk, company enrich/search/bulk/clean, location/school cleaners, and autocomplete. All endpoints, params, and response shapes verified against official PDL docs (scroll_token pagination, top-level likelihood on company enrich, per-item likelihood on bulk company, full autocomplete field enum). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(peopledatalabs): narrow conditions for fields not used by every operation - min_likelihood now only shows for pdl_person_enrich (Person Identify ignores it) - ticker, pdl_id, company_location now only show for pdl_company_enrich (Company Cleaner only accepts name/website/profile) Addresses Greptile P1 review on PR #4513. * fix(peopledatalabs): scope param renames to their operation Param renames (company_profile→profile, company_location→location, school_*→*, bulk_*_requests→requests, autocomplete_size→size, etc.) now run only when the matching operation is selected, and stale alternate-operation values are stripped from the request. This prevents values left over from a prior operation switch from leaking into the current API call (e.g. a company LinkedIn URL overwriting a person profile, or a stale search size overwriting autocomplete size). Addresses Cursor Bugbot review on PR #4513. * fix(peopledatalabs): use currentColor for icon fill The PeopleDataLabsIcon was hardcoded to white, leaving it invisible on light backgrounds when rendered outside its bgColor container (e.g., search results, menus, docs). Switch to currentColor so it inherits the surrounding text color. Addresses Cursor Bugbot review on PR #4513. * fix(peopledatalabs): scope shared fields (profile/location/name/website) per operation The block has subBlocks whose raw IDs collide with PDL API param names (profile, location for person; name, website for company). Their values persist across operation switches even though the UI hides them, so a person LinkedIn URL could leak into a Company Enrich request, etc. Reset these shared targets and repopulate them only from inputs that belong to the active operation. Addresses Greptile P1 review on PR #4513. * fix(peopledatalabs): scope `size` to search and autocomplete operations `size` is shared by the person/company search subBlock and the autocomplete_size alias. The previous logic still forwarded a stale search `size` to operations that don't accept it (e.g. enrich, clean, identify), and the autocomplete branch only cleared it when autocomplete_size was unset. Reset `size` up front and only repopulate it for the three operations that actually accept it. Found via final integration audit of PR #4513. * fix(peopledatalabs): restore `location` for Person Identify Person Identify's tool accepts `location`, and the subBlock is shown for both Enrich and Identify, but the prior reset only repopulated `result.location` for Enrich — so any value entered on Identify was silently dropped before reaching the API. Addresses Greptile P1 review on PR #4513. * fix(peopledatalabs): align endpoints + outputs with PDL API - person_identify: short-circuit on PDL 404 (no-match), matching the person_enrich pattern - company_search: drop unsupported `dataset` param (PDL company search docs do not list it) - block: expose `min_likelihood` for `pdl_company_enrich` (PDL Company Enrichment supports min_likelihood) - location_clean: surface `subregion`; drop phantom `latitude`/ `longitude` (PDL only returns `geo` as a "lat,lon" string) - school_clean: surface `domain` and `location_continent` from the nested `location` object - docs icon: switch fill to `currentColor` so the icon renders on light backgrounds * fix(peopledatalabs): restore `name` for Person Enrich / Identify The shared `name` reset at the top of `tools.config.params` was only repopulated for the company-side operations, so any programmatic `name` input to `pdl_person_enrich` or `pdl_person_identify` was silently dropped. Both PDL endpoints accept `name` as a full-name match parameter. * fix(peopledatalabs): restore `name` for Person Enrich Add the `name` parameter to `PdlPersonEnrichParams`, the tool's params definition, and the URL builder. PDL Person Enrichment accepts `name` as a full-name match alternative to first_name + last_name; without it, programmatic `name` input was silently dropped before reaching the API. * fix(peopledatalabs): isolate company `name` UI from person ops Rename Company Name subBlock id from `name` to `company_name` so a stale company value can't leak into Person Enrich/Identify when the user switches operations. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(peopledatalabs): honor programmatic inputs for clean_location/school `pdl_clean_location` and `pdl_clean_school` were only restoring values from UI subBlock IDs (`clean_location_input`, `school_*`). Programmatic callers using the declared `location`/`name`/`website`/`profile` inputs had their values dropped after the shared-field reset. Add fallbacks so both UI and programmatic inputs flow through. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(peopledatalabs): programmatic input fallbacks + required autocomplete text - Company Enrich and Clean Company now fall back to programmatic `params.profile` / `params.location` when the UI-scoped `company_profile` / `company_location` are absent. Mirrors the fallback pattern already used for `name`. - Autocomplete `text` subBlock is now required when operation is autocomplete — PDL requires it for nearly all field values. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
57f00e2a96 |
fix(table): fix table boolean, add dynamic row number col size, search & replace imporvements (#4515)
* fix(table): fix table boolean, add dynamic row number col size, fix style for search and replace * sidebar styling * refactor(search-replace): extract duplicate WORKFLOW_SEARCH_HIGHLIGHT_CLASS to shared constants * fix(tables): add missing onError toast to useAddTableColumn * fix(tables): add consistent onError toast handlers across all table mutations * subblock sweep * more resources supported * code restructuring * better organization * canonical modes search replace counterpart * more cases * tool inp edge case * more subblock cases * trigger canoncial index ops consolidation * minor codec fix * fix(tables): prevent double toast in workflow-sidebar and csv upload mutations * resolver fix for quoted js literals * code scan context * triple quotes case * escaped behaviour --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> |
||
|
|
eb871fc0de |
feat(table): live cell updates via SSE + per-table event buffer (#4508)
* feat(table): live cell updates via SSE + per-table event buffer
Replaces the polling-based row refetch with a push-based SSE stream that
patches the React Query cache directly as cell-state events arrive.
Architecture:
- New per-table event buffer in apps/sim/lib/table/events.ts. Redis sorted-set
with monotonic eventId, 1h TTL, 5000-event cap, in-memory fallback. Modeled
after apps/sim/lib/execution/event-buffer.ts but stripped of complexity
tables don't need (no per-execution lifecycle, no id-batching, no write
queue serialization). ~150 lines instead of 700.
- writeWorkflowGroupState appends a fat event after each successful 'wrote'.
Status transitions carry executionId + jobId; terminal/partial transitions
also include the new output values inline so the client can patch row data
without a follow-up refetch.
- New SSE route at /api/table/[tableId]/events/stream?from=<lastEventId>.
Replays from buffer on connect, polls at 500ms (mirrors workflow execution
stream), heartbeat every 15s, signals 'pruned' if the caller fell off the
back of the buffer.
- Client hook useTableEventStream subscribes via EventSource. Reconnect-resume
with last-seen eventId. On 'pruned', invalidates the rows query and resumes
from the new earliest. Cache patches walk every cached query under
rowsRoot(tableId) so filter/sort variants all stay live.
- Removes refetchInterval from useTableRows and the per-page polling effect
from useInfiniteTableRows. React Query's refetchOnWindowFocus +
refetchOnReconnect cover the durability gap if any push is dropped.
Out of scope:
- Bulk-cancel events (cancellation path is being redesigned separately).
- Generalizing the workflow event-buffer module to a shared primitive (defer
until a third use case appears; for now the table buffer is the simpler
cousin of the workflow one).
* fix(table): drop run-mutation refetch so SSE patches aren't overwritten
useRunColumn.onSettled was canceling in-flight queries and invalidating the
rows query — leftover behavior from the polling era. With the SSE stream
now keeping the cache live via incremental patches, this refetch races the
stream and snaps the cache back to whatever DB shows at the refetch moment,
which can lag the just-arrived queued/running events. Cells appeared stuck
on the optimistic 'pending' even though the SSE was delivering the real
transitions.
* chore(table): simplify SSE plumbing — reuse helpers, drop dead polling code
- Reuse snapshotAndMutateRows for SSE cache patches instead of reimplementing
the page-walk + cache-shape detection. Adds a {cancelInFlight: false} opt
for the SSE caller (mutations still cancel as before).
- Drop client-side type duplication in use-table-event-stream — import
TableEvent and TableEventEntry from lib/table/events directly.
- Drop the now-dead mergePagePreservingIdentity + rowEqual from tables.ts;
their only caller was the polling effect that was removed earlier.
- Drop the defensive try/catch around appendTableEvent in cell-write — the
function is documented as never-throwing (returns null on failure).
- Combine INCR + ZADD into one Lua eval in events.ts. Halves Redis RTT per
cell-write. Lua returns the new eventId; the script splices it into the
pre-built entry JSON.
- Trim refs to plain let bindings inside the effect; trim stale
comments referencing the old polling implementation.
* fix(table): address PR review on SSE buffer
- TTL-expiry silent miss: when all keys expire, hgetall(meta) returns empty
so earliestEventId is undefined and the prune branch was skipped. Reconnect
with non-zero afterEventId now checks the seq counter — its absence (TTL
expired) signals pruned so the client refetches. Memory fallback mirrors.
- Unbounded ZRANGEBYSCORE: cap reads at TABLE_EVENT_READ_CHUNK = 500 events
per call. The route's 500ms poll loop drains chunks across ticks instead of
flushing 5000 entries (multi-MB) in one tick after a long disconnect.
- Pruned handler closes EventSource client-side: server-side close was firing
onerror and routing through the 500ms backoff path. Now we close
proactively, reset the reconnect attempt counter, and reconnect immediately
from the new earliest.
* improvement(table): persist SSE lastEventId in sessionStorage
Tab refresh / navigate-away-and-back now resume the stream from where the
previous mount left off instead of replaying from from=0. Mirrors the
useExecutionStream pattern (saveExecutionPointer / loadExecutionPointer).
First-ever mounts and new tabs still start at 0 — sessionStorage is
per-tab, so the safe default applies. On a 'pruned' fallback the new
earliestEventId is also persisted so the next reconnect starts there.
* fix(table): include runningBlockIds + blockErrors in SSE event payload
The cell renderer's 'queued' vs 'running' vs 'pending-upstream' decision
reads exec.runningBlockIds + exec.blockErrors. Without those fields the
inFlight branch falls through to 'pending-upstream' (amber Pending pill)
even when the worker has already written status=running. The worker writes
both fields to DB; the SSE event was stripping them. Thread them through
events.ts → cell-write.ts → use-table-event-stream.ts.
* fix(table): show value once column output has landed mid-run
The cell renderer treated any `status: 'running'` event as in-flight,
even when the column's own output had already been written. During a
multi-block group run, partial-write events for a later block carry
the earlier block's outputs but tag only the later block as running
— that flipped the finished column back to the amber Pending pill
until the terminal `completed` event arrived.
Re-order the priority chain so the column's value wins over
`pending-upstream`. Active re-run of the column itself
(`blockRunning`) still wins over the stale value, so a re-run on a
previously-completed cell still surfaces the running pill before the
new value overwrites.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(table): address PR review nits on tables.ts
- Merge duplicate JSDoc on snapshotAndMutateRows into a single block
- Remove unused useQueryClient() calls from useTableRows and
useInfiniteTableRows (leftover from polling-era code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(table): apply biome formatting fixes
CI lint job flagged import order in two files and over-wrapped union
in lib/table/events.ts. No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6cb779601a |
feat(search-replace): search & replace, cut, deploy modal ui flicker (#4507)
* feat(search): workflow search and replace * fix alignment * fix hidden fields bug * fix loops/parallel badge case * resource resolver * add cut * update docs * address comments * make source code for func blocks dispay resolved code instead * fix match issue * fix padding |
||
|
|
11563cfbc2 |
improvement(uploads): migrate remaining FormData uploads to presigned PUT (#4509)
* fix(uploads): switch mothership uploads to presigned PUT pattern * fix(uploads): drop unreachable size guard in mothership presigned branch * improvement(uploads): migrate profile-picture and workspace-logo uploads to presigned PUT * improvement(uploads): migrate workflow file-upload sub-block to presigned PUT * improvement(uploads): migrate execution-trigger file uploads to presigned PUT * fix(uploads): tighten permission checks and fix multipart customKey for mothership/execution * fix(uploads): require workspace write+ for execution presigned, admin-only for workspace-logos, suppress doubled error toast * fix(uploads): skip per-file invalidation in batch + extract shared API fallback - Add skipInvalidation flag to useUploadWorkspaceFile; file-upload sub-block now invalidates once after the batch instead of per file - Extract uploadViaApiFallback to lib/uploads/client/api-fallback.ts (DRY across 3 hooks) |
||
|
|
6a927c9cd3 |
fix(tables): optimistic updates for column delete/update (#4512)
* fix(tables): optimistic updates for column delete/update Add onMutate/onError to useDeleteColumn and useUpdateColumn so column deletes feel instant on large tables (no flash-back during the JSONB rewrite) and concurrent type changes don't race the in-flight delete's invalidation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(tables): toast on delete-column failure, case-insensitive row cleanup, rename row-data keys Address greptile review: - useDeleteColumn now toasts on non-validation errors so users see when the column "snaps back" after a server/network failure - Row data cleanup matches keys case-insensitively in both useDeleteColumn and useUpdateColumn so a column stored as "Age" is cleaned even when the request uses "age" - useUpdateColumn now migrates row-data keys when updates.name is set, preventing blank cells during the server round-trip on a rename Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
235a62fd95 |
fix(hunter): align tools, block, and outputs with Hunter.io v2 API spec (#4511)
* fix(hunter): align tools, block, and outputs with Hunter.io v2 API spec * fix(hunter): match documented Discover response and coerce numeric employees |
||
|
|
a2aa648f86 |
improvement(sandbox): upgrade pptx/docx/pdf bootstrap with image helpers, MIME guards, and 256 MB isolate limit (#4505)
* improvement(sandbox): upgrade pptx/docx/pdf bootstrap with image helpers, MIME guards, and 256 MB isolate limit * fix(sandbox): strict MIME allowlist and nullish coalescing in docx addImage * fix(sandbox): validate required opts in pdf drawImage to prevent silent origin placement * fix(sandbox): throw on malformed data URI in docx addImage * fix(sandbox): prevent opts from clobbering computed ImageRun data/type/transformation * fix(sandbox): prevent opts from clobbering fetched data in pptx addImage * fix(sandbox): validate required opts in pptx addImage * fix(sandbox): remove silent image/png fallback in docx addImage MIME parsing * fix(sandbox): consistency and cleanup pass on doc-gen tasks and worker - DOCX addImage: upfront width/height validation (matches PDF/PPTX pattern) - PDF embedImage: remove dead Buffer ternary; drop redundant size guard already enforced in getFileBase64 - isolated-vm-worker: add friendly MemoryLimitError branch in both execute paths so OOM produces a clear message instead of a raw V8 error |
||
|
|
6a006851fa |
improvement(apollo): align tools and block with Apollo API docs (#4487)
* improvement(apollo): align tools and block with Apollo API docs
* improvement(apollo): fix tool outputs to match Apollo API response shapes
* chore(apollo): regenerate docs for output changes
* fix(apollo): address PR review comments
* fix(apollo): allow skipped_contact_ids as hash per Apollo docs
* docs
* fix(apollo): add runtime guard for account_bulk_update empty body
* fix(apollo): require contact_attributes for bulk_update
* fix(apollo): add subblock id migrations for renamed opportunity fields
* fix(apollo): tighten account_bulk_update guard and accept object attrs
* fix(apollo): require contact_ids with object-form contact_attributes
* docs(apollo): clarify contact_bulk_update parameter requirements
* fix(apollo): handle flat and wrapped contact response shapes
* validate
* fix(apollo): mirror bulk_update guard, preserve update fields in migration, expose account_bulk_create options
* fix(apollo): don't clobber user contact_attributes in migration; simplify task_create created flag
* fix(apollo): drop undocumented task type, preserve mixed-array IDs, migrate note→task_notes
* fix(apollo): align tools and block with live API docs
Final pass over the Apollo integration after a per-tool forensic audit
against Apollo.io docs. Notable fixes:
- organization_enrich: GET+querystring -> POST+JSON body (canonical, non
master-key)
- organization_bulk_enrich: ?domains[]= -> JSON body { organizations }
- people_search: declare/forward organization_num_employees_ranges; fix
contact_email_status placeholder ("likely to engage", with spaces)
- account_bulk_create: surface failed_accounts and failed count
- contact_bulk_create: expand documented per-contact fields (CRM IDs,
phone_numbers, contact_emails, typed_custom_fields, etc.)
- sequence_add_contacts: surface remaining documented filter params
- task_create: confirm wire field name (note) and remap from task_notes
- types: tighten params/responses for the above
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs
* fix(apollo): add _removed_* migrations for retired opportunity subblocks
* fix(apollo): expose webhook_url subblock for people enrich phone reveal
* fix(apollo): drop colliding account_ids migration, enforce contact bulk limit, expose async toggle for accounts
* fix(apollo): cap account_attributes at 1000 in bulk update
* fix(apollo): drop bare-id merging in bulk update migration to avoid empty attribute objects
* fix(apollo): reject ambiguous account/contact_ids + array-form attributes
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
408669dd85 | fix(md-render): inline code inherits heading size in mothership/templates/changelog (#4504) | ||
|
|
1e5cc392d5 |
improvement(deployment): solve multiple client side races, and deployed state management issues (#4502)
* improvement(deploy): state transitions * more fixes * improvements and shift to outbox policy with eager call * address comments * ux improvement * address comments * address bugbot |
||
|
|
00b5b74c74 | fix(redis): drop cached client and restart PING loop after forced reconnect (#4501) | ||
|
|
81845ae0df |
fix(table): trigger cascade race fixes, polling, workflow column flag (#4499)
* fix(table): don't let parallel queued stamp overwrite a worker that already started Stamps fire in chunks of 20 via Promise.all, so queued writes race with the worker's markWorkflowGroupPickedUp (running). When the late queued stamp landed second it overwrote running, and the cell looked stuck in queued for the rest of the run. Skip the stamp when the same execution is already past queued — the worker's authority wins. * fix(table): per-page polling, optimistic skip on filled outputs, workflow column flag - Polling now refetches only pages that contain in-flight cells instead of every loaded page. Idle pages stay untouched while a cascade runs. - run_column optimistic patch mirrors server eligibility on mode='incomplete': cells with filled outputs no longer flip to queued only to revert seconds later when the server returns 0 triggered. - Hide the Workflow column type behind NEXT_PUBLIC_WORKFLOW_COLUMNS_ENABLED (default false). Existing workflow groups keep rendering. * fix(table): memoize infinite-rows queryKey so polling effect doesn't reset every tick * fix lint * fix(table): serialize polling ticks to prevent overlapping fetches * fix(table): fire-and-forget run-column dispatch Large fan-outs (thousands of rows) issue sequential trigger.dev HTTP calls inside scheduleRunsForRows.batchEnqueue. Awaiting that loop held the HTTP response (and the AI tool span) open for ~5 min on a 6k-row table — the user saw an 11-min "running" because the tool didn't return until every job had been enqueued. Run the dispatcher in the background and return immediately; contract response now reports `triggered: null` since the count isn't known synchronously. * improvement(table): preserve row identity across poll refetches Each poll tick brings back a fresh page from the server with all-new row objects, even though most rows haven't changed. setQueryData was replacing the whole page reference, which made every memoized <DataRow> in the page re-render every 1.5s. Now we shallow-compare each fresh row against the cached one and reuse the cached reference when nothing changed; only rows whose data or exec status actually flipped re-render. * fix(table): treat manual-bypass eligibility as runnable Refactoring eligibility into classifyEligibility split the runnable answer into two reasons: 'eligible' (deps satisfied) and 'manual-bypass' (autoRun= false group on a manual run, deps don't apply). isGroupEligible only treated 'eligible' as runnable, so a manual "Run all rows" on a single autoRun=false group filtered every row out and returned triggered: 0. Cells flashed queued from the optimistic patch then went empty when the refetch landed. |
||
|
|
b045eae606 |
revert(ci): drop turbopackFileSystemCacheForBuild and restore actions/cache (#4500)
Build times grew monotonically from ~200s to 1305s (~6x) after #4478 landed. The Turbopack FS cache flag combined with sticky-disk persistence caused unbounded cache growth. Reverting both — keeping the poweredByHeader and optimizePackageImports trim from the original PR. |
||
|
|
401ebccafd |
fix(auth): Redirect to login if user session doesn't exist (#4497)
* feat(admin): Increase impersonation timeout to 100 years * fix(workspace): redirect to login on missing session instead of leaving blank workspace |
||
|
|
f708462213 |
fix(files): skip zip and return plain .md when no embedded images (#4498)
* fix(files): skip zip and return plain .md when no embedded images * fix(files): use imageIds.length guard instead of assetMap.size * fix(files): move imageIds.length guard before async fetch to avoid unnecessary work |
||
|
|
38cbefdc3b |
fix(revenuecat): align tools and block with REST v1 API spec (#4488)
* fix(revenuecat): align tools and block with REST v1 API spec
- Validated all 10 tools and the block against context7 REST v1 docs
- Unwrap {value:{subscriber}} envelope across post-receipts, attributes,
entitlements, and Google subscription endpoints
- Trim entitlement output to documented fields (expires_date,
grace_period_expires_date, product_identifier, purchase_date)
- Add subscriber output fields: last_seen, original_application_version,
other_purchases, subscriber_attributes
- create_purchase: productId optional (Google-only required); add
introductoryPrice, attributes, updated_at_ms; surface customer + subscriber;
X-Platform required; presentedOfferingIdentifier and paymentMode
- update_subscriber_attributes: read response and surface subscriber;
note required updated_at_ms
- defer_google_subscription: enforce XOR(extendByDays, expiryTimeMs) and
1-365 range; expiryTimeMs as one-of alternative
- grant_entitlement: duration optional, added endTimeMs (one-of)
- refund_google_subscription: corrected endpoint to
/transactions/{storeTransactionId}/refund
- delete_customer: read 'deleted' field (was 'was_deleted')
- list_offerings: corrected X-Platform values
- get_customer: count active subscriptions by expiry/refund
- Added shared throwIfRevenueCatError helper for {code, message} envelope
- Moved type coercions from tools.config.tool to tools.config.params to
preserve dynamic refs
* docs
* fix(revenuecat): address PR review feedback
- create_purchase: wrap JSON.parse(attributes) with try/catch and clear error
- update_subscriber_attributes: drop subscriber output (endpoint returns
empty body) and guard JSON.parse on attributes
- grant_entitlement: throw when both duration and endTimeMs are provided,
matching defer_google_subscription behavior
* fix(revenuecat): tighten docs after sub-segment validation
- delete_customer: drop dead was_deleted fallback (docs specify 'deleted')
- grant_entitlement: mark duration + startTimeMs as deprecated, clarify
startTimeMs only affects expiration calc (not grant time)
- list_offerings: replace vague platform description with documented
X-Platform enum (ios, android, amazon, stripe, roku, paddle)
* docs
* fix(revenuecat): clear duration default when endTimeMs is provided
The duration dropdown defaults to 'monthly' so any user filling in the
advanced endTimeMs field would otherwise hit the XOR guard. Clear
duration in the params mapper so endTimeMs takes precedence.
* fix(revenuecat): clear extendByDays default when expiryTimeMs is provided
Mirror the duration/endTimeMs fix from
|
||
|
|
d081ab283e |
fix(logs): relax fileSchema so execution logs with files render again (#4495)
* fix(logs): relax fileSchema so execution logs with files render again * improvement(logs): align fileSchema with shared UserFile type - contracts/logs.ts: replace local fileSchema with mediaUserFileSchema (the established UserFile boundary schema with .passthrough()) - file-download.tsx: drop local FileData interface, use UserFile from @/executor/types * improvement(contracts): promote userFileSchema to primitives Move the canonical UserFile boundary schema out of tools/media/shared.ts (where it didn't belong — logs aren't media tools) into primitives.ts as userFileSchema. Update logs, stt, and video contracts to import from the shared primitive. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
98f8e854eb |
improvement(tables): extract TablesDetail wrapper, ship trigger followups (#4476)
* ui improvements * Update status pils, make checkbox column sticky * add Run workflow to context menu * Refactor dispatching logic * fix checkbox width to be smaller if csv is small * Add drag behavior for workflows, stop workflow on multi select * fix z index of checkbox to left, add view workflow button * Switch to emcn buttons for Add inputs * Split up workflow sidebar from column sidebar, refactor cells * Lint and add auto run toggle * fix column reordering, add action bar * Create and use emcn square * Reconcile post-merge: drop positionMap, use rowId-based selection Staging refactored Tables UI to decouple from DB position (gutter from array index, checkedRows keyed by rowId, no PositionGapRows). Bring HEAD's action-bar / context-menu helpers in line: contextMenuRowIds, selectedRowIds, actionBarRowIds now key off row.id and walk `rows` directly. Drop the maxPosition / positionMap derived state. Collapse COLUMN_SIDEBAR_WIDTH_CSS to a numeric COLUMN_SIDEBAR_WIDTH used by both the sidebar shell and the table's reserved padding-right. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(table): backfill remapped workflow outputs from execution logs When a workflow column is re-pointed to a different (blockId, path), populate its existing rows with the new output's value pulled from saved execution logs instead of leaving them empty until the next run. Rows where the new mapping has no logged value clear (matching the previous behavior for those rows), but rows where the workflow already has the new output's value surface immediately. Refactor backfillAddedGroupOutputs into a generalized backfillGroupOutputsFromLogs helper with an `overwrite` flag — used in both the added-outputs path (preserves hand-edited values) and the new remapped path (overwrites since the new mapping is the source of truth). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): update column type when remapping workflow output A remap that changes the output's leaf type (string → number, json → boolean, etc.) was leaving the column's declared type stale. The clear- then-backfill flow then failed schema validation on every row, so the backfill silently aborted and the column stayed empty. Resolve the new leaf type via flattenWorkflowOutputs + columnTypeForLeaf for each mappingUpdate, and patch schema.columns[i].type before the schema write. The clear-tx then backfill ordering now works end-to-end across type changes. If the workflow or its target output can't be resolved (workflow deleted, block removed), fall back to leaving the column type alone — the backfill will skip rows whose picked value doesn't match, same as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): stringify objects instead of "[object Object]" in cells If a column's declared type lags its row data (e.g. a workflow column mid-remap, where the schema cache hasn't refetched yet but the row data already has the new mapping's value), formatValueForInput and the cell-render text variant fell through to String(value) and rendered "[object Object]". JSON-stringify objects in both spots so the transient skew shows the actual data. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): drop extra left border on workflow group meta header The meta cell had border-r/b/l while regular headers have only border-r/b. With border-separate tables, that extra 1px left border shifted the meta cell's content one pixel right of the columns below it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): align workflow meta header without dropping its left border Restore border-l and pull the cell back -1px with -ml-px so the visible left border overlaps the previous cell's right border instead of adding 1px to the meta cell's box. Content lines up with the columns below. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): draw meta header left border via ::before pseudo Adding border-l to the meta cell shifted its content right by 1px because table-fixed + border-separate honors the border inside the colspan'd cell's width budget. -ml-px doesn't work on <th>. Render the visible left edge via a ::before at left: -1px instead — paints over the prior cell's right border without consuming any of the meta cell's content area. Content lines up with the columns below. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): add missing barrels, drop doubled-path imports Match the convention used by logs/components: every component folder exposes its public API via index.ts so consumers import from the folder name, not from its internal filenames. - New barrels: column-config-sidebar/, workflow-sidebar/, table-action-bar/, table/cells/, table/headers/. - Rename table-filter/index.tsx → index.ts (barrel is not a component). - Top-level components/index.ts re-exports every sibling folder so external consumers have one import path. - Replace `from '../foo/foo'` doubled paths in table.tsx with the shorter barrel-anchored form. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): introduce TablesDetail wrapper as thin passthrough Phase 1 step 0 of the wrapper extraction (see plan okay-lets-make-a-shimmying-trinket.md). page.tsx now renders TablesDetail, which today is a passthrough to <Table>. Subsequent commits lift surface state out of <Table> into this wrapper one piece at a time. The mothership chat path (<Table embedded>) is untouched — <Table> stays exportable as a lower-level component for embedded contexts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift slideout panel state into TablesDetail wrapper The three right-edge slideout panels (column config, workflow config, execution details) move out of <Table> into the wrapper. The wrapper owns a single useReducer that encodes the at-most-one-open invariant as a discriminated union — opening any one panel automatically closes the others. <Table> emits open requests via three new callback props. Also extract <ExecutionDetailsSidebar> from inline-in-table.tsx to its own folder so the wrapper can compose it cleanly. Update the embedded mothership callsite (resource-content.tsx) to render <TablesDetail embedded> instead of <Table embedded>. Phase 1 step 1 of the wrapper extraction. <Table> shrinks from 3849 → 3787 lines; <TablesDetail> grows from 19 → 145 lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift delete-table modal + mutation into wrapper The delete-table confirmation modal and `useDeleteTable` mutation move out of <Table> into TablesDetail. <Table> exposes a new `onRequestDeleteTable` callback fired by the page-header Delete action. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift CSV import dialog into wrapper ImportCsvDialog moves out of <Table>. Grid exposes `onRequestImportCsv` fired by the page-header menu item; wrapper owns the open state and renders the dialog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift RowModal (edit + delete) into wrapper Both RowModal instances move out of <Table> into the wrapper. Grid emits `onOpenRowModal(row)` (Space key) and `onRequestDeleteRows(snapshots)` (context menu). Post-delete cleanup (push undo, clear selection) needs grid-internal state, so the grid populates an `afterDeleteRowsSinkRef` callback that the wrapper's modal `onSuccess` invokes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift delete-columns modal into wrapper The destructive delete-columns confirmation modal moves into the wrapper. Grid emits `onRequestDeleteColumns(names)`; the cascade itself (per-column mutation, undo push, columnOrder + columnWidths cleanup) stays in the grid as a sink the wrapper invokes on confirm — too grid-internal to lift cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift run/stop mutations + TableActionBar to wrapper useRunGroup and useCancelTableRuns move out of <Table> into the wrapper, along with the <TableActionBar> render. Grid receives onRunGroup, onRunRows, onStopRow, onStopRows, onStopAll, and cancelRunsPending as props — used by the per-row gutter Play/Stop, the workflow-group meta-cell run menu, and the right-click context menu's Run/Stop on selection items. Action-bar selection state (actionBarRowIds, runningInActionBar, hasWorkflowColumns) is derived from grid-internal state, so the grid emits a `SelectionSnapshot` via `onSelectionChange` from a useEffect. Wrapper uses the snapshot to drive the floating <TableActionBar>. Phase 2 step 1 of the wrapper extraction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift queryOptions to wrapper queryOptions (filter + sort) moves out of <Table> into the wrapper, making it a single source of truth that drives one useTable call. The wrapper passes the bundle down to the grid; sort/filter handlers in the grid call onQueryOptionsChange. Eliminates the previous double-useTable pattern (one for the grid's filtered/sorted view, one in the wrapper's hardcoded null/null query for sidebar metadata). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): lift page header (breadcrumbs/options/filter) to wrapper Phase 3 of the wrapper extraction. The full page-header surface moves out of <Table>: - ResourceHeader (breadcrumbs, table-rename UI, headerActions, createTrigger) - ResourceOptionsBar (sort + filter toggle) - TableFilter (filter panel — wrapper owns filterOpen state) - RunStatusControl (in the leading actions when runs are active) useRenameTable + useInlineRename for the breadcrumb name move to the wrapper. The grid populates pushTableRenameUndoSinkRef so the rename is still part of the grid's undo stack. Extract NewColumnDropdown and RunStatusControl from inline-in-table.tsx to their own folders so the wrapper composes them cleanly without reaching into the grid's internals. Hoist generateColumnName from grid-internal useCallback to a shared util so both the page-header and inline-header NewColumnDropdowns use the same logic. After this lift <Table> is the data grid only — no page surface, no modals, no slideouts, no breadcrumbs. The selection snapshot now includes totalRunning so the wrapper can render the page-header RunStatusControl from outside the grid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(tables): cleanup pass on TablesDetail wrapper extraction Six-pass cleanup against the wrapper extraction diff: - Effects: add content-compare bailout to onSelectionChange emit so unchanged snapshots don't churn wrapper re-renders. - Memos: drop unnecessary activeSortState memo, fold into sortConfig. - Callbacks: remove ~10 useCallbacks with no observed reference (sidebars not memoized, modals not memoized, inline arrows on non-memoized children); keep the ones that feed into <DataRow>/<RunStatusControl>/ <ResourceHeader> (memoized) or grid-side useCallback deps. - Dead props: drop onQueryOptionsChange/onRequestDeleteTable/ onRequestImportCsv from <Table> — the page-header lift made them unused but the props weren't removed. - React Query: drop redundant tableWorkflowGroupsRef (created when onRunRows was useCallback-wrapped; after callback cleanup it can read the query data directly). - emcn: normalize Loader sizing to h-[14px] w-[14px] to match the codebase convention. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): re-seed columnOrder when columns change server-side The metadata-seed effect short-circuited after the first seed, so any later schema change (e.g. adding a workflow output column) couldn't push the new column into local columnOrder. The new column would then fall into the "remaining" bucket of `displayColumns` and render at the end of the table — until the user refreshed and the grid re-mounted with the now-current metadata. Drop the `metadataSeededRef.current` short-circuit from the early return so the effect can also reach the after-first-load re-seed branch, which already does the right thing (only re-seeds when the set of columns changes, leaves pure-reorder cases alone). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tables): rename wrapper to <Table>, grid to <TableGrid> Match the naming convention used elsewhere in the workspace (workflow.tsx → <Workflow>, base.tsx → <Base>, logs.tsx → <Logs>). - tables-detail.tsx → table.tsx (exports <Table>) - components/table/ → components/table-grid/ (exports <TableGrid>) - components/table-grid/table.tsx → table-grid.tsx - Drop <ExecutionDetailsSidebar> — was a 3-line passthrough (executionId → useLogByExecutionId → <LogDetails>); inline directly into table.tsx where it's used. - Flatten components/run-status-control/ folder to a single components/run-status-control.tsx file. 25-line single-use component with no internal subdirs — folder was overhead. Matches knowledge's max-badge.tsx precedent. Net: 1 wrapper rename + grid rename + 2 folder collapses, all imports updated. The mothership chat callsite updates from <TablesDetail embedded> to <Table embedded>. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): don't show "Waiting" for autoRun=false workflow groups A workflow group with autoRun=false never fires from the scheduler — the cell stays empty until the user clicks Run manually. Treating empty cells as "Waiting" misleads the user into thinking the group will auto-fire once deps are filled, which it won't. Skip autoRun=false groups when computing the per-row waiting labels so their cells render the empty-dash instead of the Waiting pill. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(copilot): regenerate tool catalog from copilot dev (#247) Pulls in the workflow_group operations on user_table: add_workflow_group / update_workflow_group / delete_workflow_group / add_workflow_group_output / delete_workflow_group_output / run_workflow_group, plus the autoRun / blockId / dependencies / groupId parameters and a tightened mapping description for import_file. Also picks up biome import-order fixes from `bun run lint`. * improvement(table): action bar in mothership + per-execution mode Three related improvements to the table action bar: 1. Reposition from `position: fixed` to `position: absolute` inside the table's container. Fixed-positioning anchored to the viewport, which centered the bar across the whole window instead of the table panel — wrong in mothership embedded view, where the table sits in the right half. Absolute scopes the bar to the table's bounds. 2. Show the bar for single-execution highlights — when the user selects one workflow-output cell, or 1 row × N cols all within the same workflow group. The bar enters per-execution mode with Run / Stop / View execution buttons targeting that one cell or group. 3. Skip View execution for cancelled cells. A cancelled cell may have been cancelled before the worker ever picked the job up, so its executionId can't be relied on. Tighten the gate everywhere (context menu + action bar) to only `completed` / `error` / `running`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): backfill on add_workflow_group_output, don't re-run addWorkflowGroupOutput (the one-shot single-output add path used by the copilot user_table tool) was calling triggerWorkflowGroupRun({ mode: 'all' }) after appending the output — that re-fired the workflow on every row. Trace a307ed8fd5fe2d931aa84dedab5a60f0 shows ~75 workflow-group-cell jobs enqueued in the seconds after a single add_workflow_group_output call. Replace with backfillGroupOutputsFromLogs (overwrite: false), the same flow updateWorkflowGroup uses when receiving newOutputColumns. Reads each row's saved trace spans and writes the new output's value back — no compute beyond a JSONB write per row, no double-billing the user for runs they didn't ask for. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): drop sql.raw quote-escaping in column-name interpolation Six call sites in lib/table/service.ts built JSON-key string literals at runtime via `sql.raw(\`'\${name.replace(/'/g, "''")}'\`)` for use with PostgreSQL's `data->'key'` / `data->>'key'` operators. Practically safe (NAME_PATTERN gates column names to alphanumeric+underscore at insert time) but a smelly pattern that breaks the moment validation loosens. Both `data->` and `data->>` accept a parameterized text value as the key, so the `sql.raw` is unnecessary. Replace each with a normal `${name}::text` binding. No behavior change; eliminates the manual quote-escaping surface. Affected sites: renameColumn (the data-rewrite UPDATE), upsertRow's match filter, updateColumnType's IS-NOT-NULL gate, updateColumnConstraints' required-check + unique-duplicate-check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(copilot-tool): forward autoRun + mappingUpdates on update_workflow_group The sim-side service and contracts already accept both fields, but the copilot tool's update_workflow_group handler was dropping them on the floor. Now `args.autoRun` (toggle the persisted auto-fire flag) and `args.mappingUpdates` (per-output (blockId, path) swap) get forwarded through to updateWorkflowGroup. Pairs with the upcoming copilot-side change that exposes these in the tool catalog JSON / Go handler / prompting (see copilot branch redo-workflow-tools). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(table): keep gutter border visible when hovering Run-row button The per-row Run button sat flush against the row-gutter cell's right border. Its hover background (rounded-rect surface-2) painted over the border line for the 20px height of the button, making the gutter divider appear to disappear at the hovered row. Add mr-px to the button so the hover bg stops 1px short of the cell's right edge, leaving the divider intact. * fix(table): unify auto-fire and manual run paths in scheduler scheduleWorkflowGroupRuns now owns eligibility, autoRun semantics, dep evaluation, and enqueue for both paths. Auto-fire callers omit opts; manual callers (triggerWorkflowGroupRun) pass { groupId, isManualRun: true } to bypass the autoRun=false skip and (for autoRun=false groups) the dep check. Per-row /run-workflow-group route delegates to triggerWorkflowGroupRun with rowIds=[rowId]. Single server-side path for both manual entry points. Also: optimisticallyScheduleNewlyEligibleGroups skips autoRun=false groups so editing a row's data doesn't phantom-mark autoRun=false output cells as Queued. * fix(table): render empty cells as blank, not em-dash Empty cells (any column type) showed an em-dash placeholder. Drop it so empty cells render blank — matches what the user expects when nothing's there. * fix(table): per-row Run fires autoRun=false groups regardless of deps handleRunRow filtered out every group whose deps weren't satisfied, which silently dropped autoRun=false groups (since their deps usually aren't satisfied — that's the whole point of autoRun=false). Click Run row, the autoRun=false group's cells stayed empty. Mirror the scheduler's semantics: autoRun=false bypasses the dep check, autoRun=true still requires deps. * fix ui shape * improvement(table): collapse run ops into run_column, derive action-bar buttons from selection The action bar now reflects what's actually selected: - Selection-driven scope (cells the user highlighted, not their full rows) - Play visible when there's anything empty/failed; Refresh when there's anything completed; both for mixed - run_cell / run_row deleted; everything funnels through run_column - Per-row gutter Play, right-click "Run workflows on N rows", and column-header menu all share the canonical run path - Shared RunMode type from the contract; cleanup pass via /simplify (readExecution / isExecInFlight reuse, runScope helper, flat onViewExecution prop) * chore(copilot): regen tool catalog after dropping run_cell / run_row + dependencies.workflowGroups Mirror the copilot-side catalog change so the generated TS catalog matches the deployed copilot tool surface. * fix(table): atomic per-key writes for executions, plus run-op race fixes The executions blob on user_table_rows was read-modify-written wholesale on every update. Concurrent writers (a column edit and a manual-retry stamp, two pickup calls, a cancel and a cascade) each computed a merge from their own snapshot, and the last writer clobbered keys it never touched — producing stuck "queued" cells, vanished stamps, and stale completed exec records reappearing after retries. Fixes: - updateRow / batchUpdateRows now apply executionsPatch via a SQL jsonb merge expression. Each writer only mutates the keys it explicitly patches; other keys are preserved. Eliminates the cross-key clobber. - writeWorkflowGroupState bypasses the stale-worker guard for `queued` (new scheduler stamp) and `cancelled` (authoritative cancel) writes — those ARE the new authority for the cell. Previously the new run's stamp was being rejected by the same guard meant to block the OLD worker's writes. - skipScheduler flag on UpdateRowData / BatchUpdateByIdData lets the cancel path and runWorkflowGroupsInternal opt out of the implicit auto-fire pass (cancel was waking up siblings; manual-run was racing its own scheduler). - CELL_CONTENT pinned to h-[22px] so status badges don't grow rows. * chore(table): remove table-row sockets, both sides Tables don't use realtime sockets in prod — strip the dead path so we stop paying the per-row HTTP forward + socket emit on every cell write. Polling on running execs already covers reconciliation. Sim side: - service.ts: drop notifyTableRowUpdated/Deleted, notifyTableDeleted, the postRealtimeBridge helper, and all callsites. - hooks/queries/tables.ts: drop the socket subscription block in useTableRows; poll-on-running stays. Remove useEffect / useSocket imports. - app/.../tables/[tableId]/hooks/use-table.ts: drop the merge-on-event useEffect and unused imports. - app/workspace/providers/socket-provider.tsx: drop joinTable/leaveTable, onTableRowUpdated/Deleted/onTableDeleted, currentTableId state, related events + types. Realtime side: - handlers/tables.ts deleted; index.ts no longer wires it. - routes/http.ts: drop /api/table-row-updated, /api/table-row-deleted, /api/table-deleted endpoints. - rooms/{memory,redis}-manager.ts: drop emitToTable, handleTableRowUpdated/ Deleted, handleTableDeleted, related imports. - rooms/types.ts: drop method declarations, TableRowUpdatedPayload type, tableRoomName helper. - middleware/permissions.ts: drop unused verifyTableAccess. Bonus from parallel work: - cell-content typewriter trigger refinement. * fix(table): clearing a workflow output cell also clears its exec record When the user wipes a workflow output column value, the auto-fire reactor needs to be re-armed for that group. Previously, a stale cancelled / error exec record blocked the eligibility predicate (gate at line 79 hard-rejects those statuses on auto-fire) and the cell stayed stuck in its old terminal state — visible as "Cancelled" cells that wouldn't re-run no matter what. Both updateRow and batchUpdateRows now derive an `executionsPatch[gid] = null` for any output column the patch sets to empty. The data clear and the exec clear ride the same SQL transaction, so the row never lands in a stale- status-with-empty-data state. Symmetric to how `completed` already worked via `areOutputsFilled` in the predicate — clearing the cell wins over the prior exec status, regardless of what that status was. (Also revert typewriter-trigger experiment from a parallel session that was in-progress on this branch.) * fix(table): waiting state, optimistic UX, schema-mutation polling, exec cleanup A bundle of small UX + correctness fixes around workflow-cell run state. cell-render.tsx - In-flight (queued/running/pending) now wins over the existing value, so re-runs surface immediately instead of looking like nothing happened until the worker writes the new value. - "Waiting on X" wins over a stale `cancelled` / `error` exec when deps are unmet — clearing a dep now reads as actionable instead of stuck. useRunColumn (hooks/queries/tables.ts) - onSettled now cancels in-flight polls before invalidating. Stops a poll that landed mid-mutation from clobbering the optimistic state with stale data, which produced the queued → cancelled → queued flicker. addWorkflowGroup / updateWorkflowGroup (autoRun toggle on) - Awaits scheduleRunsForTable instead of fire-and-forget. The route returned before the queued exec stamps committed, so the post-mutation refetch saw no in-flight cells and polling never started — cells looked stuck even though the server eventually stamped them. deleteColumn / deleteColumns - Strip orphaned executions[gid] keys when deleting a column orphans its parent group. Without this, stale running/queued exec records lingered on every row forever and inflated the page-header "N running" counter even on tables with no actually-running cells. UI - Action-bar leading label: "Selected N workflow cell(s)". - Context menu: Run / Refresh items mirror the action bar's Play / Refresh split, gated on the same selection-status flags so both surfaces show the actions that match the current state. * refactor(table): consolidate exec-status helpers + fix N-running counter Cleanup pass on the recent table changes — pulls duplicated predicates and SQL snippets into shared helpers and fixes one drift bug along the way. - isExecInFlight: now single export from lib/table/deps.ts. Removed the duplicate in components/table-grid/utils.ts. Used by isGroupEligible (server eligibility) and runningByRowId (client counter). - isOptimisticInFlight: kept local to hooks/queries/tables.ts — renamed from isInFlight to disambiguate from the stricter isExecInFlight. The two predicates differ on `pending` without a jobId: optimistic patches and poll-trigger want the broader version, eligibility wants the strict one. - areOutputsFilled: single export from lib/table/deps.ts, dropped duplicate from workflow-columns.ts. - classifyExecStatusMix: shared row × group walker in table-grid/utils.ts. Replaces two copies of the same loop in table-grid.tsx (selectionStats + contextMenuStats). Both surfaces now have the same short-circuit semantics, including the seen-all-selected-rows early break that contextMenuStats was missing. - stripGroupExecutions: SQL helper in service.ts. Replaces three copies of the `UPDATE user_table_rows SET executions = executions - $gid::text` pattern across deleteColumn / deleteColumns / deleteWorkflowGroup. Drift bug: - runningByRowId / totalRunning counted only `running` and `queued`. Every other in-flight check in the codebase treats post-stamp `pending` as in-flight too, so the page-header "N running" badge briefly dropped to 0 between scheduler stamp and worker pickup. Now uses isExecInFlight. * fix(table): address pr review (drop dead workflowNameById prop, reset didDragRef on dragend, align sidebar width) * fix(table): scope post-clear schedule to targeted groups, forward mode Multi-group manual runs (Run row, gutter Play, action-bar Play across mixed completed + cancelled cells) re-fired completed-and-filled siblings. runWorkflowGroupsInternal cleared only the groups it filtered, then called scheduleRunsForRows with isManualRun: true and no group / mode filter — so the post-clear pass walked every group on the table with default mode 'all', and any autoRun=true completed sibling whose deps were satisfied got queued again. Scope the post-clear call to targetGroups and forward mode. * fix(table): meta-cell drag-leave flicker guard + plumb unique on create * fix(table): strip sibling deps when removing workflow output via updateWorkflowGroup deleteWorkflowGroup already stripped removed-column deps from sibling groups, but updateWorkflowGroup (the path the UI takes when deleting one output of a multi-output group) didn't — schema validation then rejected the update with 'Group X depends on missing column Y'. * improvement(table): debug logs at every cascade decision branch * improvement(table): parallelize queued-stamp writes within concurrency-cap chunks * Simplify stripping column names * fix lint, ci --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ec5793fbaf |
fix(billing): drop transaction wrapper in recordUsage to relieve pool contention (#4494)
* fix(billing): drop transaction wrapper in recordUsage to relieve pool contention * fix(billing): always warn on userStats drift in recordUsage |
||
|
|
49713f887f | fix(type-error): subblock migrations type error (#4492) | ||
|
|
09425553b3 |
fix(sanitize): repair malformed subblock states (#4490)
* fix(sanitize): repair malformed subblock states * address comments * address comment |
||
|
|
76d602fed5 |
fix(workday): correct SOAP service routing and reference types (#4485)
* fix(workday): correct SOAP service routing and reference types - create-prehire: route Put_Applicant to Recruiting service (was Staffing, where the operation does not exist) - assign-onboarding: use WID for Action_Event_Reference (was Background_Check_ID, wrong identifier domain for hire events) - update-worker block: rewrite labels and wand prompt to match Change_Personal_Information demographic-only scope (prior prompt instructed LLM to emit businessTitle/primaryWorkEmail which the SOAP op rejects) - enrich opaque JSON output descriptions on worker, workers, organizations, compensationPlans * fix(workday): correct Date_of_Birth casing in update wand prompt |
||
|
|
28e60bfd4e |
fix(docker): drop scripts/ from workspaces array (#4484)
`turbo prune sim --docker` strips `scripts/` from the pruned output (sim doesn't depend on it), but the pruned root package.json still listed it as a workspace, causing `bun install` to fail with "Workspace not found 'scripts'" in the Docker build. scripts/ is dev-only tooling that runs from the repo root via `bun run scripts/*.ts`. Its imports (glob, yaml) resolve against the root node_modules — they're already in root devDependencies. - Remove "scripts" from root workspaces array - Delete scripts/package.json (no longer a workspace, manifest unused) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
28f1912e89 |
feat(files): zoom controls for inline mermaid and images in markdown (#4411)
- Add pan/zoom/fit controls to mermaid diagrams rendered inline in markdown — same experience as the standalone .mmd viewer - Wrap inline markdown images in ZoomablePreview with fit-to-container scale - Allow fit zoom to upscale small diagrams to fill the view (previously capped at 100%) |