docs: hierarchy schema contract revision 2 (terra review findings 1-9)
ci/woodpecker/pr/ci Pipeline was canceled
ci/woodpecker/pr/ci Pipeline was canceled
- tenancy-FK exemption: canonical workspace_id is the one permitted business->class reference; all other forms forbidden with control test - record class = 4 node tables + hierarchy_grants per A1 8.1.2 - grant uniqueness via UNIQUE NULLS NOT DISTINCT (or 6 partial indexes); NOT NULL on role/granted_by/name/slug - grant FK delete actions split: target cascade, principal RESTRICT - audit parity: REQ-AUD-001 guarantees, same-transaction outbox, deletion-safe snapshot linkage, cascade coverage, rollback witness - roll-up never-a-write restored to full A1 strength (no canonical writes) - ownership invariant completed via contract 2; both-sides transfer rule marked new policy; legacy premise corrected - corrected cardinality witness (one-parent-per-child + sibling control) - provenance fixes: REQ-MIG-001 attribution dropped, projects table described accurately, D8-rank-1 claim removed
This commit is contained in:
@@ -5,82 +5,122 @@ Authority: PRD D2/D9/D13 (Part I §4) and the native-kanban SOT Amendment A1
|
|||||||
(`docs/requirements/native-kanban-sot.md` §8, ratified 2026-08-25). This
|
(`docs/requirements/native-kanban-sot.md` §8, ratified 2026-08-25). This
|
||||||
document turns the ratified hierarchy into a concrete schema contract:
|
document turns the ratified hierarchy into a concrete schema contract:
|
||||||
tables, cardinalities, constraints, and ownership/transfer semantics. It is
|
tables, cardinalities, constraints, and ownership/transfer semantics. It is
|
||||||
the prerequisite for the D8 rank-1 tool (hierarchy command family) and for
|
the prerequisite for the hierarchy command family and for the RBAC grant
|
||||||
the RBAC grant model (contract 2).
|
model (contract 2, `docs/requirements/rbac-grant-model.md`).
|
||||||
|
|
||||||
|
Revision 2 (independent review, GPT-5.6 terra): tenancy-FK exemption made
|
||||||
|
explicit (§1.1); record class extended to include `hierarchy_grants`
|
||||||
|
(§1.1); provenance corrections on legacy tables and the planning `projects`
|
||||||
|
table (§1.3, §2 naming note); NOT NULL and `NULLS NOT DISTINCT` grant
|
||||||
|
uniqueness (§2.6, §3.2); grant FK delete actions split cascade/restrict
|
||||||
|
(§3.3); transfer transaction includes its audit write (§4.3); ownership
|
||||||
|
invariant completed via contract 2 with the both-sides rule marked as new
|
||||||
|
policy (§4.2, §4.4); hierarchy audit brought under REQ-AUD-001-equivalent
|
||||||
|
guarantees with deletion-safe linkage (§5.2); roll-up never-a-write restored
|
||||||
|
to full A1 strength (§5.4); §6 rebuilt with bounded observables for every
|
||||||
|
MUST (allowlist, command surface, audit, corrected cardinality witness).
|
||||||
|
|
||||||
Scope: the tenancy/authorization structure record class — companies,
|
Scope: the tenancy/authorization structure record class — companies,
|
||||||
estates, platform-projects, workspaces, their parentage, and the grant
|
estates, platform-projects, workspaces, hierarchy grants, their parentage,
|
||||||
attachment points. Out of scope: the RBAC grant vocabulary and evaluation
|
and constraints. Out of scope: the RBAC grant vocabulary and evaluation
|
||||||
semantics (contract 2), roll-up projection semantics (contract 8), kanban
|
semantics (contract 2), roll-up projection semantics (contract 8), kanban
|
||||||
planning entities inside workspaces (SOT §5), migration of legacy flat data
|
planning entities inside workspaces (SOT §5), migration or retirement of
|
||||||
(SOT REQ-MIG-001).
|
legacy flat data (future work; see §1.3).
|
||||||
|
|
||||||
## 1. Record class and placement
|
## 1. Record class and placement
|
||||||
|
|
||||||
1. The four tables below form the **tenancy/authorization structure record
|
1. The **tenancy/authorization structure record class** defined by
|
||||||
class** defined by Amendment A1 §8.1.2. They carry parentage, naming, and
|
Amendment A1 §8.1.2 comprises five tables: the four node tables of §2
|
||||||
grant data only — never task, plan, or any business/orchestration
|
AND `hierarchy_grants` (§3) — A1 includes hierarchy-level access grants
|
||||||
payload. No business/orchestration row may reference a hierarchy record
|
in the class. Every rule addressed to "the class" in this contract
|
||||||
as a dependency, claim target, or work subject.
|
(payload prohibition, mutation path, audit) binds all five tables. Class
|
||||||
|
rows carry parentage, naming, grant, and audit-linkage data only — never
|
||||||
|
task, plan, or any business/orchestration payload.
|
||||||
|
References from business/orchestration rows into the class are limited
|
||||||
|
to exactly one form: the canonical `workspace_id` tenancy column that
|
||||||
|
REQ-TEN-001 requires on every canonical row, referencing
|
||||||
|
`workspaces.id`. No business/orchestration row may reference a company,
|
||||||
|
estate, platform-project, or grant id in any position, and no
|
||||||
|
business/orchestration row may reference a workspace id in any
|
||||||
|
non-tenancy position (dependency, claim target, work subject).
|
||||||
2. Hierarchy records are NOT workspace-scoped rows: REQ-TEN-001's
|
2. Hierarchy records are NOT workspace-scoped rows: REQ-TEN-001's
|
||||||
`workspace_id` obligation binds business/orchestration rows and does not
|
`workspace_id` obligation binds business/orchestration rows and does not
|
||||||
apply to this class (A1 §8.1.2). The `workspaces` table itself is the
|
apply to this class (A1 §8.1.2). The `workspaces` table itself is the
|
||||||
anchor the obligation points at.
|
anchor the obligation points at.
|
||||||
3. The legacy flat tables (`teams`, `projects` in `packages/db/src/schema.ts`
|
3. The legacy flat tables (`teams`, and the Brain planning `projects` table
|
||||||
— the Brain planning table, not the hierarchy level) are not part of this
|
in `packages/db/src/schema.ts`) are not part of this class. What A1
|
||||||
class and are not repurposed. The SOT forbids reusing the flat model for
|
§8.1.4 pins is narrower: the planning `projects` table and
|
||||||
the hierarchy; their eventual migration or retirement is REQ-MIG-001
|
`platform_projects` stay distinct tables. This contract adds, as new
|
||||||
work, out of scope here.
|
policy ratified here: neither `teams` nor `projects` is repurposed as a
|
||||||
|
hierarchy table. Their eventual migration or retirement is future work
|
||||||
|
that no existing REQ assigns; it is out of scope here.
|
||||||
|
|
||||||
## 2. Tables and cardinalities
|
## 2. Tables and cardinalities
|
||||||
|
|
||||||
Naming: the level above workspaces is `platform_projects`, per A1 §8.1.4 —
|
Naming: the level above workspaces is `platform_projects`, per A1 §8.1.4.
|
||||||
the existing `projects` table already means workspace-internal planning
|
The existing `projects` table is Brain planning data (so labeled in
|
||||||
entities, and the schema MUST NOT merge the two. (A rename of either
|
`packages/db/src/schema.ts`; it carries no `workspace_id`), and the schema
|
||||||
remains an implementation-PR decision under A1; this contract pins only that
|
MUST NOT merge the two. (A rename of either remains an implementation-PR
|
||||||
they stay distinct tables.)
|
decision under A1; this contract pins only that they stay distinct tables.)
|
||||||
|
|
||||||
1. `companies` — id (uuid pk), name, slug (unique per deployment),
|
1. `companies` — id (uuid pk), name, slug (unique per deployment),
|
||||||
created_at, updated_at. N per deployment (D2).
|
created_at, updated_at. N per deployment (D2).
|
||||||
2. `estates` — id, name, slug, `company_id` NOT NULL →
|
2. `estates` — id, name, slug, `company_id` NOT NULL →
|
||||||
`companies.id` ON DELETE RESTRICT. Exactly one company per estate.
|
`companies.id` ON DELETE RESTRICT. Exactly one company per estate; a
|
||||||
|
company holds any number of estates.
|
||||||
3. `platform_projects` — id, name, slug, `estate_id` NOT NULL →
|
3. `platform_projects` — id, name, slug, `estate_id` NOT NULL →
|
||||||
`estates.id` ON DELETE RESTRICT. Exactly one estate per
|
`estates.id` ON DELETE RESTRICT. Exactly one estate per
|
||||||
platform-project.
|
platform-project; an estate holds any number of platform-projects.
|
||||||
4. `workspaces` — id, name, slug, `platform_project_id` NOT NULL →
|
4. `workspaces` — id, name, slug, `platform_project_id` NOT NULL →
|
||||||
`platform_projects.id` ON DELETE RESTRICT. Exactly one platform-project
|
`platform_projects.id` ON DELETE RESTRICT. Exactly one platform-project
|
||||||
per workspace. This table is the referent of every `workspace_id` column
|
per workspace. This table is the referent of every `workspace_id` column
|
||||||
the SOT requires on canonical rows.
|
the SOT requires on canonical rows.
|
||||||
5. **Chain resolution is by construction.** Because every parent FK is NOT
|
5. **Chain resolution is by construction.** Because every parent FK is NOT
|
||||||
NULL and single-valued, each workspace resolves to exactly one
|
NULL and single-valued (one FK column, no parentage edge tables, no
|
||||||
platform-project → estate → company chain (A1 §8.3 acceptance 1). There
|
multi-parent forms, no nullable "detached" states), each workspace
|
||||||
are no parentage edge tables, no multi-parent forms, and no nullable
|
resolves to exactly one platform-project → estate → company chain (A1
|
||||||
"detached" states.
|
§8.3 acceptance 1). One-parent-per-child is the constrained direction;
|
||||||
6. **Slug scoping.** `estates.slug` is unique within its company,
|
many children per parent is valid data.
|
||||||
`platform_projects.slug` within its estate, `workspaces.slug` within its
|
6. **Slug scoping.** All `name` and `slug` columns are NOT NULL.
|
||||||
platform-project (composite unique constraints). Display names are
|
`estates.slug` is unique within its company, `platform_projects.slug`
|
||||||
unconstrained.
|
within its estate, `workspaces.slug` within its platform-project
|
||||||
|
(composite unique constraints). Display names are unconstrained beyond
|
||||||
|
NOT NULL.
|
||||||
7. No hierarchy table carries a `metadata` jsonb column or any
|
7. No hierarchy table carries a `metadata` jsonb column or any
|
||||||
free-form payload field. Parentage, naming, timestamps, and the audit
|
free-form payload field. The columns declared in this section and §3
|
||||||
linkage of §5 — nothing else (A1 §8.1.2).
|
are exhaustive: a class table's column set is exactly its declared set
|
||||||
|
(verified per §6.2) — nothing else (A1 §8.1.2).
|
||||||
|
|
||||||
## 3. Grant attachment points
|
## 3. Grant attachment points
|
||||||
|
|
||||||
The grant vocabulary (which roles exist, what each permits, how inheritance
|
The grant vocabulary (which roles exist, what each permits, how evaluation
|
||||||
and revocation evaluate) is contract 2. This contract pins only the schema
|
and revocation work) is contract 2. This contract pins only the schema
|
||||||
shape contract 2 attaches to:
|
shape contract 2 attaches to:
|
||||||
|
|
||||||
1. `hierarchy_grants` — id, subject (exactly one of `user_id` → `users.id`,
|
1. `hierarchy_grants` — id, subject (exactly one of `user_id` → `users.id`,
|
||||||
`team_id` → `teams.id`; CHECK-enforced exactly-one-of), target (exactly
|
`team_id` → `teams.id`; CHECK-enforced exactly-one-of), target (exactly
|
||||||
one of `company_id`, `estate_id`, `platform_project_id`;
|
one of `company_id`, `estate_id`, `platform_project_id`;
|
||||||
CHECK-enforced exactly-one-of), `role` (text; vocabulary owned by
|
CHECK-enforced exactly-one-of), `role` (text NOT NULL; vocabulary and
|
||||||
contract 2), granted_by → `users.id`, created_at.
|
its CHECK constraint owned by contract 2 §2), `granted_by` NOT NULL →
|
||||||
2. Uniqueness: at most one grant row per (subject, target, role).
|
`users.id`, created_at.
|
||||||
3. Deleting a hierarchy record deletes its grants (the one permitted
|
2. Uniqueness: at most one grant row per (subject, target, role). Because
|
||||||
cascade in this class — a grant on a deleted node is meaningless and
|
the subject and target columns are nullable by design, ordinary
|
||||||
fail-open if retained). Deleting a user or team follows the platform
|
PostgreSQL composite uniqueness treats NULLs as distinct and would not
|
||||||
rule for principal deletion (identity contract §7; deletion is currently
|
enforce this. The implementation MUST use a single
|
||||||
gated).
|
`UNIQUE NULLS NOT DISTINCT` constraint across (`user_id`, `team_id`,
|
||||||
|
`company_id`, `estate_id`, `platform_project_id`, `role`) or six
|
||||||
|
equivalent partial unique indexes (one per subject×target form). The
|
||||||
|
pinned Drizzle ORM supports `nullsNotDistinct()`.
|
||||||
|
3. Delete actions are split by column class:
|
||||||
|
- Target FKs (`company_id`, `estate_id`, `platform_project_id`):
|
||||||
|
ON DELETE CASCADE — the one permitted cascade in this class. A grant
|
||||||
|
on a deleted node is meaningless and fail-open if retained. Cascaded
|
||||||
|
grant deletions are audited per §5.2.
|
||||||
|
- Principal FKs (`user_id`, `team_id`, `granted_by`): ON DELETE
|
||||||
|
RESTRICT. The identity contract (§7.3) gates user deletion today and
|
||||||
|
defines no team-deletion rule; this contract does not invent one.
|
||||||
|
These FKs stay RESTRICT until an explicit deletion-and-retention
|
||||||
|
contract ratifies otherwise.
|
||||||
4. Workspace-level access is evaluated, not stored here: a grant at any of
|
4. Workspace-level access is evaluated, not stored here: a grant at any of
|
||||||
the three levels evaluates down the chain to workspace-scoped
|
the three levels evaluates down the chain to workspace-scoped
|
||||||
authorization (A1 §8.1.3). No `workspace_id` column exists on
|
authorization (A1 §8.1.3). No `workspace_id` column exists on
|
||||||
@@ -92,69 +132,118 @@ shape contract 2 attaches to:
|
|||||||
|
|
||||||
"Assets are transferable subject to the structure" (PRD Part I §4):
|
"Assets are transferable subject to the structure" (PRD Part I §4):
|
||||||
|
|
||||||
1. A transfer is an audited UPDATE of exactly one parent FK on exactly one
|
1. A transfer changes exactly one parent FK on exactly one hierarchy row:
|
||||||
hierarchy row: workspace → new platform-project, platform-project → new
|
workspace → new platform-project, platform-project → new estate, estate
|
||||||
estate, estate → new company. Nothing else changes: business and
|
→ new company. Nothing else in the class or the SOT changes: business
|
||||||
orchestration rows inside affected workspaces are untouched, keep their
|
and orchestration rows inside affected workspaces are untouched, keep
|
||||||
`workspace_id`, and never cross a workspace boundary (A1 §8.1.3 "chain
|
their `workspace_id`, and never cross a workspace boundary (A1 §8.1.3
|
||||||
maintenance").
|
"chain maintenance").
|
||||||
2. Transfer authorization requires authority over BOTH the source and the
|
2. Transfer authorization requires authority over BOTH the source and the
|
||||||
destination parent (the vocabulary for "authority" is contract 2; the
|
destination parent. This both-sides predicate is **new policy
|
||||||
both-sides requirement is structural and binds here).
|
introduced by this contract pair** (D2/A1 do not state it); its
|
||||||
3. A transfer is a single-row, single-statement mutation; there are no
|
evaluation semantics are contract 2 §5. The structural half — that the
|
||||||
multi-row transfer batches at the schema level. Bulk moves are N audited
|
transfer command evaluates it before mutating — binds here.
|
||||||
|
3. A transfer transaction contains exactly two writes: the single-row
|
||||||
|
parent-FK update and its audit event (§5.2). There are no multi-row
|
||||||
|
transfer batches at the schema level; bulk moves are N audited
|
||||||
transfers.
|
transfers.
|
||||||
4. Hierarchy records have no `owner_id`. Ownership in the hierarchy IS the
|
4. Hierarchy records have no `owner_id`. Ownership in the hierarchy IS the
|
||||||
grant structure (§3) — a "company owner" is a subject with the top role
|
grant structure: a "company owner" is a subject with an `owner` grant
|
||||||
on that company, not a column. This avoids the dual-write failure mode
|
on that company or an ancestor (contract 2 §2), not a column. The
|
||||||
of the legacy `teams.owner_id`/`teams.manager_id` columns, which encode
|
ownership invariant across the contract pair: a node may hold zero
|
||||||
authority outside any evaluable grant model.
|
direct owner grants (authority can derive from an ancestor grant); node
|
||||||
|
creation names the initial `owner` grant in the same audited operation
|
||||||
|
and the wizard seeds the first company's owner the same way (contract 2
|
||||||
|
§4.3); transfer and revocation semantics are contract 2 §§5–6. This
|
||||||
|
avoids column-encoded authority of the kind the legacy schema carries
|
||||||
|
(`teams.owner_id` and `teams.manager_id` are required user FKs, and
|
||||||
|
`team_members.role` is a further authority field — none of them
|
||||||
|
evaluable under a grant model).
|
||||||
|
|
||||||
## 5. Mutation path, audit, and deletion
|
## 5. Mutation path, audit, and deletion
|
||||||
|
|
||||||
1. All hierarchy mutations flow through the same sole-writable-SOT,
|
1. All hierarchy mutations flow through the same sole-writable-SOT,
|
||||||
fail-closed, audited Gateway command path as everything else (A1 §8.2.3,
|
fail-closed, audited Gateway command path as everything else (A1 §8.2.3,
|
||||||
REQ-API-001). No direct-DB writers, no raw CRUD endpoints.
|
REQ-API-001). No direct-DB writers, no raw CRUD endpoints.
|
||||||
2. Every create, rename, transfer, grant change, and delete emits a
|
2. **Audit parity.** A1 §8.2 leaves every pre-existing REQ binding, so
|
||||||
semantic audit event carrying actor, verb, target record, and (for
|
hierarchy mutations get REQ-AUD-001's guarantees, not a weakened
|
||||||
transfers) source and destination parents. Hierarchy audit events
|
substitute. Concretely:
|
||||||
reference hierarchy records; they are not workspace-scoped rows and do
|
- Every create, rename, transfer, grant create/change/revoke, and
|
||||||
not ride the workspace outbox (REQ-AUD-001 binds business rows; the
|
delete — including every grant deletion cascaded by a node delete —
|
||||||
implementing PR defines the hierarchy audit store under the same
|
emits a semantic audit event carrying actor, verb, target, and (for
|
||||||
append-only rules).
|
transfers) source and destination parents, with the correlation,
|
||||||
|
causation, idempotency, and per-target ordering guarantees REQ-AUD-001
|
||||||
|
defines.
|
||||||
|
- The state change and its audit event(s) commit in the same
|
||||||
|
transaction, delivered through a transactional outbox. Hierarchy
|
||||||
|
events are not workspace-scoped rows and do not ride the workspace
|
||||||
|
outbox; they get an equivalent hierarchy outbox under the same
|
||||||
|
append-only, same-transaction rules.
|
||||||
|
- **Deletion-safe linkage:** audit events reference their target by an
|
||||||
|
immutable snapshot (id, slug, and parent chain at event time), never
|
||||||
|
by a foreign key into the class tables, so append-only events survive
|
||||||
|
the deletion of their target.
|
||||||
3. Deletion is fail-closed bottom-up: a hierarchy record with children
|
3. Deletion is fail-closed bottom-up: a hierarchy record with children
|
||||||
cannot be deleted (RESTRICT FKs, §2). Deleting a workspace is a SOT-side
|
cannot be deleted (RESTRICT FKs, §2). Deleting a workspace is a SOT-side
|
||||||
operation subject to the kanban SOT's own rules and is not granted any
|
operation subject to the kanban SOT's own rules and is not granted any
|
||||||
new semantics by this contract.
|
new semantics by this contract.
|
||||||
4. Roll-up reads (A1 §8.1.3) touch none of these tables' write paths and
|
4. **Roll-up is never a write** (A1 §8.2.2, preserved at full strength). A
|
||||||
are specified by contract 8; this contract only guarantees the chain
|
roll-up read mutates nothing — not hierarchy state, and not business or
|
||||||
they aggregate over is unique and non-null.
|
orchestration state: it must not mutate, claim, order, or gate
|
||||||
|
workspace work. Contract 8 owns projection details but cannot narrow
|
||||||
|
this rule. This contract additionally guarantees the chain roll-ups
|
||||||
|
aggregate over is unique and non-null (§2.5).
|
||||||
|
|
||||||
## 6. Verification requirements
|
## 6. Verification requirements
|
||||||
|
|
||||||
Binding on the implementing PRs (extends A1 §8.3):
|
Binding on the implementing PRs (extends A1 §8.3):
|
||||||
|
|
||||||
1. Schema tests: chain uniqueness by construction (insert attempts with
|
1. Schema witnesses (real PostgreSQL, §6.8): chain construction — insert
|
||||||
null/duplicate parents fail); composite slug uniqueness per parent;
|
with a null parent FK refused; insert with one valid parent accepted;
|
||||||
CHECK-enforced exactly-one-of on grant subject and target; grant
|
two siblings under one parent accepted (the control proving the
|
||||||
uniqueness per (subject, target, role).
|
constraint rejects only what §2.5 forbids); catalog assertion that each
|
||||||
2. Transfer tests: parent-FK update moves the subtree resolution and
|
child table has exactly one parent-FK column and no parentage edge
|
||||||
|
table exists. Composite slug uniqueness per parent (duplicate slug
|
||||||
|
under same parent refused; same slug under different parents accepted).
|
||||||
|
Grant CHECKs: exactly-one-of subject and exactly-one-of target each
|
||||||
|
witnessed (zero and two set → refused). Grant uniqueness: a duplicate
|
||||||
|
(subject, target, role) row refused for each of the six subject×target
|
||||||
|
forms, proving NULLS-NOT-DISTINCT semantics; NOT NULL on `role`,
|
||||||
|
`granted_by`, and all `name`/`slug` columns witnessed.
|
||||||
|
2. Column allowlist: an information_schema assertion that each class
|
||||||
|
table's column set is exactly the set declared in §2/§3 — the bounded
|
||||||
|
observable for no-payload (§2.7) and no-`owner_id` (§4.4).
|
||||||
|
3. Command surface: an inventory assertion over the Gateway's registered
|
||||||
|
hierarchy routes/commands proving the mutation surface is exactly the
|
||||||
|
declared hierarchy command family — no generic CRUD endpoint and no
|
||||||
|
non-command write path touches class tables (§5.1).
|
||||||
|
4. Audit witnesses: for each mutation class (create, rename, transfer,
|
||||||
|
grant create/change/revoke, delete) — the event exists after commit
|
||||||
|
with actor/verb/target and same-transaction atomicity; a rolled-back
|
||||||
|
mutation leaves no event (rollback witness); a node delete's cascaded
|
||||||
|
grant deletions are each covered by events; events survive deletion of
|
||||||
|
their target (query the events of a deleted node).
|
||||||
|
5. Transfer tests: parent-FK update moves the subtree resolution and
|
||||||
modifies zero business/orchestration rows (row-count and content
|
modifies zero business/orchestration rows (row-count and content
|
||||||
assertions on workspace contents before/after); transfer without
|
assertions on workspace contents before/after); transfer without
|
||||||
authority on either side is refused.
|
authority on the source or on the destination side is refused (with
|
||||||
3. Deletion tests: delete with children refused at the database level;
|
contract 2 §7.8).
|
||||||
delete of a leaf cascades its grants and nothing else.
|
6. Deletion tests: delete with children refused at the database level;
|
||||||
4. Authorization tests (with contract 2): a grant at each level yields
|
delete of a leaf cascades its grants and nothing else; deleting a user
|
||||||
exactly the workspace permissions the chain implies; revocation up the
|
or team that is a grant subject (or `granted_by` referent) is refused
|
||||||
chain propagates (A1 §8.3 acceptance 2).
|
(RESTRICT witnesses for §3.3).
|
||||||
5. Negative tests: no business/orchestration table accepts a hierarchy
|
7. Negative tests: no business/orchestration table accepts a company,
|
||||||
record id in any dependency/reference position; roll-up endpoints cannot
|
estate, platform-project, or grant id in any reference position, and
|
||||||
mutate hierarchy state; readers see aggregates only over workspaces they
|
none accepts a workspace id in any non-tenancy position; the canonical
|
||||||
are authorized on, with no cross-tenant existence oracles (A1 §8.3
|
tenancy FK control — a business row inserted with a valid
|
||||||
acceptance 3).
|
`workspace_id` succeeds, with an invalid one is refused; roll-up
|
||||||
6. Real-PostgreSQL coverage for the constraint-witness tests (unique/CHECK/
|
endpoints mutate no canonical state anywhere (assert zero writes across
|
||||||
RESTRICT behavior), using the `ci-postgres` service in the `test` CI
|
hierarchy AND workspace tables, not hierarchy only); readers see
|
||||||
step; mocked specs cannot witness database constraints.
|
aggregates only over workspaces they are authorized on, with no
|
||||||
|
cross-tenant existence oracles (A1 §8.3 acceptance 3).
|
||||||
|
8. Real-PostgreSQL coverage for every constraint witness (unique/CHECK/
|
||||||
|
RESTRICT/NULLS NOT DISTINCT behavior), using the `ci-postgres` service
|
||||||
|
in the `test` CI step; mocked specs cannot witness database constraints.
|
||||||
|
|
||||||
## Ruling request
|
## Ruling request
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user