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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@

### Added

- `warp-core` optic invocation admission now has a narrow RuntimeSupport v0
boundary. After BasisResolution v0, ApertureResolution v0, and
BudgetResolution v0 all resolve, Echo checks runtime-owned support facts for
the registered requirements digest. Without that Echo-owned support fact,
admission still obstructs at `RuntimeSupportUnavailable`; with the exact
`runtime-support:resolved-fixture:v0` support fixture, the ladder advances to
`InvocationAdmissionUnavailable`. Runtime support fixture recording is scoped
through Echo-issued artifact handles, so unknown handles cannot publish
support facts, repeated recordings for the same requirements digest do not
duplicate support facts, and registration rejects artifacts whose stored
requirements digest does not match the artifact requirements digest. This does
not add caller-supplied runtime support testimony, admission tickets, law
witnesses, scheduler work, execution, budget reservation, or successful grant
validation.
- `dispatch_optic_intent(...)` and the default `KernelPort` optic dispatch path
now reject EINT payloads that use Echo's reserved scheduler/control op id,
closing the remaining application-facing control-intent ingress path.
Expand Down
24 changes: 24 additions & 0 deletions crates/warp-core/src/causal_facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ pub enum InvocationObstructionKind {
/// Echo could not prove that its runtime support surface covers the
/// registered artifact requirements.
RuntimeSupportUnavailable,
/// Echo proved runtime support, but this slice still has no lawful
/// invocation admission path.
InvocationAdmissionUnavailable,
/// Invocation supplied no capability presentation.
MissingCapability,
/// Invocation supplied a malformed capability presentation.
Expand Down Expand Up @@ -108,6 +111,7 @@ impl InvocationObstructionKind {
Self::UnsupportedApertureResolution => b"unsupported-aperture-resolution",
Self::UnsupportedBudgetResolution => b"unsupported-budget-resolution",
Self::RuntimeSupportUnavailable => b"runtime-support-unavailable",
Self::InvocationAdmissionUnavailable => b"invocation-admission-unavailable",
Self::MissingCapability => b"missing-capability",
Self::MalformedCapabilityPresentation => b"malformed-capability-presentation",
Self::UnboundCapabilityPresentation => b"unbound-capability-presentation",
Expand Down Expand Up @@ -166,6 +170,14 @@ pub enum GraphFact {
/// Structured obstruction kind.
obstruction: ArtifactRegistrationObstructionKind,
},
/// Echo recorded runtime-owned support evidence for registered artifact
/// requirements.
RuntimeSupportRecorded {
/// Registered requirements digest covered by the runtime support fact.
requirements_digest: String,
/// Digest of the Echo-owned runtime support material.
support_digest: [u8; 32],
},
/// Echo refused optic invocation before admission success.
OpticInvocationObstructed {
/// Echo-owned runtime-local artifact handle id named by the invocation.
Expand Down Expand Up @@ -247,6 +259,18 @@ impl GraphFact {
);
push_digest_field(&mut bytes, b"obstruction", obstruction.digest_label());
}
Self::RuntimeSupportRecorded {
requirements_digest,
support_digest,
} => {
push_digest_field(&mut bytes, b"variant", b"runtime-support-recorded");
push_digest_field(
&mut bytes,
b"requirements-digest",
requirements_digest.as_bytes(),
);
push_digest_field(&mut bytes, b"support-digest", support_digest);
}
Self::OpticInvocationObstructed {
artifact_handle_id,
operation_id,
Expand Down
95 changes: 93 additions & 2 deletions crates/warp-core/src/optic_artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,9 @@ 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";
const OPTIC_BUDGET_RESOLUTION_V0_FIXTURE_BYTES: &[u8] = b"budget-request:resolved-fixture:v0";
const OPTIC_RUNTIME_SUPPORT_V0_FIXTURE_BYTES: &[u8] = b"runtime-support:resolved-fixture:v0";
const OPTIC_RUNTIME_SUPPORT_V0_FIXTURE_DIGEST_DOMAIN: &[u8] =
b"echo.optic-runtime-support.fixture.v0";

/// Admission obstruction for an optic invocation.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -626,6 +629,9 @@ pub enum OpticInvocationObstruction {
/// Echo cannot prove that this runtime supports the registered artifact
/// requirements in this slice.
RuntimeSupportUnavailable,
/// Echo proved runtime support, but this slice still cannot lawfully admit
/// the invocation.
InvocationAdmissionUnavailable,
/// The invocation does not carry authority to use the registered artifact.
MissingCapability,
/// The invocation carries a presentation that is structurally unusable.
Expand Down Expand Up @@ -1021,11 +1027,19 @@ fn push_optional_receipt_field(bytes: &mut Vec<u8>, field: Option<&[u8]>) {
}
}

fn runtime_support_v0_fixture_digest() -> [u8; 32] {
digest_invocation_request_bytes(
OPTIC_RUNTIME_SUPPORT_V0_FIXTURE_DIGEST_DOMAIN,
OPTIC_RUNTIME_SUPPORT_V0_FIXTURE_BYTES,
)
}

/// Echo-owned runtime-local registry for Wesley-compiled optic artifacts.
#[derive(Clone, Debug, Default)]
pub struct OpticArtifactRegistry {
next_handle_index: u64,
artifacts_by_handle: BTreeMap<String, RegisteredOpticArtifact>,
runtime_support_v0_by_requirements: BTreeMap<String, [u8; 32]>,
published_graph_facts: Vec<PublishedGraphFact>,
artifact_registration_receipts: Vec<ArtifactRegistrationReceipt>,
}
Expand Down Expand Up @@ -1080,6 +1094,43 @@ impl OpticArtifactRegistry {
Ok(handle)
}

/// Records Echo-owned RuntimeSupport v0 fixture evidence for a registered
/// artifact's requirements digest.
///
/// This is runtime context, not invocation context. Callers cannot provide
/// a support request through [`OpticInvocation`]; the admission ladder only
/// consults facts recorded on this registry.
///
/// # Errors
///
/// Returns [`OpticArtifactRegistrationError::UnknownHandle`] if Echo did not
/// issue the handle in this registry instance.
pub fn record_runtime_support_v0_fixture_for_artifact(
&mut self,
handle: &OpticArtifactHandle,
) -> Result<(), OpticArtifactRegistrationError> {
let requirements_digest = self
.resolve_optic_artifact_handle(handle)?
.requirements_digest
.clone();
Comment thread
flyingrobots marked this conversation as resolved.
self.record_runtime_support_v0_fixture_for_requirements_digest(requirements_digest);
Ok(())
}

fn record_runtime_support_v0_fixture_for_requirements_digest(
&mut self,
requirements_digest: String,
) {
let support_digest = runtime_support_v0_fixture_digest();
if self
.runtime_support_v0_by_requirements
.insert(requirements_digest.clone(), support_digest)
.is_none()
{
self.publish_runtime_support_recorded_fact(requirements_digest, support_digest);
}
}
Comment thread
flyingrobots marked this conversation as resolved.

/// Resolves an opaque Echo handle to registered artifact metadata.
///
/// # Errors
Expand Down Expand Up @@ -1192,8 +1243,15 @@ impl OpticArtifactRegistry {
.unwrap_or_else(|| {
Self::resolve_aperture_v0(&invocation.aperture_request).unwrap_or_else(
|| {
Self::resolve_budget_v0(&invocation.budget_request).unwrap_or(
OpticInvocationObstruction::RuntimeSupportUnavailable,
Self::resolve_budget_v0(&invocation.budget_request).unwrap_or_else(
|| {
self.resolve_runtime_support_v0_for_requirements(
&registered.requirements_digest,
)
.unwrap_or(
OpticInvocationObstruction::InvocationAdmissionUnavailable,
)
},
)
},
)
Expand Down Expand Up @@ -1242,6 +1300,20 @@ impl OpticArtifactRegistry {
Some(OpticInvocationObstruction::UnsupportedBudgetResolution)
}

fn resolve_runtime_support_v0_for_requirements(
&self,
requirements_digest: &str,
) -> Option<OpticInvocationObstruction> {
if self
.runtime_support_v0_by_requirements
.contains_key(requirements_digest)
{
return None;
}

Some(OpticInvocationObstruction::RuntimeSupportUnavailable)
}
Comment thread
flyingrobots marked this conversation as resolved.

fn classify_aperture_request(
aperture_request: &OpticApertureRequest,
) -> Option<OpticInvocationObstruction> {
Expand Down Expand Up @@ -1341,6 +1413,9 @@ impl OpticArtifactRegistry {
if descriptor.requirements_digest != artifact.requirements_digest {
return Err(OpticArtifactRegistrationError::RequirementsDigestMismatch);
}
if artifact.requirements.digest != artifact.requirements_digest {
return Err(OpticArtifactRegistrationError::RequirementsDigestMismatch);
}
if descriptor.operation_id != artifact.operation.operation_id {
return Err(OpticArtifactRegistrationError::OperationIdMismatch);
}
Expand Down Expand Up @@ -1402,6 +1477,19 @@ impl OpticArtifactRegistry {
}
}

fn publish_runtime_support_recorded_fact(
&mut self,
requirements_digest: String,
support_digest: [u8; 32],
) {
self.published_graph_facts.push(PublishedGraphFact::new(
GraphFact::RuntimeSupportRecorded {
requirements_digest,
support_digest,
},
));
}

fn publish_invocation_obstruction_fact(
&mut self,
invocation: &OpticInvocation,
Expand Down Expand Up @@ -1482,6 +1570,9 @@ fn invocation_obstruction_kind(
OpticInvocationObstruction::RuntimeSupportUnavailable => {
InvocationObstructionKind::RuntimeSupportUnavailable
}
OpticInvocationObstruction::InvocationAdmissionUnavailable => {
InvocationObstructionKind::InvocationAdmissionUnavailable
}
OpticInvocationObstruction::MissingCapability => {
InvocationObstructionKind::MissingCapability
}
Expand Down
96 changes: 94 additions & 2 deletions crates/warp-core/tests/causal_fact_publication_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use warp_core::{
ArtifactRegistrationObstructionKind, GraphFact, OpticAdmissionRequirements, OpticArtifact,
OpticArtifactOperation, OpticArtifactRegistrationError, OpticArtifactRegistry,
OpticRegistrationDescriptor, ARTIFACT_REGISTRATION_RECEIPT_KIND,
OpticArtifactHandle, OpticArtifactOperation, OpticArtifactRegistrationError,
OpticArtifactRegistry, OpticRegistrationDescriptor, ARTIFACT_REGISTRATION_RECEIPT_KIND,
};

fn fixture_artifact() -> OpticArtifact {
Expand Down Expand Up @@ -112,6 +112,90 @@ fn artifact_registration_obstruction_publishes_graph_fact_without_receipt() -> R
Ok(())
}

#[test]
fn runtime_support_v0_fixture_publishes_graph_fact_without_registration_receipt(
) -> Result<(), String> {
let mut registry = OpticArtifactRegistry::new();
let handle = registry
.register_optic_artifact(fixture_artifact(), fixture_descriptor())
.map_err(|err| format!("fixture descriptor should register: {err:?}"))?;

registry
.record_runtime_support_v0_fixture_for_artifact(&handle)
.map_err(|err| format!("registered handle should record runtime support: {err:?}"))?;

assert_eq!(registry.published_graph_facts().len(), 2);
assert_eq!(registry.artifact_registration_receipts().len(), 1);
let published = &registry.published_graph_facts()[1];
assert_eq!(published.digest, published.fact.digest());
assert!(matches!(
&published.fact,
GraphFact::RuntimeSupportRecorded {
requirements_digest,
support_digest,
} if requirements_digest == "requirements-digest:stack-witness-0001"
&& *support_digest != [0_u8; 32]
));
Ok(())
}

#[test]
fn runtime_support_v0_fixture_publishes_once_per_requirements_digest() -> Result<(), String> {
let mut sibling_artifact = fixture_artifact();
sibling_artifact.artifact_id = "optic-artifact:stack-witness-0002".to_owned();
sibling_artifact.artifact_hash = "artifact-hash:stack-witness-0002".to_owned();
let mut sibling_descriptor = fixture_descriptor();
sibling_descriptor.artifact_id = sibling_artifact.artifact_id.clone();
sibling_descriptor.artifact_hash = sibling_artifact.artifact_hash.clone();
let mut registry = OpticArtifactRegistry::new();
let first_handle = registry
.register_optic_artifact(fixture_artifact(), fixture_descriptor())
.map_err(|err| format!("fixture descriptor should register: {err:?}"))?;
let second_handle = registry
.register_optic_artifact(sibling_artifact, sibling_descriptor)
.map_err(|err| format!("sibling descriptor should register: {err:?}"))?;

registry
.record_runtime_support_v0_fixture_for_artifact(&first_handle)
.map_err(|err| format!("first handle should record runtime support: {err:?}"))?;
registry
.record_runtime_support_v0_fixture_for_artifact(&second_handle)
.map_err(|err| format!("second handle should record runtime support: {err:?}"))?;

let support_fact_count = registry
.published_graph_facts()
.iter()
.filter(|published| matches!(published.fact, GraphFact::RuntimeSupportRecorded { .. }))
.count();
assert_eq!(support_fact_count, 1);
Ok(())
}

#[test]
fn runtime_support_v0_fixture_rejects_unknown_handle_without_graph_fact() -> Result<(), String> {
let mut registry = OpticArtifactRegistry::new();
registry
.register_optic_artifact(fixture_artifact(), fixture_descriptor())
.map_err(|err| format!("fixture descriptor should register: {err:?}"))?;
let unknown_handle = OpticArtifactHandle {
kind: "optic-artifact-handle".to_owned(),
id: "unregistered-handle".to_owned(),
};

let err = registration_err_or_panic(
registry.record_runtime_support_v0_fixture_for_artifact(&unknown_handle),
"unknown artifact handle should reject runtime support recording",
)?;

assert!(matches!(err, OpticArtifactRegistrationError::UnknownHandle));
assert_eq!(registry.published_graph_facts().len(), 1);
assert!(!registry
.published_graph_facts()
.iter()
.any(|published| matches!(published.fact, GraphFact::RuntimeSupportRecorded { .. })));
Ok(())
}

#[test]
fn graph_fact_digest_is_deterministic_and_kind_separated() {
let registered = GraphFact::ArtifactRegistered {
Expand All @@ -126,9 +210,17 @@ fn graph_fact_digest_is_deterministic_and_kind_separated() {
artifact_hash: Some("same-artifact".to_owned()),
obstruction: ArtifactRegistrationObstructionKind::ArtifactHashMismatch,
};
let support = GraphFact::RuntimeSupportRecorded {
requirements_digest: "requirements".to_owned(),
support_digest: [7_u8; 32],
};
let repeated_support = support.clone();

assert_eq!(registered.digest(), repeated.digest());
assert_ne!(registered.digest(), obstructed.digest());
assert_eq!(support.digest(), repeated_support.digest());
assert_ne!(registered.digest(), support.digest());
assert_ne!(obstructed.digest(), support.digest());
}

#[test]
Expand Down
29 changes: 27 additions & 2 deletions crates/warp-core/tests/optic_artifact_registry_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
//! Regression tests for Echo-owned optic artifact registration.

use warp_core::{
OpticAdmissionRequirements, OpticArtifact, OpticArtifactHandle, OpticArtifactOperation,
OpticArtifactRegistrationError, OpticArtifactRegistry, OpticRegistrationDescriptor,
GraphFact, OpticAdmissionRequirements, OpticArtifact, OpticArtifactHandle,
OpticArtifactOperation, OpticArtifactRegistrationError, OpticArtifactRegistry,
OpticRegistrationDescriptor,
};

fn fixture_artifact() -> OpticArtifact {
Expand Down Expand Up @@ -127,6 +128,30 @@ fn optic_artifact_registry_rejects_tampered_requirements_digest() -> Result<(),
Ok(())
}

#[test]
fn optic_artifact_registry_rejects_mismatched_stored_requirements_digest() -> Result<(), String> {
let mut artifact = fixture_artifact();
artifact.requirements.digest = "requirements-digest:stored-mismatch".to_owned();
let mut registry = OpticArtifactRegistry::new();

let err = registration_err_or_panic(
registry.register_optic_artifact(artifact, fixture_descriptor()),
"stored requirements digest mismatch should reject",
)?;

assert!(matches!(
err,
OpticArtifactRegistrationError::RequirementsDigestMismatch
));
assert_eq!(registry.len(), 0);
assert!(registry.artifact_registration_receipts().is_empty());
assert!(registry
.published_graph_facts()
.iter()
.all(|published| !matches!(published.fact, GraphFact::RuntimeSupportRecorded { .. })));
Comment on lines +148 to +151
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen side-effect assertion to block any fact publication on failed registration.

This currently only forbids GraphFact::RuntimeSupportRecorded. If another fact type is accidentally emitted on failure, the test still passes. Assert the publication log is empty to enforce the invariant.

Suggested diff
-    assert!(registry
-        .published_graph_facts()
-        .iter()
-        .all(|published| !matches!(published.fact, GraphFact::RuntimeSupportRecorded { .. })));
+    assert!(registry.published_graph_facts().is_empty());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert!(registry
.published_graph_facts()
.iter()
.all(|published| !matches!(published.fact, GraphFact::RuntimeSupportRecorded { .. })));
assert!(registry.published_graph_facts().is_empty());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/warp-core/tests/optic_artifact_registry_tests.rs` around lines 148 -
151, The test currently only ensures no GraphFact::RuntimeSupportRecorded was
published; instead assert that registry.published_graph_facts() is entirely
empty to block any fact emission on failed registration—replace the existing
assertion using published_graph_facts().iter().all(...) with a direct emptiness
check (e.g., assert!(registry.published_graph_facts().is_empty())) so no other
fact types can be emitted; reference the registry variable and its
published_graph_facts() method and remove the GraphFact::RuntimeSupportRecorded
pattern-based check.

Ok(())
}

#[test]
fn optic_artifact_registry_rejects_mismatched_operation_id() -> Result<(), String> {
let artifact = fixture_artifact();
Expand Down
Loading
Loading