Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e76a-f84c-7fb2-8309-fdbddf620a43
179 lines
8.5 KiB
Markdown
179 lines
8.5 KiB
Markdown
---
|
|
id: ADAPTIVE-WP-0010
|
|
type: workplan
|
|
title: "Plan-derived guardrail spend ceilings"
|
|
domain: financials
|
|
repo: adaptive-pricing
|
|
status: blocked
|
|
flavor: planning
|
|
owner: codex
|
|
topic_slug: helix-forge
|
|
created: "2026-08-18"
|
|
updated: "2026-09-28"
|
|
state_hub_workstream_id: "543e6398-d2cb-5105-977e-b90461adac3e"
|
|
---
|
|
|
|
# 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: wait
|
|
priority: high
|
|
state_hub_task_id: "7533431c-a0de-5ac8-a811-14e4604ad651"
|
|
```
|
|
|
|
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.
|
|
|
|
### 2026-09-28 interface review
|
|
|
|
Reviewed tenant-engine at commit
|
|
`4ce89969c0cc0ab4ba111f1b4009aa137c94a68b`, specifically
|
|
`docs/tenant-guardrail-policy.md`, `src/tenant_engine/guardrail/resolution.py`,
|
|
`src/tenant_engine/guardrail/model.py`, and the guardrail read in
|
|
`src/tenant_engine/app.py`. The existing consumer establishes these facts:
|
|
|
|
| Surface | Verified behavior |
|
|
| --- | --- |
|
|
| Resolver input | Optional `plan_limits: Mapping[str, LimitValue]`, already resolved for the tenant; no transport or plan lookup is defined here. |
|
|
| Monthly spend | Key `spend.monthly`; spend values carry integer minor units, currency and period. |
|
|
| Precedence | Per key: override, plan, grouping/reserved profile, fail-closed floor; lifecycle clamping follows resolution. |
|
|
| Missing plan value | Falls through to grouping; the resolver cannot distinguish a deliberately silent tier from an unavailable feed. |
|
|
| Unlimited | Explicit sentinel, never inferred from absence or parsing failure; assignment requires auditing. |
|
|
| Currency | Highest-precedence value wins; tenant-engine performs no exchange-rate conversion. |
|
|
| HTTP integration | The read endpoint calls `resolve_limits` without `plan_limits`; the documented feed remains unwired. |
|
|
|
|
This completes the local interface investigation, not the joint agreement.
|
|
T01 remains `wait`: the following proposal needs acceptance by tenant-engine
|
|
and commercial decisions by Bernd Worsch before T02 can define schema terms.
|
|
|
|
| Decision needed | Proposed basis for agreement (not adopted) |
|
|
| --- | --- |
|
|
| Representation and authority | Optional typed guardrail terms separate from charge components on the canonical tier; adaptive-pricing owns terms and revision, tenant-engine owns assignment. |
|
|
| Silent versus unavailable | Preserve grouping fallback for deliberately unmigrated tiers, with visible provenance. For migrated tiers, distinguish missing/stale/invalid terms from silence and return an explicit unavailable result rather than silently increasing the ceiling. Agree freshness and failure handling before wiring the feed. |
|
|
| Effective time | Carry an explicit approved effective time and revision. Bernd Worsch must choose immediate versus renewal application, including mid-period reductions and already-consumed spend. Do not infer this from an assignment timestamp. |
|
|
| Consumer handoff | Agree a versioned artifact or API and validation of plan identity, revision, amount, currency, period and freshness before converting to `LimitValue`; preserve overrides and lifecycle clamping. |
|
|
| Coherence basis | Define expected monthly spend in the same currency and period as the guardrail, with an evidence reference. Current charge prices alone do not establish expected spend or capacity. |
|
|
|
|
Unblock evidence is a recorded joint contract with those decisions resolved;
|
|
then implement T02, T03 and T04 under their existing task IDs. No new workplan
|
|
or task is required. No ceiling values, customer terms, or resolver behavior
|
|
were changed by this review.
|
|
|
|
## Extend The Pricing Schema With Ceiling Terms
|
|
|
|
```task
|
|
id: ADAPTIVE-WP-0010-T02
|
|
status: wait
|
|
priority: medium
|
|
state_hub_task_id: "7bd4dace-3505-5c68-977d-f80b30aebc77"
|
|
```
|
|
|
|
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
|
|
state_hub_task_id: "46e49880-21ff-580f-a2ad-82c8f1401afc"
|
|
```
|
|
|
|
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
|
|
state_hub_task_id: "a3eeffa6-57b0-5dc1-8b07-4b9b2a3d3534"
|
|
```
|
|
|
|
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.
|