Accept ActionAuthorization deferral; fix the state-hub authority constant
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 40s

approval-engine filed APPROVAL-IN-0002: secrets-engine built its PEP
validator against our ActionAuthorization schema, pointed it at
GET /v1/approvals/{id}/claim, and it rejects every response. Both
envelopes declare schema_version 0.1, so it fails late and reads like an
approval-engine outage rather than a contract mismatch.

FLEX-DEC-2026-006 accepts the deferral and argues against flex-auth's own
proposal. The composed object had the PIP republish our decision, which
crosses the same layer boundary we invoked to decline authentication
evidence and to win section 17's schema. The claim-plus-DecisionEnvelope
split drops no check; each verification lands on the layer that owns it.

approval-engine asked, before the decision, whether the open G3 finding
argues for ratifying now. It does not: G3 is already closed the other
way. FLEX-WP-0019 added lifetime to the DecisionEnvelope itself, required
on every allow by schema conditional, published 2026-09-02. The trigger
resolved by adding a field rather than by composition, so the decision
stands alone and needs no bundle.

The provenance.authority == state-hub constant is our defect and is
fixed at source. It came from examples/caring/action_authorization.json,
which contradicted the same contract's ownership section. That fixture
now names approval-engine as the approval fact's authority and flex-auth
as the decision's, and its stale secrets-engine.lifecycle pin is
corrected to the reserved coordinate from FLEX-DEC-2026-005.

The contract doc and schema are marked deferred-not-withdrawn so no
other consumer builds a validator against them. The execute-time half is
untouched: /v1/check, binding, the canonical digest, and
flex-auth.decision-record.v1 stay published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JTbVXpEiXA7mNJVpDnEPcB

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 412054@bnt-lap001
Assistant-Session: 3968fae1-8d59-4209-9bd6-c22594b8ab19
This commit is contained in:
tegwick 2026-09-06 01:30:04 +02:00
parent d144feb2e9
commit 74bfb3b664
5 changed files with 295 additions and 22 deletions

View file

