State tenant-engine's tenant relation in the write-api package, v3.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 1m24s

tenant-engine named the relation in TEN-DEC-2026-002: `tenant` is the target
tenant record and always equals `resource.id`; the write API is cross-tenant
by design and `tenant.guardrail.read` does not differ. v2 carried no tenant
rule and a constant fixture tenant, so the deliberate scope and an omitted
rule were indistinguishable.

tenant-engine.write-api.mutate v3 (FLEX-DEC-2026-016):
- allowed requires tenant_is_target; a mismatch or absent tenant is denied
  tenant_not_target (object.get, so an absent key names the right cause).
- the cross-tenant scope is stated in the package and quantified by
  test_tenant_never_changes_effect over every action, three subjects and
  four tenants, with guards against passing by denying everything.
- fixtures rotate tenant across four tenants; five cross-tenant allows and
  two tenant_not_target denies added (42 fixtures, 33 tests, all pass).
- user-engine's tenant:platform exclusion is named as a fixed-record rule,
  not a subject/tenant relation, and tested separately.

Closes FLEX-WP-0022 (T01, T02 done). Also records TEN-IN-0004 and
SECRETS-IN-0002 on FLEX-WP-0020 and acknowledges the GH-DEC-2026-017
replies on FLEX-WP-0030-T04.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 63291@bnt-lap001
Assistant-Session: 8bd77868-ca68-4f49-bb1e-d539ecc0d703
This commit is contained in:
tegwick 2026-09-21 07:39:57 +02:00
parent bd3d270531
commit 3081067325
17 changed files with 783 additions and 150 deletions

View file

@ -4,7 +4,7 @@ type: workplan
title: "Tenant scoping is unstated in tenant-engine and untested in two more packages"
domain: infotech
repo: flex-auth
status: active
status: finished
flavor: implementation
depends_on:
- FLEX-WP-0021
@ -18,7 +18,7 @@ related_workplans:
- FLEX-WP-0010
- FLEX-WP-0014
created: "2026-09-06"
updated: "2026-09-15"
updated: "2026-09-21"
state_hub_workstream_id: "804c588c-f47a-50c4-bdd7-51b24bbf9539"
---
@ -60,7 +60,7 @@ look identical in the artifact. That is the same publishing-shape argument
```task
id: FLEX-WP-0022-T01
status: progress
status: done
priority: high
state_hub_task_id: "a84dcee5-9426-5b30-8dd3-f4c076d00174"
```
@ -86,11 +86,19 @@ policy rule inferred from reading their code would make `flex-auth` the author
of their tenancy model, which is the boundary `FLEX-WP-0021-T01` exists to hold.
T02 stays `wait` rather than being guessed forward.
2026-09-21: answered by tenant-engine in its own record, `TEN-DEC-2026-002`
(hub decision `ecaebbb7-fe90-4235-a79f-23457e9727bc`) and
`docs/flex-auth-integration.md`, commit `d132db0`; message `3d3de8bc`. Read
from the committed record, not the message. `tenant` is the **target tenant
record**; it always equals `resource.id`; none of the nine write actions is
refused cross-tenant, deliberately; `tenant.guardrail.read` does not differ and
must not. Gate met: the relation is named by tenant-engine.
## 2. Encode the relation, or record that there is none
```task
id: FLEX-WP-0022-T02
status: wait
status: done
priority: high
state_hub_task_id: "712ac826-845f-54bd-8446-f11258d21eb4"
```
@ -107,6 +115,20 @@ tenants" is a rule a reviewer can check. Silence is not.
Either way the fixtures must vary `tenant`, so the suite reports on the field.
2026-09-21: done as `tenant-engine.write-api.mutate` **v3**
(`FLEX-DEC-2026-016`). Both of tenant-engine's commitments are encoded:
`allowed` requires `tenant_is_target` (non-empty `tenant` equal to
`resource.id`; otherwise `deny` / `tenant_not_target`), and the package states
the cross-tenant scope in prose and quantifies it in
`test_tenant_never_changes_effect` (every action, three subjects, four
tenants). Fixtures now rotate `tenant` across `tenant:friendly:binky`,
`tenant:acme:prod`, `tenant:platform`, `tenant:trial:demo-company`, with five
cross-tenant allows and two `tenant_not_target` denies added: 42 fixtures and
33 embedded tests pass, `go test ./...` green. One boundary reported back to
tenant-engine rather than papered over: `user-engine`'s grant excludes the
fixed record `tenant:platform`, which is target-dependent but not a
subject/tenant relation.
## 3. Vary tenant in the two suites that hold it constant
```task