Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@

### Added

- `warp-core` now publishes `GraphFact::CapabilityGrantValidationObstructed`
when recorded capability grant material fails narrow identity coverage against
a registered optic artifact. The validation checks artifact hash, operation
id, requirements digest, and explicit expiry posture, and remains
refusal-first: it does not issue successful admission tickets, law witnesses,
scheduler work, execution, delegation policy, quorum governance, or Continuum
protocol.
- `docs/design/capability-grant-validation-obstruction-facts.md` defines grant
validation obstruction as graph evidence rather than authority. A recorded
grant can fail causally before any invocation can succeed.
- `warp-core` now publishes `GraphFact::OpticInvocationObstructed` whenever the
optic invocation admission skeleton refuses an invocation. The fact records
the artifact handle id, operation id, canonical variables digest, basis and
Expand Down Expand Up @@ -74,9 +84,9 @@
registered artifact handles internally and obstructs unknown handles,
operation mismatches, and registered-handle invocations without capability
presentation. Admission outcomes are must-use, and placeholder capability
presentations still obstruct until real grant validation exists. The
registration and invocation regression fixtures avoid `expect(...)` so
all-target Clippy remains clean.
presentations still obstruct until grant validation is wired into invocation
admission. The registration and invocation regression fixtures avoid
`expect(...)` so all-target Clippy remains clean.
- Optic invocation obstruction now returns a ticket-shaped pre-admission
posture carrying the invocation handle, operation id, canonical variables
digest, basis request, aperture request, and structured obstruction reason.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!-- SPDX-License-Identifier: Apache-2.0 OR LicenseRef-MIND-UCAL-1.0 -->
<!-- © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots> -->

# RE-031 Capability Grant Validation Admission Integration

Lane: bad-code.
Status: follow-up.

## Problem

`CapabilityGrantIntentGate` can now publish obstruction facts when recorded
grant material fails narrow identity coverage against a registered optic
artifact. That is correct refusal-first substrate work, but it is not a full
authority model.

The next authority slice must not let identity coverage drift into accepted
authority by implication.

## Required follow-up

- Define accepted grant material separately from submitted grant intent
material.
- Keep `CapabilityGrantValidationObstructed` as refusal evidence, not an
admission receipt.
- Add a real admission boundary before any successful `AdmissionTicket` exists.
- Add deterministic expiry semantics instead of opaque caller-supplied expiry
posture.
- Decide where successful grant validation, admission tickets, and law witness
bundles live.

## Non-goals

- no Continuum schema freeze;
- no quorum model;
- no scheduler integration;
- no execution path.
123 changes: 122 additions & 1 deletion crates/warp-core/src/causal_facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,33 @@ pub enum InvocationObstructionKind {
MalformedCapabilityPresentation,
/// Invocation supplied a presentation not bound to a grant id.
UnboundCapabilityPresentation,
/// Invocation supplied a placeholder presentation before grant validation exists.
/// Invocation supplied a placeholder presentation before grant validation
/// is wired into invocation admission.
CapabilityValidationUnavailable,
}

/// Obstruction kind recorded when capability grant validation fails before any
/// successful admission ticket, law witness, scheduler selection, or execution
/// boundary.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CapabilityGrantValidationObstructionKind {
/// Presentation supplied unusable shape for grant validation.
MalformedCapabilityPresentation,
/// Presentation did not bind to a grant id.
UnboundCapabilityPresentation,
/// Presentation named grant material Echo has not recorded.
UnknownGrant,
/// Grant artifact hash did not cover the registered artifact.
ArtifactHashMismatch,
/// Grant operation id did not cover the registered artifact operation.
OperationIdMismatch,
/// Grant requirements digest did not cover the registered artifact
/// requirements.
RequirementsDigestMismatch,
/// Grant expiry posture obstructed validation.
ExpiredGrant,
}

impl InvocationObstructionKind {
fn digest_label(self) -> &'static [u8] {
match self {
Expand All @@ -66,6 +89,20 @@ impl InvocationObstructionKind {
}
}

impl CapabilityGrantValidationObstructionKind {
fn digest_label(self) -> &'static [u8] {
match self {
Self::MalformedCapabilityPresentation => b"malformed-capability-presentation",
Self::UnboundCapabilityPresentation => b"unbound-capability-presentation",
Self::UnknownGrant => b"unknown-grant",
Self::ArtifactHashMismatch => b"artifact-hash-mismatch",
Self::OperationIdMismatch => b"operation-id-mismatch",
Self::RequirementsDigestMismatch => b"requirements-digest-mismatch",
Self::ExpiredGrant => b"expired-grant",
}
}
}

