gate-house resolved APPROVAL-IN-0002. Two changes fell to this repo.
1. Split validate_action_authorization. The claim from approval-engine now
carries the approval fact (issuer, valid_now, consumption, binding digest,
freshness, reason_code) via approval_claim.validate_approval_claim; the
flex-auth DecisionEnvelope carries the decision (effect, binding match,
request digest, lifetime, policy pin) via validate_decision_envelope.
ActionAuthorization is deferred and never ratified (FLEX-DEC-2026-006) and
cannot be served from a step-1 call; nothing validates it now.
2. Dropped AUTHORITY = "state-hub" and the provenance.authority requirement.
State Hub is a read model with no runtime approval authority, so the check
failed closed against every correctly issued record. flex-auth traced the
constant to their own fixture and fixed it at source.
Two consequences recorded rather than buried: there are now two distinct
digests over the same action (approval-engine native over
{action,actor,principal,purpose,target}, and the flex-auth CheckRequest
digest) which are never compared to each other; and the distinct-approver
threshold is no longer checked here, since the claim exposes no approver
entries and approval-engine folds it into valid_now.
The canonical request digest is unchanged and its contract test is preserved
verbatim. Production still fails closed. 251 tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
approval-engine (APPROVAL-IN-0002) established that ActionAuthorization is a
proposed, unratified object and that the claim body is an approval-claim
envelope instead. The join in 627810b therefore validates a shape that may
never be served. Holding all rework until gate-house rules on option A.
Also records a confirmed defect they caught independently: authorization.py
pins AUTHORITY = "state-hub" and enforces it unconditionally, contradicting
flex-auth's position that State Hub is not the runtime approval authority.
Left unpatched deliberately - under option A that check moves to the claim
issuer, so patching now would be a second guess.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
resolve_consume_binding was a `return None` stub, so protocol step 1 of
docs/approval-consumption.md (GET /v1/approvals/{id}/claim) and the
validation join never existed. validate_action_authorization had no caller
in src/ at all - it was reachable only from tests. Production fail-closed
was correct, but for an undocumented second reason, and WP-0007-T04's
"what remains is not local engine work" was wrong.
The join now reproduces the exact CheckRequest via build_action_request,
fetches the durable ActionAuthorization, and validates request binding,
digest, validity, authority, policy pin, and distinct-approver threshold
before offering a consume binding. _require_lane_approval threads the exact
field set for provision/rotate/verify/exec so the digest covers the real
proposed action.
Deliberate choices:
- The approval-engine object id is never inferred from a State Hub decision
UUID; flex-auth stated GET /decisions/{uuid} is not the durable object.
- No default policy pin. flex-auth stated secrets-engine.lifecycle/v1 is
example vocabulary, not a published package.
- A half-configured join raises rather than returning None, so a partial
deployment cannot be mistaken for an unconfigured one.
Behavior is unchanged today: every new input is absent by default, so
production still fails closed and plan/--dry-run still work. 234 tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
session revoke accepts exactly one of --accessor-file or --lease-id-file.
Lease ids are read from a mode-0600 out-of-repo file and fingerprinted in
evidence; the handle is never printed. Production remains fail-closed.
Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
Select service-jwt, bootstrap, or env exclusively: JWT login uses a JSON
file, self-revokes, and never falls back to bootstrap or BAO_TOKEN. The
platform JWT mount/role is still unpublished, so auto keeps named
bootstrap/env providers.
session revoke --accessor-file revokes an already-issued token with
fingerprint-only evidence. Production remains fail-closed.
Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
Every live privileged production handler CAS-consumes through
approval-engine before OpenBao. Conflict, unavailability, or a missing
binding fail closed. Live production remains disabled until the durable
decision record is served.
Record kings-guard assent on the secret-use evidence contract.
Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
T03, T05, and T06 are done: lifecycle split, session self-revoke, and
verification/evidence acceptance are in the CLI. T04 waits on the durable
ActionAuthorization serving path rather than looking like local progress.
Known-accessor operator command is residual H4a, not unfinished T03.
Assistant: grok
Assistant-Session: 01a04cea-cb33-7c63-bad7-c1b0f9f0076b