Merge pull request #1100 from fabro-sh/codex/adopt-stage-files
Some checks failed
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Rustdoc (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Sandbox providers (Docker) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
Rust / Process titles (musl) (push) Waiting to run
Installer / Homebrew push retries (push) Has been cancelled

Adopt Petri fix for tracked files in successor prompts
This commit is contained in:
Scott Werner 2026-11-04 11:19:43 -05:00 • committed by GitHub
commit 1f5da01b47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 151 additions and 16 deletions

30
Cargo.lock generated
View file

@ -5181,7 +5181,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "petri-attractor-steps"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"globset",
@ -5212,7 +5212,7 @@ dependencies = [
[[package]]
name = "petri-driver"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"getrandom 0.3.4",
@ -5232,7 +5232,7 @@ dependencies = [
[[package]]
name = "petri-engine"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"petri-ir",
"serde",
@ -5244,7 +5244,7 @@ dependencies = [
[[package]]
name = "petri-execution"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"petri-driver",
@ -5268,7 +5268,7 @@ dependencies = [
[[package]]
name = "petri-executor"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"libc",
@ -5283,7 +5283,7 @@ dependencies = [
[[package]]
name = "petri-executor-sandbox"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"petri-executor",
@ -5305,7 +5305,7 @@ dependencies = [
[[package]]
name = "petri-frontend"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"marked-yaml",
"petri-ir",
@ -5319,7 +5319,7 @@ dependencies = [
[[package]]
name = "petri-frontend-attractor"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"minijinja",
"petri-frontend",
@ -5336,7 +5336,7 @@ dependencies = [
[[package]]
name = "petri-frontend-fabro"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"petri-frontend",
"petri-frontend-attractor",
@ -5351,7 +5351,7 @@ dependencies = [
[[package]]
name = "petri-frontend-native"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"petri-frontend",
"petri-ir",
@ -5362,7 +5362,7 @@ dependencies = [
[[package]]
name = "petri-ir"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"regex",
"serde",
@ -5375,7 +5375,7 @@ dependencies = [
[[package]]
name = "petri-runtime"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"petri-driver",
@ -5396,7 +5396,7 @@ dependencies = [
[[package]]
name = "petri-steps"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"petri-executor",
@ -5412,7 +5412,7 @@ dependencies = [
[[package]]
name = "petri-store"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"getrandom 0.3.4",
@ -5427,7 +5427,7 @@ dependencies = [
[[package]]
name = "petri-testkit"
version = "0.1.0"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#165c6ba4b6e928ec10a01688bf85de4bc5418233"
source = "git+https://github.com/lithoscomputer/petri.git?branch=main#f30a12df140fb60f55a7c6cff3d7f45673c7a7c5"
dependencies = [
"async-trait",
"petri-driver",

View file

@ -206,7 +206,7 @@ Goal: Add a /health endpoint to the API server
## Completed stages
- **plan**: success
- Model: claude-sonnet-4-5, 12.4k tokens in / 3.2k out
- Files: docs/plan.md
- Files: ["docs/plan.md"]
- **test**: success
- Script: `cargo test 2>&1 || true`
- Output:
@ -230,6 +230,18 @@ The preamble includes:
Internal keys (prefixed with `internal.`, `current`, `graph.`, `thread.`, `response.`) are excluded from preambles to avoid noise.
Native agent stages include their tracked file paths in `compact`,
`summary:medium`, and `summary:high` preambles. The list includes successful
native write, edit, and patch calls across the stage's turns, including repair
turns and child-agent writes. Paths are sorted and listed once. A moved file
uses its destination; deletions and changes made only through shell commands
are not tracked. ACP agents and prompt-only stages do not produce this list.
The file list follows the preamble size limit below; the complete list remains
in the recorded stage output. `full`, `truncate`, and `summary:low` do not add a
file-list detail. Continuing a shared conversation does not attribute an
earlier stage's edits to the next stage.
### Large preamble values
In fidelity modes that render context or completed-stage output, one value can contribute at most 8 KiB of serialized JSON inline. Fabro stores larger values as content-addressed blobs, materializes them as readable files, and puts the size, file path, and a 300-character preview in the preamble. The agent can read the file when it needs the full value.

View file

@ -0,0 +1,123 @@
//! A tracked native edit reaches both Fabro's public stage view and the next
//! prompt.
mod support;
use std::sync::Arc;
use fabro_petri::check::Launch;
use fabro_petri::engine::{self, RunStatus};
use fabro_petri::projection::{Item, RunView};
use fabro_petri::runtime::RuntimeSpec;
use fabro_store::platform_records::{PlatformRecord, RunCreatedRecord, StoredPlatformRecord};
use fabro_types::{RunId, StageId, test_support as types_support};
use pebble_coding_agent::test_support::{
ScriptedCall, message_text, scripted_client, text_response, tool_call_response,
};
use petri_execution::events::replay_run;
use petri_store::{Access, MemoryRunStore, RunKey, RunStore};
use serde_json::json;
use support::{Silent, no_questions, run_request};
#[tokio::test]
async fn compact_successor_receives_tracked_native_files() {
let root = tempfile::tempdir().expect("runtime directory");
let (client, provider) = scripted_client(vec![
ScriptedCall::response(tool_call_response(
"write_file",
"write",
json!({"file_path":"direct-edit.txt", "content":"changed"}),
)),
ScriptedCall::response(text_response("Done.")),
ScriptedCall::response(text_response("Reviewed.")),
]);
let runtime = RuntimeSpec {
model_client: Some(client),
..RuntimeSpec::default()
};
let workflow = r#"digraph Files {
graph [backend="api", default_max_retries=0]
start [shape=Mdiamond]
edit [prompt="Write the file"]
review [prompt="Review the edit", fidelity="compact"]
exit [shape=Msquare]
start -> edit -> review -> exit
}"#;
let graphs = support::admit(
&[
("workflow.fabro", workflow),
("workflow.toml", support::SETTINGS),
],
Launch {
default_model: Some("test/model".into()),
..Launch::default()
},
&runtime,
);
let store = Arc::new(MemoryRunStore::new());
let run_id = RunId::new();
let outcome = engine::run(run_request(
&run_id.to_string(),
&root.path().join("run"),
graphs,
store.clone(),
runtime,
no_questions(Arc::new(Silent)),
))
.await
.expect("workflow run settles");
assert_eq!(outcome.status, RunStatus::Success, "{outcome:?}");
let logs = store
.open(&RunKey::new(run_id.to_string()), Access::Read)
.await
.expect("stored run");
let events = replay_run(&*logs).await.expect("replayed run");
let mut spec = types_support::test_run_spec();
spec.run_id = run_id;
let created = StoredPlatformRecord {
seq: 1,
recorded_at: 0,
position: None,
record: PlatformRecord::RunCreated(RunCreatedRecord {
spec,
title: Some("Tracked files".into()),
parent_id: None,
retried_from: None,
web_url: None,
}),
};
let mut view = RunView::new();
view.fold(&Item::Platform(&created), 1);
for (index, event) in events.iter().enumerate() {
view.fold(&Item::Petri(event), index as u64 + 2);
}
let projection = view.projection().expect("public run projection");
let edit = projection
.stage(&StageId::new("edit", 1))
.expect("edit stage");
assert_eq!(
edit.agent
.as_ref()
.expect("native agent")
.totals
.files_touched,
["direct-edit.txt"]
);
let review = projection
.stage(&StageId::new("review", 1))
.expect("review stage");
assert!(
review
.agent
.as_ref()
.expect("native agent")
.totals
.files_touched
.is_empty()
);
let requests = provider.requests();
assert_eq!(requests.len(), 3);
let sent = message_text(requests[2].messages().last().expect("review input"));
assert!(sent.contains("- **edit**: succeeded"), "{sent}");
assert!(sent.contains("Files: [\"direct-edit.txt\"]"), "{sent}");
}