impl ArtifactRegistrationObstructionKind {
fn digest_label(self) -> &'static [u8] {
match self {
Expand Down Expand Up @@ -117,6 +154,30 @@ pub enum GraphFact {
/// Structured invocation obstruction kind.
obstruction: InvocationObstructionKind,
},
/// Echo refused capability grant validation before treating grant material
/// as authority.
CapabilityGrantValidationObstructed {
/// Presentation identity supplied by the caller.
presentation_id: String,
/// Grant id named by the presentation, when structurally available.
grant_id: Option<String>,
/// Echo-owned runtime-local artifact handle id being covered.
artifact_handle_id: String,
/// Registered artifact hash Echo expected the grant to cover.
expected_artifact_hash: String,
/// Artifact hash named by the grant material, when available.
grant_artifact_hash: Option<String>,
/// Registered operation id Echo expected the grant to cover.
expected_operation_id: String,
/// Operation id named by the grant material, when available.
grant_operation_id: Option<String>,
/// Registered requirements digest Echo expected the grant to cover.
expected_requirements_digest: String,
/// Requirements digest named by the grant material, when available.
grant_requirements_digest: Option<String>,
/// Structured capability grant validation obstruction kind.
obstruction: CapabilityGrantValidationObstructionKind,
},
}

impl GraphFact {
Expand Down Expand Up @@ -185,6 +246,66 @@ impl GraphFact {
);
push_digest_field(&mut bytes, b"obstruction", obstruction.digest_label());
}
Self::CapabilityGrantValidationObstructed {
presentation_id,
grant_id,
artifact_handle_id,
expected_artifact_hash,
grant_artifact_hash,
expected_operation_id,
grant_operation_id,
expected_requirements_digest,
grant_requirements_digest,
obstruction,
} => {
push_digest_field(
&mut bytes,
b"variant",
b"capability-grant-validation-obstructed",
);
push_digest_field(&mut bytes, b"presentation-id", presentation_id.as_bytes());
push_optional_digest_field(
&mut bytes,
b"grant-id",
grant_id.as_deref().map(str::as_bytes),
);
push_digest_field(
&mut bytes,
b"artifact-handle-id",
artifact_handle_id.as_bytes(),
);
push_digest_field(
&mut bytes,
b"expected-artifact-hash",
expected_artifact_hash.as_bytes(),
);
push_optional_digest_field(
&mut bytes,
b"grant-artifact-hash",
grant_artifact_hash.as_deref().map(str::as_bytes),
);
push_digest_field(
&mut bytes,
b"expected-operation-id",
expected_operation_id.as_bytes(),
);
push_optional_digest_field(
&mut bytes,
b"grant-operation-id",
grant_operation_id.as_deref().map(str::as_bytes),
);
push_digest_field(
&mut bytes,
b"expected-requirements-digest",
expected_requirements_digest.as_bytes(),
);
push_optional_digest_field(
&mut bytes,
b"grant-requirements-digest",
grant_requirements_digest.as_deref().map(str::as_bytes),
);
push_digest_field(&mut bytes, b"obstruction", obstruction.digest_label());
}
}

FactDigest(*blake3::hash(&bytes).as_bytes())
Expand Down
15 changes: 9 additions & 6 deletions crates/warp-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ pub use attachment::{
};
pub use causal_facts::{
digest_invocation_request_bytes, ArtifactRegistrationObstructionKind,
ArtifactRegistrationReceipt, FactDigest, GraphFact, InvocationObstructionKind,
PublishedGraphFact, ARTIFACT_REGISTRATION_RECEIPT_KIND,
ArtifactRegistrationReceipt, CapabilityGrantValidationObstructionKind, FactDigest, GraphFact,
InvocationObstructionKind, PublishedGraphFact, ARTIFACT_REGISTRATION_RECEIPT_KIND,
};
pub use clock::{GlobalTick, RunId, WorldlineTick};
pub use cmd::{
Expand Down Expand Up @@ -254,14 +254,17 @@ pub use optic::{
StagedIntent, StagedIntentReason, WitnessBasis, WorldlineHeadOptic,
};
pub use optic_artifact::{
AuthorityContext, AuthorityPolicy, AuthorityPolicyEvaluation, CapabilityGrantIntent,
CapabilityGrantIntentGate, CapabilityGrantIntentObstruction, CapabilityGrantIntentOutcome,
CapabilityGrantIntentPosture, ObstructionReceipt, OpticAdmissionRequirements,
AuthorityContext, AuthorityPolicy, AuthorityPolicyEvaluation, CapabilityGrantExpiryPosture,
CapabilityGrantIdentityCoverage, CapabilityGrantIntent, CapabilityGrantIntentGate,
CapabilityGrantIntentObstruction, CapabilityGrantIntentOutcome, CapabilityGrantIntentPosture,
CapabilityGrantValidationObstruction, CapabilityGrantValidationOutcome,
CapabilityGrantValidationPosture, ObstructionReceipt, OpticAdmissionRequirements,
OpticAdmissionTicketPosture, OpticApertureRequest, OpticArtifact, OpticArtifactHandle,
OpticArtifactOperation, OpticArtifactRegistrationError, OpticArtifactRegistry,
OpticBasisRequest, OpticCapabilityPresentation, OpticInvocation,
OpticInvocationAdmissionOutcome, OpticInvocationObstruction, OpticRegistrationDescriptor,
PrincipalRef, RegisteredOpticArtifact, RewriteDisposition, OBSTRUCTION_RECEIPT_KIND,
PrincipalRef, RegisteredOpticArtifact, RewriteDisposition,
CAPABILITY_GRANT_VALIDATION_POSTURE_KIND, OBSTRUCTION_RECEIPT_KIND,
OPTIC_ADMISSION_TICKET_POSTURE_KIND, OPTIC_ARTIFACT_HANDLE_KIND,
};
pub use playback::{CursorReceipt, TruthFrame, TruthSink};
Expand Down
Loading
Loading