This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
# Roll-up Projection Contract (S2 contract 8)
|
||||
|
||||
Status: DRAFT — awaiting ratification (webui-audit S2, contract 8 of 9).
|
||||
Authority: `native-kanban-sot.md` §8 (A1 amendment) — "task and status
|
||||
visualization bubbles up the hierarchy as aggregation over workspaces
|
||||
the reader is authorized on" (§8.1.3); roll-up is never a write and
|
||||
bubble-up views are generated projections, non-authoritative and never
|
||||
import sources (§8.2.2); the express, narrow carve-out from the
|
||||
portfolio-analytics non-goal covers per-workspace task counts and
|
||||
statuses aggregated up the parent chain over readable workspaces, and
|
||||
nothing beyond that boundary (§8.2.4); acceptance requires that roll-up
|
||||
endpoints cannot mutate state and that a reader sees aggregates only
|
||||
over workspaces they are authorized on, with no cross-tenant existence
|
||||
oracles (§8.3). A5 rank 5 names the deliverable: an authorized
|
||||
read-only roll-up query over only readable workspaces, at every
|
||||
hierarchy level, as its own non-mutating query tool, dependent on ranks
|
||||
1–3.
|
||||
|
||||
This contract binds the projection semantics (§2), reader authorization
|
||||
semantics (§3), read-only enforcement (§4), dependencies and phase
|
||||
timing (§5), witnesses (§6), and disclosed drafting additions (§7). It
|
||||
defines the roll-up only: hierarchy shape stays with contract 1
|
||||
(`hierarchy-schema.md`), grant vocabulary and evaluation with contract 2
|
||||
(`rbac-grant-model.md`), the task lifecycle and status taxonomy with
|
||||
`native-kanban-sot.md`'s typed surface, and the tool↔Gateway mapping
|
||||
row with contract 5 (`tool-gateway-mapping.md`).
|
||||
|
||||
## 1. Definitions
|
||||
|
||||
1. **Roll-up**: the read-only projection of per-workspace task counts
|
||||
by status, aggregated up the contract 1 parent chain (workspace →
|
||||
platform-project → estate → company).
|
||||
2. **Readable workspace** (for a given reader): a workspace where the
|
||||
reader's authorization permits reading task state — either
|
||||
workspace membership under the SOT's own membership rules
|
||||
(REQ-ID-001), or an effective chain role at that workspace per
|
||||
contract 2 §3 evaluation. The role vocabulary is contract 2 §2's;
|
||||
this contract adds no role and no new authority source.
|
||||
3. **Aggregation scope** (of a hierarchy node, for a given reader):
|
||||
the set of readable workspaces in that node's descendant subtree
|
||||
(for a workspace node, the workspace itself if readable).
|
||||
4. **Projection**: a generated, non-authoritative view in the sense of
|
||||
`native-kanban-sot.md` §3 invariant 5 — derived from SOT rows,
|
||||
never an import source, never authoritative.
|
||||
|
||||
## 2. Projection semantics
|
||||
|
||||
1. **Aggregate content.** The roll-up for a node reports, per
|
||||
workspace in the reader's aggregation scope and as subtree totals:
|
||||
task counts keyed by the typed lifecycle's status values (owned by
|
||||
`native-kanban-sot.md`; this contract introduces no status), and
|
||||
nothing else. Direct count/status aggregation is the entire
|
||||
surface.
|
||||
2. **Every level.** The roll-up is queryable at workspace,
|
||||
platform-project, estate, and company level. A node's totals equal
|
||||
the sum over its aggregation scope; chain resolution is contract 1
|
||||
§2.5's (every workspace resolves to exactly one chain), so no
|
||||
workspace is counted twice and none is orphaned.
|
||||
3. **Carve-out boundary.** Everything beyond direct count/status
|
||||
aggregation — metrics, trends, forecasting, scoring, velocity,
|
||||
cross-workspace derived analytics, dashboards computed across
|
||||
workspaces — remains a `native-kanban-sot.md` §6 non-goal
|
||||
(§8.2.4). The response schema is closed (§6.7 witness): adding any
|
||||
field beyond identifiers, names of visible nodes, and status
|
||||
counts is an amendment to this contract.
|
||||
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
|
||||
is never imported, persisted as authoritative state, or used to
|
||||
gate work.
|
||||
|
||||
## 3. Reader authorization semantics
|
||||
|
||||
1. **Scope rule.** A reader's roll-up over any node aggregates ONLY
|
||||
the reader's aggregation scope (§1.3). An unreadable workspace
|
||||
contributes nothing to any total — not a count, not a row, not a
|
||||
presence marker.
|
||||
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 holds a grant on the node itself (contract 2 §3.2 makes the
|
||||
node and its subtree the grant's domain). Per the ruling below, a
|
||||
granted node whose aggregation scope is empty appears with zero
|
||||
counts; a node the reader has no authority on does not appear at
|
||||
all.
|
||||
3. **No existence oracle.** The response MUST NOT disclose the
|
||||
existence, count, name, or any property of unreadable workspaces
|
||||
or of nodes outside §3.2 visibility — no "N workspaces hidden"
|
||||
fields, no total-vs-visible discrepancy fields. A query naming a
|
||||
node outside §3.2 visibility returns the same response as a query
|
||||
naming a nonexistent node (fail closed, `rbac-grant-model.md` §3.5
|
||||
pattern: a decision path that cannot read grant state denies).
|
||||
4. **Live evaluation.** Readability is evaluated per contract 2 §3.5
|
||||
(live rows or transactionally-invalidated cache). Revocation
|
||||
propagates per contract 2 §6: the next roll-up query decided after
|
||||
the revoking transaction commits excludes the revoked scope.
|
||||
|
||||
## 4. Read-only enforcement
|
||||
|
||||
1. **Never a write.** No roll-up path may mutate, claim, order, or
|
||||
gate work in any workspace (§8.2.2). The roll-up ships as a
|
||||
non-mutating query tool (A5 rank 5) — a query surface with no
|
||||
command counterpart.
|
||||
2. **Mechanical enforcement.** The implementing PR executes roll-up
|
||||
database work inside read-only transactions (or an equivalently
|
||||
privilege-restricted path), so a mutation attempt fails at the
|
||||
database boundary, not only by convention.
|
||||
3. **Freshness.** v1 computes the roll-up live from SOT rows at query
|
||||
time. A cache is an implementation option only if it is
|
||||
invalidated in the same transaction as any task, hierarchy, grant,
|
||||
or membership mutation that affects it, and it is never
|
||||
authoritative (§1.4).
|
||||
|
||||
## 5. Dependencies and phase timing
|
||||
|
||||
1. The roll-up depends on A5 ranks 1–3: contract 1's hierarchy tables
|
||||
(the parent chain), contract 2's evaluator (readability), and the
|
||||
typed Kanban lifecycle (the task state being counted). It ships
|
||||
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
|
||||
contract 5's regime (request/result/error/audit contracts there);
|
||||
this contract binds its semantics, not its wire shape.
|
||||
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 typed surface. No roll-up obligation attaches to v1 before
|
||||
ranks 1–3 exist.
|
||||
|
||||
## 6. Verification requirements
|
||||
|
||||
Binding on the implementing PRs. Every witness names, in its
|
||||
implementation, the exact endpoints/tools, tables, and fixtures it
|
||||
exercises. The base fixture seeds two companies; under company A an
|
||||
estate with two platform-projects and three workspaces 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
|
||||
A, roll-up totals at workspace, platform-project, estate, and
|
||||
company level equal the seeded counts, keyed by the typed status
|
||||
values, with no double count across the chain.
|
||||
2. **Scope witnesses:** a reader with a grant on one platform-project
|
||||
sees exactly its subtree's counts at every queryable level; a
|
||||
workspace-member-only reader sees exactly that workspace; for a
|
||||
reader authorized on two of three sibling workspaces, the parent
|
||||
totals equal the two readable workspaces' sum.
|
||||
3. **No-oracle witnesses:** the sibling-workspace response above
|
||||
contains no field disclosing the third workspace's existence
|
||||
(closed-schema comparison against a reader authorized on all
|
||||
three: identical field set, differing only in counts and visible
|
||||
nodes); a query by an unauthorized reader naming company B returns
|
||||
a response byte-equivalent in shape and status to the same query
|
||||
naming a random nonexistent id.
|
||||
4. **Empty-vs-hidden witness (ruling):** a reader granted on an empty
|
||||
platform-project receives it with zero counts; with the grant
|
||||
deleted, the same query returns the nonexistent-node response.
|
||||
5. **Mutation witnesses:** the roll-up surface rejects every mutating
|
||||
verb/command; a crafted attempt to issue a write through the
|
||||
roll-up's database path fails at the read-only boundary (§4.2);
|
||||
after any roll-up query, an SOT row diff is empty.
|
||||
6. **Revocation witness:** after revoking the grant that made a
|
||||
subtree readable, the next roll-up query excludes it (contract 2
|
||||
§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)
|
||||
|
||||
Proposed drafting additions, visible here for ratification, each
|
||||
severable; the aggregation itself, its authorization scope, its
|
||||
read-only nature, and the no-oracle acceptance are traced to
|
||||
`native-kanban-sot.md` §8 and are not additions:
|
||||
|
||||
1. The §3.2 node-visibility rule and the granted-but-empty behavior
|
||||
(the ruling below).
|
||||
2. The §3.3 nonexistent-node response equivalence as the concrete
|
||||
no-oracle mechanism.
|
||||
3. The §4.2 read-only-transaction mechanical enforcement.
|
||||
4. The §4.3 cache option with transactional invalidation.
|
||||
5. The §2.3 closed response schema as an amendment boundary.
|
||||
|
||||
## Ruling request
|
||||
|
||||
Ruling requested (one decision): shall a node the reader is granted on
|
||||
but whose 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
|
||||
nonexistent node until it contains a readable workspace?
|
||||
Reference in New Issue
Block a user