Skip to content

feat: block 1 models, tenancy, and event registry - #118

Merged
OriginDevIT merged 3 commits into
mainfrom
feat/block-1-models
Sep 6, 2026
Merged

feat: block 1 models, tenancy, and event registry#118
OriginDevIT merged 3 commits into
mainfrom
feat/block-1-models

Conversation

@OriginDevIT

Copy link
Copy Markdown
Owner

19 models across tenants/directory/billing/audit. Tenant-scoped default manager raising NoTenantInScope, with the manager test and its allowlist. 77-entry event-name registry with its test. Settings wired to DATABASE_URL; the suite previously ran on SQLite in CI despite the Postgres service being provisioned. Verified from zero on Postgres 16.

OriginDevIT and others added 3 commits September 5, 2026 22:45
…gistry

Foundation for the Django rebuild (mvp-plan block 1). Data model only --
no service layer, Host-resolution middleware or first-run wizard yet;
those are the next PRs.

Models
- tenants: Operator (AUTH_USER_MODEL), Tenant, StaffMembership,
  OperatorInvite. Principals and structure -- not tenant-scoped.
- directory: ListingType, Category, Listing, DirectoryUser, Claim, Lead,
  Consent, ConsentText, SuppressionKey, ImportBatch.
- billing: Tier, Entitlement.
- audit: OutboxEvent, CommandLog, AccessLog.

Tenancy layer
- osds.tenancy: ContextVar current-tenant scope plus tenant_context().
- osds.db.TenantScopedManager: declared first as `objects`, so it is the
  default manager; raises NoTenantInScope when no tenant is set. `_base_
  manager` is left unscoped; `all_tenants` is the plain, greppable escape
  hatch.
- tenants/tests/test_scoped_manager.py asserts the wiring across every
  app, with StaffMembership, CommandLog and AccessLog allowlisted and the
  reason recorded in the test.

Event registry
- audit/events.py: the 77 event-type constants from spec 3.3 and the flat
  ALL_EVENT_TYPES frozenset. media.* and search.* stay deferred.
- audit/tests/test_event_types.py keeps the constants and the set in
  lockstep and pins the catalogue to the spec.

Decisions applied: geo_precision "none" iff lat and lon are both null
(CheckConstraint); OutboxEvent.tenant NOT NULL (the envelope still omits
the block for tenant.* events); AccessLog.tenant nullable;
Listing.current_tier is a denormalised FK written only by the entitlement
service; full 26-char ULIDs for every prefix; slug unique on
(tenant, listing_type); no Entitlement.slot FK yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5qqrLcxpVZRis5jbN5pKD
Signed-off-by: Matthew Wren <info@origindev.com>
Settings built the database config from a hard-coded SQLite path and
ignored DATABASE_URL entirely -- so the Postgres service CI provisions
was never used, and the schema had only ever been exercised on SQLite.

DATABASE_URL is now the sole source: parsed with urllib, engine pinned
to django.db.backends.postgresql (psycopg 3, already in requirements).
No SQLite, no fallback, no conditional -- the process fails at import if
DATABASE_URL is unset. CI already sets it; the compose file (next PR)
will too.

Verified against PostgreSQL 16: `migrate` from an empty database,
`makemigrations --check --dry-run` (no changes), and the full suite
(18 tests) all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5qqrLcxpVZRis5jbN5pKD
Signed-off-by: Matthew Wren <info@origindev.com>
- new_ulid(): 26 characters, Crockford base32 only, leading char within
  128 bits, no collisions over 20k draws, and lexicographic order
  matches mint order across milliseconds.
- Each `<entity>_id` factory prepends its own prefix to a 26-char ULID;
  prefixes are distinct; a guard test fails if a factory is added to
  osds/ids.py without being covered here.

No assertion on within-millisecond ordering -- the low 80 bits are
random by design and the factory is intentionally not monotonic.

Plain unittest.TestCase (no database); also runs under `manage.py test`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5qqrLcxpVZRis5jbN5pKD
Signed-off-by: Matthew Wren <info@origindev.com>
@OriginDevIT
OriginDevIT merged commit 84e8136 into main Sep 6, 2026
1 check passed
@OriginDevIT
OriginDevIT deleted the feat/block-1-models branch September 6, 2026 03:47
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