Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions orchestrator_prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ On a clean launch:

1. Run `multiagent workflow context "$MULTIAGENT_WORKFLOW_ID"`.
2. Read the authenticated task artifact named by `originalTask` exactly once.
Use only the exact writable path in `resultCandidate.path` for caller-result
handoff files; never write inside the workflow directory containing
`originalTask`.
3. Route from that typed context. Do not inspect panes, rediscover state paths,
or reconstruct provider transcripts.

Expand Down Expand Up @@ -94,11 +97,11 @@ bindings, independent review, and phase completion.
request at `$MULTIAGENT_LOG_DIR/agents/OPS_NAME/request.json`. A prose proposal
or `awaiting` report is not a result; restore that ops identity, then run a new
reviewed cycle with a fresh reviewer.
- For successful external-only work, synthesize one self-contained caller
response from the original goal and all accumulated `opsResult` values. Write
it to `$MULTIAGENT_STATE_DIR/orchestrator-result.md`, then complete with
`multiagent orchestrator complete --external-only --result-file
"$MULTIAGENT_STATE_DIR/orchestrator-result.md"`. The runtime rejects external
- For successful or terminally blocked external-only work, synthesize one
self-contained caller response from the original goal and all accumulated
`opsResult` values. Write it to the exact `resultCandidate.path` returned by
`workflow context`, then complete with `multiagent orchestrator complete
--external-only --result-file RESULT_CANDIDATE_PATH`. The runtime rejects external
completion without this bounded result handoff. Do not enter source lifecycle
phases or pass a private agent artifact as the caller response.
- Preserve literal predicates from the authenticated goal. When the caller
Expand Down
3 changes: 2 additions & 1 deletion prompts/playbooks/orchestration-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ own role-specific procedure; this file does not repeat them.
- Answer directly, or ask one bounded clarification, when the authenticated
request can be handled from the current conversation without reading the
repository, calling an external service, or producing an artifact. Persist
the exact response under `MULTIAGENT_STATE_DIR`, then request
the exact response at the `resultCandidate.path` returned by workflow context,
then request
`multiagent orchestrator complete --direct-response --result-file PATH`.
- Use a `reader` when answering requires repository inspection but no source
mutation. Readers run in the repository working directory with mechanically
Expand Down
9 changes: 5 additions & 4 deletions prompts/playbooks/reviewed-ops-cycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,14 @@ response. It must include every caller-requested field and its supporting
evidence, not merely a completion statement. A new caller-authorized session is
required for more work.

For an external-only task with successful reviewed operations and no source
changes, write that caller response to
`$MULTIAGENT_STATE_DIR/orchestrator-result.md`, then finish with:
For an external-only task with successful reviewed operations, or a terminal
reviewed structural blocker, and no source changes, write that caller response
to the exact `resultCandidate.path` returned by workflow context, then finish
with:

```bash
multiagent orchestrator complete --external-only \
--result-file "$MULTIAGENT_STATE_DIR/orchestrator-result.md"
--result-file "RESULT_CANDIDATE_PATH"
```

