Skip to content

feat(postgres): multi-project isolation for the shared embedded-PG cluster (task project_id, Approach A)#2007

Merged
gsxdsm merged 1 commit into
Runfusion:feature/postgresfrom
TrinaryCompute:postgres-v057
Jul 12, 2026
Merged

feat(postgres): multi-project isolation for the shared embedded-PG cluster (task project_id, Approach A)#2007
gsxdsm merged 1 commit into
Runfusion:feature/postgresfrom
TrinaryCompute:postgres-v057

Conversation

@TrinaryCompute

Copy link
Copy Markdown

Rebase feature/postgres onto v0.57 + fix embedded-PG task→merge blockers + multi-project isolation

Builds on the embedded-PostgreSQL migration (#1793). Rebases it onto v0.57.0, fixes the blockers that stopped an autonomous task → merge cycle from completing in embedded-PG mode, and closes the multi-project isolation gap. Builds clean (core/engine/dashboard, 0 tsc errors); validated live on a 6-project instance.

Rebase

main was 154 commits ahead of feature/postgres; 43 conflicts resolved (kept the async-Drizzle PG rewrite, ported main's behavior changes into it).

Blocker fixes (one family: cwd vs rootDir)

  • recoverStaleTransitionPendingImpl threw SQLite … not available in backend mode → added the missing backendMode branch.
  • Specified tasks stuck "unplanned" → startup-factory.ts dropped options.rootDir (store rooted at process.cwd()); now if (projectId && !rootDir).
  • Merge never landed → drainMergeQueue() bound git to process.cwd(); now store.getRootDir() ?? config.workingDirectory.

Multi-project isolation

project.tasks had no project_id (SQLite isolated per-file; consolidating to shared PG tables dropped that partition). Added project_id + index, threaded the filter through the claim query / scheduler / merger / dedup / list API / id sequence.

Per-project config isolation + live concurrency hot-reload

  • project.config was a single shared row (CHECK id=1) → shared taskPrefix/caps + a cross-project mega-context on every triage. Added project_id PK + configScope(). Validated: distinct per-project prefixes; triage input 243K → 41–91K tokens.
  • Global concurrency limit hot-reloads (ProjectManager subscribes to concurrency:changed) — PUT /api/global-concurrency applies live, no restart.

Happy to split any of these out if the maintainers prefer smaller PRs.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 99b4430a-17e3-4c60-bbf6-b82a2b7812c0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

gsxdsm added a commit to TrinaryCompute/Fusion that referenced this pull request Jul 12, 2026
…conflicts)

Conflict resolution: base (feature/postgres) wins on every fix both sides
made independently — Blocker 1 transitionPending recovery (base uses the
async-transition-pending.ts helpers), the changed-columns lost-update port
in atomicWriteTaskJson/WithAudit, the async getBranchGroup engine chain,
drainMergeQueue project-root cwd, bypassFailedPreMergeReviewStep (FN-7720
audit contract), pausedReason, thinkingLevel (base also persists it via the
PG chat_sessions column), and the v0.57 CLI restructure.

Fork's multi-project isolation (Approach A) is kept and integrated with the
base's newer code:
- changed-columns fallback upserts now thread layer.projectId
- first-boot SQLite auto-migration scopes its emptiness check to the bound
  project and stamps project_id on migrated tasks/archived_tasks rows
  (strict taskProjectScope would otherwise never surface them)
- tasks.project_id / archived_tasks.project_id added to
  EXPECTED_PROJECT_COLUMNS so existing embedded-PG databases self-heal the
  new partition column on boot

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds multi-project isolation for the shared embedded PostgreSQL setup. The main changes are:

  • Adds project_id partitioning for live tasks and project config rows.
  • Threads project scoping through task reads, search, merge leases, and creation paths.
  • Updates startup migration behavior for legacy SQLite data.
  • Adds live global concurrency reload handling in the engine.

Confidence Score: 4/5

This is close, but the archive isolation gap should be fixed before merging.

  • Live task and config paths are mostly scoped by project now.
  • Archived task listings can still return rows from other projects.
  • The remaining issue needs a schema and query update for the archive storage path.

packages/core/src/postgres/schema/project.ts and the archive table/helpers

Security Review

Archived task rows can still be visible across projects because the canonical archive table remains shared without project ownership.

Important Files Changed

Filename Overview
packages/core/src/postgres/schema/project.ts Adds live task, config, and legacy archived-task partition columns, but the canonical archive storage path remains unpartitioned.
packages/core/src/postgres/startup-factory.ts Binds backend task stores to project IDs and updates legacy migration stamping for project-scoped rows.
packages/core/src/task-store/async-persistence.ts Stamps and filters live task rows with the bound project ID.
packages/core/src/task-store/async-settings.ts Moves bound project config operations to the new project_id key.
packages/core/src/task-store/async-search.ts Adds project scoping to full-text and fallback task search.
packages/core/src/task-store/async-merge-coordination.ts Scopes merge lease acquisition through the owning task's project.

Reviews (3): Last reviewed commit: "feat(postgres): multi-project isolation ..." | Re-trigger Greptile

Comment thread packages/core/src/postgres/startup-factory.ts
Comment thread packages/core/src/task-store/async-settings.ts
Comment thread packages/core/src/task-store/async-allocator.ts
Comment thread packages/core/src/postgres/startup-factory.ts
Comment thread packages/core/src/task-store/async-settings.ts
@gsxdsm gsxdsm changed the title feat(postgres): rebase feature/postgres onto v0.57 + embedded-PG blocker fixes + multi-project isolation feat(postgres): multi-project isolation for the shared embedded-PG cluster (task project_id, Approach A) Jul 12, 2026
…uster (task project_id, Approach A)

Squashed net change of TrinaryCompute/postgres-v057 rebased onto
feature/postgres so the PR is a single linear commit (GitHub rebase-merge
cannot replay the original merge-commit history).

- project_id partition key on project.tasks / project.archived_tasks
  (schema, baseline DDL, EXPECTED_PROJECT_COLUMNS self-heal), stamped on
  every insert/upsert via the layer-bound projectId
- taskProjectScope folded into every scan: readLiveTaskRows (composing
  with the SQL column filter + pagination), counts, merge-lease candidate
  scan, search, allocator
- per-project config rows (project_id-keyed upserts) + test-harness seed
- startup factory binds the AsyncDataLayer to options.projectId; first-boot
  SQLite auto-migration scopes its emptiness check per-project and stamps
  project_id on migrated rows
- ProjectManager: live global-concurrency listener (concurrency:changed)
  applied to the shared semaphore immediately

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/core/src/postgres/schema/project.ts
@gsxdsm gsxdsm merged commit 0e82ddd into Runfusion:feature/postgres Jul 12, 2026
2 checks passed
gsxdsm added a commit that referenced this pull request Jul 12, 2026
…2007 review P1)

