Add ADAPTIVE-WP-0010: plan-derived guardrail spend ceilings (proposed)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Offered by tenant-engine alongside TEN-WP-0010. Their resolver already has a
precedence layer where a plan-derived limit outranks a grouping default, and
it has no feed. Ceilings currently swing EUR 250 to EUR 20,000 on a headcount
proxy that nobody approved commercially.

Proposed rather than ready: the interface shape is not yet agreed with
tenant-engine, so T01 settles it and T02-T04 wait on that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
codex 2026-08-18 13:31:43 +02:00
parent 29223d39b7
commit 3d0f614f49

View file

@ -0,0 +1,138 @@
---
id: ADAPTIVE-WP-0010
type: workplan
title: "Plan-derived guardrail spend ceilings"
domain: financials
repo: adaptive-pricing
status: proposed
owner: codex
topic_slug: helix-forge
created: "2026-08-18"
updated: "2026-08-18"
---
# Plan-derived guardrail spend ceilings
Give `tenant-engine`'s guardrail resolver a commercial feed, so a tenant's
monthly spend ceiling derives from the plan it holds rather than from a
headcount band that nobody signed off on.
Offered by `tenant-engine` on 2026-08-17 alongside the TEN-WP-0010 grouping
change (`tenant-engine/docs/tenant-guardrail-policy.md`, TEN-WP-0006). The
precedence layer already exists in their resolver — a plan-derived limit
outranks a grouping default — and it has no feed today.
## Position
**The problem is ownership, not mechanism.** Ceilings currently resolve from
`tenant:<grouping>:<name>`'s middle segment. Reclassifying a tenant from `small`
to `enterprise` moves its ceiling from EUR 250 to EUR 20,000. That is a
commercial fact wearing operational clothing: `tenant-engine` states plainly
that every non-trial value is a conservative opener they chose so no grouping
started unbounded, and that nobody has approved them commercially. Only
`trial = 0` is canon, from ADR-0013.
This is the same shape the tenancy framework was written to stop — a
customer-affecting commercial commitment resolved by an operational default
with no recorded owner. `adaptive-pricing` owns plan terms, so this is ours to
feed.
**A plan is a better basis than a band.** The grouping is a proxy for size; the
plan is the actual agreement the customer entered. Deriving a blast-radius limit
from what someone bought is more defensible than deriving it from what bracket
their headcount fell into at onboarding — particularly now that grouping is
mutable (TEN-WP-0010) and the identifier's middle segment is historical rather
than authoritative.
**Two constraints, stated up front because they shape the design.**
*A ceiling is a guardrail, not a price.* The moment a limit derives from plan
terms there is pressure to present it as an entitlement the customer purchased.
That is a different product with different obligations — an entitlement implies
we will serve up to it, a guardrail only says we will stop beyond it. The design
must keep them distinguishable, and the customer-facing wording must not
promise capacity.
*A ceiling change is a commercial event.* It goes through the tier-terms
approval path rather than resolving silently, consistent with `SCOPE.md` on
customer-visible pricing changes and with the `ADAPTIVE-WP-0009` T04 gate.
**Not yet agreed with `tenant-engine`.** They offered the conversation; we said
yes in principle and flagged it as new scope. Nothing here is committed until
T01 produces a shape both repos accept.
## Establish The Interface Contract With tenant-engine
```task
id: ADAPTIVE-WP-0010-T01
status: todo
priority: high
```
Agree the shape of the feed jointly with `tenant-engine`: what
`adaptive-pricing` publishes, what their resolver consumes, and what happens on
stale or missing input.
Open questions to settle, not to assume:
- Is the ceiling a field on the pricing model, or a separate derivation from it?
A tier's ceiling is not obviously one of its charge components.
- What resolves for a tenant whose plan carries no ceiling — the grouping
default, or a refusal? Falling back silently reintroduces the problem.
- Which side holds the number when they disagree? `ADAPTIVE-WP-0009`'s answer
for assurance claims was that the tier definition is authoritative and other
records derive from it; the same logic likely applies, but say so explicitly.
- Does a plan change mid-period move the ceiling immediately or at renewal?
This is a commercial answer, not a technical one.
Record the outcome as a decision, since it binds two repos.
## Extend The Pricing Schema With Ceiling Terms
```task
id: ADAPTIVE-WP-0010-T02
status: wait
priority: medium
```
Blocked on T01.
Add the agreed representation to the canonical schema. Follow the
`assurance_claims` precedent from `ADAPTIVE-WP-0009-T02`: optional, typed,
defaulted, so every existing model stays valid and silent tiers keep resolving
from the grouping default until deliberately migrated.
Keep the guardrail/entitlement distinction structural rather than a comment —
if the field is named or shaped like an allowance, it will be read as one.
## Validate Ceiling Coherence
```task
id: ADAPTIVE-WP-0010-T03
status: wait
priority: medium
```
Blocked on T02.
Boundary-engine rules, explainable in the existing style:
- A ceiling below the tier's own expected usage is incoherent — the tier would
stop the customer doing what they paid for.
- A ceiling must not be presented in customer-facing wording as capacity.
- A tier with usage-based components and no ceiling is a declared choice, not an
oversight; require it to be explicit.
## Route Ceiling Changes Through Approval
```task
id: ADAPTIVE-WP-0010-T04
status: wait
priority: medium
```
Blocked on T02.
Extend the `ADAPTIVE-WP-0009-T04` governance gate so a tier acquiring or
changing a ceiling raises a blocking approval requirement at tier definition,
carrying the prior and proposed values. Reuse the existing comparison
machinery rather than adding a second gate; as with assurance claims, it must
stay a definition-time review and not become a per-campaign one.