adaptive-pricing/workplans/ADAPTIVE-WP-0009-tenancy-posture-alignment.md
codex 29223d39b7
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
ADAPTIVE-WP-0009: correct stale completion note; answer inbound
T02-T04 were completed 2026-08-18; the draft-8 outcome section still said
they were outstanding. Replies sent to railiance-platform (section 11
correction accepted; tier-P-floor trigger accepted as ours, narrowing our
earlier blanket monitoring caveat) and tenant-engine (grouping unaffected,
Q5 ownership agreed, plan-derived ceiling interface welcomed as new scope).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:28:26 +02:00

395 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: ADAPTIVE-WP-0009
type: workplan
title: "Tenancy Posture alignment — tier assurance claims"
domain: financials
repo: adaptive-pricing
status: finished
owner: codex
topic_slug: helix-forge
created: "2026-08-17"
updated: "2026-08-18"
state_hub_workstream_id: "ac4f4540-75a1-4dbf-9cb7-57e5be97499f"
---
# Tenancy Posture alignment — tier assurance claims
Align `adaptive-pricing` with **NetKingdom Tenancy Posture v0.1**
(`net-kingdom/canon/standards/tenancy-posture_v0.1.md`, proposed / draft-8),
raised by `rapp-postgres` on 2026-08-17.
The framework names this repo in three places: publish a posture vector
(§20.2), map plan tiers to minimum levels jointly with `tenant-engine` (§19 Q5),
and own the typed tier constraints that `railiance-platform` reconciles during
placement (§8.2). The earlier standing co-signature proposal was declined and
is no longer the policy.
## Position
**Nothing is blocked today.** All three models in
`projects/coulomb-pricing/data/pricing-models.json`
(`flat-899-eur-monthly`, `membership-plus-credits`, `membership-plus-overage`)
are silent on isolation, availability and retention. §11 constrains only tiers
that make such a claim, so the estate has no live over-claim to correct.
This workplan is therefore **preparatory guard-rail work**: build the ability to
express and validate an assurance claim *before* a tier is written that needs
one. It is deliberately additive — no existing catalog entry, schema consumer or
test changes meaning.
**The gap is structural, not editorial.** `PricingModel` today carries
`commitments` (untyped `kind`/`value`) and `eligibility` (free strings). Neither
can express "this tier requires `E3 P2 R2`", and `validate_pricing_model` has no
rule that would catch a tier claiming isolation it cannot evidence. §11.3
requires the mapping be "recorded once when the tier is defined" — this repo owns
tier definition, so this repo owns the recording surface.
## Draft-7 update (2026-08-17)
Draft-7 (`net-kingdom` commit `357109b`) applies three more reviews —
`audit-core`, `railiance-platform`, `flex-auth` — for eleven changes.
**Finding 6 is resolved. Withdraw it.** Decision 5.3 makes `n/a` an admissible,
conformant level, declared with a stated reason: "`P0` presupposes a database and
`R0` presupposes retained data". This is exactly what T01 needed and it arrived
from another repo's review. `tenancy.yaml` is also now the adopted convention for
the declaration (from `flex-auth`), carrying `current`, `target`, `reviewed`,
`gap`, `placement_exceptions`, `service_class`, per-path detail and — for a
provider — the §5.5 provider declaration. **T01 no longer proposes a format; it
fills in the one that exists.**
**Findings 1, 2, 3, 4, 5 and 7 all still stand, and there is a reason.** §11 is
byte-identical across drafts 5, 6 and 7. Four repos have now reviewed and none
touched it, because §11 is the commercial section and this repo owns it. Nobody
else was going to find these. That raises the priority of T06 rather than
lowering it.
**Every guessed posture has been too generous, on every repo that self-reported.**
`flex-auth` is `I1 A0 E2 P n/a R n/a``/v1/check` authenticates no caller, so
any workload with network reach can assert any subject and receive an
authoritative allow. `apps-pg` is `I0 A0 E0 P n/a R0`, where `R0` means no
backup configured at all. Combined with `tenant-engine` on SQLite, the
commercial read is blunt: **no tier can support an isolation, availability or
retention claim against the estate as it stands today.** Nothing is blocked
because nothing claims — but nothing is sellable either, and that is the fact a
tier author needs before drafting, not after.
**Providers declare separately now (§5.5).** A provider states what it *makes
reachable*; `apps-pg` provides `P1` while its own `P` is `n/a`. A tier's
guarantee therefore depends on two declarations, not one — the provider's
reachability and the consuming service's own level. T02's constraint artifact
must reference both or it will validate against half the picture.
## Draft-8 outcome (2026-08-17)
All six surviving findings were adopted, with one useful refinement to finding
4: no record is a copied derivative. The tier definition is authoritative for
its typed minimum and wording; the service declaration is authoritative for
current posture; the provider declaration is authoritative for available
infrastructure. Approval joins them and fails closed on stale or missing input.
The framework now has V0V4 availability, sanctioned honest language for
E3/P2/R2/V1, an R-plus-P rule for retention promises, P2 or an enforceable
governor for performance-differentiated tiers, and downgrade propagation to
dependent tier owners. §8.2 records the declined co-signature and its typed
constraint replacement.
T01 is complete in root `tenancy.yaml`. It declares the local observatory at
I0/A0/E0 and P/R `n/a`, rather than using `n/a` to hide its unauthenticated
single-user HTTP surface. T06 is complete because the amendments are in
draft-8. T02T04 were completed on 2026-08-18: the typed `assurance_claims`
block, the hard boundary constraint and the tier-definition approval gate are
all in place, with the suite green. No current model carries an assurance
claim, so the absence is a declared capability gap rather than a live
over-claim.
## Draft-6 update (2026-08-17)
The framework moved to **draft-6** (`net-kingdom` commit `2744ce7`) after this
workplan was written. It applies `tenant-engine`'s review only; **none of the
five findings below are addressed yet**, which is expected — T05 has not replied.
Three things in draft-6 change how we should proceed:
**The ratification test is proven, not theoretical.** tenant-engine assessed
itself, could not express itself in three places, and the document changed —
§4.1, §4.4, §5.2 and a new E registry exception. T06 now has live precedent to
cite rather than an invitation in §20.2.
**Draft-6 establishes exactly the shape findings 1 and 3 need.** The new
**registry exception** (declare an E level plus a named list of tables excluded
because they are registries, not tenant data) and **Decision 5.2** (a level
reports the weakest surface, not the best) both work by *level plus a named,
reviewable exception*. That is the pattern to reuse when proposing an
availability floor and a retention P floor — an amendment consistent with the
document's own new grammar is far cheaper to land than a novel one.
**The delivering service got weaker, and that is a sellability fact.**
`tenant-engine` self-corrected from a guessed `I2 A3 E2 P1 R1` down to
`I1 A2 E2 P— R0/R1` — acting identity arrives in the request body, three read
routes are unauthorized including the one `flex-auth` calls for `aal2`-class
decisions, and it is still on SQLite. Consequence for this repo: **no tier
resting on tenant-engine can make an isolation claim of any kind today.**
Nothing is blocked, because no tier makes one — but this is the concrete reason
T02/T03 should land before a tier author assumes otherwise.
## Findings against the framework
Raised for amendment, not as blockers. Detail belongs in T06.
Findings 15 were raised against draft-5 and all still stand in draft-6.
Findings 67 are new, from draft-6 itself.
1. **Availability has no ladder.** §11.3 and Q5 both require an availability
claim to map to "a minimum level the delivering service actually holds", but
the five axes are I, A, E, P, R — none is availability. §17 concedes it: "an
availability floor belongs in §11's minimum-level vocabulary alongside
isolation." The requirement exists; the vocabulary does not. The availability
half of Q5 is **not answerable as written**.
2. **§11.4 prohibits without sanctioning an alternative.** An "another tenant
cannot reach your data" claim requires E4, and E4 requires P3 (§3.2) — a
dedicated cluster per tenant. Correct, but it makes the strongest claim the
most expensive one, which is exactly the commercial pressure that produces
the over-claim §11 guards against. A prohibition with no compliant wording
beside it gets routed around.
3. **A retention claim needs a P floor, not just an R level.** Under §4.5 a
consumer's erasure horizon is the maximum across co-residents, so a tier's
retention promise can be broken by onboarding an unrelated customer, with no
change to the tier or its holder. §18 routes this to the consumer review
checklist; it also needs to bind the tier side, as the E4/P3 coupling does.
4. **Authority direction between tier and service is unstated.** Q5 puts the
mapping in the tier definition; §5 puts `placement_exceptions` in the
service's posture vector — and its own example says "see adaptive-pricing tier
definition". Two records, no stated primacy, silent drift in both directions.
5. **§8.3.1 constrains pricing design directly.** Co-residents are equal and no
resource governor exists, so a performance-differentiated tier is unsellable
below P2. This is a pricing-model validation rule, not only an ops fact.
6. **~~Off-ladder notation appears in a worked example.~~ RESOLVED in draft-7**
by Decision 5.3 (`n/a` is a level, and it is conformant). Kept for the record.
Draft-6 records
`tenant-engine` as `P—` (still on SQLite, below P0's meaning) and `R0/R1` (a
dual value). Neither is on any ladder. This is the same wall T01 hit from the
other side: the ladders have no way to say *not on this axis at all*, so the
first two repos to try both invented notation. Strengthens T01's proposed
`not-applicable` declaration form — it is now a demonstrated gap with two
independent instances, not a special plea from a design-time repo.
7. **`P—` is a sellability fact with no home.** A tier's minimum is only as good
as the delivering service's actual level, and draft-6 shows that level can be
revised *downward* by a self-report at any time. Nothing in §11 obliges a
service to notify the tiers that depend on it when its posture drops. A tier
recorded as conformant at definition time can silently become an over-claim
through no act of its own — the same coupling as finding 3, one layer up.
Propose: a posture downgrade (§6 permits them, declared) must notify the
owners of any tier whose recorded minimum it breaches.
## Publish The Repo Posture Vector
```task
id: ADAPTIVE-WP-0009-T01
status: done
priority: high
state_hub_task_id: "870c32dc-89dd-415f-b45f-f9484dd5119b"
```
Publish `adaptive-pricing`'s posture vector per §5 / §20.2, declared in-repo
(Decision 5.1).
The honest answer is that the datastore ladders do not fit: this repo is a
design-time analysis and definition surface with **no runtime multi-tenant
datastore**. §3.3 scopes the P and R ladders to "a service's primary datastore";
adaptive-pricing has none. It does hold customer-attributable commercial data
(member ledgers, payment records, LTV scenarios) as repo JSON under the
Coulomb proving ground — which is a data-handling fact worth stating, and not a
tenancy posture.
Completed in `tenancy.yaml`. P and R are `n/a` because there is no runtime
datastore. I/A/E/V are zero with reasons because the local observatory HTTP
surface exists and has no authentication, authorization, tenant enforcement or
availability position. A repo-level row of `n/a` would have concealed that
surface.
## Extend The Canonical Schema With Assurance Claims
```task
id: ADAPTIVE-WP-0009-T02
status: done
priority: high
state_hub_task_id: "4420edb0-c9ea-4d2c-850e-2435cbd57f34"
```
Add an **optional** `assurance_claims` block to the canonical pricing schema
(`adaptive_pricing_core/pricing_models.py`, `docs/PricingModelSchema.md`).
Shape: claim kind (`isolation` | `availability` | `retention` | `performance`),
customer-facing wording, the minimum levels it requires
(`{I,A,E,P,R,V}`), the
delivering service, and an evidence reference.
Constraints:
- Optional and defaulted. Absent block = silent tier = unaffected, per §11.
- Typed, not free-text — the point is that a validator can read it.
- Records the *internal* minimum only. §11.2 leaves marketing language free;
the customer-facing wording field exists so the two can be checked against
each other, not so the ladder leaks into a price page.
- Do not extend `commitments` for this. A commitment is something the customer
owes; an assurance claim is something we owe them.
Completed 2026-08-18. `PricingModel.assurance_claims` is optional and defaults
to an empty tuple, preserving every existing silent tier. The validator checks
claim identity, kind, customer wording, typed in-range axis minima, delivering
service, evidence, erasure horizon and mechanism. The canonical shape and its
definition-time semantics are documented in `docs/PricingModelSchema.md`.
## Add Boundary-Engine Validation For Claims
```task
id: ADAPTIVE-WP-0009-T03
status: done
priority: high
state_hub_task_id: "96c3340d-2429-4901-800c-2c7144ab14f4"
```
Extend `adaptive_pricing_core/boundary_engine.py` with explainable rules, in
keeping with the existing explainable-validation design:
- A claim present ⇒ minimum levels recorded and a delivering service named.
- "Cannot reach" isolation wording ⇒ `E4`, and `E4``P3` (§3.2, §11.4).
- Retention claim ⇒ an R level **and** a P floor sufficient for the promised
erasure horizon (finding 3). On shared substrate the horizon is not ours to
promise.
- "Deleted data is gone" ⇒ `R4`, or a disclosed erasure horizon (§11.4). Where
R4 is key-destroyed, the wording must not imply regulatory endorsement (§4.5).
- Performance-differentiated tier ⇒ `P2` or above (§8.3.1, finding 5).
Each failure must explain which axis and which framework section it answers to.
A validator that says "non-conformant" without naming the axis reproduces the
conflation §3 exists to end.
Completed 2026-08-18. The hard `assurance-claims` boundary constraint is silent
for tiers without claims and produces axis- and section-specific failures for
the E4/P3 coupling, shared-substrate erasure horizons, R4 wording,
key-destruction language, performance governors and availability failure scope.
Focused and full-suite regression coverage is green.
## Gate Claims At Tier Definition
```task
id: ADAPTIVE-WP-0009-T04
status: done
priority: medium
state_hub_task_id: "9821bc6b-d8ec-41e2-b4ea-ebf1f2dc91a6"
```
Wire the claim check into `adaptive_pricing_core/governance.py` as an approval
requirement, so §11.3's "recorded once when the tier is defined" is a gate rather
than a convention. A tier acquiring or changing an assurance claim requires
human approval with the evidence reference attached — consistent with SCOPE's
rule that customer-visible pricing changes are never autonomous.
Explicitly **not** per-campaign review. §11.3 says the review happens at tier
definition; a gate that fires more often than that will be worked around.
Completed 2026-08-18. `assess_tier_definition_assurance()` compares the new
tier definition with its previously approved definition and emits blocking
approval requirements only for added, changed or removed claims. Each gate
carries the claim's evidence reference. An unchanged definition proceeds, and
the API explicitly warns publication/campaign callers not to turn it into a
recurring gate.
## Answer Q5 And Q2
```task
id: ADAPTIVE-WP-0009-T05
status: done
priority: high
state_hub_task_id: "3aa82ae3-4a79-4a18-a74e-668e94652ecd"
```
Blocked on T02/T03 for Q5, and on Bernd's decision for Q2.
**Q5 — tier → minimum level mapping,** jointly with `tenant-engine`. Answerable
for isolation, retention and availability once T02/T03 land. V now supplies the
availability vocabulary; the typed mapping still waits on implementation.
`tenant-engine` owns which plan a tenant holds; this repo owns the terms — state
that split when replying so the seam is recorded.
**Q2 — placement co-signature. Decided 2026-08-17 by Bernd: decline the
signature, bind via artifact instead.**
adaptive-pricing publishes tier minimums as typed constraints (T02/T03) and asks
railiance-platform to validate placement against them. Ownership stays single;
the check runs every time rather than when someone remembers to route it. §8.2's
concern is right — placement decided purely on operational grounds does constrain
what can be sold — but a signature is the wrong instrument for a constraint that
is machine-checkable, and tier minimums are machine-checkable by construction.
**No interim veto.** A scoped veto was considered and dropped: its only purpose
would be protecting existing claim-bearing tiers, and there are none. Not being
live is what makes the direct move safe.
**Named revisit trigger** — if a tier carrying an isolation, availability or
retention claim is drafted before the constraint artifact lands, reopen this
decision rather than assume it. Declining must not be open-ended.
Accept the P ladder, the triggers and §8.1's monitoring obligation
unconditionally. Caveat to raise: §8.1 says triggers MUST be monitored and never
says by whom; if that defaults to adaptive-pricing it is a standing obligation on
a repo with no on-call. Propose it is railiance-platform's.
Reply on the State Hub to message `6d5293ca-4f69-46a6-a78e-b469e626b83d`.
Completed 2026-08-18. Q5 is implemented as the typed `assurance_claims`
artifact: adaptive-pricing owns the authoritative tier wording and minima;
tenant-engine owns assignment of a tenant to the tier id and does not copy the
terms; railiance-platform joins current service/provider declarations during
placement reconciliation. The current three Coulomb tiers remain explicitly
silent and therefore define no minima. Q2 remains declined: placement has one
owner and a machine-enforced artifact boundary, not a nominal co-signature.
railiance-platform owns trigger monitoring because adaptive-pricing has no
runtime/on-call surface. The implementation answer was sent on State Hub after
the full suite passed (54 tests).
## Contribute Framework Amendments Back
```task
id: ADAPTIVE-WP-0009-T06
status: done
priority: medium
state_hub_task_id: "489cffb7-9ec3-4335-a93c-0b276a843f8d"
```
Submit the §"Findings" items to `net-kingdom` while v0.1 is still `proposed`.
**Frame them in draft-6's own grammar.** The registry exception and Decision 5.2
both work by *declared level plus a named, reviewable exception*, and draft-6
adopted tenant-engine's corrections wholesale because the ratification test said
it must. Amendments shaped like the ones already accepted will land; novel
structure will not. Cite `2744ce7` as precedent.
Proposed amendments:
- **An availability axis.** Prefer adding one over striking availability from
§11.3/Q5, because §17's "no P1 tenant has HA" is a hard sellability fact that
needs somewhere to live. Sketch: `V0` no position, `V1` restart recovery on a
single instance (what `platform-pg` is today), `V2` replicated failover,
`V3` multi-zone with declared RTO/RPO. Note the letter collision — `A` is
Authorization — which is itself a small argument the framework should settle
rather than inherit.
- **A sanctioned fallback phrasing in §11**, naming what P2/E3 honestly
delivers (noisy-neighbour behaviour, capacity predictability, an independent
restore path) so a tier author has a compliant thing to say and not only a
prohibition. This is the amendment most likely to prevent a real over-claim.
- **A Decision in §4.5** binding a retention claim to a P floor, as §3.2 binds
E4 to P3.
- **A primacy statement** for finding 4: the tier definition is authoritative
for the minimum; a service's `placement_exceptions` is derived and must
reconcile against it.
- **An off-ladder declaration form** covering both `no-runtime-datastore` (T01)
and draft-6's ad-hoc `P—` (finding 6). One notation, two instances already.
- **A downgrade-notification obligation** in §11 (finding 7): a declared posture
drop must reach the owners of any tier whose recorded minimum it breaches.
Route via `policy-nexus` if it owns canon publication; otherwise direct to
`rapp-postgres` as the raising agent and `net-kingdom` as canon owner.
Completed in NetKingdom draft-8. All surviving findings were incorporated;
finding 6 remains withdrawn as resolved by draft-7.