Skip to content

feat(db): vendor the pg-boss schema into a committed migration - #104

Merged
JOY (JOY) merged 1 commit into
mainfrom
feat/vendor-pgboss-schema
Oct 17, 2026
Merged

JOY (JOY) merged 1 commit into
mainfrom
feat/vendor-pgboss-schema

Conversation

@JOY

@JOY JOY (JOY) commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #25

External-tenant gate T6: committed SQL is the only schema, crove_jobs included. Until now the migrate role started pg-boss with migrate: true and let it create its schema at runtime.

What changes

  • pnpm db:vendor:pgboss (apps/kernel/src/vendor-pgboss.ts, with the rendering in jobs-schema.ts). When the pinned pg-boss expects a schema version that no migration vendors yet, it writes supabase/migrations/<now>_crove_jobs_pgboss_v<N>.sql. The SQL comes from pg-boss's own getConstructionPlans the first time, or from getMigrationPlans starting at the vendored version for upgrades. The renderer changes pg-boss's SQL in these ways, and checks each change:
    • No BEGIN/COMMIT/advisory lock: the runner applies each file in one transaction under its own lock.
    • Every function gets SET search_path = pg_catalog, pg_temp (the AGENTS.md rule).
    • A version-guarded DO block:
      • an existing install at the same version, with exactly one version row, keeps its tables (databases where pg-boss created the schema before this PR);
      • any other state stops the migration with a clear error;
      • re-applying the file is a no-op.
    • After the guard, every crove_jobs function whose search_path differs gets the pin, so pre-Vendor the pg-boss schema into committed migrations #25 installs end up like fresh ones.
    • Upgrade migrations refuse a queue.table_name that pg-boss would not create, because pg-boss's upgrade plans put that column into SQL.
  • 20261019034500_crove_jobs_pgboss_v46.sql, generated from pg-boss 12.38.0, schema 46. It is renamed on each rebase so it sorts after every file on main (the Docs check's migration-order step enforces this); it now follows feat(social): posts, per-channel variants, review decisions and queues (S3) #297's 20261019000000. It also:
  • No migrate: true anywhere. Nothing starts pg-boss with migrate: true. The worker keeps migrate: false, createSchema: false, so pg-boss refuses to start on any other schema version. migrateJobs stays as an exported no-op only because main's invariant suite (sessions.db.test.ts) and a few ordinary tests still call it. Removing it is Remove the migrateJobs no-op once #104 is on main #130.
  • /readyz answers 503 jobs_schema until the database version equals the version the installed pg-boss expects (read from its package.json, pgboss.schema).
  • JOB_SCHEMA, QUEUES and the queue policies (feat(kernel): positions, agents, tasks and wake-ups (ADR-0029 Phase 1, T18) #245's QUEUE_OPTIONS, as queueOptions()) move to jobs-schema.ts. A new queue or policy needs a new migration; the database test fails until it has one.
  • packages/db/src/migrate.ts: the comment on feat(db): guard pinned search_path on crove_* functions and revoke TEMP from Crove roles #97's crove_jobs exemption is updated. The check that no other role may CREATE in crove_jobs now skips roles that act as the runner (members of it, like the invariant suite's _proxy), as the TEMPORARY check already does. Dropping the exemption is proposed on Widen the definer hardening guard (follow-ups from #97) #100.
  • Docs: plan T6, principle 4, the migrate role and deploy steps, the state paragraph, the Vietnamese page, and the new command in AGENTS.md.

Tests

  • Unit (jobs-schema.test.ts):
    • the vendored version equals the pinned pg-boss schema version (the CI check that fails after a pg-boss upgrade until the script has run);
    • the renderer strips the wrapper and pins every function;
    • every queue is created with the policy the worker expects;
    • an upgrade renders with the exact-version and table_name guards.
  • Database (jobs.db.test.ts, PostgreSQL 17):
    • a fresh install has the expected version and queues, with their policies;
    • every function is pinned;
    • there is no PUBLIC or crove_app EXECUTE and no CREATE for the worker, and crove_app reads only the version;
    • pg-boss as crove_worker with migrate: false starts and round-trips a job;
    • the worker cannot insert, update or delete the version row, but can stamp the maintenance columns;
    • a schema pg-boss created itself is left in place and gets its functions pinned;
    • another version, or two version rows, stop the migration.
  • kernel.db.test.ts, inbound-rotation.db.test.ts, prober.db.test.ts, feat(kernel): per-deployment module enablement #198's module-enablement.db.test.ts and the new blob-sweep.db.test.ts run on the vendored schema (QUEUES now imported from jobs-schema.ts). feat(kernel): retention purges and org offboarding with export #211's retention and offboarding tests and feat(kernel): positions, agents, tasks and wake-ups (ADR-0029 Phase 1, T18) #245's agent tests start the worker on it too.
  • Local runs, rebased on main at docs: pick work by the 2026-10-17 backlog milestones #334 (539974d):
    • pnpm lint, pnpm typecheck, pnpm docs:check and tools/ci/migration-order.mjs pass;
    • unit tests: 694 passed;
    • database tests: 628 passed, 8 skipped;
    • invariant suite: 501/501 on postgres:17.
  • Session security review: approve with nits; the hardening items are applied (comment below). The rebases add no new privilege: the agent queues are rows in crove_jobs.queue that the migration creates.

Notes

  • Upgrading pg-boss: bump the catalog, run pnpm db:vendor:pgboss, then review and commit the new migration. pg-boss's exported migration plans build indexes inline, so a large crove_jobs upgrade belongs in the promote window (plan §4).
  • The PR touches protected paths: a migration, http.ts, migrate.ts and AGENTS.md. The invariant job runs main's suite against this code, which is why the migrateJobs no-op exists.

🤖 Generated with Claude Code

https://claude.ai/code/session_013LrvuThKGaPX1uDwUiwmGQ

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
crove Ready Ready Preview Oct 17, 2026 4:06am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 32 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c3287d45-7f12-40c1-b022-0ae6e2ad4ec7
📥 Commits

Reviewing files that changed from the base of the PR and between 29a4c81 and 77d4d29.

📒 Files selected for processing: 19
  • AGENTS.md
  • apps/kernel/package.json
  • apps/kernel/src/http.ts
  • apps/kernel/src/jobs-schema.ts
  • apps/kernel/src/main.ts
  • apps/kernel/src/vendor-pgboss.ts
  • apps/kernel/src/worker.ts
  • apps/kernel/test/blob-sweep.db.test.ts
  • apps/kernel/test/inbound-rotation.db.test.ts
  • apps/kernel/test/jobs-schema.test.ts
  • apps/kernel/test/jobs.db.test.ts
  • apps/kernel/test/kernel.db.test.ts
  • apps/kernel/test/module-enablement.db.test.ts
  • apps/kernel/test/prober.db.test.ts
  • docs/plan/crove-os-kernel.md
  • docs/plan/crove-os-kernel.vi.html
  • package.json
  • packages/db/src/migrate.ts
  • supabase/migrations/20261019034500_crove_jobs_pgboss_v46.sql

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5b230e40-28f3-41df-aa66-9397d402951b
📥 Commits

Reviewing files that changed from the base of the PR and between df5f249 and 29a4c81.

📒 Files selected for processing: 16
  • AGENTS.md
  • apps/kernel/package.json
  • apps/kernel/src/http.ts
  • apps/kernel/src/jobs-schema.ts
  • apps/kernel/src/main.ts
  • apps/kernel/src/vendor-pgboss.ts
  • apps/kernel/src/worker.ts
  • apps/kernel/test/inbound-rotation.db.test.ts
  • apps/kernel/test/jobs-schema.test.ts
  • apps/kernel/test/jobs.db.test.ts
  • apps/kernel/test/kernel.db.test.ts
  • docs/plan/crove-os-kernel.md
  • docs/plan/crove-os-kernel.vi.html
  • package.json
  • packages/db/src/migrate.ts
  • supabase/migrations/20261015220100_crove_jobs_pgboss_v46.sql

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The pull request adds a generator and committed migration for the pg-boss schema. Runtime migration setup no longer creates the schema. The /readyz endpoint checks its version, and tests and plan documents are updated for the vendored migration.

Changes: pg-boss schema vendoring

Layer / File(s) Summary
Migration renderer and unit tests
apps/kernel/src/jobs-schema.ts, apps/kernel/test/jobs-schema.test.ts
The new utilities read schema versions, adapt pg-boss SQL plans, apply worker grants, and render guarded migrations. Unit tests check version alignment, plan adaptation, and rendered migration content.
Vendor command and committed migration
apps/kernel/src/vendor-pgboss.ts, apps/kernel/package.json, package.json, AGENTS.md, supabase/migrations/*, apps/kernel/test/jobs.db.test.ts, packages/db/src/migrate.ts
The vendor command compares installed and committed schema versions and writes a migration when needed. The committed migration creates the version 46 jobs schema, queues, functions, and grants. Integration tests check migration behavior and permissions.
Runtime readiness and migration handling
apps/kernel/src/http.ts, apps/kernel/src/main.ts, apps/kernel/src/worker.ts, apps/kernel/test/*db.test.ts, apps/kernel/test/inbound-rotation.db.test.ts, docs/plan/*
/readyz returns 503 when the jobs schema version does not match the pinned pg-boss version. Runtime migration setup no longer calls migrateJobs, and the affected tests no longer install the schema on demand. Plan documents describe the committed migration and readiness check.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant PackageCommand as pnpm db:vendor:pgboss
  participant VendorScript as vendor-pgboss.ts
  participant SchemaUtilities as jobs-schema.ts
  participant MigrationDirectory as Supabase migrations
  PackageCommand->>VendorScript: Run the vendor command
  VendorScript->>SchemaUtilities: Read versions and render migration SQL
  SchemaUtilities-->>VendorScript: Return migration SQL
  VendorScript->>MigrationDirectory: Write timestamped migration
Loading
Merge Risk: ⚪ Minimal · up to 29a4c

The pg-boss schema now comes from a committed, version-guarded migration instead of being created at runtime, and readiness reports jobs_schema until the database version matches. The review found no concrete defect that should block merge.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 10 files. (6 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Issue [#25] has three coding requirements. The committed migration supabase/migrations/20261015220100_crove_jobs_pgboss_v46.sql contains pg-boss schema version 46 and queue setup. `apps/kernel/src/w…
Out of Scope Changes check Passed The changes stay within issue [#25]. The vendor command and renderer generate and validate committed pg-boss migrations. Worker, readiness, migration-role, and documentation changes remove runtime sch…
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: moving the pg-boss schema into a committed database migration.
Full details: Linked Issues check

Explanation

Issue [#25] has three coding requirements. The committed migration supabase/migrations/20261015220100_crove_jobs_pgboss_v46.sql contains pg-boss schema version 46 and queue setup. apps/kernel/src/worker.ts starts PgBoss with migrate: false and createSchema: false; migrateJobs is an empty compatibility shim. The /readyz handler in apps/kernel/src/http.ts compares crove_kernel.jobs_schema_version() with pgBossSchemaVersion() and returns 503 with jobs_schema when they differ. The reported tests cover these behaviors.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue [#25]. The vendor command and renderer generate and validate committed pg-boss migrations. Worker, readiness, migration-role, and documentation changes remove runtime schema creation and document the new process. Database and unit tests verify the migration, permissions, version guards, and worker behavior. The retained migrateJobs no-op supports the existing invariant suite and is directly related to removing runtime migration.

Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 10 files. (6 skipped: 6 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request vendors the pg-boss schema (crove_jobs) into committed migrations, removing the need to run pg-boss with runtime migrations enabled. It introduces a new script pnpm db:vendor:pgboss to generate these migrations, adapts pg-boss plans to pin search_path and drop transaction wrappers, and adds database tests to verify the schema installation and upgrades. The feedback suggests a more robust way to resolve the pg-boss package.json file path directly rather than traversing up from its entry point.

Comment thread apps/kernel/src/jobs-schema.ts Outdated
dos[bot]
dos Bot approved these changes Oct 15, 2026

@dos dos Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⏱️ Code Review completed (15 eligible files · 64,935 chars · 3 model call(s) · 3 PR unit(s))

Whole diff with bounded context at base fba2813 / head b288e73.

✅ CLEAN_PASS: Complete diff reviewed

Context investigation was bounded to the explicitly requested snapshot paths.

dos[bot]
dos Bot approved these changes Oct 15, 2026

@dos dos Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⏱️ Code Review completed (16 eligible files · 67,696 chars · 3 model call(s) · 3 PR unit(s))

Whole diff with bounded context at base 67f19cc / head 1c78d1e.

✅ CLEAN_PASS: Complete diff reviewed

Context investigation was bounded to the explicitly requested snapshot paths.

JOY (JOY) pushed a commit that referenced this pull request Oct 15, 2026
…sion

Review feedback on #104: pg-boss has no exports map, so resolving its
package.json does not depend on the dist/ layout of its entry point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LrvuThKGaPX1uDwUiwmGQ
dos[bot]
dos Bot approved these changes Oct 15, 2026

@dos dos Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⏱️ Code Review completed (16 eligible files · 67,661 chars · 3 model call(s) · 3 PR unit(s))

Whole diff with bounded context at base 67f19cc / head e077359.

✅ CLEAN_PASS: Complete diff reviewed

Context investigation was bounded to the explicitly requested snapshot paths.

Copy link
Copy Markdown
Contributor Author

Security review (session)

The Security review (agent) job is still waiting on ANTHROPIC_API_KEY (#95), so this session ran its own independent review. It covered f509227 against main 7a954de. Fixes are in 8822a98.

Verdict: approve with nits. Nothing was blocking. The two hardening items are fixed here, because the migration has not been applied anywhere yet and could be regenerated in place.

Fixed in 8822a98

  • Functions on pre-Vendor the pg-boss schema into committed migrations #25 databases kept an unpinned search_path. Where pg-boss created crove_jobs itself (at v46), the guard leaves the schema alone, so its 6 functions stayed without a pinned search_path, and create_queue then ran unpinned as the migration role. This was no worse than the old migrateJobs. The migration now runs ALTER FUNCTION … SET search_path = pg_catalog, pg_temp on every crove_jobs function whose setting differs. This is idempotent; the migration role created those functions. The "already created" database test now asserts that every function is pinned.
  • crove_worker could write crove_jobs.version. That row decides whether a vendored upgrade runs. A compromised worker could set it ahead, so a future upgrade would be skipped while /readyz reported ready. The worker now has UPDATE only on the maintenance columns pg-boss stamps with migrate: false (cron_on, bam_on, flow_on, reindex_on, monitor_backoff_on), and no INSERT, DELETE or TRUNCATE. The guards also require exactly one version row. Database tests cover the refused writes, the allowed stamps and a two-row schema. On a local compose stack, the worker stamped cron_on with no permission errors.
  • Nit, future-proofing: pg-boss's upgrade plans put queue.table_name into SQL as is, and the worker can write that column. Upgrade migrations now refuse any table_name other than job_common or j<sha224>. Nothing privileged reads it today.

Follow-up

Checked and fine

  • crove_kernel.jobs_schema_version():
    • It is a definer function with a pinned search_path and a schema-qualified body.
    • It is owned by the migration role, not a login role.
    • PUBLIC is revoked and only crove_app gets EXECUTE.
    • The worker has no TRIGGER or CREATE and owns nothing in crove_jobs.
  • crove_app has no USAGE on crove_jobs, PUBLIC has no EXECUTE, and every crove_jobs function is invoker, pinned and worker-only.
  • The DO guard runs inside the runner's one transaction per file, so a mismatch rolls the whole file back. Re-applying it as the non-superuser runner is a no-op (migrate.db.test.ts).
  • The renderer fails loudly on a missed pin, a leftover BEGIN/COMMIT or the $pgboss$ tag. For example, pg-boss's 38 and 40 upgrade plans throw rather than render unpinned.
  • /readyz adds only the reason jobs_schema and exposes no version numbers.
  • Dropping migrate: true also closes an older hole. The admin pg-boss instance used to run BAM, which executes commands from crove_jobs.bam, a table the worker can insert into, with admin rights.

Re-validated locally on 7a954de:


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Rebased onto main at #186 (10b21d7).

Migration renamed again. It is now 20261016201000_crove_jobs_pgboss_v46.sql, after every file on main.

Ported fix for main. main cannot migrate a fresh database right now: #182 and #186 share version 20261016200000, which hits schema_migrations_pkey. This PR does not cause that. The fix is #189. I ported it here as a separate commit (84ac81c) so this PR's db, invariant and e2e jobs can run. It becomes a no-op once #189 lands.

Two adaptations to code that landed on main meanwhile.

  • Prober (feat: health prober and alert webhook #148). Its db test expected crove_jobs to be missing until the worker ran. With the vendored migration, the schema exists right after migrate:
    • the first test now expects jobs_schema_present: true and only the heartbeat finding;
    • a new test hides the schema (ALTER SCHEMA ... RENAME) to keep covering the jobs_schema finding;
    • the JOB_SCHEMA/QUEUES imports move to jobs-schema.ts.
  • Hardening guard (fix(db): widen the definer hardening guard #167, packages/db/src/migrate.ts, protected). The crove_jobs CREATE check now skips roles that act as the runner. That matches how the guard's TEMPORARY check treats roles acting as the owners: such a role already has everything the runner has. Before Vendor the pg-boss schema into committed migrations #25 the check never saw crove_jobs at migrate time.
    • Without this, main's invariant test migrate.db.test.ts failed on its _proxy role (a member of the runner). The vendored migration now creates crove_jobs there, so the runner owns the schema.
    • A role that cannot act as the runner and has CREATE on crove_jobs is still reported; main's invariant case with crove_worker passes.

Local results (postgres:17):


Generated by Claude Code

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@JOY

Copy link
Copy Markdown
Contributor Author

Taking over this PR from its original cloud session (idle since 2026-10-16 17:49 UTC). This session will rebase onto main, keep the migration version above every file on main, re-check the review items and push with --force-with-lease. It will not merge.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Original (cloud) session here. Understood: this PR is yours, and I won't push to feat/vendor-pgboss-schema again.

State as I left it:

  • ce8de18 is green on every check.
  • At 21:20 UTC I test-merged it with main 6ec1082 in a throwaway worktree. It merged cleanly; database tests passed 643 and the invariant suite 507/507. tools/ci/migration-order.mjs was OK: 20261019034500 is above main's newest, 20261019010000.
  • Two main tests (module-enablement.db.test.ts, blob-sweep.db.test.ts) imported QUEUES from worker.ts. They now import it from jobs-schema.ts, the same as the earlier ones.
  • I know of no open review items.

Generated by Claude Code

External-tenant gate T6: committed SQL is the only schema, also for crove_jobs.

- pnpm db:vendor:pgboss (apps/kernel/src/vendor-pgboss.ts) renders pg-boss's
  own construction plans (or, after an upgrade, its migration plans from
  the vendored version) into supabase/migrations. The SQL loses its
  BEGIN/COMMIT and advisory lock (the runner applies each file in one
  transaction under its own lock), every function pins search_path, and a
  version-guarded DO block leaves an existing install at the same version
  alone and stops on any other version.
- The generated 20261016201000_crove_jobs_pgboss_v46.sql also creates the
  three queues, grants the schema only to crove_worker (no PUBLIC EXECUTE)
  and adds crove_kernel.jobs_schema_version() for the API role.
- Nothing runs pg-boss with migrate: true. The worker keeps migrate: false,
  which refuses another schema version. migrateJobs stays as a no-op only
  because main's invariant suite (run from base by protected.yml) still
  calls it; it goes with those calls in a follow-up.
- /readyz answers 503 jobs_schema until the database version equals the one
  the pinned pg-boss expects (read from pg-boss/package.json).
- Tests: a unit test fails CI when pg-boss changes its schema version
  without a vendored migration; database tests cover a fresh install
  (version, queues, pinned functions, privileges, a job round trip as
  crove_worker with migrate: false), an install pg-boss created itself, and
  a wrong version.

Closes #25

Hardening from the session security review:
- A database where pg-boss created crove_jobs at runtime kept its six
  functions without a pinned search_path, because the version guard leaves
  the schema in place. The migration now pins every crove_jobs function
  whose setting differs (idempotent; the migration role created them).
- crove_worker could write crove_jobs.version, which decides whether a
  vendored upgrade runs: setting it ahead would skip an upgrade while
  /readyz reports ready. The worker now has UPDATE only on the maintenance
  columns pg-boss stamps with migrate: false, and the guards require
  exactly one version row.
- Upgrades refuse a queue.table_name pg-boss would not create, since its
  upgrade plans put that column into SQL and the worker may write it.

With the prober (#148) and the widened guard (#167) on main:
- The job schema exists right after migrate, so the prober's db test now
  expects it, and hides the schema to cover the missing-schema finding.
- The guard's crove_jobs CREATE check skips roles that act as the runner,
  like its TEMPORARY check: such a role already has all the runner has.
  Before #25 the check never saw crove_jobs at migrate time.

Claude-Session: https://claude.ai/code/session_013LrvuThKGaPX1uDwUiwmGQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JOY

Copy link
Copy Markdown
Contributor Author

Rebased onto main at #354 (b087ce7), now 77d4d29.

  • Conflicts: apps/kernel/src/main.ts keeps main's new roles (usage-report, pilot-check) and drops the migrateJobs import and call. In docs/plan/crove-os-kernel.md the "State on main" paragraph keeps main's text (Playwright is adopted) and removes the vendored pg-boss schema from "Not adopted yet".
  • Migration: 20261019034500_crove_jobs_pgboss_v46.sql is unchanged. It is still newer than the newest file on main (20261019030000), so tools/ci/migration-order.mjs passes. A fresh render with renderJobsMigration produces exactly the committed file, and pg-boss 12.38.0 (the catalog pin) expects schema 46.
  • Review items: the three fixes from the session security review are still in place (pinned search_path on functions pg-boss created before Vendor the pg-boss schema into committed migrations #25, worker UPDATE limited to the maintenance columns with exactly one version row, and the table_name check in upgrades). Gemini's pg-boss/package.json resolve is in pgBossSchemaVersion(). CodeRabbit found nothing actionable. Remove the migrateJobs no-op once #104 is on main #130 still tracks the migrateJobs no-op.
  • Local results (postgres:17): lint, typecheck and docs:check pass. Unit tests: 734 passed, 38 skipped. Database tests: 655 passed, 8 skipped. pnpm db:types shows no change.
  • CI: all checks are green, including the invariant suite from base, Playwright on compose and the split stack.

Generated by Claude Code

@JOY
JOY (JOY) merged commit 350995c into main Oct 17, 2026
15 checks passed
JOY (JOY) added a commit that referenced this pull request Oct 17, 2026
- social.publish.record writes only a call that has no outcome yet, so a
  reconcile that finds a post whose pending submission already succeeded
  records it published instead of failing on the ledger guard forever.
- The token-refused path reads the connection FOR UPDATE before
  mark_expired updates it, so two jobs of one channel cannot deadlock on
  an upgraded share lock.
- The publish queues are created by the S4 migration, now that the
  pg-boss schema and its queues are committed SQL (#104).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JOY (JOY) added a commit that referenced this pull request Oct 17, 2026
* feat(social): publish each post exactly once (S4)

The publish pipeline of Social plan §3.4 (#234), on the sandbox channel:

- crove_kernel.external_call: the ledger of visible calls. A call is
  written as started and committed before it is made; tenant roles only
  insert it and update its outcome, and a guard trigger refuses any change
  once it has one.
- social.publish.begin and social.publish.record around the one provider
  call; a call already started under the variant's key is made again only
  when the adapter declares the step safe or idempotent, otherwise it is
  reconciled or handed to a person. Never a blind retry.
- A worker sweep every 15 seconds (crove_social.publish_due, worker only)
  queues due, stale, pending and reconnected variants on stately
  social-publish-<provider> queues.
- Rate limits and transient refusals start a new attempt at the retry
  time; an expired token pauses the variant and marks the channel
  expired; reconnecting resumes it.
- social.variant.retry and social.variant.resolve for failed and
  needs_attention variants, with the post page showing each outcome.
- Publisher interface and the in-memory sandbox publisher in
  @crove/channel; shared variant helpers move to variants.ts.

Tests: database tests for each outcome, including a worker that dies
between the call and its result (reconciled, or needs_attention, never
posted twice); the e2e spec now sees the worker publish the post.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(social): close the review's publish gaps

- An ambiguous variant waits a minute before its reconcile in begin too,
  not only in the sweep, so a duplicate job cannot reconcile too early
  and send a person to publish again.
- publish_due filters active orgs and the worker's providers before each
  kind's limit and gives each kind its own share, so rows nobody works on
  never crowd out the rest.
- A paused variant stamps its connection's version (read under a share
  lock, stored in SQL) and resumes when the connection is active and has
  changed; no clocks from two processes are compared.
- A token refused after a reconnect starts a new attempt instead of
  pausing, and mark_expired leaves a changed connection alone.
- begin leaves a variant in a queue paused after the sweep alone; a run
  that stalled after begin records unknown without calling; a post
  processing for a day is reconciled; a disconnected channel says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(social): leave a finished call alone, lock the channel once

- social.publish.record writes only a call that has no outcome yet, so a
  reconcile that finds a post whose pending submission already succeeded
  records it published instead of failing on the ledger guard forever.
- The token-refused path reads the connection FOR UPDATE before
  mark_expired updates it, so two jobs of one channel cannot deadlock on
  an upgraded share lock.
- The publish queues are created by the S4 migration, now that the
  pg-boss schema and its queues are committed SQL (#104).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 77d4d29d Deployed Oct 17, 2026 by vercel[bot]
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.

Vendor the pg-boss schema into committed migrations

1 participant