use shepherd_core::vocabulary::RunStatus;
use shepherd_core::{
Harness,
dispatch::{
AgentId, AgentType, AttachmentKind, CapabilityContract, CapabilityProbe,
CapabilityReadiness, CarrierAttachmentExpectation, ContextEntry, ContextQuery,
DispatchBudget, DispatchBudgetInput, DispatchId, DispatchRecord, DispatchStart,
DispatchState, GitCommit, IdentityError, LaneId, LoadedCarrierAttestationV1,
LoadedSkillAttestation, NativeIdentity, PathAuthority, PathClass, PathFactKind,
PendingDispatch, PendingLaunchState, Profile, ProfileAttachmentExpectation, ProfileLease,
ProfileLeaseState, ProjectFilesystemId, ProjectId, ROOT_SESSION_SCHEMA, ReviewCustody,
ReviewCustodyState, ReviewRuling, ReviewVerdict, Role, RootSessionBinding, RunId,
SessionId, SkillUseChallenge, SkillUsePrepare, SkillUseStage, SkillUseState, StopRequest,
TrustedPathFacts, WorkKind, classify_trusted_path, materialize_context,
path_in_write_scope, resolve_native_identity, validate_pending_edge,
},
};
use std::collections::BTreeSet;
fn capabilities(values: &[&str]) -> BTreeSet<String> {
values.iter().map(|value| (*value).to_owned()).collect()
}
fn budget(harness: Harness, observed_live: u32) -> DispatchBudget {
DispatchBudget::measure(DispatchBudgetInput {
project_id: ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a").expect("project"),
run: RunId::new("v657").expect("run"),
root_session_id: SessionId::new("root-session").expect("root session"),
parent_dispatch_id: Some(DispatchId::new("conductor-a").expect("parent")),
lane: Some(LaneId::new("lane-a").expect("lane")),
parent_role: Role::Conductor,
harness,
host_concurrent_limit: harness.limits().max_concurrent_agents,
project_parallel_limit: 3,
plan_parallel_limit: 8,
parent_parallel_limit: 12,
remaining_run_dispatches: 97,
observed_live,
observed_total: 3,
measured_at: 10,
})
.expect("budget")
}
#[test]
fn effective_dispatch_budget_never_treats_unbounded_pi_as_unbounded_sprint_authority() {
for harness in [Harness::ClaudeCode, Harness::Codex, Harness::Pi] {
let at_capacity = budget(harness, 3);
assert_eq!(at_capacity.effective_live_limit, 3, "{harness}");
assert!(at_capacity.authorize_next().is_err(), "{harness}");
budget(harness, 2)
.authorize_next()
.expect("third overlapping child is within project maximum");
}
let mut plan_bound = budget(Harness::Pi, 1);
plan_bound.plan_parallel_limit = 2;
plan_bound.effective_live_limit = 2;
plan_bound.validate().expect("plan is the narrowest fact");
assert!(plan_bound.authorize_next().is_ok());
plan_bound.observed_live = 2;
assert!(plan_bound.authorize_next().is_err());
}
fn ruling(subject: &str, reviewer: &str, generation: u32, marker: u8, at: i64) -> ReviewRuling {
ReviewRuling {
schema: "shepherd.review-ruling/1".into(),
project_id: ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a").expect("project"),
run: RunId::new("v657").expect("run"),
subject_agent_id: AgentId::new(subject).expect("subject"),
task_sha256: [7; 32],
task_generation: generation,
reviewer_dispatch_id: DispatchId::new(reviewer).expect("reviewer"),
findings_sha256: [marker; 32],
verdict: ReviewVerdict::Redo,
ruled_at: at,
}
}
#[test]
fn fourth_bound_rejection_terminally_quarantines_only_the_explicit_subject_generation() {
let first = ruling("worker-a", "auditor-1", 1, 1, 10);
let mut custody = ReviewCustody::begin(
&first,
SessionId::new("root-session").expect("root session"),
SessionId::new("worker-session").expect("worker session"),
Role::Worker,
Some(LaneId::new("lane-a").expect("lane")),
[8; 32],
)
.expect("custody");
custody.apply(&first).expect("first rejection");
for (reviewer, marker, at) in [
("auditor-2", 2, 11),
("auditor-3", 3, 12),
("auditor-4", 4, 13),
] {
custody
.apply(&ruling("worker-a", reviewer, 1, marker, at))
.expect("bounded rejection");
}
assert_eq!(custody.rejected_revisions, 4);
assert_eq!(custody.state, ReviewCustodyState::Malignant);
assert!(custody.claim_revoked);
assert!(custody.write_revoked);
assert!(custody.session_quarantined);
assert_eq!(custody.stopped_at, Some(13));
assert_eq!(custody.rulings.len(), 4);
assert_eq!(
custody.destroyed_agent_id.as_ref().map(AgentId::as_str),
Some("worker-a")
);
assert!(
custody
.apply(&ruling("worker-a", "auditor-5", 1, 5, 14))
.is_err(),
"malignant identity cannot resume or reclaim"
);
let mut wrong_subject = ruling("worker-b", "auditor-5", 1, 5, 14);
assert!(custody.apply(&wrong_subject).is_err());
wrong_subject.subject_agent_id = AgentId::new("worker-a").expect("subject");
wrong_subject.task_generation = 2;
assert!(custody.apply(&wrong_subject).is_err());
custody
.replace(AgentId::new("worker-replacement").expect("replacement"), 15)
.expect("exact-lineage root replacement");
assert_eq!(custody.state, ReviewCustodyState::Replaced);
assert_eq!(
custody.replacement_agent_id.as_ref().map(AgentId::as_str),
Some("worker-replacement")
);
}
#[test]
fn write_scope_matching_is_segment_aware_and_fail_closed() {
let scopes = vec![
"crates/core/src/dispatch/**".to_owned(),
"docs/*/report.md".to_owned(),
"*.md".to_owned(),
];
assert!(path_in_write_scope("crates/core/src/dispatch/identity.rs", &scopes).unwrap());
assert!(path_in_write_scope("docs/l1/report.md", &scopes).unwrap());
assert!(!path_in_write_scope("crates/core/src/guard.rs", &scopes).unwrap());
assert!(!path_in_write_scope("docs/l1/nested/report.md", &scopes).unwrap());
assert!(path_in_write_scope("notes.md", &scopes).unwrap());
assert!(!path_in_write_scope("notes.txt", &scopes).unwrap());
assert!(!path_in_write_scope("docs/notes.md", &["*.md".into()]).unwrap());
for unsafe_path in [
"/tmp/escape.rs",
"../escape.rs",
"crates/core/src/dispatch/../../escape.rs",
"crates/core/src/dispatch/*.rs",
] {
assert!(
path_in_write_scope(unsafe_path, &scopes).is_err(),
"{unsafe_path}"
);
}
for unsupported_scope in ["crates/co*re/**", "crates\\core\\**"] {
assert!(
path_in_write_scope("crates/core/src/lib.rs", &[unsupported_scope.into()]).is_err(),
"{unsupported_scope}"
);
}
let scoped_markdown = vec![String::from(".shepherd/runs/v660/**/*.md")];
for admitted in [
".shepherd/runs/v660/report.md",
".shepherd/runs/v660/lanes/l1/findings.md",
".shepherd/runs/v660/a/b/c/d/deep.md",
] {
assert!(
path_in_write_scope(admitted, &scoped_markdown).expect("valid path"),
"{admitted} must be inside the run's Markdown scope"
);
}
for refused in [
".shepherd/runs/v660/notes.txt",
".shepherd/runs/v660/lanes/l1/patch.rs",
".shepherd/runs/v659/report.md",
"crates/core/src/lib.rs",
] {
assert!(
!path_in_write_scope(refused, &scoped_markdown).expect("valid path"),
"{refused} must be outside the run's Markdown scope"
);
}
let mid = vec![String::from("crates/**/src")];
assert!(path_in_write_scope("crates/core/src", &mid).expect("valid path"));
assert!(path_in_write_scope("crates/a/b/src", &mid).expect("valid path"));
assert!(!path_in_write_scope("crates/core/tests", &mid).expect("valid path"));
let trailing = vec![String::from("crates/core/**")];
assert!(path_in_write_scope("crates/core/src/lib.rs", &trailing).expect("valid path"));
assert!(!path_in_write_scope("crates/registry/src/lib.rs", &trailing).expect("valid path"));
}
fn contract(required: &[&str], optional: &[&str], forbidden: &[&str]) -> CapabilityContract {
CapabilityContract::new(required, optional, forbidden).expect("fixture contract is valid")
}
fn probe(observed: &[&str], at: i64) -> CapabilityProbe {
CapabilityProbe::new(
observed,
"native-startup-probe",
"1.2.3",
Some("provider-4.5.6"),
at,
)
.expect("fixture probe is valid")
}
fn start(agent: &str, harness: Harness, role: Role, lane: &str) -> DispatchStart {
let capability_contract = role
.dispatch_capability_contract()
.expect("compiled role contract");
let observed = capability_contract
.required
.union(&capability_contract.optional)
.cloned()
.collect::<BTreeSet<_>>();
DispatchStart {
project_id: ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a")
.expect("fixture project id"),
run: RunId::new("v645").expect("fixture run"),
root_session_id: SessionId::new("root-session").expect("fixture root session id"),
run_incarnation: "incarnation-test".into(),
nonce: format!("nonce-{agent}"),
harness,
agent_id: AgentId::new(agent).expect("fixture agent id"),
agent_type: AgentType::new(role.carrier()).expect("fixture agent type"),
role,
lane: Some(LaneId::new(lane).expect("fixture lane")),
parent_agent_id: None,
session_id: SessionId::new("session-1").expect("fixture session"),
write_scope: vec!["crates/core/src/dispatch/**".into()],
model: Some("frontier".into()),
capability_contract,
capability_probe: CapabilityProbe::new(
observed,
"native-startup-probe",
"1.2.3",
Some("provider-4.5.6"),
1_000,
)
.expect("fixture probe is valid"),
startup_attachment: None,
attachment_nonce: None,
result_artifact: None,
result_nonce: None,
review_artifact: None,
review_nonce: None,
started_at: 1_000,
lease_expires_at: 11_000,
resumes_agent_id: None,
}
}
fn identity(record: &DispatchRecord, tool_call_id: &str, now: i64) -> NativeIdentity {
NativeIdentity {
harness: record.harness,
project_id: record.project_id.clone(),
run: record.run.clone(),
lane: record.lane.clone(),
session_id: record.session_id.clone(),
agent_id: Some(record.agent_id.clone()),
agent_type: Some(record.agent_type.clone()),
role: Some(record.role),
tool_call_id: Some(tool_call_id.into()),
now,
root_binding: None,
}
}
#[test]
fn closed_role_registry_has_exactly_nine_stable_carriers() {
let carriers: Vec<String> = Role::ALL.into_iter().map(Role::carrier).collect();
assert_eq!(
carriers,
[
"shepherd:auditor",
"shepherd:coder",
"shepherd:conductor",
"shepherd:critic",
"shepherd:discovery",
"shepherd:engineer",
"shepherd:planter",
"shepherd:shepherd",
"shepherd:worker",
]
);
assert!(Role::from_carrier("shepherd:architect").is_err());
assert!(Role::from_carrier("coder").is_err());
}
#[test]
fn all_nine_roles_expose_exact_compiled_capability_contracts() {
let expected = [
(
Role::Auditor,
&[
"code-intelligence",
"read",
"report-write",
"search",
"shell",
"skill-load",
][..],
&["tool-discovery"][..],
),
(
Role::Coder,
&["read", "search", "shell", "skill-load", "write"][..],
&["tool-discovery"][..],
),
(
Role::Conductor,
&[
"dispatch",
"message-peer",
"read",
"search",
"shell",
"skill-load",
"task-tracking",
][..],
&["schedule-wakeup", "tool-discovery"][..],
),
(
Role::Critic,
&["read", "search", "shell", "skill-load"][..],
&[][..],
),
(
Role::Discovery,
&["read", "report-write", "search", "shell", "skill-load"][..],
&["tool-discovery", "web-research"][..],
),
(
Role::Engineer,
&[
"dispatch",
"message-peer",
"read",
"search",
"shell",
"skill-load",
"write",
][..],
&["tool-discovery"][..],
),
(
Role::Planter,
&[
"ask-operator",
"read",
"search",
"shell",
"skill-load",
"write",
][..],
&["tool-discovery"][..],
),
(
Role::Shepherd,
&[
"dispatch",
"message-peer",
"read",
"search",
"shell",
"skill-load",
"task-tracking",
"write",
][..],
&["tool-discovery", "web-research"][..],
),
(
Role::Worker,
&["read", "search", "shell", "skill-load", "write"][..],
&["tool-discovery"][..],
),
];
for (role, required, optional) in expected {
let contract = role
.capability_contract()
.expect("compiled contract is valid");
assert_eq!(contract.required, capabilities(required), "{role}");
assert_eq!(contract.optional, capabilities(optional), "{role}");
assert!(contract.forbidden.contains("admin"), "{role}");
assert!(contract.forbidden.contains("sudo"), "{role}");
if matches!(role, Role::Auditor | Role::Critic | Role::Discovery) {
assert!(contract.forbidden.contains("write"), "{role}");
}
let dispatch = role
.dispatch_capability_contract()
.expect("dispatch contract");
assert!(dispatch.required.contains("subagent-provider"), "{role}");
}
assert!(Role::Conductor.write_eligible());
assert!(!Role::Planter.may_dispatch_to(Role::Engineer));
let planter = Role::Planter
.capability_contract()
.expect("planter contract");
for denied in ["dispatch", "task-tracking", "web-research"] {
assert!(planter.forbidden.contains(denied), "{denied}");
}
}
#[test]
fn native_planter_and_conductor_flags_match_the_authoritative_role_sources() {
let planter = include_str!("../../../content/roles/planter.md");
let conductor = include_str!("../../../content/roles/conductor.md");
for source in [planter, conductor] {
assert!(source.lines().any(|line| line == "write_eligible: true"));
}
assert!(planter.lines().any(|line| line == "dispatchable: false # meta tier: the root-local singleton profile, never a child role"));
assert!(planter.lines().any(|line| line
== "capabilities: [read, search, shell, write, skill-load, tool-discovery, ask-operator]"));
assert!(
!Role::Planter
.capability_contract()
.expect("planter")
.required
.contains("dispatch")
);
assert!(Role::Conductor.write_eligible());
}
#[test]
fn capability_diff_separates_present_missing_extra_and_forbidden_extra() {
let report = contract(&["read", "write"], &["web"], &["admin", "sudo"])
.evaluate(probe(&["read", "web", "admin", "telemetry"], 50));
assert_eq!(report.declared, capabilities(&["read", "web", "write"]));
assert_eq!(
report.observed,
capabilities(&["admin", "read", "telemetry", "web"])
);
assert_eq!(report.present, capabilities(&["read", "web"]));
assert_eq!(report.missing, capabilities(&["write"]));
assert_eq!(report.missing_required, capabilities(&["write"]));
assert!(report.missing_optional.is_empty());
assert_eq!(report.extra, capabilities(&["admin", "telemetry"]));
assert_eq!(report.forbidden_extra, capabilities(&["admin"]));
assert_eq!(report.readiness(), CapabilityReadiness::Blocked);
assert_eq!(report.source, "native-startup-probe");
assert_eq!(report.provider_version.as_deref(), Some("provider-4.5.6"));
}
#[test]
fn optional_gap_degrades_but_required_provider_gap_blocks() {
let degraded = contract(&["read"], &["web"], &[]).evaluate(probe(&["read"], 10));
assert_eq!(degraded.missing_optional, capabilities(&["web"]));
assert_eq!(degraded.readiness(), CapabilityReadiness::Degraded);
let provider_absent = contract(&["subagent-provider"], &[], &[]).evaluate(probe(&[], 10));
assert_eq!(
provider_absent.missing_required,
capabilities(&["subagent-provider"])
);
assert_eq!(provider_absent.readiness(), CapabilityReadiness::Blocked);
}
#[test]
fn start_builds_dispatch_v3_and_blocks_before_work_on_capability_failure() {
let active = DispatchRecord::start(start(
"claude-agent-1",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
))
.expect("valid start");
assert_eq!(active.schema, "shepherd.dispatch/3");
assert_eq!(active.revision, 1);
assert_eq!(active.state, DispatchState::Active);
assert_eq!(active.agent_id.as_str(), "claude-agent-1");
assert_eq!(active.role, Role::Coder);
let mut explicitly_non_writable = start(
"claude-critic-read-only",
Harness::ClaudeCode,
Role::Critic,
"l1-review",
);
explicitly_non_writable.write_scope.clear();
let explicitly_non_writable = DispatchRecord::start(explicitly_non_writable)
.expect("an empty scope explicitly carries no write authority");
assert!(explicitly_non_writable.write_scope.is_empty());
let mut blocked_start = start("pi-agent-1", Harness::Pi, Role::Coder, "l1-engine");
blocked_start
.capability_probe
.observed
.remove("subagent-provider");
let blocked = DispatchRecord::start(blocked_start).expect("blocked dispatch is auditable");
assert_eq!(blocked.state, DispatchState::CapabilityBlocked);
assert_eq!(
blocked.capabilities.missing_required,
capabilities(&["subagent-provider"])
);
let mut mismatched_claude = start(
"claude-mismatched-carrier",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
);
mismatched_claude.agent_type = AgentType::new("shepherd:auditor").expect("safe carrier");
assert!(DispatchRecord::start(mismatched_claude).is_err());
}
#[test]
fn native_identity_uses_agent_fields_and_never_tool_call_correlation() {
let record = DispatchRecord::start(start(
"claude-agent-1",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
))
.expect("valid start");
let first = resolve_native_identity(Some(&record), &identity(&record, "tool-1", 2_000))
.expect("first tool resolves");
let second = resolve_native_identity(Some(&record), &identity(&record, "tool-2", 2_001))
.expect("fresh tool id from same agent resolves");
assert_eq!(first, second);
let other = DispatchRecord::start(start(
"claude-agent-2",
Harness::ClaudeCode,
Role::Auditor,
"l1-engine",
))
.expect("valid second start");
let error = resolve_native_identity(Some(&record), &identity(&other, "tool-1", 2_000))
.expect_err("same tool id from another agent cannot cross-resolve");
assert!(matches!(error, IdentityError::AgentIdMismatch { .. }));
}
#[test]
fn codex_native_worker_type_preserves_separate_semantic_role() {
let mut input = start("codex-coder-1", Harness::Codex, Role::Coder, "l1-engine");
input.agent_type = AgentType::new("worker").expect("Codex native type");
let record = DispatchRecord::start(input).expect("native type is not a semantic role claim");
let mut native = identity(&record, "turn-tool-1", 2_000);
native.role = None;
let resolution = resolve_native_identity(Some(&record), &native)
.expect("Codex resolves native agent id and type against durable semantic role");
assert_eq!(
resolution,
shepherd_core::dispatch::IdentityResolution::Agent {
agent_id: record.agent_id.clone(),
role: Role::Coder,
}
);
}
#[test]
fn identity_mismatch_missing_stale_terminal_wrong_run_and_wrong_lane_are_typed() {
let mut record = DispatchRecord::start(start(
"codex-agent-1",
Harness::Codex,
Role::Worker,
"l2-cli",
))
.expect("valid start");
assert!(matches!(
resolve_native_identity(None, &identity(&record, "tool-a", 2_000)),
Err(IdentityError::MissingRecord { .. })
));
let mut wrong_type = identity(&record, "tool-b", 2_000);
wrong_type.agent_type = Some(AgentType::new("explorer").expect("fixture type"));
assert!(matches!(
resolve_native_identity(Some(&record), &wrong_type),
Err(IdentityError::AgentTypeMismatch { .. })
));
let mut wrong_role = identity(&record, "tool-c", 2_000);
wrong_role.role = Some(Role::Auditor);
assert!(matches!(
resolve_native_identity(Some(&record), &wrong_role),
Err(IdentityError::RoleMismatch { .. })
));
let mut wrong_run = identity(&record, "tool-d", 2_000);
wrong_run.run = RunId::new("v646").expect("fixture run");
assert!(matches!(
resolve_native_identity(Some(&record), &wrong_run),
Err(IdentityError::WrongRun { .. })
));
let mut wrong_lane = identity(&record, "tool-e", 2_000);
wrong_lane.lane = Some(LaneId::new("l9-other").expect("fixture lane"));
assert!(matches!(
resolve_native_identity(Some(&record), &wrong_lane),
Err(IdentityError::WrongLane { .. })
));
assert!(matches!(
resolve_native_identity(Some(&record), &identity(&record, "tool-f", 11_000)),
Err(IdentityError::Stale { .. })
));
record
.stop(StopRequest {
agent_id: record.agent_id.clone(),
expected_revision: 1,
stopped_at: 5_000,
result_artifact: Some("lanes/l2-cli/reports/codex-agent-1.md".into()),
})
.expect("active record stops once");
assert!(matches!(
resolve_native_identity(Some(&record), &identity(&record, "tool-g", 5_001)),
Err(IdentityError::Terminal { .. })
));
}
#[test]
fn root_identity_requires_a_current_explicit_session_binding() {
let record = DispatchRecord::start(start(
"claude-root-fixture",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
))
.expect("fixture record");
let mut native = identity(&record, "root-tool", 2_000);
native.agent_id = None;
native.agent_type = None;
native.role = None;
native.lane = None;
assert!(matches!(
resolve_native_identity(None, &native),
Err(IdentityError::MissingRootBinding)
));
native.root_binding = Some(RootSessionBinding {
schema: ROOT_SESSION_SCHEMA.into(),
project_id: native.project_id.clone(),
run: native.run.clone(),
harness: native.harness,
session_id: native.session_id.clone(),
role: Role::Shepherd,
mode: shepherd_core::dispatch::RootMode::Execution,
project_filesystem_id: None,
bound_at: 1_000,
expires_at: 3_000,
});
assert!(resolve_native_identity(None, &native).is_ok());
native.root_binding.as_mut().expect("binding").bound_at = 2_001;
assert!(matches!(
resolve_native_identity(None, &native),
Err(IdentityError::InvalidRootBinding(_))
));
let binding = native.root_binding.as_mut().expect("binding");
binding.bound_at = 1_000;
binding.expires_at = 1_000;
assert!(matches!(
resolve_native_identity(None, &native),
Err(IdentityError::InvalidRootBinding(_))
));
assert!(
serde_json::from_str::<shepherd_core::dispatch::RootMode>(r#""""#).is_err(),
"an empty mode must not deserialize"
);
assert!(
serde_json::from_str::<shepherd_core::dispatch::RootMode>(r#""nonsense""#).is_err(),
"an unknown mode must not deserialize"
);
assert_eq!(
serde_json::from_str::<shepherd_core::dispatch::RootMode>(r#""planning""#)
.expect("legacy spelling still deserializes"),
shepherd_core::dispatch::RootMode::Planting
);
let binding = native.root_binding.as_mut().expect("binding");
binding.expires_at = 3_000;
binding.mode = shepherd_core::dispatch::RootMode::Execution;
native.now = 3_000;
assert!(matches!(
resolve_native_identity(None, &native),
Err(IdentityError::Stale { .. })
));
}
#[test]
fn stop_is_monotonic_revision_checked_and_stores_only_artifact_reference() {
let mut record = DispatchRecord::start(start(
"claude-agent-stop",
Harness::ClaudeCode,
Role::Auditor,
"l3-audit",
))
.expect("valid start");
record
.stop(StopRequest {
agent_id: record.agent_id.clone(),
expected_revision: 1,
stopped_at: 2_000,
result_artifact: Some("reports/auditor-claude-agent-stop.md".into()),
})
.expect("first stop succeeds");
assert_eq!(record.state, DispatchState::Stopped);
assert_eq!(record.revision, 2);
assert_eq!(
record.result_artifact.as_deref(),
Some("reports/auditor-claude-agent-stop.md")
);
assert!(
record
.stop(StopRequest {
agent_id: record.agent_id.clone(),
expected_revision: 2,
stopped_at: 3_000,
result_artifact: None,
})
.is_err()
);
assert!(
serde_json::to_string(&record)
.expect("record serializes")
.find("transcript")
.is_none()
);
}
#[test]
fn resume_lineage_assigns_new_native_ids_across_harnesses() {
let mut claude = DispatchRecord::start(start(
"claude-a",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
))
.expect("Claude starts");
claude
.stop(StopRequest {
agent_id: claude.agent_id.clone(),
expected_revision: 1,
stopped_at: 2_000,
result_artifact: Some("lanes/l1-engine/reports/claude-a.md".into()),
})
.expect("Claude stops");
let mut codex_start = start("codex-b", Harness::Codex, Role::Coder, "l1-engine");
codex_start.resumes_agent_id = Some(claude.agent_id.clone());
let mut codex = claude.resume(codex_start).expect("Codex resumes Claude");
codex
.stop(StopRequest {
agent_id: codex.agent_id.clone(),
expected_revision: 1,
stopped_at: 4_000,
result_artifact: Some("lanes/l1-engine/reports/codex-b.md".into()),
})
.expect("Codex stops");
let mut pi_start = start("pi-c", Harness::Pi, Role::Coder, "l1-engine");
pi_start.resumes_agent_id = Some(codex.agent_id.clone());
let pi = codex.resume(pi_start).expect("Pi resumes Codex");
assert_eq!(codex.resumes_agent_id.as_ref(), Some(&claude.agent_id));
assert_eq!(pi.resumes_agent_id.as_ref(), Some(&codex.agent_id));
assert_ne!(claude.agent_id, codex.agent_id);
assert_ne!(codex.agent_id, pi.agent_id);
assert_eq!(pi.run, claude.run);
assert_eq!(pi.lane, claude.lane);
assert_eq!(pi.write_scope, claude.write_scope);
}
#[test]
fn unsafe_identifiers_and_artifact_parent_escapes_are_rejected() {
assert!(AgentId::new("../agent").is_err());
assert!(AgentId::new("agent/child").is_err());
assert!(AgentId::new(".").is_err());
assert!(AgentId::new("a".repeat(129)).is_err());
assert!(ProjectId::new("not-a-project-uuid").is_err());
assert!(RunId::new("../v645").is_err());
assert!(LaneId::new("L1-UPPER").is_err());
let mut absolute_scope = start(
"claude-absolute-scope",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
);
absolute_scope.write_scope = vec!["/tmp/outside/**".into()];
assert!(DispatchRecord::start(absolute_scope).is_err());
let mut dotted_scope = start(
"claude-dotted-scope",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
);
dotted_scope.write_scope = vec!["crates/./core/**".into()];
assert!(DispatchRecord::start(dotted_scope).is_err());
let mut control_scope = start(
"claude-control-scope",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
);
control_scope.write_scope = vec!["crates/core/**\nforged".into()];
assert!(DispatchRecord::start(control_scope).is_err());
let mut invalid_model = start(
"claude-control-model",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
);
invalid_model.model = Some("model\nforged".into());
assert!(DispatchRecord::start(invalid_model).is_err());
assert!(
CapabilityProbe::new(["read"], "native-startup-probe", "1.2.3", Some(""), 1_000,).is_err()
);
let mut record = DispatchRecord::start(start(
"claude-artifact",
Harness::ClaudeCode,
Role::Auditor,
"l3-audit",
))
.expect("valid start");
assert!(
record
.stop(StopRequest {
agent_id: record.agent_id.clone(),
expected_revision: 1,
stopped_at: 2_000,
result_artifact: Some("../transcript.jsonl".into()),
})
.is_err()
);
}
#[test]
fn loaded_records_reject_schema_drift_inconsistent_capability_diffs_and_states() {
let record = DispatchRecord::start(start(
"claude-loaded",
Harness::ClaudeCode,
Role::Coder,
"l1-engine",
))
.expect("valid start");
record.validate_loaded().expect("fresh record validates");
let mut wrong_schema = record.clone();
wrong_schema.schema = "shepherd.dispatch/99".into();
assert!(wrong_schema.validate_loaded().is_err());
let mut inconsistent_diff = record.clone();
inconsistent_diff.capabilities.present.clear();
assert!(inconsistent_diff.validate_loaded().is_err());
let mut relabeled_required_gap = record.clone();
let required = relabeled_required_gap
.capabilities
.declared
.iter()
.next()
.expect("compiled role declares capabilities")
.clone();
relabeled_required_gap
.capabilities
.observed
.remove(&required);
relabeled_required_gap
.capabilities
.present
.remove(&required);
relabeled_required_gap
.capabilities
.missing
.insert(required.clone());
relabeled_required_gap
.capabilities
.missing_optional
.insert(required);
relabeled_required_gap.state = DispatchState::Active;
assert!(
relabeled_required_gap.validate_loaded().is_err(),
"a durable record cannot downgrade a compiled required capability to optional"
);
let mut omitted_forbidden = record.clone();
omitted_forbidden
.capabilities
.observed
.insert("admin".into());
omitted_forbidden.capabilities.extra.insert("admin".into());
assert!(
omitted_forbidden.validate_loaded().is_err(),
"a durable record cannot omit a compiled forbidden extra"
);
let mut invalid_provider_version = record.clone();
invalid_provider_version.capabilities.provider_version = Some(String::new());
assert!(invalid_provider_version.validate_loaded().is_err());
let mut mismatched_claude_carrier = record.clone();
mismatched_claude_carrier.agent_type =
AgentType::new("shepherd:auditor").expect("safe carrier");
assert!(mismatched_claude_carrier.validate_loaded().is_err());
let mut forged_terminal = record.clone();
forged_terminal.state = DispatchState::Stopped;
assert!(forged_terminal.validate_loaded().is_err());
let malformed_role = serde_json::to_value(&record)
.expect("record serializes")
.as_object()
.cloned()
.map(|mut value| {
value.insert("role".into(), serde_json::json!("arbitrary-admin"));
serde_json::Value::Object(value)
})
.expect("record is an object");
assert!(serde_json::from_value::<DispatchRecord>(malformed_role).is_err());
}
#[test]
fn context_materialization_is_scoped_ranked_and_budget_bounded() {
let project = ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a").expect("fixture project");
let run = RunId::new("v645").expect("fixture run");
let lane = LaneId::new("l1-engine").expect("fixture lane");
let entries = vec![
ContextEntry::new(
"lane-new",
project.clone(),
run.clone(),
Some(lane.clone()),
"checkpoint",
300,
10,
15,
100,
"new lane context",
)
.expect("fixture entry"),
ContextEntry::new(
"shared-high",
project.clone(),
run.clone(),
None,
"memory",
250,
8,
10,
90,
"shared context",
)
.expect("fixture entry"),
ContextEntry::new(
"wrong-lane",
project.clone(),
run.clone(),
Some(LaneId::new("l2-cli").expect("fixture lane")),
"memory",
400,
1,
1,
999,
"must be filtered",
)
.expect("fixture entry"),
ContextEntry::new(
"too-old",
project.clone(),
run.clone(),
None,
"memory",
10,
1,
1,
500,
"stale",
)
.expect("fixture entry"),
];
let bundle = materialize_context(
&entries,
&ContextQuery {
project_id: project,
run,
lane: Some(lane),
min_freshness: 100,
max_entries: 2,
max_words: 20,
max_tokens: 30,
},
);
let ids: Vec<&str> = bundle
.entries
.iter()
.map(|entry| entry.id.as_str())
.collect();
assert_eq!(ids, ["lane-new", "shared-high"]);
assert_eq!(bundle.words, 18);
assert_eq!(bundle.tokens, 25);
}
fn digest(byte: u8) -> [u8; 32] {
[byte; 32]
}
fn planning_root() -> RootSessionBinding {
RootSessionBinding {
schema: ROOT_SESSION_SCHEMA.into(),
project_id: ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a").expect("project"),
run: RunId::new("v657").expect("run"),
harness: Harness::Codex,
session_id: SessionId::new("root-session").expect("session"),
role: Role::Shepherd,
mode: shepherd_core::dispatch::RootMode::Planting,
project_filesystem_id: Some("a".repeat(64)),
bound_at: 100,
expires_at: 10_000,
}
}
#[test]
fn planter_profile_lease_is_same_root_nonce_bound_and_gains_no_early_write() {
let root = planning_root();
let expected = ProfileAttachmentExpectation::new(
Harness::Codex,
Profile::Planter,
"planting",
digest(9),
digest(1),
digest(2),
)
.expect("plant attachment");
let mut lease = ProfileLease::enter(
&root,
expected.clone(),
200,
5_000,
&RunStatus::Planted.into(),
false,
)
.expect("missing seed permits an entered lease");
assert_eq!(lease.state, ProfileLeaseState::Entered);
assert!(lease.write_scope.is_empty());
assert!(
lease
.authorize_write(&root, ".shepherd/runs/v657/seed.md", 201)
.is_err()
);
lease
.activate(&root, &expected.attestation(), 202)
.expect("exact nonce-bound carrier activates");
assert_eq!(lease.state, ProfileLeaseState::Active);
assert_eq!(
lease
.write_scope
.iter()
.map(PathAuthority::as_str)
.collect::<Vec<_>>(),
[".shepherd/runs/v657/mesh.md", ".shepherd/runs/v657/seed.md"]
);
lease
.authorize_write(&root, ".shepherd/runs/v657/seed.md", 203)
.expect("exact seed path");
for denied in [
".shepherd/runs/v657/run.json",
".shepherd/runs/v657/plan.md",
".shepherd/runs/v656/seed.md",
"content/skills/plant/SKILL.md",
] {
assert!(
lease.authorize_write(&root, denied, 203).is_err(),
"{denied}"
);
}
assert!(
lease.exit(&root, 204, false).is_err(),
"unverified seed blocks exit"
);
lease
.exit(&root, 204, true)
.expect("verified persisted seed permits exit");
assert_eq!(lease.state, ProfileLeaseState::Exited);
assert!(lease.write_scope.is_empty());
}
#[test]
fn planter_profile_lease_rejects_expiry_replay_child_and_identity_changes() {
let root = planning_root();
let expected = ProfileAttachmentExpectation::new(
Harness::Codex,
Profile::Planter,
"planting",
digest(9),
digest(3),
digest(4),
)
.expect("plant attachment");
for (mut wrong, label) in [
(
{
let mut value = root.clone();
value.session_id = SessionId::new("other-root").unwrap();
value
},
"root",
),
(
{
let mut value = root.clone();
value.run = RunId::new("v658").unwrap();
value
},
"run",
),
(
{
let mut value = root.clone();
value.harness = Harness::Pi;
value
},
"carrier",
),
(
{
let mut value = root.clone();
value.role = Role::Planter;
value
},
"child-profile",
),
] {
let result = ProfileLease::enter(
&wrong,
expected.clone(),
200,
5_000,
&RunStatus::Planted.into(),
false,
);
if label == "root" || label == "run" || label == "carrier" {
let mut lease = ProfileLease::enter(
&root,
expected.clone(),
200,
5_000,
&RunStatus::Planted.into(),
false,
)
.expect("fixture lease");
assert!(
lease
.activate(&wrong, &expected.attestation(), 201)
.is_err(),
"{label}"
);
} else {
assert!(result.is_err(), "{label}");
}
wrong.role = Role::Shepherd;
}
assert!(
ProfileLease::enter(
&root,
expected.clone(),
200,
5_000,
&RunStatus::Planned.into(),
false
)
.is_err()
);
assert!(
ProfileLease::enter(
&root,
expected.clone(),
200,
5_000,
&RunStatus::Planted.into(),
true
)
.is_err()
);
let mut lease = ProfileLease::enter(
&root,
expected.clone(),
200,
300,
&RunStatus::Planted.into(),
false,
)
.expect("fixture lease");
let mut wrong_attestation = expected.attestation();
wrong_attestation.attachment_nonce_sha256 = digest(9);
assert!(lease.activate(&root, &wrong_attestation, 250).is_err());
assert!(
lease.activate(&root, &expected.attestation(), 300).is_err(),
"expiry is exclusive"
);
let mut active = ProfileLease::enter(
&root,
expected.clone(),
200,
5_000,
&RunStatus::Planted.into(),
false,
)
.expect("active fixture");
active
.activate(&root, &expected.attestation(), 201)
.expect("activation");
assert!(
active
.activate(&root, &expected.attestation(), 202)
.is_err(),
"activation replay"
);
active
.revoke(&root, 203)
.expect("crash/disconnect revocation");
assert_eq!(active.state, ProfileLeaseState::Revoked);
assert!(
active
.authorize_write(&root, ".shepherd/runs/v657/mesh.md", 204)
.is_err()
);
let mut expired = ProfileLease::enter(
&root,
expected.clone(),
200,
300,
&RunStatus::Planted.into(),
false,
)
.expect("expiring fixture");
expired
.activate(&root, &expected.attestation(), 201)
.expect("activation");
assert!(
expired
.authorize_write(&root, ".shepherd/runs/v657/seed.md", 300)
.is_err()
);
expired
.revoke(&root, 300)
.expect("an expired lease remains persistently revocable by its exact root");
assert_eq!(expired.state, ProfileLeaseState::Revoked);
assert!(expired.write_scope.is_empty());
}
#[test]
fn trusted_path_facts_classify_unknowns_conflicts_and_native_state_fail_closed() {
let path = |value: &str| PathAuthority::new(value).expect("scope");
let facts = |value: &str| TrustedPathFacts {
path: path(value),
kind: PathFactKind::ExistingFile,
git_tracked: false,
workspace_member: false,
known_production: false,
known_non_code: false,
run_artifact: false,
native_state: false,
inherited_parent: None,
conflict: false,
};
let mut production = facts("content/skills/**");
production.known_production = true;
assert_eq!(classify_trusted_path(&production), PathClass::Production);
let mut docs = facts("docs/report.md");
docs.known_non_code = true;
assert_eq!(classify_trusted_path(&docs), PathClass::NonCodeDeliverable);
let mut run = facts(".shepherd/runs/v657/reports/auditor.md");
run.run_artifact = true;
assert_eq!(classify_trusted_path(&run), PathClass::RunArtifact);
let mut state = facts(".shepherd/runs/v657/run.json");
state.run_artifact = true;
state.native_state = true;
assert_eq!(classify_trusted_path(&state), PathClass::NativeState);
let mut unknown = facts("notes/new.md");
unknown.kind = PathFactKind::Missing;
assert_eq!(classify_trusted_path(&unknown), PathClass::Production);
unknown.inherited_parent = Some(PathClass::NonCodeDeliverable);
assert_eq!(
classify_trusted_path(&unknown),
PathClass::NonCodeDeliverable
);
unknown.conflict = true;
assert_eq!(classify_trusted_path(&unknown), PathClass::Production);
assert!(Role::Coder.may_write_class(PathClass::Production));
assert!(!Role::Worker.may_write_class(PathClass::Production));
assert!(Role::Worker.may_write_class(PathClass::NonCodeDeliverable));
assert!(!Role::Worker.may_write_class(PathClass::NativeState));
assert!(Role::Engineer.may_write_class(PathClass::RunArtifact));
assert!(!Role::Engineer.may_write_class(PathClass::Production));
}
fn skill_use_prepare(
role: Role,
startup_skill: &str,
skill: &str,
stage: SkillUseStage,
) -> SkillUsePrepare {
SkillUsePrepare {
project_id: ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a").expect("project"),
run: RunId::new("v657").expect("run"),
dispatch_id: Some(AgentId::new("coder-1").expect("dispatch")),
root_authority: None,
session_id: SessionId::new("coder-session").expect("session"),
target: Harness::Codex,
role,
startup_skill: startup_skill.into(),
skill: skill.into(),
stage,
installed_carrier_path: "/opt/shepherd/codex".into(),
candidate_sha256: digest(9),
carrier_sha256: digest(10),
compiler_tree_sha256: digest(11),
skill_bundle_sha256: digest(12),
nonce_sha256: digest(13),
prepared_at: 100,
expires_at: 1_000,
}
}
#[test]
fn native_skill_use_is_closed_single_use_and_exactly_identity_bound() {
let mut challenge = SkillUseChallenge::prepare(skill_use_prepare(
Role::Coder,
"implementing",
"debugging",
SkillUseStage::DuringWork,
))
.expect("Coder may load debugging under implementing");
assert_eq!(challenge.state, SkillUseState::Pending);
let attestation = LoadedSkillAttestation::from_challenge(&challenge, 200);
challenge
.attest(&attestation, 200)
.expect("exact loaded skill");
assert_eq!(challenge.state, SkillUseState::Attested);
assert_eq!(challenge.attested_at, Some(200));
assert!(
challenge.attest(&attestation, 201).is_err(),
"single-use nonce cannot replay"
);
for mutate in [
|value: &mut LoadedSkillAttestation| value.run = RunId::new("v658").unwrap(),
|value: &mut LoadedSkillAttestation| {
value.dispatch_id = Some(AgentId::new("coder-2").unwrap())
},
|value: &mut LoadedSkillAttestation| {
value.session_id = SessionId::new("other-session").unwrap()
},
|value: &mut LoadedSkillAttestation| value.target = Harness::Pi,
|value: &mut LoadedSkillAttestation| value.role = Role::Worker,
|value: &mut LoadedSkillAttestation| value.installed_carrier_path = "/opt/other".into(),
|value: &mut LoadedSkillAttestation| value.compiler_tree_sha256 = digest(99),
|value: &mut LoadedSkillAttestation| value.skill_bundle_sha256 = digest(99),
|value: &mut LoadedSkillAttestation| value.nonce_sha256 = digest(99),
] {
let mut fresh = SkillUseChallenge::prepare(skill_use_prepare(
Role::Coder,
"implementing",
"debugging",
SkillUseStage::DuringWork,
))
.expect("challenge");
let mut wrong = LoadedSkillAttestation::from_challenge(&fresh, 200);
mutate(&mut wrong);
assert!(fresh.attest(&wrong, 200).is_err());
}
}
#[test]
fn skill_use_map_denies_unknown_role_startup_stage_and_expiry() {
for denied in [
skill_use_prepare(
Role::Worker,
"artifact-work",
"debugging",
SkillUseStage::DuringWork,
),
skill_use_prepare(
Role::Coder,
"planning",
"debugging",
SkillUseStage::DuringWork,
),
skill_use_prepare(
Role::Coder,
"implementing",
"debugging",
SkillUseStage::Completion,
),
skill_use_prepare(
Role::Coder,
"implementing",
"unknown",
SkillUseStage::DuringWork,
),
skill_use_prepare(
Role::Shepherd,
"shepherd",
"verification",
SkillUseStage::Completion,
),
skill_use_prepare(
Role::Planter,
"planting",
"verification",
SkillUseStage::Completion,
),
skill_use_prepare(
Role::Engineer,
"planning",
"verification",
SkillUseStage::DuringWork,
),
] {
assert!(SkillUseChallenge::prepare(denied).is_err());
}
for (role, startup) in [
(Role::Engineer, "planning"),
(Role::Conductor, "lane-execution"),
(Role::Coder, "implementing"),
(Role::Worker, "artifact-work"),
(Role::Auditor, "reviewing"),
(Role::Critic, "reviewing"),
(Role::Discovery, "researching"),
] {
SkillUseChallenge::prepare(skill_use_prepare(
role,
startup,
"verification",
SkillUseStage::Completion,
))
.unwrap_or_else(|error| panic!("{role} verification should be allowed: {error}"));
}
let mut expired = SkillUseChallenge::prepare(skill_use_prepare(
Role::Coder,
"implementing",
"debugging",
SkillUseStage::DuringWork,
))
.expect("challenge");
let attestation = LoadedSkillAttestation::from_challenge(&expired, 1_000);
assert!(
expired.attest(&attestation, 1_000).is_err(),
"expiry is exclusive"
);
assert_eq!(expired.state, SkillUseState::Expired);
let mut swept = SkillUseChallenge::prepare(skill_use_prepare(
Role::Coder,
"implementing",
"debugging",
SkillUseStage::DuringWork,
))
.expect("challenge");
assert!(
swept.expire(999).is_err(),
"live challenges cannot be swept"
);
swept
.expire(1_000)
.expect("expired challenge becomes durable terminal state");
assert_eq!(swept.state, SkillUseState::Expired);
}
#[test]
fn root_and_planter_have_explicit_completion_verification_skill_rules() {
for (role, startup) in [(Role::Shepherd, "shepherd"), (Role::Planter, "planting")] {
assert!(
shepherd_core::dispatch::allowed_skill_use(
role,
startup,
"verification",
SkillUseStage::Completion
),
"{role} must be able to verify completion under its own startup skill"
);
assert!(!shepherd_core::dispatch::allowed_skill_use(
role,
startup,
"verification",
SkillUseStage::DuringWork
));
assert!(!shepherd_core::dispatch::allowed_skill_use(
role,
startup,
"debugging",
SkillUseStage::DuringWork
));
assert!(!shepherd_core::dispatch::allowed_skill_use(
role,
"implementing",
"verification",
SkillUseStage::Completion
));
}
}
fn root_skill_use_prepare(planter: bool) -> SkillUsePrepare {
let root = planning_root();
let mut input = skill_use_prepare(
if planter {
Role::Planter
} else {
Role::Shepherd
},
if planter { "planting" } else { "shepherd" },
"verification",
SkillUseStage::Completion,
);
input.dispatch_id = None;
input.session_id = root.session_id.clone();
input.prepared_at = 300;
let profile_lease = if planter {
let expected = ProfileAttachmentExpectation::new(
Harness::Codex,
Profile::Planter,
"planting",
digest(9),
digest(1),
digest(2),
)
.expect("profile attachment");
let mut lease = ProfileLease::enter(
&root,
expected.clone(),
200,
5_000,
&RunStatus::Planted.into(),
false,
)
.expect("profile enter");
lease
.activate(&root, &expected.attestation(), 201)
.expect("profile activate");
Some(lease)
} else {
None
};
input.root_authority = Some(shepherd_core::dispatch::SkillUseRootAuthority {
binding: root,
profile_lease,
});
input
}
#[test]
fn root_skill_use_rejects_forged_matching_loaded_identity() {
let mut challenge =
SkillUseChallenge::prepare(root_skill_use_prepare(false)).expect("root challenge");
challenge.root_authority = None;
let loaded = LoadedSkillAttestation::from_challenge(&challenge, 400);
assert!(
loaded.validate_against(&challenge, 400).is_err(),
"two matching envelopes without Native root authority must not validate"
);
}
#[test]
fn root_skill_use_binds_exact_root_profile_and_exclusive_lease() {
for planter in [false, true] {
let mut challenge = SkillUseChallenge::prepare(root_skill_use_prepare(planter))
.expect("native root challenge");
let loaded = LoadedSkillAttestation::from_challenge(&challenge, 400);
challenge.attest(&loaded, 400).expect("root attestation");
for mutate in [
|input: &mut SkillUsePrepare| {
input.dispatch_id = Some(AgentId::new("fake-child").unwrap())
},
|input: &mut SkillUsePrepare| input.root_authority = None,
|input: &mut SkillUsePrepare| {
input.session_id = SessionId::new("foreign-root").unwrap()
},
|input: &mut SkillUsePrepare| input.run = RunId::new("v658").unwrap(),
|input: &mut SkillUsePrepare| input.target = Harness::Pi,
|input: &mut SkillUsePrepare| {
input.root_authority.as_mut().unwrap().binding.role = Role::Planter
},
|input: &mut SkillUsePrepare| input.expires_at = 10_001,
|input: &mut SkillUsePrepare| input.prepared_at = 99,
] {
let mut input = root_skill_use_prepare(planter);
mutate(&mut input);
assert!(SkillUseChallenge::prepare(input).is_err());
}
let mut changed = loaded.clone();
changed.root_authority.as_mut().unwrap().binding.expires_at += 1;
assert!(changed.validate_against(&challenge, 400).is_err());
}
for mutate in [
|input: &mut SkillUsePrepare| input.expires_at = 5_001,
|input: &mut SkillUsePrepare| input.prepared_at = 199,
|input: &mut SkillUsePrepare| input.candidate_sha256 = digest(99),
|input: &mut SkillUsePrepare| {
input
.root_authority
.as_mut()
.unwrap()
.profile_lease
.as_mut()
.unwrap()
.root_session_id = SessionId::new("other").unwrap()
},
|input: &mut SkillUsePrepare| {
let lease = input
.root_authority
.as_mut()
.unwrap()
.profile_lease
.as_mut()
.unwrap();
lease.state = ProfileLeaseState::Entered;
lease.write_scope.clear();
},
] {
let mut input = root_skill_use_prepare(true);
mutate(&mut input);
assert!(SkillUseChallenge::prepare(input).is_err());
}
let mut resumed = root_skill_use_prepare(true);
let authority = resumed.root_authority.as_mut().unwrap();
authority
.profile_lease
.as_mut()
.unwrap()
.exit(&authority.binding, 400, true)
.expect("exit");
resumed.role = Role::Shepherd;
resumed.startup_skill = "shepherd".into();
resumed.prepared_at = 5_001;
resumed.expires_at = 6_000;
SkillUseChallenge::prepare(resumed).expect("inactive profile retained after its expired lease");
}
#[cfg(feature = "json")]
#[test]
fn child_skill_use_wire_omits_root_authority_and_rejects_unknown_mixtures() {
let child = SkillUseChallenge::prepare(skill_use_prepare(
Role::Coder,
"implementing",
"verification",
SkillUseStage::Completion,
))
.unwrap();
let mut wire = serde_json::to_value(&child).unwrap();
assert_eq!(wire["dispatch_id"], "coder-1");
assert!(wire.get("root_authority").is_none());
assert_eq!(
serde_json::from_value::<SkillUseChallenge>(wire.clone()).unwrap(),
child
);
wire["root_authority"] =
serde_json::to_value(root_skill_use_prepare(false).root_authority).unwrap();
assert!(
serde_json::from_value::<SkillUseChallenge>(wire.clone())
.unwrap()
.validate()
.is_err()
);
wire["root_authority"]["selected_path"] = serde_json::json!("../other");
assert!(serde_json::from_value::<SkillUseChallenge>(wire).is_err());
}
fn attachment_expectation(role: Role, agent_id: &str) -> CarrierAttachmentExpectation {
CarrierAttachmentExpectation {
target: Harness::Pi,
role,
agent_id: AgentId::new(agent_id).expect("fixture agent"),
installed_carrier_path: "/tmp/shepherd-carrier".into(),
candidate_sha256: digest(1),
carrier_sha256: digest(2),
compiler_tree_sha256: digest(3),
startup_skill: "reviewing".into(),
skill_bundle_sha256: digest(4),
attachment_kind: AttachmentKind::PiSkillPath,
}
}
fn pending_fixture() -> PendingDispatch {
PendingDispatch {
schema: "shepherd.pending-dispatch/2".into(),
launch_id_hash: digest(5),
parent_process_hash: digest(8),
project_id: ProjectId::new("018f47ce-72d7-7f64-9eb1-2f651d521c2a")
.expect("fixture project"),
project_filesystem_id: ProjectFilesystemId::new("a".repeat(64)).expect("filesystem"),
run: RunId::new("v645").expect("run"),
run_status: RunStatus::Planted.into(),
root_session_id: SessionId::new("root-session").expect("root session"),
caller_role: Role::Engineer,
parent_dispatch_id: Some(DispatchId::new("dispatch-engineer").expect("dispatch")),
replaces_agent_id: None,
role: Role::Auditor,
work_kind: WorkKind::Review,
lane: Some(LaneId::new("l1-engine").expect("lane")),
baseline_commit: GitCommit::new("b".repeat(40)).expect("commit"),
read_scope: vec![PathAuthority::new("crates/core/**").expect("read scope")],
write_scope: vec![],
result_artifact: PathAuthority::exact(".shepherd/runs/v645/reports/auditor.md")
.expect("result"),
review_artifact: PathAuthority::exact(".shepherd/runs/v645/reviews/auditor.md")
.expect("review"),
task_path: PathAuthority::exact("crates/core/src/dispatch/record.rs").expect("task"),
task_sha256: digest(6),
expected_child_session_id: SessionId::new("child-session").expect("child session"),
expected_attachment: attachment_expectation(Role::Auditor, "auditor-agent"),
expires_at: 20,
launch_state: PendingLaunchState::Pending,
claimed_at: None,
child_process_hash: None,
activated_at: None,
nonce_sha256: digest(7),
}
}
#[test]
fn pending_dispatch_binds_every_authority_fact_and_rejects_replay_shape() {
let pending = pending_fixture();
pending.validate().expect("complete pending contract");
let wire = serde_json::to_value(&pending).expect("pending wire value");
assert!(wire["launch_id_hash"].is_string());
assert!(wire["expected_attachment"]["carrier_sha256"].is_string());
assert!(wire.get("token").is_none());
let mut claimed = pending.clone();
claimed.claim(10, digest(9)).expect("one claim");
assert_eq!(claimed.launch_state, PendingLaunchState::ClaimedUnspawned);
claimed
.validate()
.expect("claimed contract remains auditable");
claimed.activate(15, digest(9)).expect("one activation");
assert_eq!(claimed.launch_state, PendingLaunchState::Active);
assert!(
claimed.activate(16, digest(9)).is_err(),
"activation cannot replay"
);
assert!(pending.clone().claim(10, digest(9)).is_ok());
}
#[test]
fn direct_start_root_execution_has_only_the_authored_role_work_edges() {
let work_kinds = [
WorkKind::Planning,
WorkKind::Coordination,
WorkKind::ProductionCode,
WorkKind::Artifact,
WorkKind::Review,
WorkKind::Research,
];
for role in Role::ALL {
for work_kind in work_kinds {
let expected = matches!(
(role, work_kind),
(Role::Conductor, WorkKind::Coordination)
| (Role::Coder, WorkKind::ProductionCode)
| (Role::Worker, WorkKind::Artifact)
| (Role::Auditor, WorkKind::Review)
);
assert_eq!(
validate_pending_edge(
&RunStatus::Executing.into(),
Role::Shepherd,
role,
work_kind
)
.is_ok(),
expected,
"direct start edge must be exact for {role}/{work_kind:?}",
);
assert!(
validate_pending_edge(&RunStatus::Executing.into(), Role::Planter, role, work_kind)
.is_err(),
"the root-local Planter cannot dispatch {role}/{work_kind:?}",
);
}
}
for status in [
RunStatus::Planted,
RunStatus::Planned,
RunStatus::Closing,
RunStatus::Closed,
] {
for (role, work_kind) in [
(Role::Coder, WorkKind::ProductionCode),
(Role::Worker, WorkKind::Artifact),
] {
assert!(
validate_pending_edge(&status.into(), Role::Shepherd, role, work_kind).is_err(),
"direct start never opens execution authority during {status}",
);
}
}
}
#[test]
fn pending_dispatch_edge_and_work_kind_rules_fail_closed() {
validate_pending_edge(
&RunStatus::Planted.into(),
Role::Shepherd,
Role::Engineer,
WorkKind::Planning,
)
.expect("root may prepare the Engineer");
validate_pending_edge(
&RunStatus::Planted.into(),
Role::Engineer,
Role::Auditor,
WorkKind::Review,
)
.expect("Engineer may prepare an Auditor");
validate_pending_edge(
&RunStatus::Planted.into(),
Role::Engineer,
Role::Discovery,
WorkKind::Research,
)
.expect("Engineer may prepare Discovery");
validate_pending_edge(
&RunStatus::Executing.into(),
Role::Conductor,
Role::Coder,
WorkKind::ProductionCode,
)
.expect("Conductor may prepare Coder");
validate_pending_edge(
&RunStatus::Executing.into(),
Role::Conductor,
Role::Worker,
WorkKind::Artifact,
)
.expect("Conductor may prepare Worker");
for invalid in [
(
RunStatus::Planted,
Role::Shepherd,
Role::Coder,
WorkKind::ProductionCode,
),
(
RunStatus::Planted,
Role::Engineer,
Role::Coder,
WorkKind::ProductionCode,
),
(
RunStatus::Executing,
Role::Conductor,
Role::Engineer,
WorkKind::Planning,
),
(
RunStatus::Executing,
Role::Coder,
Role::Worker,
WorkKind::Artifact,
),
(
RunStatus::Planted,
Role::Engineer,
Role::Auditor,
WorkKind::Research,
),
] {
assert!(
validate_pending_edge(&invalid.0.into(), invalid.1, invalid.2, invalid.3).is_err(),
"invalid edge {:?} must fail closed",
invalid
);
}
}
#[test]
fn loaded_carrier_attestation_requires_exact_nonce_identity_and_digests() {
let pending = pending_fixture();
let expected = &pending.expected_attachment;
let attestation = LoadedCarrierAttestationV1 {
schema: "shepherd.loaded-carrier/1".into(),
nonce_sha256: pending.nonce_sha256,
target: expected.target,
role: expected.role,
agent_id: expected.agent_id.clone(),
installed_carrier_path: expected.installed_carrier_path.clone(),
candidate_sha256: expected.candidate_sha256,
carrier_sha256: expected.carrier_sha256,
compiler_tree_sha256: expected.compiler_tree_sha256,
startup_skill: expected.startup_skill.clone(),
skill_bundle_sha256: expected.skill_bundle_sha256,
attachment_kind: expected.attachment_kind,
};
attestation
.validate_against(expected, &pending.nonce_sha256)
.expect("exact attachment attestation");
for mutate in [
|value: &mut LoadedCarrierAttestationV1| value.nonce_sha256 = digest(9),
|value: &mut LoadedCarrierAttestationV1| value.role = Role::Coder,
|value: &mut LoadedCarrierAttestationV1| value.carrier_sha256 = digest(9),
|value: &mut LoadedCarrierAttestationV1| value.startup_skill = "plant".into(),
|value: &mut LoadedCarrierAttestationV1| {
value.attachment_kind = AttachmentKind::ClaudePreload
},
] {
let mut wrong = attestation.clone();
mutate(&mut wrong);
assert!(
wrong
.validate_against(expected, &pending.nonce_sha256)
.is_err(),
"mutated attachment must fail closed"
);
}
}
#[test]
fn path_authority_rejects_traversal_universal_and_ambiguous_scopes() {
for invalid in ["../escape", "/tmp/escape", "crates/./core/**", "**"] {
assert!(
PathAuthority::new(invalid).is_err(),
"{invalid} must be bounded"
);
}
assert!(PathAuthority::new("crates/core/**").is_ok());
assert!(PathAuthority::exact(".shepherd/runs/v645/reports/result.md").is_ok());
let mut pending = pending_fixture();
pending.expected_attachment.installed_carrier_path = "carrier.md".into();
assert!(
pending.validate().is_err(),
"carrier attachment paths must be absolute and no-follow"
);
}
#[test]
fn role_write_scopes_separate_implementers_from_authors() {
use shepherd_core::dispatch::Role;
for implementer in [Role::Coder, Role::Worker] {
let scope = implementer.default_write_scope("v660");
assert!(
path_in_write_scope("crates/cli/src/lib.rs", &scope).expect("valid path"),
"{implementer} must be able to write code"
);
}
for author in [
Role::Engineer,
Role::Conductor,
Role::Critic,
Role::Auditor,
Role::Discovery,
] {
let scope = author.default_write_scope("v660");
assert!(
path_in_write_scope(".shepherd/runs/v660/lanes/l1/findings.md", &scope)
.expect("valid path"),
"{author} must be able to author inside its run"
);
assert!(
!path_in_write_scope("crates/cli/src/lib.rs", &scope).expect("valid path"),
"{author} must not be able to write code"
);
assert!(
!path_in_write_scope(".shepherd/runs/v660/patch.rs", &scope).expect("valid path"),
"{author} is scoped to Markdown, not to the directory"
);
assert!(
!path_in_write_scope(".shepherd/runs/v659/report.md", &scope).expect("valid path"),
"{author} must not reach another run"
);
}
let planter = Role::Planter.default_write_scope("v660");
assert!(path_in_write_scope(".shepherd/runs/v660/seed.md", &planter).expect("valid path"));
assert!(path_in_write_scope(".shepherd/runs/v660/mesh.md", &planter).expect("valid path"));
assert!(!path_in_write_scope(".shepherd/runs/v660/plan.md", &planter).expect("valid path"));
}
#[test]
fn role_registry_matches_the_derived_variants() {
use core::str::FromStr;
use shepherd_core::dispatch::Role;
use strum::{EnumCount, VariantArray};
assert_eq!(Role::ALL.len(), Role::COUNT, "the flock is exactly nine");
assert_eq!(
Role::ALL.as_slice(),
Role::VARIANTS,
"ALL drifted from the variants"
);
for role in Role::ALL {
assert_eq!(
role.as_str(),
role.to_string(),
"as_str and Display disagree"
);
assert_eq!(Role::from_name(role.as_str()).expect("round-trips"), role);
assert_eq!(Role::from_str(role.as_str()).expect("parses"), role);
assert_eq!(
Role::from_carrier(&role.carrier()).expect("carrier round-trips"),
role
);
}
assert!(
Role::from_name("reviewer").is_err(),
"a tenth role is refused"
);
}
#[test]
fn role_is_case_sensitive_on_every_boundary() {
use core::str::FromStr;
use shepherd_core::dispatch::Role;
for spelling in ["Engineer", "ENGINEER", "eNgInEeR", " engineer", "engineer "] {
assert!(
Role::from_name(spelling).is_err(),
"from_name accepted {spelling:?}"
);
assert!(
Role::from_str(spelling).is_err(),
"FromStr accepted {spelling:?}"
);
assert!(
serde_json::from_str::<Role>(&format!("\"{spelling}\"")).is_err(),
"deserialize accepted {spelling:?}"
);
assert!(
Role::from_carrier(&format!("shepherd:{spelling}")).is_err(),
"from_carrier accepted {spelling:?}"
);
}
assert_eq!(
Role::from_name("engineer").expect("canonical"),
Role::Engineer
);
assert_eq!(
serde_json::from_str::<Role>("\"engineer\"").expect("canonical"),
Role::Engineer
);
}