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

### Added

- `warp-core` optic invocation admission now has a narrow SchedulerAdmission v0
boundary. After BasisResolution v0, ApertureResolution v0, BudgetResolution
v0, RuntimeSupport v0, capability identity coverage, and InvocationAdmission
v0 all resolve, Echo checks runtime-owned scheduler admission facts for the
registered artifact handle. Without that Echo-owned scheduler admission fact,
the ladder obstructs at `SchedulerAdmissionUnavailable`; with the exact
`scheduler-admission:resolved-fixture:v0` scheduler admission fixture, the
ladder advances to `SchedulerWorkUnavailable`. Scheduler admission fixture
recording is scoped through Echo-issued artifact handles, publishes
idempotent graph facts, and rejects unknown handles without publishing
scheduler admission evidence. This does not add admission tickets, law
witnesses, scheduler work, scheduler enqueueing, handler dispatch, execution,
or caller-supplied scheduler admission testimony.
- `warp-core` optic invocation admission now has a narrow InvocationAdmission v0
boundary. After BasisResolution v0, ApertureResolution v0, BudgetResolution
v0, RuntimeSupport v0, and capability identity coverage all resolve, Echo
Expand Down
45 changes: 43 additions & 2 deletions crates/warp-core/src/causal_facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ pub enum InvocationObstructionKind {
/// Echo proved runtime support, but this slice still has no lawful
/// invocation admission path.
InvocationAdmissionUnavailable,
/// Echo proved invocation admission, but scheduler admission/work enqueueing
/// does not exist in this slice.
/// Echo proved invocation admission, but has no scheduler admission fact.
SchedulerAdmissionUnavailable,
/// Echo proved scheduler admission, but this slice still has no scheduler
/// work candidate or queue item.
SchedulerWorkUnavailable,
/// Invocation supplied no capability presentation.
MissingCapability,
/// Invocation supplied a malformed capability presentation.
Expand Down Expand Up @@ -116,6 +118,7 @@ impl InvocationObstructionKind {
Self::RuntimeSupportUnavailable => b"runtime-support-unavailable",
Self::InvocationAdmissionUnavailable => b"invocation-admission-unavailable",
Self::SchedulerAdmissionUnavailable => b"scheduler-admission-unavailable",
Self::SchedulerWorkUnavailable => b"scheduler-work-unavailable",
Self::MissingCapability => b"missing-capability",
Self::MalformedCapabilityPresentation => b"malformed-capability-presentation",
Self::UnboundCapabilityPresentation => b"unbound-capability-presentation",
Expand Down Expand Up @@ -195,6 +198,20 @@ pub enum GraphFact {
/// Digest of the Echo-owned invocation admission material.
admission_digest: [u8; 32],
},
/// Echo recorded runtime-owned scheduler admission evidence for a
/// registered artifact handle.
SchedulerAdmissionRecorded {
/// Echo-owned runtime-local artifact handle id covered by the scheduler
/// admission fact.
artifact_handle_id: String,
/// Registered operation id covered by the scheduler admission fact.
operation_id: String,
/// Registered requirements digest covered by the scheduler admission
/// fact.
requirements_digest: String,
/// Digest of the Echo-owned scheduler admission material.
scheduler_admission_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 @@ -308,6 +325,30 @@ impl GraphFact {
);
push_digest_field(&mut bytes, b"admission-digest", admission_digest);
}
Self::SchedulerAdmissionRecorded {
artifact_handle_id,
operation_id,
requirements_digest,
scheduler_admission_digest,
} => {
push_digest_field(&mut bytes, b"variant", b"scheduler-admission-recorded");
push_digest_field(
&mut bytes,
b"artifact-handle-id",
artifact_handle_id.as_bytes(),
);
push_digest_field(&mut bytes, b"operation-id", operation_id.as_bytes());
push_digest_field(
&mut bytes,
b"requirements-digest",
requirements_digest.as_bytes(),
);
push_digest_field(
&mut bytes,
b"scheduler-admission-digest",
scheduler_admission_digest,
);
}
Self::OpticInvocationObstructed {
artifact_handle_id,
operation_id,
Expand Down
111 changes: 106 additions & 5 deletions crates/warp-core/src/optic_artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,10 @@ const OPTIC_INVOCATION_ADMISSION_V0_FIXTURE_BYTES: &[u8] =
b"invocation-admission:resolved-fixture:v0";
const OPTIC_INVOCATION_ADMISSION_V0_FIXTURE_DIGEST_DOMAIN: &[u8] =
b"echo.optic-invocation-admission.fixture.v0";
const OPTIC_SCHEDULER_ADMISSION_V0_FIXTURE_BYTES: &[u8] =
b"scheduler-admission:resolved-fixture:v0";
const OPTIC_SCHEDULER_ADMISSION_V0_FIXTURE_DIGEST_DOMAIN: &[u8] =
b"echo.optic-scheduler-admission.fixture.v0";

/// Admission obstruction for an optic invocation.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -636,9 +640,11 @@ pub enum OpticInvocationObstruction {
/// Echo proved runtime support, but this slice still cannot lawfully admit
/// the invocation.
InvocationAdmissionUnavailable,
/// Echo proved invocation admission, but scheduler admission/work enqueueing
/// does not exist in this slice.
/// Echo proved invocation admission, but has no scheduler admission fact.
SchedulerAdmissionUnavailable,
/// Echo proved scheduler admission, but scheduler work candidates do not
/// exist in this slice.
SchedulerWorkUnavailable,
/// 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 @@ -1048,13 +1054,21 @@ fn invocation_admission_v0_fixture_digest() -> [u8; 32] {
)
}

fn scheduler_admission_v0_fixture_digest() -> [u8; 32] {
digest_invocation_request_bytes(
OPTIC_SCHEDULER_ADMISSION_V0_FIXTURE_DIGEST_DOMAIN,
OPTIC_SCHEDULER_ADMISSION_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]>,
invocation_admission_v0_by_artifact: BTreeMap<String, [u8; 32]>,
scheduler_admission_v0_by_artifact: BTreeMap<String, [u8; 32]>,
published_graph_facts: Vec<PublishedGraphFact>,
artifact_registration_receipts: Vec<ArtifactRegistrationReceipt>,
}
Expand Down Expand Up @@ -1159,6 +1173,33 @@ impl OpticArtifactRegistry {
Ok(())
}

/// Records Echo-owned SchedulerAdmission v0 fixture evidence for a
/// registered artifact handle.
///
/// This is runtime context, not invocation context. Callers cannot provide
/// scheduler admission evidence 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_scheduler_admission_v0_fixture_for_artifact(
&mut self,
handle: &OpticArtifactHandle,
) -> Result<(), OpticArtifactRegistrationError> {
let registered = self.resolve_optic_artifact_handle(handle)?;
let artifact_handle_id = registered.handle.id.clone();
let operation_id = registered.operation_id.clone();
let requirements_digest = registered.requirements_digest.clone();
self.record_scheduler_admission_v0_fixture_for_artifact_id(
artifact_handle_id,
operation_id,
requirements_digest,
);
Ok(())
}

