From 60392ea5d82cc6592c7a1dbc45cb2d620e795384 Mon Sep 17 00:00:00 2001 From: Bo Wu Date: Tue, 25 Aug 2026 05:24:51 -0700 Subject: [PATCH 1/3] fix reviewed ops execution context --- orchestrator_prompt.md | 9 +++++++-- prompts/playbooks/reviewed-ops-cycle.md | 18 ++++++++++++------ prompts/roles/ops-agent.md | 13 ++++++++++--- src/runtime.rs | 11 ++++++++++- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/orchestrator_prompt.md b/orchestrator_prompt.md index d15e910..6765bd8 100644 --- a/orchestrator_prompt.md +++ b/orchestrator_prompt.md @@ -59,8 +59,13 @@ bindings, independent review, and phase completion. only when operational work completes or reaches a blocker. - `reviewed-ops-cycle` waits for both review and the ops continuation. Consume its compact result directly: never call `subagent wait` afterward and never - inspect logs, transcripts, role homes, operation directories, or receipts to - rediscover its result. + inspect unrelated logs, transcripts, role homes, or operation directories to + rediscover its result. The ops continuation itself verifies the exact + immutable request and digest-bound runbook and may inspect its exact receipt. +- If an accepted immutable request is not executed because the ops continuation + reports a structural blocker, do not repeat that unchanged request with a new + reviewer or ops context. Surface the blocker; a new reviewed cycle requires a + materially distinct request. - After a reviewed ops cycle, treat any required follow-up operation as incomplete until the same ops identity has materialized its complete bound request at `$MULTIAGENT_LOG_DIR/agents/OPS_NAME/request.json`. A prose proposal diff --git a/prompts/playbooks/reviewed-ops-cycle.md b/prompts/playbooks/reviewed-ops-cycle.md index ba6e107..463b46f 100644 --- a/prompts/playbooks/reviewed-ops-cycle.md +++ b/prompts/playbooks/reviewed-ops-cycle.md @@ -43,7 +43,9 @@ This command: Do not reconstruct these mechanics manually. Prior panes, transcripts, final messages, and native provider resume state are intentionally excluded from the -continuation boundary. +continuation boundary. A fresh context must not execute opaque instructions: it +reads and verifies the exact supervisor-published immutable request and its +digest-bound runbook before execution. Never pass the supervisor-owned published artifact back as `--request-file`; that path is intentionally outside the ops identity directory. On rejection or @@ -52,13 +54,17 @@ and reviewer. A review correction may use a fresh reviewer on the same immutable request. Never create a second ops identity. The cycle already waits. Do not call `subagent wait` afterward, and do not read, -tail, grep, find, or list agent logs, transcripts, role homes, operation -directories, or receipts. Use only the returned `opsResult` and +tail, grep, find, or list unrelated agent logs, transcripts, role homes, or +operation directories. The ops continuation must inspect the exact immutable +request and bound runbook before execution, and may inspect the exact receipt +path returned by execution. Use only the returned `opsResult` and `followUpRequest`. If `followUpRequest` is non-null, run a new cycle on that exact path with a fresh reviewer. If it is null, use `opsResult` as the durable -conclusion. A prose proposal, an `awaiting` statement`, or a draft under a -private role-home path is incomplete work: restore the same ops identity to -materialize the canonical request rather than treating it as a result or +conclusion. If an accepted immutable request is not executed because the ops +continuation reports a structural blocker, do not submit that unchanged request +to another review cycle. A prose proposal, an `awaiting` statement, or a draft +under a private role-home path is incomplete work: restore the same ops identity +to materialize the canonical request rather than treating it as a result or spawning a replacement. For an external-only task with successful reviewed operations and no source diff --git a/prompts/roles/ops-agent.md b/prompts/roles/ops-agent.md index 31a3f7b..da55b3e 100644 --- a/prompts/roles/ops-agent.md +++ b/prompts/roles/ops-agent.md @@ -54,12 +54,19 @@ When restored, use the exact command supplied by the supervisor: multiagent ops execute --request-file PATH --reviewer REVIEWER_NAME ``` +This is a fresh provider context restored into the existing OS-enforced ops +identity. Verify that identity, then read the exact supervisor-published +immutable request and its digest-bound runbook before execution. The command +independently verifies the request, reviewer approval, and signed permit. Never +bypass it with direct provider access. + Execute the reviewed request once. Interpret the structured outcome under the runbook and decide whether to finish, escalate, or prepare a distinct request. The command prints a compact result and persists the full receipt at -`receiptPath`. Use the compact result directly. Do not search logs, transcripts, -role homes, or operation directories, and do not reread the receipt unless the -compact result explicitly reports missing or truncated evidence. An +`receiptPath`; inspect both. Do not search unrelated logs, transcripts, role +homes, or operation directories. If you do not execute an accepted request, +report one exact structural blocker and never ask the supervisor to retry the +same immutable request with another reviewer or ops context. An `operationId` or `actionId` returned by execution is evidence, not an operation capability ID; never pass it to `ops describe`. Changed bytes always require a new review. For every follow-up operation, rerun diff --git a/src/runtime.rs b/src/runtime.rs index 5ca8ec0..2f079f9 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1770,7 +1770,10 @@ fn reviewed_ops_reviewer_instruction( fn reviewed_ops_execute_instruction(request_file: &Path, reviewer: &str, ops_name: &str) -> String { format!( - "Continue the same runbook with the independently reviewed immutable request. Execute exactly:\n\nmultiagent ops execute --request-file {} --reviewer {}\n\nUse the compact execution result printed by that command; the full receipt is already persisted at its receiptPath. Do not inspect agent logs, transcripts, operation directories, or the receipt unless the compact result explicitly reports missing or truncated evidence. Decide from the runbook whether to stop, escalate, or prepare another distinct reviewed operation. Never execute the same immutable request twice and never run `ops describe` on the returned operationId or actionId. If another operation is needed, first run `multiagent ops describe OPERATION_ID`, then materialize and bind the complete next request at exactly `$MULTIAGENT_LOG_DIR/agents/{}/request.json`, run `chmod 0640` on it, and report that exact path plus the two digest lines. Do not use a role-home path, do not call `ops publish`, and do not finish with only a proposed request. If no operation remains, report the final result or exact blocker. Do not create a replacement ops identity.", + "Continue the same runbook in a fresh provider context restored into the existing OS-enforced ops identity `{}`. The request below is the supervisor-published immutable artifact accepted by independent reviewer `{}`. Before acting, verify that `$MULTIAGENT_SUBAGENT_NAME` is `{}`, then read that exact request and its exact digest-bound runbook. Checking these trusted artifacts is required; this is not blind execution. Do not inspect unrelated agent logs, transcripts, role homes, or operation directories.\n\nExecute exactly:\n\nmultiagent ops execute --request-file {} --reviewer {}\n\nThis command independently verifies the immutable request, runbook binding, reviewer approval, and signed permit before prod-mcp receives an operation. Do not bypass it with direct provider access. Inspect its compact execution result and the persisted receipt at `receiptPath`, then decide from the runbook whether to stop, escalate, or prepare another distinct reviewed operation. Never execute the same immutable request twice and never run `ops describe` on the returned operationId or actionId. If the command is not executed, report one exact structural blocker and do not ask the supervisor to retry this unchanged request. If another operation is needed, first run `multiagent ops describe OPERATION_ID`, then materialize and bind the complete next request at exactly `$MULTIAGENT_LOG_DIR/agents/{}/request.json`, run `chmod 0640` on it, and report that exact path plus the two digest lines. Do not use a role-home path, do not call `ops publish`, and do not finish with only a proposed request. If no operation remains, report the final result or exact blocker. Do not create a replacement ops identity.", + ops_name, + reviewer, + ops_name, shell_escape(&request_file.display().to_string()), shell_escape(reviewer), ops_name, @@ -4240,6 +4243,12 @@ mod tests { assert!(execute.contains("Never execute the same immutable request twice")); assert!(execute.contains("compact execution result")); assert!(execute.contains("never run `ops describe` on the returned operationId or actionId")); + assert!(execute.contains("this is not blind execution")); + assert!(execute.contains("read that exact request and its exact digest-bound runbook")); + assert!(execute.contains("independently verifies the immutable request")); + assert!(execute.contains("Do not bypass it with direct provider access")); + assert!(execute.contains("do not ask the supervisor to retry this unchanged request")); + assert!(!execute.contains("Do not inspect agent logs")); } #[cfg(unix)] From 80820e7afbcdb70429ec553e1703adba376796e2 Mon Sep 17 00:00:00 2001 From: Bo Wu Date: Tue, 25 Aug 2026 05:26:12 -0700 Subject: [PATCH 2/3] apply Rust 1.98 formatting --- src/authority.rs | 8 +++--- src/prod_ops.rs | 65 ++++++++++++++++++++++------------------------- src/runtime.rs | 15 +++++------ src/supervisor.rs | 7 +++-- src/workflow.rs | 26 +++++++++++-------- 5 files changed, 59 insertions(+), 62 deletions(-) diff --git a/src/authority.rs b/src/authority.rs index 230c6a5..e8f9e78 100644 --- a/src/authority.rs +++ b/src/authority.rs @@ -153,9 +153,7 @@ impl AuthorityRequest { | AuthorityOperation::GateCheck => uid == config::ORCHESTRATOR_UID, AuthorityOperation::OpsDescribe | AuthorityOperation::OpsPublish - | AuthorityOperation::OpsExecute => { - uid == config::OPS_UID - } + | AuthorityOperation::OpsExecute => uid == config::OPS_UID, AuthorityOperation::OpsPublishBound => uid == config::ORCHESTRATOR_UID, AuthorityOperation::FindingCreate => uid == config::READER_UID, AuthorityOperation::FindingDismiss | AuthorityOperation::TodoClose => { @@ -251,7 +249,9 @@ mod tests { #[test] fn typed_api_excludes_runtime_and_arbitrary_execution() { assert!(AuthorityRequest::from_cli("workflow", &strings(&["status"])).is_some()); - assert!(AuthorityRequest::from_cli("workflow", &strings(&["context", "workflow-1"])).is_none()); + assert!( + AuthorityRequest::from_cli("workflow", &strings(&["context", "workflow-1"])).is_none() + ); assert!(AuthorityRequest::from_cli("subagent", &strings(&["assignment-create"])).is_some()); assert!(AuthorityRequest::from_cli("agent", &strings(&["run"])).is_none()); assert!(AuthorityRequest::from_cli("role-exec", &[]).is_none()); diff --git a/src/prod_ops.rs b/src/prod_ops.rs index 73f0d04..3787fce 100644 --- a/src/prod_ops.rs +++ b/src/prod_ops.rs @@ -95,7 +95,10 @@ fn operation_capability<'a>(response: &'a Value, operation_id: &str) -> Result<& .and_then(Value::as_object) .ok_or("prod-mcp capabilities response has no result object")?; if result.get("isError").and_then(Value::as_bool) == Some(true) { - return Err(format!("prod-mcp capabilities failed: {}", Value::Object(result.clone()))); + return Err(format!( + "prod-mcp capabilities failed: {}", + Value::Object(result.clone()) + )); } let operations = result .get("structuredContent") @@ -201,8 +204,8 @@ pub(crate) fn publish_bound_request( state: &Path, request_file: &Path, ) -> Result { - let state = fs::canonicalize(state) - .map_err(|error| format!("resolve multiagent state: {error}"))?; + let state = + fs::canonicalize(state).map_err(|error| format!("resolve multiagent state: {error}"))?; let (_, bytes) = read_reviewable_request(&state, request_file)?; let template: Value = serde_json::from_slice(&bytes) .map_err(|error| format!("decode bound ops request: {error}"))?; @@ -235,10 +238,7 @@ fn bind_request_template( "runbookDocument".into(), Value::String(runbook_document.into()), ); - object.insert( - "runbookContentSha256".into(), - Value::String(digest.clone()), - ); + object.insert("runbookContentSha256".into(), Value::String(digest.clone())); validate_request_template(&template)?; Ok((template, digest)) } @@ -248,10 +248,7 @@ fn publish_request_bytes(state: &Path, bytes: &[u8]) -> Result Result Result Result, String> { - let agents = fs::canonicalize(PathBuf::from(required_env("MULTIAGENT_LOG_DIR")?).join("agents")) - .map_err(|error| format!("resolve ops agents log directory: {error}"))?; - let canonical = fs::canonicalize(path) - .map_err(|error| format!("resolve ops draft file: {error}"))?; + let agents = + fs::canonicalize(PathBuf::from(required_env("MULTIAGENT_LOG_DIR")?).join("agents")) + .map_err(|error| format!("resolve ops agents log directory: {error}"))?; + let canonical = + fs::canonicalize(path).map_err(|error| format!("resolve ops draft file: {error}"))?; if !canonical.starts_with(&agents) { return Err("ops draft file must be inside MULTIAGENT_LOG_DIR/agents".into()); } - let (bytes, _metadata) = - read_bounded_file(&canonical, MAX_OPERATION_REQUEST_BYTES, true)?; + let (bytes, _metadata) = read_bounded_file(&canonical, MAX_OPERATION_REQUEST_BYTES, true)?; if bytes.is_empty() { return Err("ops request must contain between 1 and 65536 bytes".into()); } #[cfg(target_os = "linux")] if _metadata.uid() != crate::config::OPS_UID || _metadata.mode() & 0o022 != 0 { - return Err("ops draft must be owned by the ops UID and not group- or world-writable".into()); + return Err( + "ops draft must be owned by the ops UID and not group- or world-writable".into(), + ); } Ok(bytes) } fn read_reviewable_request(state: &Path, path: &Path) -> Result<(PathBuf, Vec), String> { - let canonical = fs::canonicalize(path) - .map_err(|error| format!("resolve ops request file: {error}"))?; + let canonical = + fs::canonicalize(path).map_err(|error| format!("resolve ops request file: {error}"))?; if !canonical.starts_with(state) { return Err("ops request file must be inside MULTIAGENT_STATE_DIR".into()); } - let (bytes, _metadata) = - read_bounded_file(&canonical, MAX_OPERATION_REQUEST_BYTES, true)?; + let (bytes, _metadata) = read_bounded_file(&canonical, MAX_OPERATION_REQUEST_BYTES, true)?; if bytes.is_empty() { return Err("ops request must contain between 1 and 65536 bytes".into()); } @@ -379,7 +373,10 @@ fn read_bounded_file( .read_to_end(&mut bytes) .map_err(|error| format!("read {}: {error}", path.display()))?; if bytes.len() as u64 > limit { - return Err(format!("{} exceeds the configured byte limit", path.display())); + return Err(format!( + "{} exceeds the configured byte limit", + path.display() + )); } Ok((bytes, metadata)) } @@ -1081,7 +1078,9 @@ fn verified_runbook_content(template: &Value) -> Result { } if let Some(target) = canonical_runbook_target(&bytes)? { if object.get("target") != Some(&target) { - return Err("ops request target does not match the exact Markdown runbook bytes".into()); + return Err( + "ops request target does not match the exact Markdown runbook bytes".into(), + ); } } Ok(actual) @@ -1550,10 +1549,9 @@ fn base64_decode(value: &str) -> Result, String> { mod tests { use super::{ base64_decode, base64url_encode, build_request, canonical, curl_command, ecdsa_der_to_raw, - operation_capability, parse_mcp_body, private_temp_path, review_binding_marker, review_binding_matches, - review_binding_value, review_evidence_is_bound, - reviewer_accepted, runbook_content_digest, validate_request_template, - write_mcp_headers, TrustedApproval, + operation_capability, parse_mcp_body, private_temp_path, review_binding_marker, + review_binding_matches, review_binding_value, review_evidence_is_bound, reviewer_accepted, + runbook_content_digest, validate_request_template, write_mcp_headers, TrustedApproval, }; use chrono::{TimeZone, Utc}; use serde_json::json; @@ -1782,5 +1780,4 @@ mod tests { let _ = fs::remove_file(request_headers); let _ = fs::remove_file(response_headers); } - } diff --git a/src/runtime.rs b/src/runtime.rs index 2f079f9..e21a4ad 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -255,8 +255,8 @@ pub fn role_agent_exec(args: &[String]) -> Result { let trace_dir = cfg.logs.join("agents").join(name); let resume_session = (restored && env::var("MULTIAGENT_FORCE_FRESH_CONTEXT").as_deref() != Ok("1")) - .then(|| native_resume_session(&trace_dir)) - .flatten(); + .then(|| native_resume_session(&trace_dir)) + .flatten(); let executable = env::current_exe().map_err(io_error("resolve multiagent executable"))?; let role_uid = if authorization.role == "ops" { config::OPS_UID @@ -1920,12 +1920,7 @@ fn publish_reviewed_ops_request(request_file: &Path) -> Result<(PathBuf, String) let request_file = request_file .to_str() .ok_or("reviewed ops request path is not valid UTF-8")?; - let output = run_self_output(&[ - "ops", - "publish-bound", - "--request-file", - request_file, - ])?; + let output = run_self_output(&["ops", "publish-bound", "--request-file", request_file])?; let descriptor = String::from_utf8(output.stdout) .map_err(|error| format!("decode published ops request descriptor: {error}"))?; let descriptor = descriptor.trim().to_string(); @@ -4242,7 +4237,9 @@ mod tests { assert!(execute.contains("decide from the runbook")); assert!(execute.contains("Never execute the same immutable request twice")); assert!(execute.contains("compact execution result")); - assert!(execute.contains("never run `ops describe` on the returned operationId or actionId")); + assert!( + execute.contains("never run `ops describe` on the returned operationId or actionId") + ); assert!(execute.contains("this is not blind execution")); assert!(execute.contains("read that exact request and its exact digest-bound runbook")); assert!(execute.contains("independently verifies the immutable request")); diff --git a/src/supervisor.rs b/src/supervisor.rs index ae792ca..8b99e1a 100644 --- a/src/supervisor.rs +++ b/src/supervisor.rs @@ -718,8 +718,8 @@ fn serve_connection(stream: &mut UnixStream) -> Result { return Ok(false); } let mut bytes = Vec::new(); - if let Err(error) = Read::take(&mut *stream, MAX_AUTHORITY_REQUEST_BYTES + 1) - .read_to_end(&mut bytes) + if let Err(error) = + Read::take(&mut *stream, MAX_AUTHORITY_REQUEST_BYTES + 1).read_to_end(&mut bytes) { eprintln!("authority supervisor: read request: {error}"); return Ok(false); @@ -730,8 +730,7 @@ fn serve_connection(stream: &mut UnixStream) -> Result { &Response { code: 1, stdout: String::new(), - stderr: "authority supervisor: request exceeds the configured byte limit\n" - .into(), + stderr: "authority supervisor: request exceeds the configured byte limit\n".into(), }, ); return Ok(false); diff --git a/src/workflow.rs b/src/workflow.rs index 970dbfa..e164abe 100644 --- a/src/workflow.rs +++ b/src/workflow.rs @@ -544,13 +544,16 @@ fn typed_identity_context( let name = entry.file_name().to_string_lossy().to_string(); valid_id("subagent identity", &name)?; let metadata = read_env_optional(&entry.path().join("meta.env"))?; - let role = metadata.get("role").map(String::as_str).unwrap_or("unknown"); + let role = metadata + .get("role") + .map(String::as_str) + .unwrap_or("unknown"); let role = bounded_identity_label(role, "identity role")?; - let raw_status = fs::read_to_string(entry.path().join("status")) - .unwrap_or_else(|_| "unknown".into()); + let raw_status = + fs::read_to_string(entry.path().join("status")).unwrap_or_else(|_| "unknown".into()); let status = match raw_status.trim() { - "starting" | "running" | "restoring" | "exited" | "done" | "blocked" - | "stopped" | "killed" | "finalized" => raw_status.trim(), + "starting" | "running" | "restoring" | "exited" | "done" | "blocked" | "stopped" + | "killed" | "finalized" => raw_status.trim(), _ => "unknown", }; identities.push(serde_json::json!({ @@ -1254,7 +1257,10 @@ pub fn supervisor_complete_external(id: &str) -> Result { || !state_value(&state, "decision_id").is_empty() || !state_value(&state, "candidate_diff_hash").is_empty() { - return Err("external-only completion cannot bypass a started source implementation lifecycle".into()); + return Err( + "external-only completion cannot bypass a started source implementation lifecycle" + .into(), + ); } validate_original_task(&state)?; let todos = read_todos(&p.todos)?; @@ -1275,8 +1281,8 @@ pub fn supervisor_complete_external(id: &str) -> Result { for entry in fs::read_dir(&operations_dir) .map_err(|error| format!("list external operation receipts: {error}"))? { - let entry = entry - .map_err(|error| format!("read external operation receipt entry: {error}"))?; + let entry = + entry.map_err(|error| format!("read external operation receipt entry: {error}"))?; let receipt_path = entry.path().join("receipt.json"); if !receipt_path.is_file() { continue; @@ -1294,9 +1300,7 @@ pub fn supervisor_complete_external(id: &str) -> Result { let structured = receipt .pointer("/result/structuredContent") .unwrap_or(&serde_json::Value::Null); - let succeeded = structured - .get("state") - .and_then(serde_json::Value::as_str) + let succeeded = structured.get("state").and_then(serde_json::Value::as_str) == Some("succeeded") && structured .pointer("/outcome/disposition") From 1dd8128be26f96124caaeec460481748be2696b7 Mon Sep 17 00:00:00 2001 From: Bo Wu Date: Tue, 25 Aug 2026 05:28:55 -0700 Subject: [PATCH 3/3] restore orchestrator prompt contracts --- orchestrator_prompt.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/orchestrator_prompt.md b/orchestrator_prompt.md index 6765bd8..30a9fb1 100644 --- a/orchestrator_prompt.md +++ b/orchestrator_prompt.md @@ -3,6 +3,9 @@ Coordinate isolated agents to satisfy the authenticated caller goal. Do not do worker, ops, scout, or reviewer work yourself. +The authenticated caller request is the goal authority. The orchestrator decides the DAG. +The supervisor enforces role isolation, evidence bindings, and phase gates. + All framework paths in this prompt resolve under `$MULTIAGENT_FRAMEWORK_ROOT`. Read policies, role modules, playbooks, and runbooks only from that image-owned root; never use same-named files from the cloned application repository. @@ -18,8 +21,10 @@ On a clean launch: Only when `MULTIAGENT_RESUME=1`, load `prompts/playbooks/recovery.md` before restoring work. +Do not inspect recovery state on a clean launch. When MULTIAGENT_RESUME=1, +inspect it only in the recovery workflow. -## Choose roles +## Role Catalog | Need | Role | | --- | --- | @@ -31,6 +36,7 @@ Only when `MULTIAGENT_RESUME=1`, load External access always belongs to ops. A scout may analyze an immutable artifact returned by ops, but cannot call Slack, GitHub, Grafana, AWS, Kubernetes, prod-mcp, or another deployed service. +External access is an authority boundary, not a mutability classification. ## Build the DAG @@ -48,6 +54,8 @@ bindings, independent review, and phase completion. - Spawn roles with `multiagent subagent spawn`; provider-native agents do not establish the required Linux identity or evidence boundary. +- Load reusable role and lifecycle modules from `MULTIAGENT_PROMPT_MODULE_ROOT`. + Use `prompts/playbooks/agent-spawning.md` for spawning mechanics. - Source changes follow `$MULTIAGENT_FRAMEWORK_ROOT/prompts/playbooks/implementation-lifecycle.md`. - External-only work skips the source lifecycle and uses reviewed ops requests. @@ -55,6 +63,7 @@ bindings, independent review, and phase completion. `$MULTIAGENT_FRAMEWORK_ROOT/prompts/playbooks/reviewed-ops-cycle.md`. Keep one ops identity for the session and invoke `multiagent subagent reviewed-ops-cycle` for every immutable request. + When selecting ops, load only prompts/playbooks/reviewed-ops-cycle.md. - Use a fresh reviewer for each immutable ops request. Finalize the ops identity only when operational work completes or reaches a blocker. - `reviewed-ops-cycle` waits for both review and the ops continuation. Consume