Copy architecture docs, ADRs, storage domain specs, research, reviews, and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for standalone @alkdev/hub repo structure (src/ not packages/hub/). Sanitize all sensitive information: - Replace private IPs (10.0.0.1) with localhost defaults - Remove internal server hostnames (dev1, ns528096) - Replace /workspace/ private paths with npm package references - Remove hardcoded credentials from examples - Rewrite infrastructure.md without private network details Add Deno project scaffolding: deno.json (pinned deps), .gitignore, AGENTS.md, entry point. Migrate existing code stubs (crypto, config types, logger) with updated import paths.
43 lines
1.9 KiB
Markdown
43 lines
1.9 KiB
Markdown
---
|
|
id: resolve-org-dual-ownership
|
|
name: Resolve dual ownership model for organizations
|
|
status: completed
|
|
depends_on: [resolve-sessions-accountid-cascade]
|
|
scope: narrow
|
|
risk: high
|
|
impact: component
|
|
level: implementation
|
|
---
|
|
|
|
## Description
|
|
|
|
Two competing ownership concepts with no documented relationship:
|
|
1. `organizations.ownerId` — a single FK to one account (`identity.md:44`)
|
|
2. `organization_members.membershipLevel: "owner"` — can exist on multiple rows (`identity.md:58`)
|
|
|
|
Can `ownerId` point to an account with `membershipLevel: "member"` (not "owner")? Can an org have zero members with `membershipLevel: "owner"` but a non-null `ownerId`? An implementer cannot determine which field is authoritative for ownership queries.
|
|
|
|
The resolution of `sessions.accountId` cascade (C01) affects this because if account deletion is enabled (nullable FKs), the org ownership transfer workflow depends on knowing which field is authoritative.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] `identity.md` documents the invariant relating `organizations.ownerId` and `organization_members.membershipLevel`
|
|
- [ ] One of the following invariants is chosen and documented:
|
|
- "`ownerId` is always a member with `membershipLevel: 'owner'` (enforced by app logic). If all owner-level members are removed, `ownerId` must be transferred first."
|
|
- "`ownerId` is the creator; `membershipLevel: 'owner'` is a separate authorization concept."
|
|
- [ ] The account deletion workflow interacts correctly with the chosen invariant (e.g., RESTRICT on ownerId means account deletion blocked if account owns an org)
|
|
- [ ] `table-reference.md` cascade rationale for `organizations.ownerId → accounts.id` (RESTRICT) is updated with the invariant reference
|
|
|
|
## References
|
|
|
|
- docs/reviews/storage-architecture-review-2026-04-21.md#C13
|
|
- docs/architecture/storage/identity.md:44
|
|
- docs/architecture/storage/identity.md:58
|
|
|
|
## Notes
|
|
|
|
> To be filled by implementation agent
|
|
|
|
## Summary
|
|
|
|
> To be filled on completion |