feat: PR-5 - entity layer + provenance-chained seed loader - #5
Merged
Eth-Interchained merged 1 commit intoJul 7, 2026
Merged
Conversation
…a flows into NEDB
Phase 1 opens: the engine becomes the source of truth for salon data.
- src/server/entities.ts: collections, seed-file zod schemas (the
salon.template.json contract), slugify/categorySlug, typed reads the
render plane consumes (resolveHandle, getIdentityByHandle, listTeam,
listServiceMenus, getCity)
- src/server/seed.ts: content-addressed, idempotent loader — every run
writes a seed_runs provenance root (id = sha256(file)[:16]); every doc
chains caused_by to it; idem keys are content-addressed so re-running
an unchanged seed writes NOTHING; duplicate handles fail loud
- Entities stored identity-SHAPED (identityType/handle/displayName/
entity) so Phase 2's registry formalizes without migration; relations
are flat keys (salonHandle, cityId) queried via NQL WHERE — the
client doesn't wrap /link//neighbors yet (queued upstream for
nedb-engine-client; flat keys are the honest Phase-1 model)
- scripts/seed.ts (npm run seed): summary + writes-this-run counter
(0 = idempotent no-op) + verify() gate — a seed run that can't prove
integrity fails
- campaigns: geography cities are now {slug, name} — display names are
curated, never slug-derived (Dr. Phillips)
- indexes at seed time: identities.identityType, identities.salonHandle,
services.salonHandle
Verified locally: tsc clean · unit 24/24 · live 3/3 vs nedbd 2.6.1 —
seed test proves: 8 identities (salon + 7-member roster) + 8 menus +
13 cities land, real NAP intact, WHERE on flat keys works on the real
engine, re-run seq delta = 0, verify green · seed CLI smoke (38 writes,
verify ok) · portal build clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
PR-5 opens Phase 1: the entity layer and the seed loader. Mint on the Avenue's real data — 7-member roster, 8 menu categories, NAP, GBP facts — now flows from
data/seeds/into NEDB with full causal provenance, idempotently.Why this matters
The engine is now the source of truth for salon data, exactly as the spec demands ("everything editable through Portal" starts with everything living in NEDB). Every seeded document TRACEs back to a content-addressed
seed_runsroot: which file, which hash, which run. Re-running an unchanged seed writes nothing — proven by sequence number, not by hope.What's inside
src/server/entities.ts— collections, seed-file zod schemas (thesalon.template.jsoncontract), typed reads for the render plane (getIdentityByHandle,listTeam,listServiceMenus,getCity)src/server/seed.ts— content-addressed loader:seed_runsprovenance root → salon identity + handle → 7 stylist identities + handles → menu docs per category → cities from campaign geography (deduped, campaign-tagged) → indexes. Duplicate handles fail loud.scripts/seed.ts(npm run seed) — summary, writes-this-run counter (0 = no-op), and averify()gate: a seed run that can't prove integrity failsidentityType/handle/displayName/entity, so Phase 2'sdefineIdentityTyperegistry lands without migrationsalonHandle,cityId) + NQL WHERE — honest Phase-1 model; the engine's/link+/neighborsedges are queued as an upstreamnedb-engine-clientfeature (the flagship feeds the library){slug, name}; display names curated, never derived ("Dr. Phillips")Tests run (locally, real engine)
tsc --noEmitclean · unit 24/24 (slugify, categorySlug, deterministic ids, content-addressed idem keys, template skipping, schema rejection)+1-407-645-2264), WHERE on flat keys proven on the engine, re-run seq delta = 0,verify()greenportal buildcleanFollow-up work
link()/neighbors()on nedb-engine-client → migrate relations to typed DAG edges + TRAVERSEdefineIdentityTyperegistry validation over these documentsSpec: §5 (schema), §6 (identity model), §15 (Phase 1).