fn record_runtime_support_v0_fixture_for_requirements_digest(
&mut self,
requirements_digest: String,
Expand Down Expand Up @@ -1194,6 +1235,27 @@ impl OpticArtifactRegistry {
}
}

fn record_scheduler_admission_v0_fixture_for_artifact_id(
&mut self,
artifact_handle_id: String,
operation_id: String,
requirements_digest: String,
) {
let scheduler_admission_digest = scheduler_admission_v0_fixture_digest();
if self
.scheduler_admission_v0_by_artifact
.insert(artifact_handle_id.clone(), scheduler_admission_digest)
.is_none()
{
self.publish_scheduler_admission_recorded_fact(
artifact_handle_id,
operation_id,
requirements_digest,
scheduler_admission_digest,
);
}
}

/// Resolves an opaque Echo handle to registered artifact metadata.
///
/// # Errors
Expand Down Expand Up @@ -1315,9 +1377,14 @@ impl OpticArtifactRegistry {
self.resolve_invocation_admission_v0_for_artifact(
&registered.handle.id,
)
.unwrap_or(
OpticInvocationObstruction::SchedulerAdmissionUnavailable,
)
.unwrap_or_else(|| {
self.resolve_scheduler_admission_v0_for_artifact(
&registered.handle.id,
)
.unwrap_or(
OpticInvocationObstruction::SchedulerWorkUnavailable,
)
})
})
},
)
Expand Down Expand Up @@ -1396,6 +1463,20 @@ impl OpticArtifactRegistry {
Some(OpticInvocationObstruction::InvocationAdmissionUnavailable)
}

fn resolve_scheduler_admission_v0_for_artifact(
&self,
artifact_handle_id: &str,
) -> Option<OpticInvocationObstruction> {
if self
.scheduler_admission_v0_by_artifact
.contains_key(artifact_handle_id)
{
return None;
}

Some(OpticInvocationObstruction::SchedulerAdmissionUnavailable)
}

