Finish TEN-WP-0006-T01: specify guardrail model and boundary
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9f37b3cf6e
commit
0d1435c2d2
3 changed files with 299 additions and 1 deletions
|
|
@ -12,6 +12,7 @@ updated: "2026-08-16"
|
|||
depends_on:
|
||||
- TEN-WP-0005
|
||||
unblocks: []
|
||||
state_hub_workstream_id: "bddb6699-cb53-472a-9757-a3ed0eb0ce27"
|
||||
---
|
||||
|
||||
# TEN-WP-0006 - Guardrail and quota policy
|
||||
|
|
@ -46,8 +47,9 @@ them:
|
|||
|
||||
```task
|
||||
id: TEN-WP-0006-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "b0693ec3-b3a7-48fb-9f4f-baeedb488e9d"
|
||||
```
|
||||
|
||||
Write the guardrail domain contract before any code. Decide and document:
|
||||
|
|
@ -73,12 +75,50 @@ Done when the precedence rules are unambiguous, the fail-closed default is
|
|||
explicit for every grouping, and the doc states plainly what this service does
|
||||
*not* do (meter, bill, decide).
|
||||
|
||||
Done 2026-08-16: `docs/tenant-guardrail-policy.md`. Decisions worth carrying:
|
||||
|
||||
- **Limit kinds:** `spend` (integer minor units + ISO-4217, never floats),
|
||||
`entity_count`, `action_count`. Rate limiting stays out — it is a gateway
|
||||
concern with different latency and storage needs, not a free by-product.
|
||||
- **Registry, not open keys.** A limit key must be registered to resolve; an
|
||||
unregistered key errors (`unknown_limit_key`) rather than resolving to zero
|
||||
or unlimited. Zero would break a caller who merely misspelled a key;
|
||||
unlimited would fail open. Startup validation requires a default for every
|
||||
grouping, so an unmapped grouping cannot reach production.
|
||||
- **Precedence, per key:** override → plan → grouping → fail-closed floor. Per
|
||||
key, not per set, so a plan supplying `spend.monthly` does not wipe out a
|
||||
grouping-derived `entity.*`. The floor is only reachable if the registry is
|
||||
internally inconsistent, and reports provenance `fail_closed` so that state
|
||||
is visible rather than mistaken for policy.
|
||||
- **`unlimited` is an explicit sentinel**, never a default and never the result
|
||||
of absence. The rule is "no unset means unlimited" — that bans *inferring* an
|
||||
open ceiling, not deliberately declaring one.
|
||||
- **Reserved identifiers** get a `reserved` profile at layer 3 rather than
|
||||
falling through: spend 0 (infrastructure identities are not billable
|
||||
spenders), counts explicitly unlimited. Falling through would clamp the
|
||||
platform's own identity to zero and take the platform down with it.
|
||||
- **Lifecycle clamp** applies after resolution and may only reduce; `retired`
|
||||
clamps every limit to the floor while leaving reads working.
|
||||
- **Consumption does not live in this repo.** tenant-engine is a low-write
|
||||
policy authority with audit and CAS semantics; consumption is high-frequency
|
||||
telemetry with opposite needs, and holding it here would drag metering into a
|
||||
service `SCOPE.md` disowns it from. Reads return limits plus provenance only.
|
||||
|
||||
Two things flagged rather than settled: the non-`trial` grouping ceilings are
|
||||
conservative opening values needing product sign-off (only `trial` = 0 is
|
||||
canon), and **no repo owns metering** — the same gap `SCOPE.md` records for
|
||||
payment processing. Until canon names one, `flex-auth` can enforce
|
||||
ceiling-presence semantics but not consumption-relative ones. A zero budget is
|
||||
fully enforceable with no meter at all, which is why the ADR-0013 `trial`
|
||||
default lands immediately.
|
||||
|
||||
## T02 - Implement the guardrail domain model
|
||||
|
||||
```task
|
||||
id: TEN-WP-0006-T02
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "1b28d836-11d9-4ef3-967b-05bfa74c304a"
|
||||
```
|
||||
|
||||
Add a `guardrail/` module (the namespace `architecture.md` reserves) holding
|
||||
|
|
@ -100,6 +140,7 @@ plans, and conflicting overrides.
|
|||
id: TEN-WP-0006-T03
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "f1c93573-6322-4505-b738-7d66d67e60a8"
|
||||
```
|
||||
|
||||
Extend the `TenantStore` Protocol and both implementations (`InMemoryTenantStore`
|
||||
|
|
@ -126,6 +167,7 @@ semantics.
|
|||
id: TEN-WP-0006-T04
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "4a256517-2773-4679-ad57-2909f22ac8a4"
|
||||
```
|
||||
|
||||
Add the API surface, authorized through the existing flex-auth `WriteAuthorizer`
|
||||
|
|
@ -157,6 +199,7 @@ provider-neutral, and the OpenAPI document makes the semantics unambiguous.
|
|||
id: TEN-WP-0006-T05
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "92036fa3-9031-4b42-a67e-93196e236e08"
|
||||
```
|
||||
|
||||
Cover: every grouping's default, `trial` resolving to zero spend, precedence
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue