docs(rollup): revision 2 — membership locality, executable no-oracle predicate, closed schema enumeration, widened fixtures (terra F1-F7)
ci/woodpecker/pr/ci Pipeline was canceled

This commit is contained in:
fred
2026-08-26 19:57:56 -05:00
parent 61c495dd63
commit df0085dbe2
+180 -66
View File
@@ -16,6 +16,23 @@ read-only roll-up query over only readable workspaces, at every
hierarchy level, as its own non-mutating query tool, dependent on ranks hierarchy level, as its own non-mutating query tool, dependent on ranks
13. 13.
Revision 2 (terra review F1F7): membership-only readability is now
workspace-local — it contributes at the workspace node only and never
promotes ancestor visibility; upward aggregation requires an effective
chain role, and §1 defines direct vs effective grants in contract 2's
terms (F1). The no-oracle rule gains a defined equivalence predicate
(normalized byte equality with an enumerated volatile-field set) and a
partial-scope hidden-sibling witness (F2). §2.5 enumerates the closed
semantic result and denial schemas field-by-field, including the
explicit-zero representation (F3). Cache invalidation, when a cache
exists, is witnessed per invalidator class (F4). Non-authoritative and
never-gate rules gain an import-graph/data-flow witness, and the
mutation check is aligned to contract 1 §6.7's both-table zero-write
assertion (F5). The fixture gains a second estate with distinct counts
and explicit company-, estate-, project-grant, and membership cases
(F6). The §5.3 legacy-row exclusion and pre-rank no-obligation rules
are disclosed as drafting additions (F7).
This contract binds the projection semantics (§2), reader authorization This contract binds the projection semantics (§2), reader authorization
semantics (§3), read-only enforcement (§4), dependencies and phase semantics (§3), read-only enforcement (§4), dependencies and phase
timing (§5), witnesses (§6), and disclosed drafting additions (§7). It timing (§5), witnesses (§6), and disclosed drafting additions (§7). It
@@ -30,16 +47,26 @@ row with contract 5 (`tool-gateway-mapping.md`).
1. **Roll-up**: the read-only projection of per-workspace task counts 1. **Roll-up**: the read-only projection of per-workspace task counts
by status, aggregated up the contract 1 parent chain (workspace → by status, aggregated up the contract 1 parent chain (workspace →
platform-project → estate → company). platform-project → estate → company).
2. **Readable workspace** (for a given reader): a workspace where the 2. **Effective chain role** (at a node, for a reader): the role
reader's authorization permits reading task state — either contract 2 §3 evaluation yields at that node — from a grant on the
workspace membership under the SOT's own membership rules node itself (a **direct grant**) or from a grant on an ancestor
(REQ-ID-001), or an effective chain role at that workspace per whose domain covers it (an **inherited grant**, contract 2 §3.2).
contract 2 §3 evaluation. The role vocabulary is contract 2 §2's; The role vocabulary is contract 2 §2's; this contract adds no role
this contract adds no role and no new authority source. and no new authority source.
3. **Aggregation scope** (of a hierarchy node, for a given reader): 3. **Chain-readable workspace** (for a reader): a workspace where the
the set of readable workspaces in that node's descendant subtree reader's effective chain role permits reading task state.
(for a workspace node, the workspace itself if readable). 4. **Member-readable workspace** (for a reader): a workspace readable
4. **Projection**: a generated, non-authoritative view in the sense of only through workspace membership under the SOT's own membership
rules (REQ-ID-001), with no effective chain role. Membership
confers workspace-local semantics only (contract 2 §3.1, §7.4): it
never contributes authority, visibility, or aggregation upward.
5. **Aggregation scope** (of a hierarchy node, for a reader): the set
of chain-readable workspaces in that node's descendant subtree;
plus, when the node is itself a workspace, that workspace if it is
chain-readable or member-readable. A member-readable workspace
therefore contributes to exactly one node's aggregation scope: its
own.
6. **Projection**: a generated, non-authoritative view in the sense of
`native-kanban-sot.md` §3 invariant 5 — derived from SOT rows, `native-kanban-sot.md` §3 invariant 5 — derived from SOT rows,
never an import source, never authoritative. never an import source, never authoritative.
@@ -60,35 +87,68 @@ row with contract 5 (`tool-gateway-mapping.md`).
aggregation — metrics, trends, forecasting, scoring, velocity, aggregation — metrics, trends, forecasting, scoring, velocity,
cross-workspace derived analytics, dashboards computed across cross-workspace derived analytics, dashboards computed across
workspaces — remains a `native-kanban-sot.md` §6 non-goal workspaces — remains a `native-kanban-sot.md` §6 non-goal
(§8.2.4). The response schema is closed (§6.7 witness): adding any (§8.2.4). The response schema is closed (§2.5; §6.7 witness):
field beyond identifiers, names of visible nodes, and status adding any field is an amendment to this contract.
counts is an amendment to this contract.
4. **Non-authoritative.** No consumer may treat roll-up output as a 4. **Non-authoritative.** No consumer may treat roll-up output as a
source of record; it is recomputable at any time from SOT rows and source of record; it is recomputable at any time from SOT rows and
is never imported, persisted as authoritative state, or used to is never imported, persisted as authoritative state, or used to
gate work. gate or deny work (witness §6.8 — both the write-path and the
decision-path prohibitions are witnessed).
5. **Closed semantic schema.** The successful result consists of
exactly these semantic fields, and no others:
- `node`: the queried node — exactly `id`, `type` (one of the four
contract 1 levels), `name`.
- `totals`: one entry per status value of the typed lifecycle —
every status key present, a count of zero represented explicitly
as `0`, never by key absence.
- `children`: the reader-visible (§3.2) child nodes of `node`,
each carrying exactly the same `id`/`type`/`name`/`totals`
shape, recursively down to the workspaces in the reader's
aggregation scope. A node outside §3.2 visibility never appears
at any depth.
There are no optional result fields. The denial/nonexistent
response is the contract 5 §4.2 not-found-class error envelope
with no fields beyond that envelope. The wire DTO is expressed
under contract 5 §4.1, and MUST be a faithful serialization of
exactly this semantic field set: a wire field with no
corresponding semantic field above is a conformance defect.
## 3. Reader authorization semantics ## 3. Reader authorization semantics
1. **Scope rule.** A reader's roll-up over any node aggregates ONLY 1. **Scope rule.** A reader's roll-up over any node aggregates ONLY
the reader's aggregation scope (§1.3). An unreadable workspace the reader's aggregation scope (§1.5). An unreadable workspace
contributes nothing to any total — not a count, not a row, not a contributes nothing to any total — not a count, not a row, not a
presence marker. presence marker. A member-readable workspace contributes only at
the workspace node itself (§1.4–§1.5): querying it directly
succeeds; it never appears in, and never adds to, any ancestor's
response for that reader.
2. **Node visibility.** A node appears in a roll-up response iff the 2. **Node visibility.** A node appears in a roll-up response iff the
reader's aggregation scope at that node is non-empty, or the reader's aggregation scope at that node is non-empty, or the
reader holds a grant on the node itself (contract 2 §3.2 makes the reader holds an effective chain role at the node (§1.2 — direct or
node and its subtree the grant's domain). Per the ruling below, a inherited; contract 2 §3.2 makes a grant's domain the node and its
granted node whose aggregation scope is empty appears with zero subtree, so an ancestor grant makes empty descendants visible per
counts; a node the reader has no authority on does not appear at the ruling). Per the ruling below, a node with an effective chain
all. role but an empty aggregation scope appears with zero counts.
Workspace membership alone never makes any non-workspace node
visible. A node where the reader has neither an effective chain
role nor a non-empty aggregation scope does not appear at all.
3. **No existence oracle.** The response MUST NOT disclose the 3. **No existence oracle.** The response MUST NOT disclose the
existence, count, name, or any property of unreadable workspaces existence, count, name, or any property of unreadable workspaces
or of nodes outside §3.2 visibility — no "N workspaces hidden" or of nodes outside §3.2 visibility — no "N workspaces hidden"
fields, no total-vs-visible discrepancy fields. A query naming a fields, no total-vs-visible discrepancy fields. A query naming a
node outside §3.2 visibility returns the same response as a query node outside §3.2 visibility MUST satisfy the §3.4 response
naming a nonexistent node (fail closed, `rbac-grant-model.md` §3.5 equivalence with a query naming a nonexistent node (fail closed,
pattern: a decision path that cannot read grant state denies). `rbac-grant-model.md` §3.5 pattern: a decision path that cannot
4. **Live evaluation.** Readability is evaluated per contract 2 §3.5 read grant state denies).
4. **Response equivalence predicate.** Two responses are equivalent
when they carry the identical HTTP status, the identical contract
5 §4.2 error code, and byte-identical bodies after normalizing
exactly the declared volatile envelope fields — correlation id and
response timestamp, and nothing else. The implementing PR declares
that volatile-field list in the witness; any additional
normalization is a conformance defect. This is contract 5 §4.2's
same code/status/shape rule made executable.
5. **Live evaluation.** Readability is evaluated per contract 2 §3.5
(live rows or transactionally-invalidated cache). Revocation (live rows or transactionally-invalidated cache). Revocation
propagates per contract 2 §6: the next roll-up query decided after propagates per contract 2 §6: the next roll-up query decided after
the revoking transaction commits excludes the revoked scope. the revoking transaction commits excludes the revoked scope.
@@ -106,8 +166,8 @@ row with contract 5 (`tool-gateway-mapping.md`).
3. **Freshness.** v1 computes the roll-up live from SOT rows at query 3. **Freshness.** v1 computes the roll-up live from SOT rows at query
time. A cache is an implementation option only if it is time. A cache is an implementation option only if it is
invalidated in the same transaction as any task, hierarchy, grant, invalidated in the same transaction as any task, hierarchy, grant,
or membership mutation that affects it, and it is never or membership mutation that affects it (each invalidator class
authoritative (§1.4). witnessed, §6.5), and it is never authoritative (§1.6).
## 5. Dependencies and phase timing ## 5. Dependencies and phase timing
@@ -117,55 +177,100 @@ row with contract 5 (`tool-gateway-mapping.md`).
after them and reads their surfaces; it defines none of them. after them and reads their surfaces; it defines none of them.
2. The roll-up query is one tool with one Gateway mapping row under 2. The roll-up query is one tool with one Gateway mapping row under
contract 5's regime (request/result/error/audit contracts there); contract 5's regime (request/result/error/audit contracts there);
this contract binds its semantics, not its wire shape. this contract binds its semantics (§2.5 defines the semantic
fields the contract 5 §4.1 DTO serializes), not its wire encoding.
3. Legacy task rows outside the typed lifecycle are not aggregated; 3. Legacy task rows outside the typed lifecycle are not aggregated;
the roll-up begins counting a workspace's tasks when they exist in the roll-up begins counting a workspace's tasks when they exist in
the typed surface. No roll-up obligation attaches to v1 before the typed surface. No roll-up obligation attaches to v1 before
ranks 13 exist. ranks 13 exist. Both rules are drafting additions disclosed in §7
(they trace to no §8 sentence).
## 6. Verification requirements ## 6. Verification requirements
Binding on the implementing PRs. Every witness names, in its Binding on the implementing PRs. Every witness names, in its
implementation, the exact endpoints/tools, tables, and fixtures it implementation, the exact endpoints/tools, tables, and fixtures it
exercises. The base fixture seeds two companies; under company A an exercises. The base fixture seeds two companies; under company A **two
estate with two platform-projects and three workspaces with known task estates with distinct, non-identical count profiles**: estate A1 with
counts across at least three statuses; under company B one workspace. two platform-projects and three workspaces, estate A2 with one
platform-project and one workspace, all with known task counts across
at least three statuses; under company B one workspace.
1. **Correctness witness:** for a reader authorized on all of company 1. **Correctness witnesses:** for a reader holding a direct company-A
A, roll-up totals at workspace, platform-project, estate, and grant, roll-up totals at every level equal the seeded sums — each
company level equal the seeded counts, keyed by the typed status workspace, each platform-project, estate A1 and estate A2
values, with no double count across the chain. separately (their distinct profiles asserted distinct), and the
2. **Scope witnesses:** a reader with a grant on one platform-project company total equal to A1+A2 — keyed by the typed status values,
sees exactly its subtree's counts at every queryable level; a with no double count across the chain. For a reader holding a
workspace-member-only reader sees exactly that workspace; for a direct estate-A1 grant, the estate-A1 result equals the A1 sum and
reader authorized on two of three sibling workspaces, the parent a company-A query returns company A with exactly A1's contribution
totals equal the two readable workspaces' sum. (estate A2 invisible). Each of the four grant levels — company,
estate, platform-project (below), workspace — has an explicit
case; none is simulated by unioning workspace access.
2. **Scope witnesses:** a reader with a direct grant on one
platform-project sees exactly its subtree's counts at every
queryable level; for a reader authorized on two of three sibling
workspaces, the parent totals equal the two readable workspaces'
sum. **Membership locality (§1.4):** a member-only reader queries
the workspace directly and receives its counts; the same reader
querying the workspace's parent (or any ancestor) receives the
§3.4-equivalent nonexistent-node response, and no ancestor
response for any other reader changes because of that membership.
3. **No-oracle witnesses:** the sibling-workspace response above 3. **No-oracle witnesses:** the sibling-workspace response above
contains no field disclosing the third workspace's existence contains no field disclosing the third workspace's existence
(closed-schema comparison against a reader authorized on all (closed-schema comparison against a reader authorized on all
three: identical field set, differing only in counts and visible three: identical field set, differing only in counts and visible
nodes); a query by an unauthorized reader naming company B returns nodes). **Partial-scope hidden node:** the reader who sees the
a response byte-equivalent in shape and status to the same query parent and two siblings queries the hidden third workspace by its
naming a random nonexistent id. real id — the response satisfies the §3.4 equivalence predicate
4. **Empty-vs-hidden witness (ruling):** a reader granted on an empty against the same query naming a nonexistent id, under one fixed
platform-project receives it with zero counts; with the grant request context with the declared volatile-field normalization.
deleted, the same query returns the nonexistent-node response. **Cross-tenant:** an unauthorized reader naming company B receives
5. **Mutation witnesses:** the roll-up surface rejects every mutating a response §3.4-equivalent to naming a nonexistent id. Each
equivalence check is executable byte comparison after the declared
normalization, not a shape judgment.
4. **Empty-vs-hidden witness (ruling):** a reader granted (direct
chain grant) on an empty platform-project receives it with zero
counts — every status key present at `0` (§2.5); with the grant
deleted, the same query returns the §3.4-equivalent
nonexistent-node response. An inherited-grant case: a company
grant makes an empty descendant platform-project visible with zero
counts.
5. **Cache-invalidation witnesses (conditional):** bound only if the
implementation caches — for EACH invalidator class, prime the
cache, commit one mutation of that class, and assert the next
query reflects it: a task status change, a task creation, a
membership removal (the member-readable workspace disappears from
its own node's next query), a workspace reparenting (both old and
new parent totals correct), and a grant revocation. A live
(cacheless) v1 implementation records that fact and the witnesses
bind at the PR that introduces a cache.
6. **Mutation witnesses:** the roll-up surface rejects every mutating
verb/command; a crafted attempt to issue a write through the verb/command; a crafted attempt to issue a write through the
roll-up's database path fails at the read-only boundary (§4.2); roll-up's database path fails at the read-only boundary (§4.2);
after any roll-up query, an SOT row diff is empty. after any roll-up query, the row diff is empty across BOTH the
6. **Revocation witness:** after revoking the grant that made a workspace tables and the hierarchy tables (contract 1 §6.7's
both-table zero-write assertion).
7. **Closed-schema witness:** the response is asserted field-exact
against the §2.5 enumeration — `node` (id/type/name), `totals`
(every typed status present, explicit zeros), `children`
(recursive, visible only) — and a response carrying any field
outside the enumeration fails the assertion (carve-out boundary,
§2.3). The denial envelope is asserted field-exact against
contract 5 §4.2's envelope.
8. **Non-authoritative and never-gate witnesses:** (a) a static
production import-graph inventory (hierarchy contract §6.3 style,
production code over `apps/` and `packages/`, tests excluded)
shows no production module imports the roll-up query module or its
result DTO into any SOT write path, any authorization/gating
decision path, or any persistence beyond the response lifetime —
asserted in both directions (the roll-up module's consumers are
enumerated and each is a presentation surface); (b) a behavioral
probe: with roll-up output artificially perturbed (test double),
no authorization outcome and no work-gating decision anywhere in
the fixture suite changes — proving no gate consumes it.
9. **Revocation witness:** after revoking the grant that made a
subtree readable, the next roll-up query excludes it (contract 2 subtree readable, the next roll-up query excludes it (contract 2
§6.2 bound). §6.2 bound).
7. **Closed-schema witness:** the response schema is asserted
field-exact against §2.1/§3.2 (identifiers, visible-node names,
status counts) — a field outside the closed set fails the
assertion (carve-out boundary, §2.3).
8. **Non-authoritative witness:** no production module imports roll-up
output into SOT writes; a static route inventory (hierarchy
contract §6.3 style, production code over `apps/` and `packages/`,
tests excluded) shows the roll-up query modules perform no SOT
writes and no module feeds roll-up responses into a write path.
## 7. Drafting additions (PRD §12.1 disclosure) ## 7. Drafting additions (PRD §12.1 disclosure)
@@ -176,16 +281,25 @@ read-only nature, and the no-oracle acceptance are traced to
1. The §3.2 node-visibility rule and the granted-but-empty behavior 1. The §3.2 node-visibility rule and the granted-but-empty behavior
(the ruling below). (the ruling below).
2. The §3.3 nonexistent-node response equivalence as the concrete 2. The §3.3–§3.4 nonexistent-node response equivalence, with its
no-oracle mechanism. normalized-byte-equality predicate, as the concrete no-oracle
mechanism.
3. The §4.2 read-only-transaction mechanical enforcement. 3. The §4.2 read-only-transaction mechanical enforcement.
4. The §4.3 cache option with transactional invalidation. 4. The §4.3 cache option with transactional invalidation and the
5. The §2.3 closed response schema as an amendment boundary. §6.5 per-invalidator witnesses.
5. The §2.5 closed response schema as an amendment boundary.
6. The §1.4 membership-locality rule — membership-only readability
contributes at the workspace node only (this contract's
reconciliation of `native-kanban-sot.md` §8.1.3 "authorized on"
with contract 2 §3.1/§7.4's workspace-local membership).
7. The §5.3 legacy-row exclusion and the §5.3 pre-rank no-obligation
rule.
## Ruling request ## Ruling request
Ruling requested (one decision): shall a node the reader is granted on Ruling requested (one decision): shall a node the reader holds an
but whose aggregation scope is empty appear in the roll-up with zero effective chain role on (direct or inherited, §1.2) but whose
counts (recommended — it lets the UI show a granted-but-empty subtree aggregation scope is empty appear in the roll-up with zero counts
(recommended — it lets the UI show a granted-but-empty subtree
honestly) — or, as the alternative, be indistinguishable from a honestly) — or, as the alternative, be indistinguishable from a
nonexistent node until it contains a readable workspace? nonexistent node until it contains a readable workspace?