Skip to content
Closed
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
9 changes: 9 additions & 0 deletions crucible-contract/src/emits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ impl FieldType {
}
}

/// True for a type every value of which is a JSON object.
pub fn is_object(&self) -> bool {
match self {
FieldType::Object => true,
FieldType::Schema(schema) => schema.coarse() == Some(FieldType::Object),
_ => false,
}
}

/// True for a type every value of which is a JSON array.
pub fn is_list(&self) -> bool {
match self {
Expand Down
2 changes: 1 addition & 1 deletion crucible-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub mod verdict;
/// `crucible --contract-version` and the runtime image carries it as the
/// `io.crucible.contract-version` OCI label, so a deployed image can be matched against the
/// controller it talks to without a probe.
pub const CONTRACT_VERSION: &str = "1.15.0";
pub const CONTRACT_VERSION: &str = "1.16.0";

pub use admission::{
ADMISSION_WIRE_VERSION, AdmissionEvent, AdmissionKey, AdmissionOutcome, AdmittedInput,
Expand Down
6 changes: 6 additions & 0 deletions crucible-contract/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ pub struct PlanTaskWire {
/// worst-case width before any spend.
#[serde(default)]
pub max_fanout: u32,
/// Each `producer.field` a mapped task narrows to its own element's entry (contract 1.16.0),
/// empty when it declares none.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keyed: Vec<String>,
/// `route.question in a|b` when the task runs only on those answers, empty otherwise.
#[serde(default)]
pub when: String,
Expand Down Expand Up @@ -927,6 +931,7 @@ mod tests {
stage: "iteration".into(),
over: "discover.targets".into(),
max_fanout: 8,
keyed: vec!["discover.facts".into()],
when: String::new(),
revise: vec!["draft".into(), "check".into()],
max_rounds: 3,
Expand Down Expand Up @@ -1024,6 +1029,7 @@ mod tests {
stage: "iteration".into(),
over: String::new(),
max_fanout: 0,
keyed: Vec::new(),
when: String::new(),
revise,
max_rounds: 0,
Expand Down
2 changes: 2 additions & 0 deletions crucible/src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ fn command_backend(
session: Option<&crate::agent::agent_session::SessionTurn>,
) -> Command {
let mut c = Command::new("sh");
crucible::inference::withhold_from_task(&mut c);
c.arg("-c")
.arg(cmd)
.current_dir(&p.workspace)
Expand Down Expand Up @@ -191,6 +192,7 @@ fn local_command(
return Err(std::io::Error::other("harness produced an empty argv"));
};
let mut cmd = Command::new(program);
crucible::inference::withhold_from_task(&mut cmd);
cmd.args(rest)
.current_dir(&p.workspace)
.stdin(Stdio::null())
Expand Down
2 changes: 2 additions & 0 deletions crucible/src/control/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,7 @@ mod tests {
stage: String::new(),
over: String::new(),
max_fanout: 0,
keyed: Vec::new(),
when: String::new(),
revise: Vec::new(),
timeout: String::new(),
Expand All @@ -951,6 +952,7 @@ mod tests {
stage: String::new(),
over: String::new(),
max_fanout: 0,
keyed: Vec::new(),
when: String::new(),
revise: Vec::new(),
timeout: String::new(),
Expand Down
17 changes: 16 additions & 1 deletion crucible/src/inference.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
//! The run's injected inference bindings, read from the process environment once per use.

use crucible_contract::inference::{ENV_INFERENCE, InferenceError, ResolvedInference};
use crucible_contract::inference::{
ENV_INFERENCE, InferenceError, InferenceRole, ResolvedInference,
};

pub fn from_process_env() -> Result<ResolvedInference, InferenceError> {
ResolvedInference::parse(&std::env::var(ENV_INFERENCE).unwrap_or_default())
}

/// Keep the inference document and the decision binding's credential out of a task process. A
/// decision has no consumer outside the engine (RFC-0001 C-INFERENCE): only a route task asks it.
pub fn withhold_from_task(cmd: &mut std::process::Command) {
if let Ok(inference) = from_process_env()
&& let Some(key) = inference
.binding(InferenceRole::Decision)
.and_then(|binding| binding.key_env.as_ref())
{
cmd.env_remove(key.as_str());
}
cmd.env_remove(ENV_INFERENCE);
}
4 changes: 3 additions & 1 deletion crucible/src/plan/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ pub fn render(plan: &ValidPlan, caps: &BTreeSet<String>) -> String {
.unwrap_or_default()
),
TaskKind::TopK { k, .. } => format!("top_k[k={k}]"),
TaskKind::Route { questions, decider } => format!(
TaskKind::Route {
questions, decider, ..
} => format!(
"route[{}; {}]",
questions
.keys()
Expand Down
1 change: 1 addition & 0 deletions crucible/src/plan/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub(crate) fn plan_admitted_event(
.map(crate::plan::ir::OutputRef::to_string)
.unwrap_or_default(),
max_fanout: t.max_fanout.unwrap_or_default(),
keyed: t.keyed.iter().map(ToString::to_string).collect(),
when: t.when.as_ref().map(ToString::to_string).unwrap_or_default(),
revise: t
.revise
Expand Down
Loading
Loading