Skip to content

v0.8.42: knowledgebase queueing improvemnets, file search time fixes, outbox fixes - #7945

Merged
waleedlatif1 merged 15 commits into
mainfrom
staging
Sep 17, 2026
Merged

waleedlatif1 merged 15 commits into
mainfrom
staging

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

icecrasher321 and others added 15 commits September 17, 2026 09:48
…hould You Choose? (#7929)

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* docs(library): update n8n-alternatives

* Pi Babysit: address PR #7930 feedback

* docs(library): update n8n-alternatives

* Pi Babysit: address PR #7930 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* fix(coda): enable inline token setup and clarify search guide

* docs(coda): clarify workspace access scope
… to traditional automation tools like Zapier? (#7928)

Co-authored-by: Sim Pi Agent <pi@sim.ai>
The workflow-level `cancel-in-progress: true` applied to every trigger, but
push and schedule both resolve to `refs/heads/main` and therefore share the
`codeql-refs/heads/main` concurrency group. A merge landing shortly before the
daily cron had its scan cancelled mid-extraction, leaving main with a red
status rollup for a commit the cron then scanned clean.

Scoping the cancel to `pull_request` keeps the superseding behavior where it
belongs -- each PR is its own group via `refs/pull/N/merge` -- and lets non-PR
events queue behind an in-progress run instead of killing it.
…n Platforms: Sim, Zapier, Make, and n8n (#7936)

* feat(library): AI-Native Workflow Automation vs Traditional Automation Platforms: Sim, Zapier, Make, and n8n

* Pi Babysit: address PR #7936 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
Document processing ran through one Trigger.dev queue with a global
concurrency limit and no concurrency key, so a single connector backfill
could hold every slot and leave every other tenant's uploads queued
behind it for hours.

Dispatch now names a lane — interactive for work a person is waiting on,
backfill for connector-driven ingestion — and keys each run by the
entity that owns the knowledge base, so the limit applies per tenant
copy of the queue rather than fleet-wide.

- Key on the owning entity, not the workspace: an organization-scoped
  knowledge base carries a null workspace id and would otherwise share
  one bucket with every other such tenant. Owned scopes reuse
  resourceScopeKey so tenant identity keeps one spelling.
- Stamp the lane on the payload so quota and capacity continuations
  resume in the lane they were admitted against instead of promoting
  themselves out of the backfill ceiling.
- Parse an absent or unrecognized lane as backfill rather than throwing,
  so payloads written before the lanes existed and payloads stamped by a
  newer version mid-rollout cannot burn a run's retry budget.
- Keep interactive work on the pre-existing queue name; a queue the
  running worker has not registered parks its runs in PENDING_VERSION,
  and the app deploys separately from the worker.
- Stop absorbing a rejected backfill chunk in-process: those documents
  are reported failed and reclaimed by the stuck-document sweep instead
  of outlasting the connector lease they run under.
* improvement(provenance): enforce tracked durable reads

* fix(provenance): mark legacy attachment replay fixtures
* fix(search): resume Google crawls around unavailable accounts

* fix(search): harden crawl completion and recovery

* improvement(search): generalize connector partition progress

* fix(search): clear unverifiable placeholder permissions
…nts (#7935)

* feat(atlassian): resolve dynamic scopes and index Confluence attachments

* fix(atlassian): reject mixed All scopes and run permission tests in CI

* fix(ci): isolate Confluence migration test database
…sion (#7941)

* fix(file-search): store workspace file revisions at millisecond precision

The content revision round-trips through JavaScript Date and JSON, both of
which truncate to milliseconds, while now() stores microseconds. A
sub-millisecond revision stops comparing equal to its own round-trip, so
every SQL equality keyed on it matches zero rows and the file is never
claimed, indexed, or cleaned up.

Truncate at the column default and enforce it with a BEFORE trigger for the
writers a default cannot reach, then retire the rows minted before the
invariant existed in a batched script migration.

* fix(file-search): normalize revisions promoted by metadata-only writes

A chat upload is materialized into a workspace file by setting context
alone, so the revision is never written and the search-index trigger would
key the promoted file to a value nothing can claim. Fire the normalizer on
every update; `UPDATE OF` is evaluated against the statement's target
columns, so this does not demote provenance on a metadata write.

Accept the shared integration database in the repair suite the way the
sibling suites do, and record the new script migration in the registry
order assertions.

* chore(db): drop the 0357 migration ahead of renumbering

* chore(db): renumber the content revision migration to 0358

Staging took 0357 for the outbox autovacuum migration. The SQL is byte for
byte the previously reviewed file; only the number and the references to it
changed.
* fix(knowledge): write external group membership as a diff

Every directory sync deleted a group's entire membership and reinserted
it, whether or not anything had changed. That took the membership table
to roughly 55M lifetime inserts and 55M deletes against ~127k live rows
— about 430x write amplification — holding it near 91% dead tuples
across 2,447 autovacuum cycles, an order of magnitude more churn than
any comparable table. The vacuum load that generates competes for the
same I/O as every other query on the instance.

Membership is now written as a difference: read the group's current
tokens, delete only those the enumeration no longer lists, insert only
those it newly lists. An unchanged group writes nothing.

The group row is locked before that read. The two callers fence on
different leases — the directory lease and the connector sync lease — so
neither excludes the other, and on the directory path the group upsert
commits in a separate transaction, so its row lock is already released.
Without the lock, the removal set is computed against a snapshot a
concurrent pass may have moved past, and a subject that pass inserted
would survive an enumeration that never observed it: membership retained
rather than revoked. Locking also closes the narrower window the blind
delete already had between its delete and its commit.

`created_at` on a member row now means first-observed rather than
last-observed. No reader projects it, and the group's `lastSyncedAt`
remains the freshness signal and is still written every pass.

Also corrects the processing-queue TSDoc, which argued the per-tenant
lane limits were sized rather than inherited and cited a concurrency
figure derived from an unsound measurement.

* test(knowledge): assert the group lock precedes the membership read

Presence of the lock was the only thing asserted, so moving it below the
read would have kept the test green while reopening the stale-snapshot
race the lock exists to close. The assertion is now the relative call
order of the lock and the member-table read.
@vercel

vercel Bot commented Sep 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Sep 17, 2026 9:12pm UTC

Request Review

@waleedlatif1
waleedlatif1 merged commit 03dd370 into main Sep 17, 2026
27 checks passed
@greptile-apps

greptile-apps Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR should not merge until concurrent non-Slack OAuth callers can continue using a valid token or wait long enough for the refresh leader to finish.

Findings

  1. P1 Followers Reject Valid Tokens
  2. P2 Logs Expose Subject Emails

Summary

This release combines several knowledge and search reliability changes with OAuth, outbox, provenance, connector, migration, CI, and documentation updates.

  • Splits document processing into per-tenant interactive and backfill queue lanes and adds resumable connector partition work.
  • Adds Confluence attachment indexing and dynamic all-space selection with parent-based permission resolution.
  • Bounds workspace-file search dispatch, normalizes file revision timestamps, and repairs historical revisions.
  • Reworks outbox event-type discovery and external group membership updates to reduce backlog and database churn.
  • Makes tracked durable provenance reads fail closed while retaining compatibility for untracked legacy records.
  • Refreshes OAuth access tokens before expiry and adds retryable Google service-account exchanges.
  • Updates connector setup UI, CI coverage, documentation, and library content.
  • The OAuth refresh change still has a concurrent-follower failure window, and service-account diagnostics now log impersonated email addresses.
Diagram
sequenceDiagram
  participant A as Caller A
  participant B as Caller B
  participant L as Leader lock
  participant P as OAuth provider
  participant D as Account row
  A->>L: Acquire refresh lock
  L-->>A: Leader
  A->>P: Refresh near-expiry token
  B->>L: Acquire same lock
  L-->>B: Follower
  loop For up to 3 seconds
    B->>D: Poll stored token
    D-->>B: Token valid but inside 5-minute window
    Note over B: Token rejected as expiring
  end
  B-->>B: Return no credential
  P-->>A: Refreshed token
  A->>D: Persist refreshed token
Loading

Reviews (1) · Last reviewed commit: "fix(knowledge): write external group mem..."

Comment on lines 1053 to +1056
if (
row?.accessToken &&
row.accessTokenExpiresAt &&
row.accessTokenExpiresAt > new Date()
!isOAuthAccessTokenExpiring(row.accessTokenExpiresAt, providerId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Followers Reject Valid Tokens

When concurrent callers encounter a token inside the new five-minute refresh window, only one becomes the refresh leader. Other callers stop waiting after three seconds, and this check rejects the stored token even though it remains valid and the leader may still be refreshing it. Those callers therefore receive no credential and fail their provider requests. Extend the non-Slack wait to cover the refresh operation, or allow the valid stored token while a refresh is in progress.

@@ -267,6 +285,7 @@ export async function getServiceAccountToken(
hasSubject: Boolean(impersonateEmail),
scopes: filteredScopes.join(' '),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Logs Expose Subject Emails

The service-account token path now writes the impersonated user's email to info-level logs on every ordinary token mint. This unnecessarily exposes customer identities and increases the amount of sensitive data retained in application logs. Keep the existing hasSubject field, but omit the raw subject as the selector-safe branch already does.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants