Skip to content

Commit fe7ddeb

Browse files
committed
docs: replace examples that overlapped with flue's README
The previous Issue Triage / Daytona Coding / GitHub MCP / approved-comments schema sections mirrored flue's README too closely. Replaced with examples that are differentiated and lean into Rust-specific strengths: - Snapshot Repair (CI): cargo-test-fail loop, blesses safe insta snapshots. - Codebase Cartographer (Parallel Tasks): fan out one detached Session::task per module to produce ARCHITECTURE.md. - Reproducer Sandbox (Remote Linux): focused HttpSessionEnv use "reproduce this Linux-only failure" instead of generic "deploy a coding agent." - MCP Tools (Sentry): swapped from GitHub MCP to a Sentry workflow. - Schema-Guided Cargo Audit: typed advisories/risk/next_action result. Quickstart and Coding Agent (Local Repo) kept as-is — neither overlaps.
1 parent 8147c08 commit fe7ddeb

1 file changed

Lines changed: 130 additions & 54 deletions

File tree

README.md

Lines changed: 130 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ agentic-harness code --workspace . --llm auto \
9797

9898
The harness writes a run-scoped brief to `.agentic-harness/runs/<id>/coding-brief.md`, streams progress, captures the agent result, and saves a structured summary at `.agentic-harness/runs/latest.{md,json}` so a person or another agent can read what happened.
9999

100-
### Issue Triage (CI)
100+
### Snapshot Repair (CI)
101101

102-
A CLI-only agent that runs in CI when an issue is opened. No HTTP trigger — just `agentic-harness run triage`. Skills and roles in the workspace shape behavior; secrets stay in env.
102+
A CLI-only agent that runs in CI after `cargo test` produces failing `*.snap.new` files. It reads the diffs, decides which are safe to bless under a workspace policy (additive output, ordering changes, whitespace), applies the safe ones, and flags the rest for human review. No HTTP trigger.
103103

104104
```rust
105105
// src/main.rs
@@ -108,115 +108,191 @@ use serde::Deserialize;
108108
use serde_json::json;
109109

110110
#[derive(Deserialize)]
111-
struct TriagePayload {
112-
issue: String,
111+
struct Payload {
112+
failing: Vec<String>, // paths to *.snap.new files
113113
}
114114

115115
fn app() -> Result<AgentApp, AgenticHarnessError> {
116116
Ok(AgentApp::new()
117117
.with_workspace(".")
118118
.load_workspace_context()?
119-
.agent(AgentDefinition::cli("triage", |ctx: AgentContext| {
120-
let TriagePayload { issue } = ctx.payload()?;
121-
122-
// Roles and skills are auto-discovered from the workspace.
119+
.agent(AgentDefinition::cli("snapshot-repair", |ctx: AgentContext| {
120+
let Payload { failing } = ctx.payload()?;
123121
let session = ctx.session_with_id(ctx.id());
124-
let response = session.prompt_with_options(
125-
format!("Triage this issue and return severity + summary:\n\n{issue}"),
126-
PromptOptions::new().role("triager"),
122+
123+
// The "snapshot-reviewer" role lives in .agentic-harness/roles/.
124+
// It tells the model what counts as a safe bless vs. a human-only call.
125+
let report = session.prompt_with_options(
126+
format!(
127+
"Review these failing snapshots and bless only the safe ones:\n\n{}",
128+
failing.join("\n"),
129+
),
130+
PromptOptions::new().role("snapshot-reviewer"),
127131
)?;
128132

129-
Ok(json!({
130-
"summary": response.text(),
131-
}))
133+
Ok(json!({ "report": report.text() }))
132134
})))
133135
}
134136

135137
fn main() { std::process::exit(app().and_then(run_cli).unwrap_or(1)); }
136138
```
137139

138140
```bash
139-
agentic-harness run triage --workspace . --id issue-1234 \
140-
--payload '{"issue":"login flakes on Safari"}'
141+
# In CI, after a failed test run, hand the new snapshots to the agent
142+
SNAPS=$(find . -name '*.snap.new' | jq -Rsc 'split("\n") | map(select(length>0))')
143+
agentic-harness run snapshot-repair --workspace . --id "ci-$RUN" \
144+
--payload "{\"failing\":$SNAPS}"
141145
```
142146

143-
### Remote Sandbox (Daytona / Vercel Sandbox / E2B)
147+
### Codebase Cartographer (Parallel Tasks)
144148

