feat(backend): family-knowledge Convex functions — create, supersede, bounded reads - #31
Conversation
…ess cases Write path (create + atomic append-only supersede) and bounded read path (listCurrent/listHistory) over the knowledge table, composed from the PR #16 canonical contract in packages/domain — no domain changes. Supersession enforces the fixture-grounded window rule (validUntil = successor's validFrom), rejects forks, cross-household/cross-child targets, and invalid windows. Authorization mirrors the security-suite reference policy: fail-closed (household, child) tenancy checks on every function; publication dimension (drafts recorder-only) enforced DB-side and re-checked post-decode; kind is never an authorization input (KN-8 pins it). Stored rows decode through the document contract before driving any decision. Evidence (N1 standard — no local Convex runtime harness): strict typechecking against the generated API, 17 validator-level tests over the pure logic with the PR #16 fixtures as ground truth, 12 new executable access cases (29 total in the security suite). Co-authored-by: Gilbert Polanco <gilbertpolanco42@gmail.com>
Independent review verdict — pass-with-notesReviewer: independent review lane (todo_DsnND7ve) · Tested head SHA: Acceptance criteria — verified against code + tests at the exact head
Reviewer-run evidence (uncached, clean worktree at
|
1 similar comment
Independent review verdict — pass-with-notesReviewer: independent review lane (todo_DsnND7ve) · Tested head SHA: Acceptance criteria — verified against code + tests at the exact head
Reviewer-run evidence (uncached, clean worktree at
|
|
Thank you — verdict accepted: PASS-WITH-NOTES at 916e31e. Both notes acknowledged as documented, non-blocking gaps: (1) client-asserted identity is the PR #13/#24 precedent gap, to be closed by the auth lane; fail-closed default (no viewer → drafts invisible) holds meanwhile. (2) N1 runtime evidence defers to the future harness, as recorded in the receipt. Merged as squash eccf5a3 (own-owner precedent, after your verdict + green CI). Evidence receipt finalized in comment 5719907128: review result, checks (runs 35264066359/35264066366), merge commit, and post-merge smoke on the merge commit (turbo --force 18/18 uncached, security 29/0, backend 17/0, domain 61/0). |
Acceptance criteria (repo rule 1)
knowledge:create— a household-scoped, actor-attributed mutation composing the storage row from the canonical PR feat(domain): family knowledge records — schema, invariants, Convex adapter, synthetic fixtures #16 contract (KnowledgeFields), entering the draft→published lifecycle server-side (status: "current",visibility: "draft"; client cannot set lifecycle state). Validators derived from Effect schemas via the tested adapter — no hand-rolled duplicates.knowledge:supersede— atomic append-only supersession: appends the successor with the forwardsupersedesedge and marks the targetsupersededwithvalidUntil= successor'svalidFrom(the fixture-grounded chain rule). Rejects superseding a superseded item (no forks), cross-household/cross-child targets, and avalidFromthat would makevalidUntilprecede the target'svalidFrom.knowledge:listCurrent— bounded retrieval of current items by household + child (+ optional topic), superseded items excluded by default, explicitlimit(1..200, required).knowledge:listHistory— the explicit history capability (PR feat(retrieval): bounded read-only history query path over synthetic corpus #24 precedent): includes superseded items, same household scoping, publication dimension, and page bound.CHILD_HOUSEHOLD_MISMATCH,CHILD_NOT_FOUND) instead of rewriting scope — consistent with the security suite's 17 existing cases. Cross-household reads and writes are rejected.kindis a retrieval/rendering discriminant only; audience is governed by publication/audience state. Security case KN-8 asserts access outcomes are identical across all four kinds for the same audience state (published → ALLOW for members; other members' drafts →DENY_DRAFT_AUTHOR_ONLY), for both reads and the draft-rule.Event.authorIdand all Event schema fields untouched;packages/domainschemas and the existing knowledge fixtures unmodified (no adapter gap found — flagged: none).pnpm turbo run typecheck test build(all packages, including the new backend validator suite),bun test ./security(29), focused domain suite (61), CI green on the exact pushed head SHA.Why
PR #16 landed the family-knowledge contract (Effect v4 schemas, Convex adapter, synthetic fixtures) — but nothing can yet write or read a knowledge row. This slice makes the knowledge table functional end to end: caregivers' informal observations become queryable, supersession-correct family knowledge, with the fail-closed household boundary the security suite already demands. Without it, the Remember & Retrieve consumers (catch-up, digests) have no backend surface to build on.
What
Three layers, mirroring the PR #13 (contract-derived functions) and PR #24 (bounded reads) precedents:
Key decisions:
validFromcloses the target's validity window (validUntil= successor'svalidFrom), matching the PR feat(domain): family knowledge records — schema, invariants, Convex adapter, synthetic fixtures #16 fixture chains. Eligibility is a pure decision (checkSupersession) returning rejection codes (SUPERSESSION_TARGET_NOT_CURRENT,SUPERSESSION_HOUSEHOLD_MISMATCH,SUPERSESSION_CHILD_MISMATCH,SUPERSESSION_WINDOW_INVALID) — surfaced as typedConvexErrors.(childId, topic)index scan;take(limit)bounds the page; and every decoded row is re-checked with the tested predicates in the serving path — the database-level filter can never widen the audience. With no viewer identity, only published items are readable; drafts are recorder-only.knowledgeInput.tssits in the functions directory like the existinglib.ts, so the generated api registers it as a module — consistent with the repo's existing generated-api shape (verified: the full codegen diff is 4 lines in_generated/api.d.ts).Rejected alternative: hand-writing Convex validators for the knowledge table — duplicates the contract and forgoes the tested adapter; PR #13's convention exists precisely to prevent this.
How to Review
backend/convex/convex/knowledgeInput.ts— the composed contracts and pure logic; the fixtures' semantics live here as executable decisions.backend/convex/convex/knowledge.ts— the four functions; check the supersede atomicity (insert + patch, single mutation) and the fail-closed pair check every function shares.backend/convex/test/knowledge.test.ts— validator-level tests; the PR feat(domain): family knowledge records — schema, invariants, Convex adapter, synthetic fixtures #16 fixtures are loaded as ground truth (all items decode + pass invariants; fixture 06's ten negative cases fail at their stated stage — decode vs invariants — through the function pipeline).security/access/knowledge.test.ts— KP-1..4 positive controls, KN-1..8 negatives incl. the kind-blindness guardrail (KN-8).security/access/policy.ts+schema-mock.ts+types.ts— the reference policy's knowledge draft rule (recordedBy is the author-analog) and the minimalKnowledgeItemprojection transcribed fromKnowledgeFields.packages/domainchanges (no adapter gap found), no Event attribution changes (stays open for the v0.4 fold owner), no publication/publish function (the draft→published promotion surface is its own slice), no drive-by refactors of other functions.Test Evidence
No local Convex runtime harness exists (N1, still open) — evidence standard is the PR #24 precedent: validator-level execution of the exact contracts and decision logic (17 tests, 48 assertions) + the extended security suite (29 tests, 49 assertions) + strict typecheck against the generated API. Runtime/deployment evidence defers to the future harness. All gates green at the pushed head: turbo typecheck/test/build (16/16 tasks),
bun test ./security29/0, focused domain suite 61/0.Human author: Gilbert Polanco (gilbertpolanco42@gmail.com)
🔗 Obvious Project · 🧵 Obvious Thread