Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: simstudioai/sim
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: simstudioai/sim
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2-api-spec
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 20 commits
  • 224 files changed
  • 3 contributors

Commits on Jun 29, 2026

  1. Configuration menu
    Copy the full SHA
    98c8567 View commit details
    Browse the repository at this point in the history

Commits on Jul 30, 2026

  1. Merge origin/staging: align table v2 surface with the v2 endpoint sta…

    …ndard
    
    Conflict resolution + reconciliation of the two v2 table surfaces:
    
    - contracts/v2/tables unified into one module (staging's tables/index.ts
      folded into tables.ts): adds the POST /query contract, drops `position`
      from the public row shape, restricts v2 bulk filters to the typed
      predicate grammar, and slims the rows GET to a plain cursor page
    - table v2 routes (list + query from staging) rewritten to the v2
      envelope standard (v2Data/v2CursorList/v2Error, resolveWorkspaceAccess,
      contract-bound parseRequest); tables-v2-api rollout gate extended
      across the whole v2 tables surface, rendered in the v2 envelope
    - toApiRow now uses namedRowMapper so select cells surface option names
    - Cache-Control: private, no-store baked into all v2 response helpers
    - v1 audit-logs auth: staging's org-scoped lookup + self-hosted
      billing-off path merged into the resolve/render split
    - v2 knowledge/workflows routes updated to staging's evolved service
      signatures (billing attribution, deleteRowsByIds, deploy/rollback)
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    3e8141c View commit details
    Browse the repository at this point in the history
  2. feat(usage): accept X-API-Key on usage-logs list + export

    /api/users/me/usage-logs and /export now use checkHybridAuth — the same
    auth /api/users/me/usage-limits already accepts — so external monitors
    can read summary.bySourceCredits (the source breakdown of usage-limits'
    aggregate currentPeriodCost) instead of estimating Copilot spend by
    subtraction. Workspace-scoped keys are pinned to their own workspace's
    slice of the ledger: the filter defaults to the key's workspace and an
    explicit mismatch 403s. Both endpoints documented in openapi-core.json.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    65dc7e8 View commit details
    Browse the repository at this point in the history
  3. feat(billing): dedicated v2 usage endpoints; keep internal usage rout…

    …es session-only
    
    Replaces the earlier X-API-Key enablement on /api/users/me/usage-logs
    with a dedicated public surface, so the internal Billing-settings
    endpoints can evolve with the UI while external monitors get a stable
    versioned contract:
    
    - GET /api/v2/billing/usage — current-billing-period summary with
      bySourceCredits (the source breakdown external monitors need to watch
      e.g. Copilot consumption without estimating by subtraction), plus
      limitCredits and plan
    - GET /api/v2/billing/usage/logs — cursor-paged credit ledger in the v2
      envelope
    - workspace-scoped keys are pinned to their own workspace's slice;
      personal keys read the account ledger
    
    The public wire is credits-only: usage-logs rows now carry a hasCost
    boolean instead of dollarCost (the Billing UI only needed the >0
    signal), and the rateLimit block is removed from the usage-limits
    response and docs (deploy-modal tab relabeled accordingly).
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    3a3ceb5 View commit details
    Browse the repository at this point in the history
  4. feat(docs): validate OpenAPI specs against the Zod contracts in CI

    The specs in apps/docs are hand-authored because they carry what Zod
    never defines — error envelopes, status codes, prose, examples — so
    they can't be generated; check:openapi validates them instead:
    
    - spec integrity: $refs resolve, operationIds unique, 2xx documented,
      no orphaned component schemas
    - v2 conventions: every /api/v2 operation documents 401 + 429 and every
      4xx/5xx resolves to the canonical { error: { code, message } } envelope
    - contract cross-check: contracts are auto-discovered from
      lib/api/contracts/v2 (each carries its method + path); doc<->contract
      coverage both ways, query/body/response field diffs via z.toJSONSchema
    - examples: documented request/response examples must parse with the
      matching contract's actual Zod schemas
    
    First run caught real drift, fixed here: 16 stale orphaned schemas in
    the core spec, the v2 billing ops referencing v1-shaped error
    components, deploy/rollback examples missing the required nullable
    lifecycle keys, CreateTableBody missing folderId, a legacy-grammar
    delete-rows example, and four knowledge document ops missing their
    required workspaceId query param.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    d8021bf View commit details
    Browse the repository at this point in the history
  5. fix(docs): recursive field diff in check:openapi + the deep drift it …

    …found
    
    A mutation test showed the doc<->contract field diff only compared
    top-level properties, so a typo inside the { data } envelope passed.
    The diff now descends through matching object properties and array
    items (both sides must expose a property set — passthrough contracts
    and prose-only docs end the descent instead of false-positive), with
    the Zod JSON-schema root doubling as the $defs context.
    
    Deep drift it immediately caught, fixed here: select-column config
    (options/multiple) missing from every tables column schema, AddColumnBody
    hand-rolling a third column shape (now composed from ColumnInput, with
    position/workflowGroupId as the per-op extensions the contracts actually
    admit), chunking strategyOptions undocumented, and the deployment
    lifecycle fields (activeDeployment/latestDeploymentAttempt) missing from
    DeploymentState.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 30, 2026
    Configuration menu
    Copy the full SHA
    9ee4099 View commit details
    Browse the repository at this point in the history