145-
Agentic Harness's deployment primitive for coding agents: the agent stays native Rust; shell and file operations run inside a remote Linux sandbox over a small HTTP protocol. Configure once with `setup sandbox`, then any session can target it via `HttpSessionEnv`.
149+
A one-shot agent that produces `ARCHITECTURE.md` for a repo it's never seen. It fans out one detached `Session::task` per top-level module, each with its own message history but sharing the workspace, then merges the children's notes into a single document. This is the Rust analogue of "kick off N research subagents in parallel and stitch the results."
150+
151+
```rust
152+
// src/main.rs
153+
use agentic_harness::prelude::*;
154+
use serde::Deserialize;
155+
use serde_json::json;
156+
157+
#[derive(Deserialize)]
158+
struct Payload { src_dir: Option<String> }
159+
160+
fn app() -> Result<AgentApp, AgenticHarnessError> {
161+
Ok(AgentApp::new()
162+
.with_workspace(".")
163+
.load_workspace_context()?
164+
.agent(AgentDefinition::cli("cartograph", |ctx: AgentContext| {
165+
let src = ctx.payload::<Payload>()?.src_dir.unwrap_or_else(|| "src".into());
166+
let session = ctx.session_with_id(ctx.id());
167+
168+
let mut sections = Vec::new();
169+
for entry in session.readdir(&src)?.into_iter().filter(|e| e.is_dir) {
170+
let child = session.task_with_id(
171+
format!("module-{}", entry.name),
172+
format!(
173+
"Summarize the public surface and responsibilities of {}/{}.\n\
174+
List entry points and any cross-module imports.",
175+
src, entry.name,
176+
),
177+
TaskOptions::new().role("module-summarizer"),
178+
)?;
179+
sections.push(format!("## {}\n\n{}\n", entry.name, child.text()));
180+
}
181+
182+
session.write("ARCHITECTURE.md", &sections.join("\n"))?;
183+
Ok(json!({ "modules": sections.len() }))
184+
})))
185+
}
186+
187+
fn main() { std::process::exit(app().and_then(run_cli).unwrap_or(1)); }
188+
```
189+
190+
Each child task gets a fresh `AGENTS.md` + skill discovery scoped to its working directory, so adding a `module-summarizer` role tunes every task at once.
191+
192+
### Reproducer Sandbox (Remote Linux)
193+
194+
When an issue says "this fails on Linux but I'm on macOS," the agent provisions a clean Linux sandbox over `HttpSessionEnv`, checks out the branch, runs the reproducer steps, and captures evidence. The agent stays a native Rust binary on your laptop; shell and file operations run on the other side of an HTTP boundary.
146195

147196
```rust
148197
use agentic_harness::HttpSessionEnv;
149198

150-
let remote = HttpSessionEnv::new(
151-
"https://sandbox.example/session",
199+
let sandbox = HttpSessionEnv::new(
200+
std::env::var("SANDBOX_URL")?, // Vercel Sandbox / Daytona / E2B / your own
152201
"/workspace",
153202
)
154-
.header(
155-
"Authorization",
156-
format!("Bearer {}", std::env::var("SANDBOX_TOKEN")?),
157-
);
158-
159-
let session = ctx.session_with_id_and_env("remote", remote);
160-
let test_run = session.shell("cargo test")?;
161-
session.write("notes.md", "Findings...")?;
203+
.header("Authorization", format!("Bearer {}", std::env::var("SANDBOX_TOKEN")?));
204+
205+
let session = ctx.session_with_id_and_env("repro", sandbox);
206+
session.shell(&format!("git clone {repo} /workspace/repo && git -C /workspace/repo checkout {branch}"))?;
207+
let probe = session.shell("cd /workspace/repo && cargo test --no-fail-fast 2>&1 | tail -200")?;
208+
209+
session.write(
210+
"/workspace/repro-report.md",
211+
&format!("## exit: {}\n\n```\n{}\n```\n", probe.status, probe.stdout),
212+
)?;
162213
```
163214

164-
The CLI mirrors this for ad-hoc work:
215+
The same protocol is documented in [`docs/http-session-env.md`](docs/http-session-env.md) — any sandbox provider that speaks it works without a custom adapter. The CLI surfaces it for ad-hoc use too:
165216

166217
```bash
167-
agentic-harness setup sandbox --target daytona --print | claude
218+
agentic-harness setup sandbox --target e2b --endpoint $SANDBOX_URL
168219
agentic-harness sandbox status --json
169-
agentic-harness sandbox exec "cargo test" --json
220+
agentic-harness sandbox exec "uname -a && rustc --version" --json
170221
```
171222

