Redesign compact and summary:high preamble rendering

Compact mode now renders nested-bullet summaries with handler-specific
sub-items (script: command/stdout/stderr; codergen: model/tokens/files)
under a ## Completed stages heading. Summary:high renders per-stage
## Stage sections with full detail and a ## Current context table.

Also captures script.stderr in context_updates for both success and
failure branches, and improves context filtering across all modes to
exclude engine keys (graph.*, thread.*, response.*, last_stage, etc.).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-02-27 14:26:07 -05:00
parent 92a4c5e506
commit b11c92bf66
3 changed files with 1011 additions and 156 deletions

View file

@ -132,6 +132,9 @@ impl Handler for ScriptHandler {
outcome
.context_updates
.insert("tool.output".to_string(), serde_json::json!(stdout));
outcome
.context_updates
.insert("script.stderr".to_string(), serde_json::json!(stderr));
outcome.notes = Some(format!("Script completed: {script}"));
Ok(outcome)
} else {
@ -143,7 +146,11 @@ impl Handler for ScriptHandler {
} else {
format!("Script failed: {}", stderr.trim())
};
Ok(Outcome::fail(reason))
let mut outcome = Outcome::fail(reason);
outcome
.context_updates
.insert("script.stderr".to_string(), serde_json::json!(stderr));
Ok(outcome)
}
}
Err(e) => Ok(Outcome::fail(e.to_string())),
@ -210,6 +217,8 @@ mod tests {
assert!(outcome.notes.as_deref().unwrap().contains("echo hello"));
let script_output = outcome.context_updates.get("script.output").unwrap();
assert!(script_output.as_str().unwrap().contains("hello"));
let script_stderr = outcome.context_updates.get("script.stderr").unwrap();
assert_eq!(script_stderr.as_str().unwrap(), "");
}
#[tokio::test]
@ -544,6 +553,31 @@ mod tests {
assert!(script_output.as_str().unwrap().contains("legacy"));
}
#[tokio::test]
async fn script_handler_captures_stderr() {
let handler = ScriptHandler;
let mut node = Node::new("script_node");
node.attrs.insert(
"script".to_string(),
AttrValue::String("echo out && echo err >&2".to_string()),
);
let context = Context::new();
let graph = Graph::new("test");
let logs_root = tempfile::tempdir().unwrap();
let outcome = handler
.execute(&node, &context, &graph, logs_root.path(), &make_services())
.await
.unwrap();
assert_eq!(outcome.status, StageStatus::Success);
let script_stderr = outcome.context_updates.get("script.stderr").unwrap();
assert!(
script_stderr.as_str().unwrap().contains("err"),
"script.stderr should contain 'err', got: {:?}",
script_stderr
);
}
#[tokio::test]
async fn tool_output_context_key_dual_write() {
let handler = ScriptHandler;

File diff suppressed because it is too large Load diff

View file

@ -4599,7 +4599,7 @@ async fn fidelity_compact_preamble_includes_completed_stages_and_context() {
"compact preamble should contain the goal"
);
assert!(
step_b_preamble.contains("Completed stages:"),
step_b_preamble.contains("## Completed stages"),
"compact preamble should include completed stages section"
);
assert!(
@ -4672,10 +4672,17 @@ async fn fidelity_summary_low_excludes_context_values_in_pipeline() {
let preambles_med = captures_med.preambles.lock().unwrap();
let med_preamble = &preambles_med[1].1;
// summary:medium should include "Context values:" section (graph.goal is always set)
// summary:medium should include stage details (unlike summary:low which omits them)
assert!(
med_preamble.contains("Context values:"),
"summary:medium preamble should include context values section"
med_preamble.contains("step_a"),
"summary:medium preamble should include completed stage step_a"
);
// Verify medium and low differ: medium shows more recent stages
let preambles_low = captures_low.preambles.lock().unwrap();
let low_preamble = &preambles_low[1].1;
assert!(
!low_preamble.contains("## Context"),
"summary:low preamble should not include context section"
);
}
@ -6729,11 +6736,12 @@ async fn run_fidelity_prompt_pipeline(fidelity: &str) -> String {
async fn fidelity_prompt_compact() {
let prompt = run_fidelity_prompt_pipeline("compact").await;
// Preamble should contain goal, completed stages, and context values
// Preamble should contain goal, completed stages with handler details, and context
assert!(prompt.contains("Validate the build"), "compact: should contain goal");
assert!(prompt.contains("Completed stages:"), "compact: should list completed stages");
assert!(prompt.contains("run_tests"), "compact: should mention run_tests node");
assert!(prompt.contains("Context values:"), "compact: should include context values section");
assert!(prompt.contains("## Completed stages"), "compact: should list completed stages");
assert!(prompt.contains("**run_tests**"), "compact: should mention run_tests node in bold");
assert!(prompt.contains("Script:"), "compact: should show script sub-item for run_tests");
assert!(prompt.contains("Stdout:"), "compact: should show stdout sub-item for run_tests");
// Original prompt at the end
assert!(
@ -6779,7 +6787,7 @@ async fn fidelity_prompt_summary_medium() {
// summary:medium includes goal, stages, and context values
assert!(prompt.contains("Validate the build"), "summary:medium: should contain goal");
assert!(prompt.contains("run_tests"), "summary:medium: should mention run_tests");
assert!(prompt.contains("Context values:"), "summary:medium: should include context values");
assert!(prompt.contains("## Context"), "summary:medium: should include context section");
// Original prompt at the end
assert!(
@ -6792,10 +6800,11 @@ async fn fidelity_prompt_summary_medium() {
async fn fidelity_prompt_summary_high() {
let prompt = run_fidelity_prompt_pipeline("summary:high").await;
// summary:high includes goal, all stages, context values
// summary:high includes goal, all stages as ## Stage headings
assert!(prompt.contains("Validate the build"), "summary:high: should contain goal");
assert!(prompt.contains("run_tests"), "summary:high: should mention run_tests");
assert!(prompt.contains("Context values:"), "summary:high: should include context values");
assert!(prompt.contains("## Stage: run_tests"), "summary:high: should have stage heading for run_tests");
assert!(prompt.contains("## Stage: start"), "summary:high: should have stage heading for start");
assert!(prompt.contains("Pipeline progress:"), "summary:high: should show pipeline progress");
// Original prompt at the end
assert!(