Commits on Jul 31, 2026

  1. fix(security): close the triggerType rate-limit bypass on workflow ex…

    …ecute
    
    Caller-supplied triggerType flowed unchecked into preprocessExecution,
    whose checkRateLimit default turns OFF for 'manual'/'chat' — so any
    API-key caller, and any anonymous public-API caller billed to the
    workspace owner, could execute unthrottled by sending
    {"triggerType":"manual"} (async runs also skipped the worker-side check
    via admissionCompleted). External callers may now only send the
    redundant 'api' value; internal JWT callers ('workflow'/'mcp') are
    unaffected.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    f6c9cdb View commit details
    Browse the repository at this point in the history
  2. refactor(execution): extract enqueue/status/cancel into shared libs

    Prepares the v2 execution surface: handleAsyncExecution's queue logic
    moves to lib/workflows/executor/enqueue-execution.ts (slot/claim
    semantics encoded in a discriminated outcome, not HTTP statuses), the
    execution-status read to execution-status.ts, and the order-sensitive
    cancel machinery to lib/execution/cancel-workflow-execution.ts. The v1
    routes re-render identically — their suites pass unmodified.
    
    Also: preprocessExecution gains rateLimitCounter ('sync'|'async') and
    its 429 now carries code RATE_LIMIT_EXCEEDED + retryAfterMs (previously
    indistinguishable from the concurrency 429 and Retry-After was
    discarded); and the duplicate cancel contract in contracts/logs.ts is
    unified on the full 5-value reason enum — its narrower copy made
    requestJson throw a client ZodError when cancelling a paused HITL run.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    51b0cf1 View commit details
    Browse the repository at this point in the history
  3. feat(execution): callable execution service + structured error classi…

    …fier
    
    executeWorkflowService composes the same libs the v1 route holds inline
    (call-chain guard, execution-id claim, LoggingSession, preprocessing,
    deployed-state load + file-field processing, timeout-bound
    executeWorkflowCore, output hydration/compaction) for the deployed-state
    caller class — the seam the v2 execute route and in-process internal
    callers share, making the HTTP endpoint syntactic sugar.
    
    classifyExecutionError stops discarding the block context that
    buildBlockExecutionError already attaches at throw sites: failed runs
    now yield {message, code, blockId, blockName, blockType} with a stable
    append-only code enum (TIMEOUT/CANCELLED/USAGE_LIMIT_EXCEEDED/
    INVALID_INPUT/BLOCK_EXECUTION_FAILED/CHILD_WORKFLOW_FAILED/
    OUTPUT_TOO_LARGE/EXECUTION_FAILED), so callers route on error class
    instead of substring-matching messages — the single place raw errors
    are interpreted.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    4320fed View commit details
    Browse the repository at this point in the history
  4. feat(api): POST /api/v2/workflows/[id]/execute

    Thin route over executeWorkflowService: X-API-Key or anonymous
    public-API auth (sync/stream only for anonymous), strict body with
    body-flag async (no mode headers on v2), SSE passthrough for stream,
    and the execution resource response — executionId always present,
    in-band run failures are status:'failed' with the structured
    {message, code, blockId, blockName, blockType} error, sync timeout is
    status:'failed' + TIMEOUT instead of v1's 408, and a Response block's
    payload stays inside output (authors never control response
    status/headers on this origin). Async debits the async bucket and the
    202 statusUrl points at the v2 executions resource. Adds
    CLIENT_CLOSED_REQUEST/SERVICE_UNAVAILABLE to the v2 error codes.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    9c36400 View commit details
    Browse the repository at this point in the history
  5. feat(api): v2 executions status + cancel with queued backfill

    GET /api/v2/workflows/[id]/executions/[executionId] is the single
    status URL for sync and async runs: before the async worker writes the
    durable log row, status is backfilled from the job queue (deterministic
    job id) as 'queued'/'running' — closing v1's 202-to-pickup 404 window —
    and failed runs carry the structured error object. POST .../cancel
    renders the shared cancellation lib in the v2 envelope with the
    tightened 5-value reason enum. Both authenticate via the shared
    resolveV2WorkflowAccess (X-API-Key, authz masked as 404,
    allowPersonalApiKeys honored).
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    3387ee7 View commit details
    Browse the repository at this point in the history
  6. feat(execution): workflow tool + MCP bridge run in-process

    workflow_executor (workflow-as-agent-tool) short-circuits in executeTool
    through WorkflowBlockHandler — the same invocation boundary canvas child
    workflows use — mirroring the deployed_block_executor precedent. The
    MCP serve bridge calls executeWorkflowService directly instead of
    fetching its own execute endpoint; deployment-version pinning, MCP
    response-size rejection, and the actor override become typed options
    instead of header sniffing. Both callers drop the double admission slot
    and duplicate top-level log row the HTTP hop cost, and failed child
    runs now surface the structured error + child executionId so parents
    and MCP clients can route on error class and hand providers a
    reproducible handle.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    c411f6e View commit details
    Browse the repository at this point in the history
  7. feat(infra): CORS + CSP coverage for the v2 execute path

    /api/v2/workflows/:id/execute gets the same wildcard-origin,
    credential-free CORS policy as v1 (the default credentialed policy
    would block browser API-key calls and open a cookie CSRF surface) with
    X-Sim-Stream-Protocol allowed and no X-Execution-Mode (async is
    body-selected on v2), plus the COEP/COOP/CSP header block.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    63fcfe2 View commit details
    Browse the repository at this point in the history
  8. feat(ui): deploy modal + copilot advertise the v2 execute surface

    All 20 API-tab snippets move to POST /api/v2/workflows/{id}/execute with
    the nested {"input": ...} body, async as the "async": true body flag
    (X-Execution-Mode gone), status polling against the v2 executions
    resource, the third tab renamed Usage and pointed at
    /api/v2/billing/usage, and {data} envelope unwraps in the printed
    responses. Fixes the latent baseUrl derivation
    (endpoint.split('/api/workflows/')) that would have silently built
    garbage URLs under a v2 endpoint, and deletes dead code (exampleCommand
    across 3 sites, getAsyncExampleTitle). Copilot deploy/manage/serializer
    endpoint builders and the api_trigger bestPractices example follow (the
    latter also drops its hardcoded staging host).
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    272de32 View commit details
    Browse the repository at this point in the history
  9. docs(api): document the v2 execution surface

    Adds execute, execution status, and cancel to openapi-v2-workflows.json
    with the structured ExecutionError schema (append-only code enum + block
    attribution) and the ExecutionResource contract, documenting the rules
    that differ from v1: modes are body-selected, a failed run is HTTP 200
    with status 'failed', an executionId always means data (never the error
    envelope), queued status is visible immediately, and Response-block
    payloads stay inside output. Registers the three pages in the generated
    workflows meta.json and bumps the route-count baseline.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    35d306d View commit details
    Browse the repository at this point in the history
  10. feat(api): gate the whole /api/v2 surface behind one flag; UI stays o…

    …n v1
    
    Every v2 route now runs exactly one check immediately after auth —
    v2ApiGateError — and answers 404 when the `v2-api` flag is off, so the
    surface is invisible until it is deliberately rolled out. The gate is
    keyed on userId only: a workspace/org-keyed check would have to read
    membership for a caller-supplied id before authorization runs, and its
    404-vs-403 split would leak cohort membership (the trap the per-domain
    table gate worked around by running late). The two executions routes
    inherit it from the shared access resolver; the tables-specific gate is
    removed so no route checks twice.
    
    `tables-v2-api` stays, now gating only the internal predicate-grammar
    route /api/table/[tableId]/query — note v2 tables routes move to the
    unified flag, so enabling them is a `v2-api` decision now.
    
    Reverts the deploy modal, copilot handlers, and api_trigger example to
    the v1 execute endpoint: v1 works unchanged, and the UI must not
    advertise a surface most users would get a 404 from.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    53ea6a8 View commit details
    Browse the repository at this point in the history
  11. Merge origin/staging into improvement/v2-endpoints

    Two conflicts:
    
    - next.config.ts: staging corrected a comment typo (COEP -> COOP) on the
      block immediately below the one this branch added for the v2 execute CSP.
      Kept both.
    - check-api-validation-contracts.ts: both sides moved the route-count ratchet.
      Set to 1028, the actual count of the merged tree.
    
    Staging also widened two contracts this branch's hand-authored OpenAPI specs
    mirror, so the spec checker failed on the merge result even though both sides
    were individually clean:
    
    - knowledge search gained `searchMode` (vector | hybrid), which the v2 contract
      inherits by reusing the v1 body schema.
    - table columns gained `currencyCode` for the new `currency` column type,
      across the column, column-input, and column-update shapes. The documented
      `type` enum was stale independently of this and now lists all seven types.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    528b52d View commit details
    Browse the repository at this point in the history
  12. fix(executor): restore child-cost aggregation dropped by the staging …

    …merge
    
    Staging's custom-block rewrite deleted `aggregateChildCost` from
    workflow-handler.ts, and git merged that file cleanly — but this branch's
    workflow-tool-runner.ts, added for the v2 execute migration, still imports it.
    A silent semantic conflict: no marker, broken build.
    
    Taking staging's rewrite is correct, so the helper is defined locally in its
    one remaining consumer rather than resurrected in the file staging just
    rewrote. Same four lines over the still-exported `calculateCostSummary`, so a
    failed child workflow keeps billing the hosted-key spend it consumed instead
    of reporting $0.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    TheodoreSpeaks and claude committed Jul 31, 2026
    Configuration menu
    Copy the full SHA
    fbc7b17 View commit details
    Browse the repository at this point in the history

