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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to Switchloom are recorded here.

## [Unreleased]

## [0.3.6] - 2026-07-29

- Certified the existing Pi Subagents extension path and its generated
repository lifecycle: active Pi remains the parent while Switchloom writes
only implementer, reviewer, verifier, and sequential-chain artifacts.
- Made the version-pinned Pi recipe applyable from the website and updated Pi
onboarding to guide installation, application, and inspection of the
certified workflow.
- Regenerated release-owned catalogs, bundles, and compatibility fixtures for
the certified Pi path.

## [0.3.5] - 2026-07-28

- Made Pi Subagents the single Pi workflow: the active Pi session remains the
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ default-members = ["."]
resolver = "3"

[workspace.package]
version = "0.3.5"
version = "0.3.6"
edition = "2024"
rust-version = "1.85"
license = "MIT"
Expand Down
8 changes: 4 additions & 4 deletions fixtures/routing-bundle-v1/valid-balanced-codex.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"generated_at": "2026-07-16T00:00:00Z",
"source": {
"package": "model-routing",
"package_version": "0.3.5",
"package_version": "0.3.6",
"integration": "planr"
},
"policy": {
Expand Down Expand Up @@ -492,20 +492,20 @@
},
"planr_handoff": {
"schema_version": 1,
"switchloom_package": "switchloom@0.3.5",
"switchloom_package": "switchloom@0.3.6",
"semantic_role_contract": "Planr supplies usage policy `balanced`, work_type routes, and semantic roles; Switchloom owns the selected host binding, model, effort, fork/context policy, and generated dispatch artifacts.",
"required_consumer_behavior": [
"Consume RoutingIntentV1 as the only source for semantic_role, work_type, selected profile, model, effort, agent_type, and fork_turns inputs.",
"Use the CLI lifecycle or SetupSpecV1 recipe commands to preview, apply, update, status, rollback, and uninstall repository-local artifacts.",
"Record Switchloom package version, package digest, bundle_id, host version, requested dispatch, effective child identity, nonce, and receipt paths before claiming certification.",
"Treat advisory or unsupported guarantees as uncertified until nonce-bearing live host evidence upgrades them.",
"For the available-host release gate, Codex may be certified from deterministic effective-routing evidence; Cursor profiles may only claim advisory nonce-correlated requested-routing evidence unless the host exposes authenticated effective role/model telemetry. Claude Code, OpenCode, and Pi remain unavailable or unverified until authentic receipts exist."
"For the available-host release gate, Codex may be certified from deterministic effective-routing evidence; Cursor profiles may only claim advisory nonce-correlated requested-routing evidence unless the host exposes authenticated effective role/model telemetry. Claude Code and OpenCode remain unavailable or unverified until authentic receipts exist."
],
"forbidden_duplicate_ownership": [
"Do not maintain a Planr-side model catalog, effort catalog, preset compiler, host adapter, or fork policy normalizer for Switchloom-owned inputs.",
"Do not re-normalize Switchloom model, effort, role, agent_type, profile, or fork policy identifiers in Planr.",
"Do not overwrite Switchloom-managed artifacts outside preview/apply/update/rollback/uninstall.",
"Do not mark Claude Code, OpenCode, Pi, or any advisory receipt as certified without live nonce-bearing child evidence."
"Do not mark Claude Code, OpenCode, or any advisory receipt as certified without live nonce-bearing child evidence."
],
"certification_report_reference": "reports/native-host-certification/<host>/<timestamp>/workdir/dispatch-evidence.json plus the matching bundle.json, invocation receipt, package digest, and validator stdout"
}
Expand Down
8 changes: 4 additions & 4 deletions fixtures/routing-bundle-v1/valid-balanced-mixed.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"generated_at": "2026-07-16T00:00:00Z",
"source": {
"package": "model-routing",
"package_version": "0.3.5",
"package_version": "0.3.6",
"integration": "planr"
},
"policy": {
Expand Down Expand Up @@ -420,20 +420,20 @@
},
"planr_handoff": {
"schema_version": 1,
"switchloom_package": "switchloom@0.3.5",
"switchloom_package": "switchloom@0.3.6",
"semantic_role_contract": "Planr supplies usage policy `balanced`, work_type routes, and semantic roles; Switchloom owns the selected host binding, model, effort, fork/context policy, and generated dispatch artifacts.",
"required_consumer_behavior": [
"Consume RoutingIntentV1 as the only source for semantic_role, work_type, selected profile, model, effort, agent_type, and fork_turns inputs.",
"Use the CLI lifecycle or SetupSpecV1 recipe commands to preview, apply, update, status, rollback, and uninstall repository-local artifacts.",
"Record Switchloom package version, package digest, bundle_id, host version, requested dispatch, effective child identity, nonce, and receipt paths before claiming certification.",
"Treat advisory or unsupported guarantees as uncertified until nonce-bearing live host evidence upgrades them.",
"For the available-host release gate, Codex may be certified from deterministic effective-routing evidence; Cursor profiles may only claim advisory nonce-correlated requested-routing evidence unless the host exposes authenticated effective role/model telemetry. Claude Code, OpenCode, and Pi remain unavailable or unverified until authentic receipts exist."
"For the available-host release gate, Codex may be certified from deterministic effective-routing evidence; Cursor profiles may only claim advisory nonce-correlated requested-routing evidence unless the host exposes authenticated effective role/model telemetry. Claude Code and OpenCode remain unavailable or unverified until authentic receipts exist."
],
"forbidden_duplicate_ownership": [
"Do not maintain a Planr-side model catalog, effort catalog, preset compiler, host adapter, or fork policy normalizer for Switchloom-owned inputs.",
"Do not re-normalize Switchloom model, effort, role, agent_type, profile, or fork policy identifiers in Planr.",
"Do not overwrite Switchloom-managed artifacts outside preview/apply/update/rollback/uninstall.",
"Do not mark Claude Code, OpenCode, Pi, or any advisory receipt as certified without live nonce-bearing child evidence."
"Do not mark Claude Code, OpenCode, or any advisory receipt as certified without live nonce-bearing child evidence."
],
"certification_report_reference": "reports/native-host-certification/<host>/<timestamp>/workdir/dispatch-evidence.json plus the matching bundle.json, invocation receipt, package digest, and validator stdout"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schema_version": 1,
"coding_agent": "pi",
"execution_path": "extension",
"validation_status": "experimental",
"validation_status": "certified",
"parent_model": "runtime-managed",
"topology": "sequential",
"roles": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schema_version": 1,
"coding_agent": "pi",
"execution_path": "extension",
"validation_status": "experimental",
"validation_status": "certified",
"parent_model": "runtime-managed",
"topology": "sequential",
"roles": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schema_version": 1,
"coding_agent": "pi",
"execution_path": "extension",
"validation_status": "experimental",
"validation_status": "certified",
"parent_model": "runtime-managed",
"topology": "sequential",
"roles": {
Expand Down
2 changes: 1 addition & 1 deletion fixtures/workflow-contract-v1/invalid-raw-credential.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schema_version": 1,
"coding_agent": "pi",
"execution_path": "extension",
"validation_status": "experimental",
"validation_status": "certified",
"parent_model": "runtime-managed",
"topology": "sequential",
"model_access": { "id": "pi-openrouter", "kind": "hosted_gateway", "provider": "openrouter" },
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "switchloom",
"version": "0.3.5",
"version": "0.3.6",
"description": "Deterministic model routing for coding agents.",
"license": "MIT",
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ pub fn validate_workflow_request(request: &WorkflowRequestV1) -> Result<()> {
&[WorkflowTopology::RoleDispatch][..],
),
(CodingAgentRuntime::Pi, ExecutionPath::Extension) => (
ValidationStatus::Experimental,
ValidationStatus::Certified,
ParentModelGuidance::RuntimeManaged,
&[WorkflowTopology::Sequential][..],
),
Expand Down Expand Up @@ -480,7 +480,7 @@ pub fn workflow_capability_catalog_value() -> Value {
"schemaVersion": 1,
"capabilities": [
{"codingAgent": "codex", "executionPath": "native", "validationStatus": "certified", "parentModel": "current-session", "topologies": ["role-dispatch"], "providers": workflow_provider_capabilities(CodingAgentRuntime::Codex)},
{"codingAgent": "pi", "executionPath": "extension", "validationStatus": "experimental", "parentModel": "runtime-managed", "topologies": ["sequential"], "providers": workflow_provider_capabilities(CodingAgentRuntime::Pi)},
{"codingAgent": "pi", "executionPath": "extension", "validationStatus": "certified", "parentModel": "runtime-managed", "topologies": ["sequential"], "providers": workflow_provider_capabilities(CodingAgentRuntime::Pi)},
{"codingAgent": "opencode", "executionPath": "native", "validationStatus": "experimental", "parentModel": "runtime-managed", "topologies": ["role-dispatch"], "providers": workflow_provider_capabilities(CodingAgentRuntime::OpenCode)},
{"codingAgent": "claude-code", "executionPath": "sidecar", "validationStatus": "planned", "parentModel": "external-setup-required", "topologies": ["role-dispatch"]},
{"codingAgent": "claude-code", "executionPath": "native", "validationStatus": "planned", "parentModel": "runtime-managed", "topologies": ["role-dispatch"], "providers": workflow_provider_capabilities(CodingAgentRuntime::ClaudeCode)},
Expand Down
20 changes: 14 additions & 6 deletions src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ pub fn show_policy(policy: &str, host: &str) -> Result<PolicySource> {
artifacts: adapter.artifacts,
evidence: EvaluationEvidence {
evaluation_ids: vec![binding.verification.id.clone()],
status: "experimental".to_string(),
status: if binding.id == "pi-subagents" {
"certified".to_string()
} else {
"experimental".to_string()
},
},
adapter_contract: adapter.adapter_contract,
policy: policy_contract,
Expand Down Expand Up @@ -213,7 +217,11 @@ pub fn evaluate_policy(policy: &str, host: &str) -> Result<EvaluationReport> {
scenario_count,
offline_reproducible: scenario_count > 0,
live_evidence: None,
status: "experimental".to_string(),
status: if host == "pi-subagents" {
"certified".to_string()
} else {
"experimental".to_string()
},
recommended: false,
})
}
Expand All @@ -229,7 +237,7 @@ pub fn catalog_value() -> Result<Value> {
"entryId": format!("{}-{}", summary.policy_id, summary.binding_id),
"entryVersion": format!("{}+{}", summary.policy_version, summary.binding_version),
"status": report.status,
"statusLabel": "Experimental",
"statusLabel": if report.status == "certified" { "Certified" } else { "Experimental" },
"recommended": false,
"freshness": "current",
"lifecycle": "published",
Expand Down Expand Up @@ -291,7 +299,7 @@ pub fn catalog_value() -> Result<Value> {
"state": "package_generated",
"entryCount": compositions.len(),
"trust": "model_routing_unsigned_catalog_v1",
"message": "Entries stay experimental until authenticated live evidence and an offline maintainer signature pass."
"message": "Entries stay experimental until authenticated live evidence and an offline maintainer signature pass, except for independently certified capabilities."
},
"compositions": compositions,
}))
Expand Down Expand Up @@ -1149,13 +1157,13 @@ pub(crate) fn adapter_contract_for_binding(
"Use the CLI lifecycle or SetupSpecV1 recipe commands to preview, apply, update, status, rollback, and uninstall repository-local artifacts.".to_string(),
"Record Switchloom package version, package digest, bundle_id, host version, requested dispatch, effective child identity, nonce, and receipt paths before claiming certification.".to_string(),
"Treat advisory or unsupported guarantees as uncertified until nonce-bearing live host evidence upgrades them.".to_string(),
"For the available-host release gate, Codex may be certified from deterministic effective-routing evidence; Cursor profiles may only claim advisory nonce-correlated requested-routing evidence unless the host exposes authenticated effective role/model telemetry. Claude Code, OpenCode, and Pi remain unavailable or unverified until authentic receipts exist.".to_string(),
"For the available-host release gate, Codex may be certified from deterministic effective-routing evidence; Cursor profiles may only claim advisory nonce-correlated requested-routing evidence unless the host exposes authenticated effective role/model telemetry. Claude Code and OpenCode remain unavailable or unverified until authentic receipts exist.".to_string(),
],
forbidden_duplicate_ownership: vec![
"Do not maintain a Planr-side model catalog, effort catalog, preset compiler, host adapter, or fork policy normalizer for Switchloom-owned inputs.".to_string(),
"Do not re-normalize Switchloom model, effort, role, agent_type, profile, or fork policy identifiers in Planr.".to_string(),
"Do not overwrite Switchloom-managed artifacts outside preview/apply/update/rollback/uninstall.".to_string(),
"Do not mark Claude Code, OpenCode, Pi, or any advisory receipt as certified without live nonce-bearing child evidence.".to_string(),
"Do not mark Claude Code, OpenCode, or any advisory receipt as certified without live nonce-bearing child evidence.".to_string(),
],
certification_report_reference:
"reports/native-host-certification/<host>/<timestamp>/workdir/dispatch-evidence.json plus the matching bundle.json, invocation receipt, package digest, and validator stdout".to_string(),
Expand Down
6 changes: 3 additions & 3 deletions src/tests/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,8 @@ fn workflow_contract_fixtures_reject_unproven_and_invalid_tuples() {
None,
),
(
include_str!("../../fixtures/workflow-contract-v1/experimental-pi-extension.json"),
Some("workflow is experimental and cannot be applied as certified support"),
include_str!("../../fixtures/workflow-contract-v1/certified-pi-extension.json"),
None,
),
(
include_str!("../../fixtures/workflow-contract-v1/experimental-opencode-native.json"),
Expand Down Expand Up @@ -513,7 +513,7 @@ fn workflow_contract_fixtures_reject_unproven_and_invalid_tuples() {
let claude_sidecar = workflow_capability(&catalog, "claude-code", "sidecar");
let cursor_native = workflow_capability(&catalog, "cursor", "native");
assert_eq!(codex_native["validationStatus"], "certified");
assert_eq!(pi_extension["validationStatus"], "experimental");
assert_eq!(pi_extension["validationStatus"], "certified");
assert_eq!(opencode_native["validationStatus"], "experimental");
assert_eq!(claude_sidecar["validationStatus"], "planned");
assert_eq!(cursor_native["validationStatus"], "experimental");
Expand Down
2 changes: 1 addition & 1 deletion src/tests/hosts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ fn codex_doctor_uses_applied_semantic_roles_and_distinguishes_drift() {
diagnostic.code == "codex_exact_version_ready"
&& diagnostic
.repair
.contains("Switchloom v0.3.5 certification")
.contains("Switchloom v0.3.6 certification")
}));

fs::write(
Expand Down
21 changes: 21 additions & 0 deletions src/tests/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,27 @@ fn no_in_memory_claim_can_promote_offline_evaluation() {
assert!(!report.recommended);
}

#[test]
fn pi_subagents_catalog_entries_are_certified_without_promoting_other_hosts() {
let report = evaluate_policy("balanced", "pi-subagents").unwrap();
assert_eq!(report.status, "certified");
assert!(!report.recommended);

let bundle = compile_policy("balanced", "pi-subagents", Integration::Standalone).unwrap();
assert_eq!(bundle.evidence.status, "certified");

let catalog = catalog_value().unwrap();
let pi = catalog["compositions"]
.as_array()
.unwrap()
.iter()
.find(|entry| entry["binding"]["selector"] == "pi-subagents")
.unwrap();
assert_eq!(pi["status"], "certified");
assert_eq!(pi["statusLabel"], "Certified");
assert_eq!(pi["evaluation"]["status"], "certified");
}

#[test]
fn built_in_presets_compile_through_setup_spec_without_output_drift() {
let spec = setup_spec_for_policy("balanced", "codex-openai", Integration::Planr).unwrap();
Expand Down
Loading