The result artifact is the control server handoff, not a substitute for an
Expand Down
19 changes: 12 additions & 7 deletions runtime/src/workflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@ fn context(args: &[String]) -> Result<(), String> {
.map_err(|error| format!("inspect original task artifact: {error}"))?
.len();
let identities = typed_identity_context(&store.state_dir, MAX_IDENTITIES)?;
let result_candidate = store.state_dir.join("orchestrator-result-candidate.md");
let value = serde_json::json!({
"apiVersion": "multiagent.moveindustries.io/v1",
"kind": "WorkflowContext",
Expand All @@ -723,6 +724,11 @@ fn context(args: &[String]) -> Result<(), String> {
"mediaType": "text/plain",
"truncated": false
},
"resultCandidate": {
"path": result_candidate,
"mediaType": "text/plain",
"maxBytes": 6000
},
"activeTodoCount": read_todos(&p.todos)?.iter().filter(|row| active(row.get(4))).count(),
"reviewCount": read_reviews(&p.reviews)?.len(),
"identities": identities
Expand Down Expand Up @@ -1541,6 +1547,7 @@ pub fn supervisor_complete_external(id: &str) -> Result<String, String> {
let operations_dir = store.state_dir.join("operations");
let mut successful_operations = 0usize;
let mut failed_operations = 0usize;
let mut blocked_operations = 0usize;
if operations_dir.is_dir() {
for entry in fs::read_dir(&operations_dir)
.map_err(|error| format!("list external operation receipts: {error}"))?
Expand Down Expand Up @@ -1581,9 +1588,8 @@ pub fn supervisor_complete_external(id: &str) -> Result<String, String> {
.and_then(serde_json::Value::as_str),
) {
(Some("succeeded"), Some("succeeded")) => successful_operations += 1,
(Some("failed"), Some("failed")) | (Some("blocked"), Some("blocked")) => {
failed_operations += 1
}
(Some("failed"), Some("failed")) => failed_operations += 1,
(Some("blocked"), Some("blocked")) => blocked_operations += 1,
_ => {
return Err(format!(
"external-only completion requires consistently classified terminal receipts; {} has mismatched state and disposition",
Expand All @@ -1593,10 +1599,9 @@ pub fn supervisor_complete_external(id: &str) -> Result<String, String> {
}
}
}
if successful_operations == 0 {
if successful_operations == 0 && (blocked_operations == 0 || failed_operations > 0) {
return Err(
"external-only completion requires at least one successful reviewed operation receipt"
.into(),
"external-only completion requires a successful reviewed operation receipt or a terminal reviewed blocker without executor failures".into(),
);
}
crate::subagent::external_completion_gate_check()?;
Expand All @@ -1610,7 +1615,7 @@ pub fn supervisor_complete_external(id: &str) -> Result<String, String> {
&p.events,
"phase_transitioned",
&format!(
"from=pre-implementation\tto=complete\titeration={}\tauthority=supervisor\troute=external-only\toperations={successful_operations}\tfailed_operations={failed_operations}",
"from=pre-implementation\tto=complete\titeration={}\tauthority=supervisor\troute=external-only\toperations={successful_operations}\tfailed_operations={failed_operations}\tblocked_operations={blocked_operations}",
state_value(&state, "iteration")
),
)?;
Expand Down
41 changes: 40 additions & 1 deletion tests/lifecycle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ PROMPT_BUNDLE="$TEST_TMP/orchestrator-bundle.md"
--output "$PROMPT_BUNDLE" >/dev/null
assert_contains "$PROMPT_BUNDLE" "BEGIN ORCHESTRATION ROUTING CONTRACT"
assert_contains "$PROMPT_BUNDLE" "--direct-response"
assert_contains "$PROMPT_BUNDLE" "resultCandidate.path"
assert_contains "$PROMPT_BUNDLE" "BEGIN MANDATORY IMPLEMENTATION LIFECYCLE"
assert_contains "$PROMPT_BUNDLE" "post-implementation -> pre-implementation"

Expand Down Expand Up @@ -588,8 +589,46 @@ assert_contains "$EXTERNAL_STATE/workflows/WF-EXTERNAL/lifecycle/lifecycle.env"
assert_contains "$EXTERNAL_STATE/workflows/WF-EXTERNAL/lifecycle/events.log" \
"route=external-only"
assert_contains "$EXTERNAL_STATE/workflows/WF-EXTERNAL/lifecycle/events.log" \
$'operations=1\tfailed_operations=3'
$'operations=1\tfailed_operations=2\tblocked_operations=1'
assert_contains "$EXTERNAL_STATE/orchestrator-result.md" \
"External operation completed with reviewed evidence."

BLOCKED_EXTERNAL_STATE="$TEST_TMP/blocked-external-state"
mkdir -p "$BLOCKED_EXTERNAL_STATE/operations/OP-BLOCKED"
MULTIAGENT_STATE_DIR="$BLOCKED_EXTERNAL_STATE" \
"$MULTIAGENT" workflow init WF-BLOCKED-EXTERNAL >/dev/null
cp "$EXTERNAL_STATE/operations/OP-BLOCKED/receipt.json" \
"$BLOCKED_EXTERNAL_STATE/operations/OP-BLOCKED/receipt.json"
BLOCKED_EXTERNAL_RESULT="$BLOCKED_EXTERNAL_STATE/external-result-candidate.md"
printf 'The reviewed operation reached a terminal structural blocker.\n' \
>"$BLOCKED_EXTERNAL_RESULT"
MULTIAGENT_ROOT="$EXTERNAL_ROOT" MULTIAGENT_STATE_DIR="$BLOCKED_EXTERNAL_STATE" \
MULTIAGENT_WORKFLOW_ID=WF-BLOCKED-EXTERNAL MULTIAGENT_RUN_ID=RUN-BLOCKED-EXTERNAL \
MULTIAGENT_LIFECYCLE_ENFORCEMENT=1 \
"$MULTIAGENT" orchestrator complete --external-only \
--result-file "$BLOCKED_EXTERNAL_RESULT" >"$TEST_TMP/blocked-external-complete.out"
assert_contains "$BLOCKED_EXTERNAL_STATE/workflows/WF-BLOCKED-EXTERNAL/lifecycle/events.log" \
$'operations=0\tfailed_operations=0\tblocked_operations=1'
assert_contains "$BLOCKED_EXTERNAL_STATE/orchestrator-result.md" \
"terminal structural blocker"

FAILED_EXTERNAL_STATE="$TEST_TMP/failed-external-state"
mkdir -p "$FAILED_EXTERNAL_STATE/operations/OP-FAILED"
MULTIAGENT_STATE_DIR="$FAILED_EXTERNAL_STATE" \
"$MULTIAGENT" workflow init WF-FAILED-EXTERNAL >/dev/null
cp "$EXTERNAL_STATE/operations/OP-FAILED/receipt.json" \
"$FAILED_EXTERNAL_STATE/operations/OP-FAILED/receipt.json"
FAILED_EXTERNAL_RESULT="$FAILED_EXTERNAL_STATE/external-result-candidate.md"
printf 'The executor failed.\n' >"$FAILED_EXTERNAL_RESULT"
if MULTIAGENT_ROOT="$EXTERNAL_ROOT" MULTIAGENT_STATE_DIR="$FAILED_EXTERNAL_STATE" \
MULTIAGENT_WORKFLOW_ID=WF-FAILED-EXTERNAL MULTIAGENT_RUN_ID=RUN-FAILED-EXTERNAL \
MULTIAGENT_LIFECYCLE_ENFORCEMENT=1 \
"$MULTIAGENT" orchestrator complete --external-only \
--result-file "$FAILED_EXTERNAL_RESULT" >"$TEST_TMP/failed-external-complete.out" 2>&1; then
echo "expected executor failure without success or blocker to reject completion" >&2
exit 1
fi
assert_contains "$TEST_TMP/failed-external-complete.out" \
"terminal reviewed blocker without executor failures"

echo "implementation lifecycle tests passed"
Loading