diff --git a/src/resources/extensions/sf/skills/dispatching-subagents/SKILL.md b/src/resources/extensions/sf/skills/dispatching-subagents/SKILL.md
index 4f75f6ee0..78fdb777d 100644
--- a/src/resources/extensions/sf/skills/dispatching-subagents/SKILL.md
+++ b/src/resources/extensions/sf/skills/dispatching-subagents/SKILL.md
@@ -121,6 +121,34 @@ Use debate mode for high-stakes decisions, plan review, architecture review,
and migrations where the cost of a weak plan is high. Keep `rounds` between 2
and 3 by default; max is 5.
+### Parent trace (for verifier/review subagents)
+
+Set `parentTrace` when dispatching a verifier or reviewer that should audit
+what the parent **actually did**, not just what the parent's prose claims. The
+dispatch tool wraps the value in a `...` block
+prepended to the task, with embedded verifier instructions to look for hedge
+words, glossed-over tool errors, and claims without Command/Output traces. The
+parent assembles its own trace (recent tool-call summary); the dispatch tool
+only plumbs it through.
+
+```
+subagent({
+ parentTrace: "",
+ tasks: [
+ { agent: "reviewer", task: "Audit the migration for correctness against the spec." },
+ { agent: "tester", task: "Audit test coverage for the new code paths." }
+ ]
+})
+```
+
+Per-task `parentTrace` overrides the batch-level value — set it on a single
+`TaskItem` (or `ChainItem`) when one subagent needs a different trace.
+
+For chain mode, only step 0 receives the trace; later steps see `{previous}`.
+For debate mode, only round 1 receives the trace; later rounds have the debate
+transcript. For parallel and single mode, the trace is always injected when
+set.
+
### Agent selection and model overrides
sf routes subagents through agent definitions in `src/resources/agents/`,
diff --git a/src/resources/extensions/subagent/index.ts b/src/resources/extensions/subagent/index.ts
index a7770a1c9..65075f7fa 100644
--- a/src/resources/extensions/subagent/index.ts
+++ b/src/resources/extensions/subagent/index.ts
@@ -1034,7 +1034,7 @@ async function mapWithConcurrencyLimit(
* words, glossed-over tool errors, untraced self-reports) so review subagent
* prompts do not need to repeat them.
*/
-function composeTaskWithParentTrace(
+export function composeTaskWithParentTrace(
task: string,
parentTrace: string | undefined,
): string {
diff --git a/src/resources/extensions/subagent/tests/parent-trace.test.ts b/src/resources/extensions/subagent/tests/parent-trace.test.ts
new file mode 100644
index 000000000..03037eb72
--- /dev/null
+++ b/src/resources/extensions/subagent/tests/parent-trace.test.ts
@@ -0,0 +1,78 @@
+import assert from "node:assert/strict";
+import { describe, it } from "node:test";
+import { composeTaskWithParentTrace } from "../index.js";
+
+describe("composeTaskWithParentTrace", () => {
+ it("returns task unchanged when parentTrace is undefined", () => {
+ const task = "do the thing";
+ assert.equal(composeTaskWithParentTrace(task, undefined), task);
+ });
+
+ it("returns task unchanged when parentTrace is an empty string", () => {
+ const task = "do the thing";
+ assert.equal(composeTaskWithParentTrace(task, ""), task);
+ });
+
+ it("returns task unchanged when parentTrace is whitespace-only", () => {
+ const task = "do the thing";
+ assert.equal(composeTaskWithParentTrace(task, " \n\t "), task);
+ });
+
+ it("wraps trace content in tags when content is provided", () => {
+ const task = "verify the parent's claim";
+ const trace = "ran `npm test`\nexit 0";
+ const result = composeTaskWithParentTrace(task, trace);
+ assert.ok(
+ result.includes(""),
+ "should include opening tag",
+ );
+ assert.ok(
+ result.includes(""),
+ "should include closing tag",
+ );
+ assert.ok(result.includes(trace), "should include trimmed trace content");
+ });
+
+ it("places the original task after the closing tag", () => {
+ const task = "verify the parent's claim";
+ const trace = "ran `npm test`\nexit 0";
+ const result = composeTaskWithParentTrace(task, trace);
+ const closeIdx = result.indexOf("");
+ const taskIdx = result.indexOf(task);
+ assert.ok(closeIdx >= 0, "closing tag must be present");
+ assert.ok(taskIdx >= 0, "task must be present");
+ assert.ok(
+ taskIdx > closeIdx,
+ "task must appear after the closing tag",
+ );
+ });
+
+ it("trims whitespace from the parentTrace content before injection", () => {
+ const task = "audit";
+ const trace = "the actual trace line";
+ const padded = `\n\n ${trace} \n\n`;
+ const result = composeTaskWithParentTrace(task, padded);
+ assert.ok(
+ result.includes(trace),
+ "trimmed trace content should appear in result",
+ );
+ assert.ok(
+ !result.includes(padded),
+ "untrimmed padded trace should not appear verbatim",
+ );
+ });
+
+ it("includes verifier instruction phrases for hedge words and tool errors", () => {
+ const task = "audit";
+ const trace = "some trace";
+ const result = composeTaskWithParentTrace(task, trace);
+ assert.ok(
+ result.includes("hedge words"),
+ "should mention hedge words in verifier instructions",
+ );
+ assert.ok(
+ result.includes("tool errors"),
+ "should mention tool errors in verifier instructions",
+ );
+ });
+});