Repository navigation
feat: add ownership ingestion core - #78
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Deterministic, privacy-preserving ingestion core for Code Ownership Over Time — pure modules under lib/ownership/ plus a hard-off gate. No runtime wiring, no persistence, no production callers: production keeps emitting exactly zero ownership rows. - gate.mjs: OWNERSHIP_INGEST_ENABLED literal-true + internal-uuid canary allowlist; everything else disabled by construction - queries.mjs: audited metadata-only GraphQL builders (history(path:), 50-alias blame ceiling, tree entries) with a static field audit that rejects source/identity-creep selections - scopes.mjs: path normalization, repo+dir scope candidates only (never file paths), in-memory vendor/generated/build/submodule exclusions, deterministic <=4-depth / <=500-scope cap - contributors.mjs: repository-scoped sha256 contributor keys on the GraphQL node id, single unlinked bucket, conservative [bot] heuristic - history-reducer.mjs: pure UTC-month aggregation — touch shares, pre-fold human counts, Top-25 + Other folding, durable-identity minimization, numeric(5,4) share stability Dormancy tests evolved: the core may exist but nothing may call it, write to ownership tables, or read the gate env outside gate.mjs. .env.example documents the gate off-by-default; SecurityPrivacyPage unchanged. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Aliferous3
force-pushed
the
feat/ownership-ingestion-core
branch
from
October 18, 2026 16:21
9045264 to
1d622a7
Compare
…uery Review corrections for the ingestion core: - classifyAuthorIdentity: 'unlinked' now means ONLY author.user == null. A present-but-malformed user object (missing/empty/wrong-typed id or login) throws OwnershipIdentityError carrying no identifying values — malformed provider data can never silently become unresolved attribution. The reducer propagates it with no partial result. - scopes.mjs gains a conservative binary-blob classifier (images, A/V, archives, compiled objects, fonts, PDFs, binary documents). Directory candidates now derive ONLY from eligible descendant blobs: a dir of only binary or excluded blobs produces no scope; 'tree' entries alone create nothing; submodules still excluded by entry type. - treeEntriesQuery removed — tree enumeration is measured only through the REST recursive git tree endpoint; the GraphQL route was not part of the spike evidence. - spec §9.1: explicit persistence-handoff blocker for Phase 1C-B — no page-local folding, full-set human counts, no durable overflow identities, atomic cursor+aggregate checkpoints required before any ownership writes. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 1C-A — the deterministic, privacy-preserving ingestion core for Code Ownership Over Time. Pure modules under
lib/ownership/plus a hard-off feature gate. No runtime wiring, no persistence, no production callers — production keeps emitting exactly zero ownership rows. Implements the contract fromdocs/ownership-data-privacy-spec.mdagainst the schema deployed by migration 012.Modules
gate.mjs—OWNERSHIP_INGEST_ENABLEDmust be the literal stringtrueand the repository's internal uuid must appear inOWNERSHIP_CANARY_REPOSITORY_IDS. Every other state (absent/malformed/empty/not-listed) is disabled. No truthiness, wildcards, names, or logins.queries.mjs— production-audited GraphQL builders promoted from the spike:history(path:)(metadata-only:committedDate+author{user{id login}}— no oid, no whole-commit stats), batched blame capped at the measured 50-alias ceiling (built but uncalled), and tree entries (name path type).assertOwnershipQuerySafestatically rejects forbidden fields (text/patch/message/email/name/committer/files/additions/deletions/…) and non-{id login}user selections.scopes.mjs— path normalization (canonical/, traversal/malformed rejection, ≤200 chars), repo + directory scope candidates only (file paths never durable), sha256 scope hashes (constant repo sentinel, dir-hash domain separation, deliberately not rename-stable), in-memory exclusion of vendor/generated/build/dependency classes + submodule gitlinks by entry type, deterministic ≤4-depth / ≤500-dir cap with overflow returned for latercardinality_capmarking.contributors.mjs— opaque repo-scoped sha256contributor_keyderived from repository id + GitHub node id (login renames don't move it; same person ≠ same key across repos), singleunlinkedbucket forauthor.user == null(never pseudonymized),otheraggregate bucket, conservative[bot]-suffix heuristic, human-metric basis =linked ∧ !is_bot.history-reducer.mjs— pure UTC-month aggregation: per-contributortouch_commits/touch_share, scope totals (total_touch_commits,human_contributor_countcomputed pre-fold over the full transient set,unlinked_touch_share), deterministic Top-25 + Other folding (touches desc, key asc), durable-identity minimization (overflow-only identities get no contributor candidate), numeric(5,4) share stability (clamped [0,1], no NaN/−0, no forced sum-to-1).Safety envelope
OwnershipDisabledError).lib/ownership/may import it, reference ownership tables, or read the gate env; the core itself may import onlynode:builtins + siblings (no db/octokit/supabase/fs), may not console-log, and may not contain persistence-shaped calls..env.exampledocuments the gate off (OWNERSHIP_INGEST_ENABLED=false, empty allowlist); placeholder test now permitsfalsebut nevertrue.Explicitly NOT in this PR
No ownership DB writes, no
ownership_sync_staterows, no runners/callers, no cron/webhook/dashboard wiring, noOWNERSHIP_ENABLED, no HEAD-blame reducer or cursors, no UI, no GitHub permission changes, no production config changes.migrations/012_ownership.sqluntouched.Test plan
npm test— 608 tests, 605 pass, 0 fail, 3 skippednpm run typecheck— passnpm run build— passnpm audit --audit-level=moderate— 0 vulnerabilitiesnpm run db:drill— PASS on Postgres 17 (docker, disposable)lib/ownership/*, ownership tests,.env.example, secret inventory metadata, internal spec status; no api/src/migrations/vercel.json changesGenerated with Devin