v0.7.66: provenance logs, nuqs consolidation - #6487
Conversation
waleedlatif1
commented
Aug 10, 2026
- improvement(emails): funnel every sender through the shared render and subject layer (improvement(emails): funnel every sender through the shared render and subject layer #6482)
- fix(auth): stop offering account creation when registration is disabled (fix(auth): stop offering account creation when registration is disabled #6484)
- fix(provenance): report why a projection was refused, at the point of refusal (fix(provenance): report why a projection was refused, at the point of refusal #6483)
- chore(deps): drop the archived image-size dependency (chore(deps): drop the archived image-size dependency #6485)
- improvement(url-state): use nuqs setters and derive state instead of mirroring it (improvement(url-state): use nuqs setters and derive state instead of mirroring it #6486)
…d subject layer (#6482) * improvement(emails): funnel every sender through the shared render and subject layer * fix(emails): cover dynamic imports in the boundary guard and mock the new subject helper * fix(emails): mock the module the limit-notification sender actually imports
…ed (#6484) DISABLE_REGISTRATION blocks /signup server-side, but the invite flow, the login form, the SSO form, and the CLI handoff all kept routing people there, stranding invited users on a dead end. The flag also never covered OAuth account creation, so social sign-in still minted accounts for unknown identities.
… refusal (#6483) * fix(provenance): report why a projection was refused, at the point of refusal A refusal fails closed and reaches the user as one fixed sentence. The guard that caused it may have tripped many frames — or a whole process — earlier, and the incompleteness latch is one-way, so by then the causing call has long returned. #6478 recorded the reason when the guard tripped, but marking early-returns once a registry is already incomplete, so a run that inherits an incomplete registry refused with nothing recorded anywhere. That is the case production actually hits. Retain reasons on the registry and report them where the refusal happens. The reason is recorded before the already-incomplete return so a causal chain accumulates, and before the silence checks so a by-design origin that logs nothing when marked is still nameable at refusal. Propagation inherits through markIncomplete's source argument, and copying incomplete input paths carries their reasons, so a fork cannot latch without its cause. Route all 68 refusal sites through one choke point that logs boundary, cause, input path and workspace before throwing. It returns never, so callers still narrow; messages and thrown types are unchanged at every site. Records carry a cause discriminator, since a latched registry and a caller-side cross-check of the projection's own output both arrive here and only the former has reasons. No behaviour change. * fix(provenance): stop registry-less refusals deduplicating across requests A refusal with no registry aborts the request rather than iterating, so it reaches the reporter at most once per request. Remembering it for the process silenced every later request, including the one being investigated. * improvement(provenance): inherit copied-path reasons once per copy Semantics are unchanged — reasons are still inherited only when at least one incomplete path was actually copied — but the source set is walked once rather than once per copied path.
* chore(deps): drop the archived image-size dependency * improvement(content): cover GIF and warn when OG dimensions are unreadable * improvement(content): read OG dimensions via sharp instead of a bespoke parser
…mirroring it (#6486) * improvement(url-state): use nuqs setters and derive state instead of mirroring it Wave 1 of a URL-state audit sweep. - files: replace the last hand-built same-path query mutation with the nuqs group setter, which no longer drops shareFileId/search/type/size/uploaded-by/sort/dir - suspense: give six page entries their co-located loading.tsx skeleton instead of fallback={null} - invite: derive isNewUser/urlError/token during render so the invitation query key is correct on first commit - resume: derive selectedStatus/queuePosition from the query cache the mutation already writes - verify, logs, terminal: delete dead and duplicate state - rules: document same-path router.replace as a query mutation, and the loading.tsx-as-Suspense-fallback convention * fix(invite): wait for the stored token before enabling the invitation query An authenticated user opening an invite without a token in the URL fired the query with a null token before the effect restored the session-stored one, producing a transient forbidden state and a redundant request under a second cache key. Distinguish 'storage not yet read' (undefined) from 'read and empty' (null) and gate the query on that.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview Auth redirect logic is centralized in Resolved-secret projection failures now go through Email sends funnel through URL/nuqs rules tighten query mutation (use nuqs setters, no Reviewed by Cursor Bugbot for commit 5bfcc67. Configure here. |
Greptile SummaryThe PR consolidates URL state around nuqs, enforces registration-disabled behavior across authentication entry points, centralizes email rendering and subjects, and adds provenance-aware projection-refusal reporting.
Confidence Score: 5/5The PR appears safe to merge because no concrete changed-code failure remains after tracing the affected authentication, projection, email, and state-management paths. Unsafe provenance projections terminate before reaching response sinks, registration-disabled flows retain valid sign-in destinations, and resume state remains synchronized through explicit query-cache updates.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/auth/auth.ts | Extends the registration gate across applicable account-creation paths while preserving existing-account and SSO behavior. |
| apps/sim/app/invite/[id]/invite.tsx | Derives invitation URL signals during render and adapts signed-out actions to the registration configuration. |
| apps/sim/components/emails/render.ts | Adds shared rendering for agent inbox responses and a final unsafe-URL neutralization pass. |
| apps/sim/executor/utils/resolved-secret-projection-refusal.ts | Centralizes projection-refusal diagnostics and guarantees termination by throwing. |
| apps/sim/app/api/mcp/serve/[serverId]/route.ts | Replaces generic projection failures with logged, non-returning refusal calls without exposing rejected content. |
| apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx | Derives selected pause-point state from query data while synchronously updating the relevant caches after resume. |
| apps/sim/tools/request-transport.ts | Adds stricter model-input projection verification and refuses requests whose projected shape cannot be validated. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
User[User or external caller] --> Auth[Authentication and invitation flows]
User --> URL[Workspace URL state]
Workflow[Workflow execution] --> Projection[Resolved-secret projection]
Projection -->|safe| Output[Projected model or MCP output]
Projection -->|unsafe| Refusal[Log refusal and throw]
Agent[Agent or system sender] --> Render[Shared email render layer]
Render --> Subject[Shared subject helpers]
Subject --> Mailer[Email delivery]
Reviews (1): Last reviewed commit: "improvement(url-state): use nuqs setters..." | Re-trigger Greptile
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5bfcc67. Configure here.
…6488) Two gaps left by #6486. The query was gated on isTokenResolved but the loading state was not. With enabled: false React Query still reports success when the key already holds data, so a cached null-token entry made isPending false and rendered the accept UI for one frame before the effect applied the stored token. Reachable only on a client-side remount after a tokenless fetch already succeeded. An empty ?token= also stopped falling back to storage: searchParams.get returns '' which is not null, so token became '' where the pre-#6486 truthiness check had read sessionStorage. Normalize to null at the source.