fn classify_aperture_request(
aperture_request: &OpticApertureRequest,
) -> Option<OpticInvocationObstruction> {
Expand Down Expand Up @@ -1589,6 +1670,23 @@ impl OpticArtifactRegistry {
));
}

fn publish_scheduler_admission_recorded_fact(
&mut self,
artifact_handle_id: String,
operation_id: String,
requirements_digest: String,
scheduler_admission_digest: [u8; 32],
) {
self.published_graph_facts.push(PublishedGraphFact::new(
GraphFact::SchedulerAdmissionRecorded {
artifact_handle_id,
operation_id,
requirements_digest,
scheduler_admission_digest,
},
));
}

fn publish_invocation_obstruction_fact(
&mut self,
invocation: &OpticInvocation,
Expand Down Expand Up @@ -1675,6 +1773,9 @@ fn invocation_obstruction_kind(
OpticInvocationObstruction::SchedulerAdmissionUnavailable => {
InvocationObstructionKind::SchedulerAdmissionUnavailable
}
OpticInvocationObstruction::SchedulerWorkUnavailable => {
InvocationObstructionKind::SchedulerWorkUnavailable
}
OpticInvocationObstruction::MissingCapability => {
InvocationObstructionKind::MissingCapability
}
Expand Down
80 changes: 80 additions & 0 deletions crates/warp-core/tests/causal_fact_publication_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,75 @@ fn invocation_admission_v0_fixture_rejects_unknown_handle_without_graph_fact() -
Ok(())
}

#[test]
fn scheduler_admission_fixture_publishes_graph_fact_once() -> 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_scheduler_admission_v0_fixture_for_artifact(&handle)
.map_err(|err| format!("registered handle should record scheduler admission: {err:?}"))?;
registry
.record_scheduler_admission_v0_fixture_for_artifact(&handle)
.map_err(|err| format!("repeated handle should remain idempotent: {err:?}"))?;

let scheduler_facts = registry
.published_graph_facts()
.iter()
.filter_map(|published| match &published.fact {
GraphFact::SchedulerAdmissionRecorded {
artifact_handle_id,
operation_id,
requirements_digest,
scheduler_admission_digest,
} => Some((
artifact_handle_id,
operation_id,
requirements_digest,
scheduler_admission_digest,
)),
_ => None,
})
.collect::<Vec<_>>();
assert_eq!(scheduler_facts.len(), 1);
let (artifact_handle_id, operation_id, requirements_digest, scheduler_admission_digest) =
scheduler_facts[0];
assert_eq!(artifact_handle_id, &handle.id);
assert_eq!(operation_id, "operation:textWindow:v0");
assert_eq!(
requirements_digest,
"requirements-digest:stack-witness-0001"
);
assert_ne!(*scheduler_admission_digest, [0_u8; 32]);
Ok(())
}

#[test]
fn scheduler_admission_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_scheduler_admission_v0_fixture_for_artifact(&unknown_handle),
"unknown artifact handle should reject scheduler admission 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::SchedulerAdmissionRecorded { .. })
}));
Ok(())
}

#[test]
fn graph_fact_digest_is_deterministic_and_kind_separated() {
let registered = GraphFact::ArtifactRegistered {
Expand All @@ -309,15 +378,26 @@ fn graph_fact_digest_is_deterministic_and_kind_separated() {
admission_digest: [8_u8; 32],
};
let repeated_admission = admission.clone();
let scheduler = GraphFact::SchedulerAdmissionRecorded {
artifact_handle_id: "handle-1".to_owned(),
operation_id: "operation".to_owned(),
requirements_digest: "requirements".to_owned(),
scheduler_admission_digest: [9_u8; 32],
};
let repeated_scheduler = scheduler.clone();

assert_eq!(registered.digest(), repeated.digest());
assert_ne!(registered.digest(), obstructed.digest());
assert_eq!(support.digest(), repeated_support.digest());
assert_eq!(admission.digest(), repeated_admission.digest());
assert_eq!(scheduler.digest(), repeated_scheduler.digest());
assert_ne!(registered.digest(), support.digest());
assert_ne!(obstructed.digest(), support.digest());
assert_ne!(support.digest(), admission.digest());
assert_ne!(support.digest(), scheduler.digest());
assert_ne!(admission.digest(), scheduler.digest());
assert_ne!(registered.digest(), admission.digest());
assert_ne!(registered.digest(), scheduler.digest());
}

#[test]
Expand Down
Loading
Loading