archive.archived_tasks is one shared table across every project on the
embedded cluster, and the archived board read it unfiltered — project A's
archived list/count surfaced project B's rows. Now:
- project_id column on archive.archived_tasks (schema + baseline DDL +
  index), stamped by both archive writers from the bound layer projectId
- listArchivedTaskEntriesPage / getArchivedRowCount / listArchivedTasks /
  filterArchived / searchArchivedTasks(+Like/Tsvector) take an optional
  projectId and filter to it; the archived-board impl threads
  layer.projectId; id-keyed get/delete stay unscoped (globally unique ids)
- drift self-heal generalized to schema-qualified entries so existing
  databases gain archive.archived_tasks.project_id on boot
- regression test in store-list.pg.test.ts (gate)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gsxdsm

gsxdsm commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Archive isolation P1 — fixed on feature/postgres (commit 46fea60)

Confirmed: the archived board read the shared archive.archived_tasks cold-storage table unfiltered (the PR's project.archived_tasks.project_id only covers the ID allocator's table). Since this PR merged while the fix was in flight, it landed directly on the base branch:

  • project_id added to archive.archived_tasks (schema + baseline DDL + index), stamped by both archive writers from the bound layer's projectId
  • listArchivedTaskEntriesPage / getArchivedRowCount / listArchivedTasks / filterArchived / searchArchivedTasks (+ the tsvector/LIKE search helpers) take an optional projectId and filter to it; the archived-board impl threads layer.projectId; id-keyed get/delete stay unscoped (ids are globally unique via the distributed allocator)
  • the drift self-heal now supports schema-qualified entries, so existing embedded-PG databases gain the column on boot
  • gate regression test: two projects' archive entries, page/count/membership/search each scoped (store-list.pg.test.ts)

Gates green: PG 23 files / 99 tests, engine-core 294, core typecheck clean.

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