Skip to content

feat: add ownership schema foundation - #76

Merged
Aliferous3 merged 2 commits into
mainfrom
feat/ownership-schema-foundation
Oct 18, 2026
Merged

Aliferous3 merged 2 commits into
mainfrom
feat/ownership-schema-foundation

Conversation

@Aliferous3

Copy link
Copy Markdown
Owner

Summary

Phase 1B of the Code Ownership Over Time tranche (contract: docs/ownership-data-privacy-spec.md, RFC: docs/code-ownership-over-time-rfc.md, spike: docs/ownership-graphql-spike-results.md). Adds the production database foundation only:

  • migrations/012_ownership.sql — four tables (ownership_scopes, ownership_contributors, ownership_snapshots, ownership_sync_state) with tenant-qualified composite FKs, row-shape CHECKs, closed phase/error taxonomies, calculation-version coexistence, RLS (zero browser-facing policies), and service-role-only grants.
  • Dormant dash_ownership_* SECURITY INVOKER RPCs (scopes summary / head contributors / monthly timeline), p_user-scoped, serving only active_version rows — never internal ids, github_node_id, contributor_key, SHAs, or cursors.
  • Backup classification: ownership history tables are CORE (irreplaceable for retained/disconnected repos); ownership_sync_state is OPERATIONAL.
  • scripts/recovery-drill.mjs now seeds synthetic ownership fixtures and proves tenant-FK rejection, both cascade paths, row-shape/identity/period/version checks, and the full build → promote → coexist → flip serve-gate on real Postgres 17.
  • New test/ownership-schema.test.mjs structural guards + backup-split assertions in test/data-minimization.test.mjs.

Scope

Ownership ingestion remains disabled. No GitHub ownership API calls, no blame/history processing, no backfill, no sync/cron/webhook/worker wiring, no UI, no feature flag, no api/dashboard.mjs changes, no lib/sync.mjs changes, no SecurityPrivacyPage.tsx changes (the public trust record only changes when ownership actually collects). Production will hold empty ownership tables.

Security / privacy impact

  • Security/privacy behavior changed and is explained below

New empty tables with the contract's strictest posture: tenant pairing enforced by composite FKs ((repository_id, user_id) → repositories(id, user_id), surrogate unique added — the only existing-table change); RLS enabled with zero policies; public/anon/authenticated fully revoked; only service_role holds table and RPC privileges. Linked identity is github_node_id + display_login — no databaseId, email, avatar, or author-name columns can exist (CHECKs + test guards). unlinked/aggregate synthetic rows are shape-pinned and can never carry identity. error is a CHECK-constrained closed taxonomy — no provider text.

Database / deployment impact

  • Migration included
  • Deployment order matters

Migration 012 is additive and idempotent (no drops, no rewrites; only new unique index repositories(id, user_id) on an existing table). Per the recovery runbook, a fresh npm run db:backup is mandatory before applying it to production. This PR validates it only via local/CI disposable Postgres.

Verification

  • npm run build
  • npm run typecheck
  • npm test — 527 tests, 0 failures (new ownership suite + updated backup/data-minimization guards)
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • npm run db:drill — PASS on local Postgres 17: backup→migrate→restore round-trip, RLS/grants/SECURITY INVOKER checks, plus ~30 real-Postgres ownership proofs (tenant mismatch rejection, cascades, version serve-gate)

Contributor agreement

Generated with Devin

Phase 1B of the Code Ownership Over Time contract
(docs/ownership-data-privacy-spec.md): additive migration 012 creates the
four ownership tables with database-enforced tenant/repository pairing,
row-shape checks, closed error taxonomy, RLS with zero browser-facing
policies, service-role-only grants, and dormant SECURITY INVOKER
dash_ownership_* RPCs gated on active_version.

No ingestion exists in this tranche — the tables hold zero production
rows. Backup scope classifies ownership history as CORE and sync state as
OPERATIONAL; the recovery drill seeds synthetic fixtures and proves
tenant-FK rejection, cascades, the version serve-gate
(build/promote/coexist/flip), and RPC output safety against real
Postgres 17.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@vercel

vercel Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
dev-ledger Ready Ready Preview Oct 18, 2026 1:57pm UTC

- month boundary check now evaluates on the UTC wall clock
  (AT TIME ZONE 'UTC' both ways) instead of following session TimeZone;
  drill proves Dubai-local midnight is rejected while the true UTC
  boundary is accepted
- linked contributors can no longer squat the reserved synthetic keys
  'unlinked'/'other' (ownership_contributors_linked)
- mustFail now asserts the intended constraint name + SQLSTATE so
  malformed inserts cannot masquerade as passing invariant proofs
- composite-FK proofs are isolated: scope-FK test uses a scope-total row
  on a fresh period; contributor-FK test uses a valid repo-B scope
- user-delete cascade proven on a dedicated tenant whose repo and all
  four ownership tables hold live rows when the user row is deleted

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@Aliferous3
Aliferous3 merged commit d908791 into main Oct 18, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
Preview — 014dc986 Deployed Oct 18, 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.

1 participant