172-
When `.agentic-harness/sandbox.toml` points at a remote endpoint, `agentic-harness code` syncs the workspace into the sandbox and runs checks there instead of locally.
223+
### MCP Tools (Sentry)
173224

174-
### MCP Tools
175-
176-
MCP servers plug in as runtime tool providers. Streamable HTTP by default; pass `transport: Sse` for legacy SSE servers.
225+
MCP servers plug in as runtime tool providers. Connect once, hand the tools to a session, and the model can call `find_event`, `list_issues`, etc. directly. Streamable HTTP by default; pass `transport: Sse` for legacy SSE servers.
177226

178227
```rust
179-
use agentic_harness::{McpServerOptions, McpTransport};
228+
use agentic_harness::McpServerOptions;
180229

181-
let github_tools = ctx.connect_mcp(
182-
"github",
183-
McpServerOptions::new("https://mcp.github.com/mcp")
184-
.header("Authorization", format!("Bearer {}", std::env::var("GITHUB_TOKEN")?)),
230+
let sentry = ctx.connect_mcp(
231+
"sentry",
232+
McpServerOptions::new("https://mcp.sentry.io/mcp")
233+
.header("Authorization", format!("Bearer {}", std::env::var("SENTRY_TOKEN")?)),
185234
)?;
186235

187-
let session = ctx.session_with_id(ctx.id()).with_tools(github_tools);
188-
let answer = session.prompt(payload.prompt)?;
236+
let session = ctx.session_with_id(ctx.id()).with_tools(sentry);
237+
let plan = session.prompt(
238+
"Find the highest-volume new error in the last 24h, locate the commit that introduced it, \
239+
and draft a hot-fix plan with rollback steps.",
240+
)?;
189241
```
190242

191-
### Schema-Guided Results
243+
### Schema-Guided Cargo Audit
192244

193-
Get typed, schema-validated data back from a prompt without manual JSON wrangling.
245+
Get typed, schema-validated data back from a prompt without manual JSON wrangling. The model returns prose plus a structured block; `prompt_json_with_options` extracts and decodes it directly into your type.
194246

195247
```rust
196248
use agentic_harness::PromptOptions;
197249
use serde::Deserialize;
198250
use serde_json::json;
199251

200252
#[derive(Deserialize)]
201-
struct TriageResult {
202-
approved: bool,
203-
comments: Vec<String>,
253+
struct CrateAudit {
254+
advisories: Vec<Advisory>,
255+
risk: Risk,
256+
next_action: String,
204257
}
205258

206-
let result: TriageResult = session.prompt_json_with_options(
207-
"Review this change.",
259+
#[derive(Deserialize)]
260+
struct Advisory { id: String, package: String, severity: Severity }
261+
262+
#[derive(Deserialize)]
263+
#[serde(rename_all = "lowercase")]
264+
enum Severity { Low, Medium, High, Critical }
265+
266+
#[derive(Deserialize)]
267+
#[serde(rename_all = "lowercase")]
268+
enum Risk { None, Low, Medium, High, Critical }
269+
270+
let audit: CrateAudit = session.prompt_json_with_options(
271+
"Run `cargo audit`, group by severity, and pick the smallest safe upgrade plan.",
208272
PromptOptions::new().result_schema(json!({
209273
"type": "object",
274+
"required": ["advisories", "risk", "next_action"],
210275
"properties": {
211-
"approved": { "type": "boolean" },
212-
"comments": { "type": "array", "items": { "type": "string" } }
213-
},
214-
"required": ["approved", "comments"]
276+
"advisories": {
277+
"type": "array",
278+
"items": {
279+
"type": "object",
280+
"required": ["id", "package", "severity"],
281+
"properties": {
282+
"id": { "type": "string" },
283+
"package": { "type": "string" },
284+
"severity": { "enum": ["low", "medium", "high", "critical"] }
285+
}
286+
}
287+
},
288+
"risk": { "enum": ["none", "low", "medium", "high", "critical"] },
289+
"next_action": { "type": "string" }
290+
}
215291
})),
216292
)?;
217293
```
218294

219-
Structured `---RESULT_START---` / `---RESULT_END---` block extraction is built in, so the model can return prose plus a typed payload.
295+
Structured `---RESULT_START---` / `---RESULT_END---` block extraction is built in, so the model can return reasoning prose alongside the typed payload.
220296

221297
## Agents And Sessions
222298

0 commit comments

Comments
 (0)