diff --git a/CHANGELOG.md b/CHANGELOG.md index 34807b9c..3bc2972e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ ### Added +- `warp-core` optic invocation admission now has a narrow ApertureResolution v0 + boundary. Identity-covered invocations with exact fixture basis bytes + `basis-request:resolved-fixture:v0` and exact fixture aperture bytes + `aperture-request:resolved-fixture:v0` progress past aperture resolution and + still obstruct at `UnsupportedBudgetResolution`; unsupported aperture shapes + continue to obstruct at `UnsupportedApertureResolution`, and aperture + resolution remains unreachable when basis resolution fails. This does not + issue admission tickets, law witnesses, scheduler work, execution, budget + evaluation, runtime support checks, or successful grant validation. - `warp-core` optic invocation admission now has a narrow BasisResolution v0 boundary. Identity-covered invocations with exact fixture basis bytes `basis-request:resolved-fixture:v0` progress past basis resolution and still diff --git a/crates/warp-core/src/optic_artifact.rs b/crates/warp-core/src/optic_artifact.rs index e075839c..17907068 100644 --- a/crates/warp-core/src/optic_artifact.rs +++ b/crates/warp-core/src/optic_artifact.rs @@ -597,6 +597,7 @@ pub struct OpticInvocation { } const OPTIC_BASIS_RESOLUTION_V0_FIXTURE_BYTES: &[u8] = b"basis-request:resolved-fixture:v0"; +const OPTIC_APERTURE_RESOLUTION_V0_FIXTURE_BYTES: &[u8] = b"aperture-request:resolved-fixture:v0"; /// Admission obstruction for an optic invocation. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -615,10 +616,11 @@ pub enum OpticInvocationObstruction { /// outside the narrow deterministic BasisResolution v0 fixture. UnsupportedBasisResolution, /// The invocation reached aperture resolution after BasisResolution v0, but - /// Echo has no aperture resolver wired into admission in this slice. + /// the requested aperture shape is outside the narrow deterministic + /// ApertureResolution v0 fixture. UnsupportedApertureResolution, - /// The invocation reached budget resolution, but Echo has no budget - /// evaluator wired into admission in this slice. + /// The invocation reached budget resolution after ApertureResolution v0, + /// but Echo has no budget evaluator wired into admission in this slice. UnsupportedBudgetResolution, /// Echo cannot prove that this runtime supports the registered artifact /// requirements in this slice. @@ -1186,7 +1188,10 @@ impl OpticArtifactRegistry { CapabilityGrantValidationOutcome::IdentityCovered(_) ) { final_obstruction = Self::resolve_basis_v0(&invocation.basis_request) - .unwrap_or(OpticInvocationObstruction::UnsupportedApertureResolution); + .unwrap_or_else(|| { + Self::resolve_aperture_v0(&invocation.aperture_request) + .unwrap_or(OpticInvocationObstruction::UnsupportedBudgetResolution) + }); } } @@ -1211,6 +1216,16 @@ impl OpticArtifactRegistry { Some(OpticInvocationObstruction::UnsupportedBasisResolution) } + fn resolve_aperture_v0( + aperture_request: &OpticApertureRequest, + ) -> Option { + if aperture_request.bytes == OPTIC_APERTURE_RESOLUTION_V0_FIXTURE_BYTES { + return None; + } + + Some(OpticInvocationObstruction::UnsupportedApertureResolution) + } + fn classify_aperture_request( aperture_request: &OpticApertureRequest, ) -> Option { diff --git a/crates/warp-core/tests/optic_invocation_admission_tests.rs b/crates/warp-core/tests/optic_invocation_admission_tests.rs index 936fda48..515199dd 100644 --- a/crates/warp-core/tests/optic_invocation_admission_tests.rs +++ b/crates/warp-core/tests/optic_invocation_admission_tests.rs @@ -112,6 +112,17 @@ fn fixture_invocation_with_resolved_basis_and_presentation( invocation } +fn fixture_invocation_with_resolved_basis_aperture_and_presentation( + handle: OpticArtifactHandle, + grant_id: &str, +) -> OpticInvocation { + let mut invocation = fixture_invocation_with_resolved_basis_and_presentation(handle, grant_id); + invocation.aperture_request = OpticApertureRequest { + bytes: b"aperture-request:resolved-fixture:v0".to_vec(), + }; + invocation +} + fn expected_obstructed_posture( invocation: &OpticInvocation, obstruction: OpticInvocationObstruction, @@ -856,6 +867,84 @@ fn resolved_basis_still_obstructs_before_aperture_resolution() -> Result<(), Str Ok(()) } +#[test] +fn unsupported_aperture_still_stops_at_unsupported_aperture_resolution() -> Result<(), String> { + let (mut registry, handle) = fixture_registry_and_handle()?; + let invocation = + fixture_invocation_with_resolved_basis_and_presentation(handle, "grant:covered"); + let mut gate = fixture_gate_with_grant(fixture_grant("grant:covered")); + + let outcome = registry.admit_optic_invocation_with_capability_validator(&invocation, &mut gate); + + assert_eq!( + obstruction_for(&outcome), + OpticInvocationObstruction::UnsupportedApertureResolution + ); + assert!(matches!( + latest_invocation_obstruction_fact(®istry)?, + GraphFact::OpticInvocationObstructed { + aperture_request_digest, + obstruction, + .. + } if *aperture_request_digest == digest_invocation_request_bytes( + b"echo.optic-invocation.aperture-request.v0", + b"aperture-request:fixture" + ) + && *obstruction == InvocationObstructionKind::UnsupportedApertureResolution + )); + Ok(()) +} + +#[test] +fn aperture_resolution_is_unreachable_when_basis_resolution_fails() -> Result<(), String> { + let (mut registry, handle) = fixture_registry_and_handle()?; + let mut invocation = fixture_invocation_with_presentation(handle, "grant:covered"); + invocation.aperture_request = OpticApertureRequest { + bytes: b"aperture-request:resolved-fixture:v0".to_vec(), + }; + let mut gate = fixture_gate_with_grant(fixture_grant("grant:covered")); + + let outcome = registry.admit_optic_invocation_with_capability_validator(&invocation, &mut gate); + + assert_eq!( + obstruction_for(&outcome), + OpticInvocationObstruction::UnsupportedBasisResolution + ); + assert_ne!( + obstruction_for(&outcome), + OpticInvocationObstruction::UnsupportedApertureResolution + ); + Ok(()) +} + +#[test] +fn resolved_aperture_still_obstructs_before_budget_resolution() -> Result<(), String> { + let (mut registry, handle) = fixture_registry_and_handle()?; + let invocation = + fixture_invocation_with_resolved_basis_aperture_and_presentation(handle, "grant:covered"); + let mut gate = fixture_gate_with_grant(fixture_grant("grant:covered")); + + let outcome = registry.admit_optic_invocation_with_capability_validator(&invocation, &mut gate); + + assert_eq!( + obstruction_for(&outcome), + OpticInvocationObstruction::UnsupportedBudgetResolution + ); + assert!(matches!( + latest_invocation_obstruction_fact(®istry)?, + GraphFact::OpticInvocationObstructed { + aperture_request_digest, + obstruction, + .. + } if *aperture_request_digest == digest_invocation_request_bytes( + b"echo.optic-invocation.aperture-request.v0", + b"aperture-request:resolved-fixture:v0" + ) + && *obstruction == InvocationObstructionKind::UnsupportedBudgetResolution + )); + Ok(()) +} + #[test] fn invocation_obstruction_fact_is_not_counterfactual_candidate() -> Result<(), String> { let (mut registry, handle) = fixture_registry_and_handle()?; diff --git a/docs/design/optic-admission-ladder-checkpoint.md b/docs/design/optic-admission-ladder-checkpoint.md index 5965a02b..394eb912 100644 --- a/docs/design/optic-admission-ladder-checkpoint.md +++ b/docs/design/optic-admission-ladder-checkpoint.md @@ -3,13 +3,13 @@ # Optic Admission Ladder Checkpoint -Status: BasisResolution v0 boundary checkpoint. -Scope: refusal ladder with one narrow controlled basis-resolution fixture. +Status: ApertureResolution v0 boundary checkpoint. +Scope: refusal ladder with narrow controlled basis and aperture fixtures. ## Doctrine This checkpoint records the optic invocation admission ladder at the first -controlled basis-resolution boundary. +controlled aperture-resolution boundary. Echo can now explain why an optic invocation is refused, but it cannot yet admit one. There is no successful admission path in this checkpoint. @@ -17,8 +17,10 @@ one. There is no successful admission path in this checkpoint. A registered artifact handle is not authority. A capability presentation slot is not a validated grant. A basis request is not a resolved basis unless it matches the narrow deterministic BasisResolution v0 fixture. A resolved basis is not -permission to act. An aperture request is not a resolved scope. A budget request -is not spendable runtime capacity. +permission to act. An aperture request is not a resolved scope unless it matches +the narrow deterministic ApertureResolution v0 fixture after basis resolution. +A resolved aperture is not permission to act. A budget request is not spendable +runtime capacity. Refusal is causal evidence. Refusal is not admission, not execution, not a law witness, and not a counterfactual candidate. @@ -37,13 +39,17 @@ The current optic invocation admission path evaluates checks in this order: 8. Optionally publish grant-validation obstruction evidence. 9. If capability validation returns identity-covered material, resolve the narrow BasisResolution v0 fixture or obstruct unsupported basis material. -10. If that basis fixture resolves, obstruct before aperture resolution. -11. Publish the invocation obstruction fact. +10. If that basis fixture resolves, resolve the narrow ApertureResolution v0 + fixture or obstruct unsupported aperture material. +11. If that aperture fixture resolves, obstruct before budget resolution. +12. Publish the invocation obstruction fact. Presence checks come before resolution checks. Basis resolution gates aperture resolution. Aperture resolution gates budget evaluation and runtime support checks. BasisResolution v0 accepts exactly one deterministic fixture shape: `basis-request:resolved-fixture:v0`. +ApertureResolution v0 accepts exactly one deterministic fixture shape: +`aperture-request:resolved-fixture:v0`. ## Obstruction reachability @@ -59,17 +65,21 @@ checks. BasisResolution v0 accepts exactly one deterministic fixture shape: | `UnboundCapabilityPresentation` | Reachable today | Capability presentation material is structurally usable but not bound to the invocation. | | `CapabilityValidationUnavailable` | Reachable today | A bound presentation exists, but no successful validation or admission has occurred yet. | | `UnsupportedBasisResolution` | Reachable today | Identity-covered material reaches the basis boundary, but the basis shape is outside BasisResolution v0. | -| `UnsupportedApertureResolution` | Reachable today | BasisResolution v0 succeeded, but aperture resolution does not exist yet. | -| `UnsupportedBudgetResolution` | Future vocabulary | Must remain unreachable until lawful basis and aperture resolution exist. | +| `UnsupportedApertureResolution` | Reachable today | BasisResolution v0 succeeded, but the aperture shape is outside ApertureResolution v0. | +| `UnsupportedBudgetResolution` | Reachable today | ApertureResolution v0 succeeded, but budget evaluation does not exist yet. | | `RuntimeSupportUnavailable` | Future vocabulary | Must remain unreachable until lawful basis, aperture, and budget resolution exist. | -`UnsupportedBudgetResolution` and `RuntimeSupportUnavailable` are deliberately -defined but not lawfully reachable at this checkpoint. +`RuntimeSupportUnavailable` is deliberately defined but not lawfully reachable +at this checkpoint. `UnsupportedApertureResolution` is reachable only after the exact BasisResolution v0 fixture resolves. For identity-covered material, unsupported basis shapes must still stop at `UnsupportedBasisResolution`. +`UnsupportedBudgetResolution` is reachable only after the exact +ApertureResolution v0 fixture resolves. Unsupported aperture shapes must still +stop at `UnsupportedApertureResolution`. + ## Non-behavior This checkpoint does not introduce: @@ -85,7 +95,6 @@ This checkpoint does not introduce: - authority success - runtime support enforcement - budget reservation -- aperture resolution The system remains obstruction-first. It records refusal; it does not authorize work. @@ -103,9 +112,22 @@ Resolving that fixture establishes only the causal state under evaluation. It does not create authority, admission, aperture scope, budget capacity, runtime support, scheduler work, or execution. +## ApertureResolution v0 + +ApertureResolution v0 is not general aperture resolution. It recognizes exactly +one deterministic fixture shape: + +```text +aperture-request:resolved-fixture:v0 +``` + +Resolving that fixture establishes only the bounded observation/action window +inside a resolved basis. It does not create authority, admission, budget +capacity, runtime support, scheduler work, or execution. + ## Next transition point -The next transition point is ApertureResolution v0. +The next transition point is BudgetResolution v0. That transition must be narrow and explicit. It must not imply successful admission, budget spendability, runtime support, execution, or authority @@ -113,12 +135,11 @@ validation. ## Tripwire -If a future slice makes `UnsupportedBudgetResolution` or -`RuntimeSupportUnavailable` reachable before a lawful aperture resolution -boundary exists, the admission ladder is wrong. +If a future slice makes `RuntimeSupportUnavailable` reachable before a lawful +budget resolution boundary exists, the admission ladder is wrong. If a future slice introduces a successful admission path before a resolved basis, resolved aperture, evaluated budget, runtime support check, and validated grant exist, the admission ladder is wrong. -BasisResolution v0 is controlled resolved state, not admission. +ApertureResolution v0 is controlled resolved state, not admission.