@ -744,3 +744,137 @@ that order: publish the package, then pin it.
published. Nothing in this record authorises a fallback value.
- No change to `flex-auth.decision-record.v1`. The digest join secrets-engine
built against it is correct as described.
## FLEX-DEC-2026-006 — ActionAuthorization deferral accepted; G3 is closed and does not argue for ratifying it
```yaml
id: FLEX-DEC-2026-006
kind: decision
title: ActionAuthorization deferral accepted; G3 is closed and does not argue for
ratifying it
status: resolved
origin: cross-repo
origin_ref: APPROVAL-IN-0002 / GH-DEC-2026-003
owner: flex-auth
affects:
- flex-auth
- approval-engine
- gate-house
- secrets-engine
requested_dispositions:
- accept
- contest
decided_by: flex-auth (access-engine / PDP)
rationale: 'Accepted, and flex-auth argues against its own proposal. The composed
ActionAuthorization object was never ratified; the approval-claim plus DecisionEnvelope
split lands each check on the layer that owns it and drops none. approval-engine
asked whether the open G3 finding (DecisionEnvelope carries no lifetime) argues
for ratifying the composed object now. It does not, because G3 is already closed
the other way: FLEX-WP-0019 added lifetime to the DecisionEnvelope itself, required
on every allow by schema conditional, published 2026-09-02. The revisit trigger
is spent, and it resolved by adding a field rather than by composition, so the decision
now stands alone and needs no bundle. Separately, the provenance.authority == state-hub
constant is flex-auth defect: it came from examples/caring/action_authorization.json,
which contradicted our own ownership section. Corrected at source, with the schema
and contract doc marked deferred-not-withdrawn so no other consumer builds a validator
against them.'
created: '2026-09-05T23:29:20.156770Z'
updated: '2026-09-05T23:29:20.156770Z'
```
## Context
`approval-engine` filed `APPROVAL-IN-0002` with gate-house and raised it here
directly, because the request touches flex-auth's artifact and accepting the
shelving is flex-auth's call.
The trigger was concrete damage: secrets-engine built its PEP validator against
the `ActionAuthorization` schema in
`docs/action-bound-authorization-contract.md`, then pointed it at
`GET /v1/approvals/{id}/claim`. It rejects every response. Both envelopes
declare `schema_version: 0.1`, so the mismatch surfaces late as a field or
authority error that reads like an `approval-engine` outage rather than a
contract mismatch.
## Disposition
**Accepted.** The approval-claim is the step-1 artifact; `ActionAuthorization`
is not required on that path; a PEP validates across the claim (approval fact)
and flex-auth's step-2 `DecisionEnvelope` (exact `CheckRequest` match and policy
pin).
flex-auth argues **against its own proposal** here. The composed object had the
PIP republish flex-auth's decision, which is the part that was wrong: it moves an
artifact across a layer boundary that the estate's own §2 rule says it should not
cross. flex-auth used that rule to decline authentication evidence in
`FLEX-DEC-2026-002` and to win §17's decision-record schema in
`FLEX-DEC-2026-003`. It applies the same way when it costs us the object.
The split drops no check. Each of the five verifications in the contract's
"Required verification" section lands on the layer that owns it — status,
validity window, supersession, and distinct authenticated approvers on the
approval-claim; effect, binding match, digest, and policy pin on the
`DecisionEnvelope`.
### Answer to the question asked before the decision: G3 does not argue for it
`approval-engine` named the open G3 finding — `DecisionEnvelope` carries no
lifetime — as a revisit trigger, *if* it is settled by composition rather than
by adding a lifetime field, and asked flex-auth to say so **before** the
decision if G3 is the strongest case for ratifying now.
It is not, and the reason is that **G3 is already closed, the other way.**
`FLEX-WP-0019` closed it by adding the field. `schemas/decision_envelope.schema.json`
now carries a conditional requiring `lifetime` whenever `effect` is `allow`; the
duration comes from the policy package's `allow_ttl` with a `15m` engine default,
and a package declaring `allow_ttl: none` produces a deny with reason
`allow_lifetime_unstated` rather than a standing grant. This was published in
`docs/decision-record-contract.md` on 2026-09-02.
So the trigger is spent, and it resolved on the branch that removes the argument
rather than the one that supports it. A `DecisionEnvelope` now states its own
end without borrowing `ActionAuthorizationValidity`. The bundle is not needed to
give an allow a lifetime, which was the only structural thing it did that the
split does not.
The 2026-08-29 alignment review's table still shows G3 `open`. That file is a
dated review record and is not rewritten; this record supersedes its status.
### The `provenance.authority == "state-hub"` constant is flex-auth's defect
Correct on the merits, and correctable regardless of how the deferral landed.
`approval-engine` located a real contradiction inside one flex-auth document
set, and the source is narrower than the report suggested — it is a fixture, not
prose:
```json
"provenance": { "authority": "state-hub" }
```
That was the tail of `examples/caring/action_authorization.json`, directly
contradicting the same contract's ownership section: *"State Hub decision
records are coordination and provenance evidence; they are not the runtime
approval authority."* A consumer reading the example rather than the paragraph
gets the wrong constant, which is what happened.
Corrected at source. The example now names `approval-engine` as the approval
fact's authority and flex-auth as the decision's, and says State Hub is neither.
Its stale `secrets-engine.lifecycle` policy pin is also corrected to the
reserved-but-unpublished coordinate from `FLEX-DEC-2026-005`.
## Consequences
- `docs/action-bound-authorization-contract.md` status is now **deferred, not
withdrawn**, with the two-step split stated up front and an explicit "do not
build a validator against it" warning. The `PROPOSED` material is retained as
the record of what was proposed.
- `schemas/action_authorization.schema.json` carries the same deferral in its
`description`, so a machine reader sees it too. `schema_version` stays `0.1`;
bumping it would imply a ratified successor that does not exist.
- The execute-time half is untouched and stays published: `POST /v1/check`,
`binding`, the canonical request digest, and `flex-auth.decision-record.v1`.
Nothing secrets-engine built against the digest join is invalidated.
- No revisit trigger remains open on flex-auth's side. If the composed object is
ever revisited, it needs a fresh argument, not G3.