Commits on Aug 1, 2026

  1. refactor(tables): make lib/table/orchestration the single implementat…

    …ion (#6134)
    
    * refactor(orchestration): move the shared error contract out of lib/workflows
    
    OrchestrationErrorCode and statusForOrchestrationError are the contract every
    lib/[resource]/orchestration module returns against, but they lived inside the
    workflows module, so resource-neutral code (lib/folders) already had to import
    from a workflow path. Moved to lib/core/orchestration/types.
    
    Adds a 'locked' class mapping to 423. Both tables and workflows have a lock
    that forbids a mutation, and each caller was translating that to a status
    itself.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * refactor(tables): make lib/table/orchestration the single implementation
    
    Column update was implemented four times — the UI route, v1, v2, and the
    copilot table tool — each calling the same column services but owning its own
    guards, error mapping, and audit. The copies had drifted, and the drift was the
    bug: v2 was missing both guards, only the copilot copy minted stable option
    ids, and only v1/v2 audited.
    
    performUpdateTableColumn, performDeleteTable, and performDeleteTableRow now own
    that logic; all ten call sites reduce to auth, parse, call, render. The guards
    are asserted once in lib/table/orchestration rather than four times against
    four routes.
    
    Behavior this consolidates, previously true on only some paths:
    
    - The typeChanging guard. updateColumnType early-returns on an unchanged type
      and drops any options sent with it, so restating the current type alongside
      new options silently discarded them. v2 had no guard at all and, since its
      contract shares v1's body schema, accepted options and ignored them.
    - The select-unique guard. Each write is its own locked transaction, so a
      rename or type change paired with a constraint write that is going to fail
      commits first and then throws, half-applying the schema change.
    - Stable select-option ids. Cells reference the option id, so an edit that
      re-sends an option by name has to reuse it or every cell holding it is
      orphaned. Only the copilot path did this; normalizeSelectOptionsInput moves to
      lib/table/select-options and now covers every caller. It preserves a supplied
      id, so it is a no-op for the fully-formed options the HTTP contracts accept.
    - required forwarded into the type and options writes, so a conversion
      validates against the constraint the same request is setting.
    - An audit on every successful update. The UI route and the copilot tool
      emitted none.
    - Single-row delete through the row service. v2 did a raw db.delete, skipping
      assertRowDelete and deleteOrderedRow, so a delete-locked table returned 200
      and the row-count bookkeeping never ran.
    - The delete actor handed to deleteTable, which audits only when a row was
      actually archived. v1 and v2 omitted it and audited themselves outside that
      check, emitting TABLE_DELETED for a no-op delete of an archived table.
    
    Failure classes come back as OrchestrationErrorCode; v2 renders them through a
    new v2ErrorForOrchestration, mirroring statusForOrchestrationError on the v1
    and UI surfaces, so a given failure maps to the same status everywhere.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * test(tables): bind the column-update tests to the orchestration function
    
    The base's route tests assert which column service each payload reaches — the
    behavior that now lives in performUpdateTableColumn. They mocked the `@/lib/table`
    barrel; the orchestration module imports the service directly, so they mock that
    too and keep asserting the same thing through the extracted implementation.
    
    The orchestration tests move onto the base's semantics: writes address the
    stable column id, a rename rides inside the write it accompanies rather than
    running first, and the currency guards replace the non-select options guard the
    service now owns.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * chore(copilot): drop the column-type import the delegation made dead
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * refactor(tables): move the audit log out of the table service
    
    `lib/table/service.ts` wrote its own audit rows, so whether an operation was
    audited depended on which function a caller reached for rather than on a user
    having performed it. That is what let v1 and v2 audit a no-op delete, and what
    made `deleteTable`'s optional `actingUserId` double as an audit opt-out flag.
    
    Worse, most sites fell back to `actingUserId ?? createdBy`, so an unattributed
    call was logged against the table's *creator*. The copilot `mv` path passed no
    actor at all: renaming someone else's table recorded them as the renamer.
    
    Audit now lives in the orchestration functions — performDeleteTable,
    performRenameTable, performMoveTableToFolder, performUpdateTableLocks — and
    the services just write. Internal callers (folder cascade, import rollback)
    keep calling the service and are silent by construction rather than by
    remembering to omit an argument.
    
    Two services now return what the audit needs: `deleteTable` reports whether it
    actually archived a row, so a repeat delete logs nothing; `updateTableLocks`
    returns the before/after locks, since only the locked write can observe the
    transition its description names.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * fix(tables): restore audit provenance and conflict status in orchestration
    
    Moving the audits into the orchestration functions dropped three things the
    routes had been carrying, and added one the orchestration now owns twice.
    
    - The v1 and v2 column-update routes passed `request` to `recordAudit`, so
      their audit rows recorded the caller's IP and user-agent. The orchestration
      function had no way to receive it. Every table orchestration function now
      takes an optional `OrchestrationRequestContext` and every HTTP route
      forwards it; the copilot and VFS callers, which have no request, omit it.
    - `classifyTableMutation` matched `TableConflictError` on "already exists"
      appearing in the message and reported it as `validation`, turning the UI
      route's 409 on a duplicate table rename into a 400. It now matches the type,
      the way `performRestoreTable` already did.
    - `captureServerEvent` ran on every delete while the audit was gated on a row
      actually being archived, so a repeat delete of an archived table still
      reported `table_deleted`. Both now hang off the same evidence.
    - The copilot delete path kept its own `captureServerEvent` from when the
      service did not emit one, double-counting every copilot table delete.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01YGzbVDZpe2dEALbu2BUU8a
    
    * fix(tables): say which type a no-op column update restated
    
    A copilot `update_column` payload whose only content was the column's current
    type used to return success with the live schema, while the v1, v2, and UI
    routes rejected the same payload with "No updates specified". Delegating to
    `performUpdateTableColumn` unified them onto the routes' rejection — correct,
    but the message tells the caller its request was empty when it named a type.
    
    The orchestration function now reports the same thing `updateColumnType` reports
    when it loses this race concurrently: the column is already that type, re-issue
    without the type change. An empty payload still reads "No updates specified".
    
    Drops the copilot's `outcome.table ?? tableForUpdate` fallback with it — the
    comment described the no-op that can no longer reach that line, and a success
    always carries a table.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01YGzbVDZpe2dEALbu2BUU8a
    
    * refactor(tables): classify failures by type instead of by message text
    
    The table module decided HTTP statuses by searching error messages for
    phrases. `VALIDATION_MESSAGE_FRAGMENTS` and `ROW_WRITE_ERROR_PATTERNS` held 32
    substrings between them, and fifteen more lists were inlined in routes — 83
    matchers over 17 files, each its own copy of the guesswork and already drifted
    apart. It made message wording load-bearing: `TableRowLimitError`'s own doc
    comment noted that its text had to contain "row limit" for a route to answer
    400, and adding "already exists" to a rename message silently demoted a 409 to
    a 400 (the bug fixed one commit ago, by adding another special case).
    
    Services now throw `OrchestrationError`, which carries the transport-neutral
    `OrchestrationErrorCode` the layers above already speak. Classification is one
    `instanceof` in `orchestrationErrorResponse` (UI + v1) and
    `v2CaughtOrchestrationError` (v2). Every pattern list is gone. Wording is free
    to change; an unclassified error still becomes a generic 500, which is what an
    unexpected fault should be.
    
    `asOrchestrationError` walks the `cause` chain rather than testing the caught
    value directly: drizzle wraps a throw raised inside a transaction callback in a
    `DrizzleQueryError` whose own message is the failed SQL, so a bare `instanceof`
    would drop every failure raised inside `withLockedTable`. That is the same
    reason `rootErrorMessage` had to dig for a root cause before.
    
    Three throws stay bare `Error` deliberately — `Table ID mismatch`, `Workspace
    ID mismatch`, and `Failed to build upsert conflict predicate` are internal
    invariants no consumer classified, and they keep falling through to a 500.
    `Insufficient capacity` was in the pattern list with no producer anywhere in
    the codebase.
    
    Status changes, all deliberate:
    
    - `'forbidden'` joins the code union so the table-row-limit ceiling keeps its
      403; without it this refactor would have flattened it to 400.
    - import-async's table-limit rejection: 400 -> 403, matching the two other
      create routes it had drifted from.
    - Renaming a table to an invalid name: 500 -> 400. `validateTableName`
      messages don't contain "Invalid", so no matcher ever caught them.
    - Restoring a table that isn't archived, or into an archived workspace:
      500 -> 400.
    - A duplicate *column* name stays `validation`/400 rather than becoming a 409
      like a duplicate table name. Both v1 and the orchestration have always
      answered 400 for it; changing a published status is not this refactor's job.
    
    The twelve tests that changed were asserting the substring mechanism itself,
    constructing plain `Error`s with magic strings. They now assert the real
    contract, plus new cases pinning that identical wording carrying no
    classification stays internal and keeps its message off the wire.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01YGzbVDZpe2dEALbu2BUU8a
    
    ---------
    
    Co-authored-by: Claude Opus 5 <[email protected]>
    TheodoreSpeaks and claude authored Aug 1, 2026
    Configuration menu
    Copy the full SHA
    5fea5f7 View commit details
    Browse the repository at this point in the history
  2. feat(api): add v2 endpoints for MCP servers, skills, custom tools, fo…

    …lders, and credentials (#6150)
    
    * feat(api): add v2 endpoints for MCP servers, skills, custom tools, folders, and credentials
    
    * fix(api): correct credential role, skill permission bar, MCP url identity, and custom-tool conflict mapping
    
    * fix(api): align credential mutation gating, provider-outage status, and unique-violation conflicts
    
    * fix(api): close unique-violation, revival, orphan-write, and env-rename gaps
    
    * fix(api): treat every provider-outage code as unavailable on create and update
    
    * fix(credentials): use the shared outage predicate on the session update path
    TheodoreSpeaks authored Aug 1, 2026
    Configuration menu
    Copy the full SHA
    eddd53a View commit details
    Browse the repository at this point in the history
Loading