feat(compound-engineering): bundle and install ce-* pipeline skills (U2)
Bundle pinned copies of 7 CE pipeline-stage skills (strategy, ideate, brainstorm, plan, work, code-review, compound) under src/skills/ and declare them via PluginSkillContribution. Empirical finding: the skills contribution alone does not make a SKILL.md resolvable in a session -- the engine ingests it as a name only. So onLoad runs an idempotent, isolation-guarded physical install into a plugin-local .fusion-ce-skills/ dir (never a global ~/.claude/skills), which the engine skill-resolver can then discover. Proven against the real loadSkills + resolveSessionSkills pipeline.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* U2 — Empirical proof of how Compound Engineering bundled skills become
|
||||
* resolvable in an agent session.
|
||||
*
|
||||
* This drives the REAL engine skill pipeline:
|
||||
* pi-coding-agent `loadSkills` (disk discovery) →
|
||||
* `resolveSessionSkills` + `createSkillsOverrideFromSelection` (the same
|
||||
* path `createFnAgent` uses in pi.ts via DefaultResourceLoader.skillsOverride).
|
||||
*
|
||||
* THE QUESTION: does declaring `skills: PluginSkillContribution[]` (whose
|
||||
* contribution surfaces only as a *name* in `requestedSkillNames`) make a
|
||||
* bundled SKILL.md resolvable, OR is a physical install into a discoverable
|
||||
* directory also required?
|
||||
*
|
||||
* ANSWER (asserted below): the contribution alone is NOT enough. The engine
|
||||
* never ingests `PluginSkillContribution.skillFiles` into the discovered set;
|
||||
* the requested name has nothing on disk to match. A physical, plugin-local
|
||||
* install (so the SKILL.md lives on a path `loadSkills` scans) is REQUIRED.
|
||||
*
|
||||
* The test is self-contained on the engine side: it models "a physical install"
|
||||
* by materializing a `ce-plan/SKILL.md` on disk and pointing disk discovery at
|
||||
* its parent dir — exactly what the plugin's `installBundledCeSkills` does into
|
||||
* a plugin-local directory wired through `additionalSkillPaths`. (The plugin's
|
||||
* own cpSync + isolation behavior is verified in the plugin package's
|
||||
* skill-installation.test.ts; the engine package cannot import plugin source
|
||||
* without violating its tsc rootDir.)
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { loadSkills } from "@earendil-works/pi-coding-agent";
|
||||
import {
|
||||
createSkillsOverrideFromSelection,
|
||||
resolveSessionSkills,
|
||||
} from "../skill-resolver.js";
|
||||
|
||||
vi.mock("../logger.js", () => ({
|
||||
piLog: { log: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||
}));
|
||||
|
||||
const CE_STAGES = [
|
||||
"ce-strategy",
|
||||
"ce-ideate",
|
||||
"ce-brainstorm",
|
||||
"ce-plan",
|
||||
"ce-work",
|
||||
"ce-code-review",
|
||||
"ce-compound",
|
||||
] as const;
|
||||
|
||||
/** Model the plugin-local physical install: write each stage's SKILL.md to disk. */
|
||||
function materializeInstalledSkills(root: string, stages: readonly string[]): void {
|
||||
for (const id of stages) {
|
||||
const dir = join(root, id);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(dir, "SKILL.md"),
|
||||
`---\nname: ${id}\ndescription: ${id} pipeline stage\n---\n\n# ${id}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the exact engine resolution path for a session that requests CE skill
|
||||
* names (as if a plugin contributed them via getPluginSkills ->
|
||||
* requestedSkillNames), over whatever skills `loadSkills` discovers from
|
||||
* `discoveredSkillPaths`. Returns the resolved skill names visible to the
|
||||
* session.
|
||||
*/
|
||||
function resolveSessionFor(opts: {
|
||||
projectRootDir: string;
|
||||
agentDir: string;
|
||||
discoveredSkillPaths: string[];
|
||||
requestedSkillNames: string[];
|
||||
}): string[] {
|
||||
// 1. Disk discovery — exactly what DefaultResourceLoader feeds to its override.
|
||||
const discovered = loadSkills({
|
||||
cwd: opts.projectRootDir,
|
||||
agentDir: opts.agentDir,
|
||||
skillPaths: opts.discoveredSkillPaths,
|
||||
includeDefaults: false,
|
||||
});
|
||||
|
||||
// 2. Engine resolver (project settings + requested names).
|
||||
const selection = resolveSessionSkills({
|
||||
projectRootDir: opts.projectRootDir,
|
||||
requestedSkillNames: opts.requestedSkillNames,
|
||||
sessionPurpose: "executor",
|
||||
});
|
||||
const override = createSkillsOverrideFromSelection(selection, {
|
||||
requestedSkillNames: opts.requestedSkillNames,
|
||||
sessionPurpose: "executor",
|
||||
});
|
||||
|
||||
const result = override({ skills: discovered.skills, diagnostics: discovered.diagnostics });
|
||||
return result.skills.map((s) => s.name);
|
||||
}
|
||||
|
||||
describe("U2: CE bundled skill session-resolution (empirical)", () => {
|
||||
let tmp: string;
|
||||
let projectRootDir: string;
|
||||
let agentDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmp = mkdtempSync(join(tmpdir(), "ce-resolve-"));
|
||||
// An empty project + empty agent dir: NOTHING ce-* is discoverable yet.
|
||||
projectRootDir = join(tmp, "project");
|
||||
agentDir = join(tmp, "agent");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("FAILING-FIRST: contribution name alone (no physical install) does NOT resolve ce-plan", () => {
|
||||
// Simulate: plugin declared skills -> requestedSkillNames includes ce-plan,
|
||||
// but no SKILL.md was installed anywhere discoverable.
|
||||
const resolved = resolveSessionFor({
|
||||
projectRootDir,
|
||||
agentDir,
|
||||
discoveredSkillPaths: [], // nothing on disk
|
||||
requestedSkillNames: ["ce-plan"],
|
||||
});
|
||||
// Proves the contribution alone is insufficient: ce-plan is NOT resolvable.
|
||||
expect(resolved).not.toContain("ce-plan");
|
||||
expect(resolved).toEqual([]);
|
||||
});
|
||||
|
||||
it("PASSING: after a plugin-local physical install, ce-plan IS resolvable for the session", () => {
|
||||
const installRoot = join(tmp, "plugin-local", ".fusion-ce-skills");
|
||||
materializeInstalledSkills(installRoot, ["ce-plan"]);
|
||||
|
||||
const resolved = resolveSessionFor({
|
||||
projectRootDir,
|
||||
agentDir,
|
||||
discoveredSkillPaths: [installRoot], // installed dir is now discoverable
|
||||
requestedSkillNames: ["ce-plan"],
|
||||
});
|
||||
|
||||
expect(resolved).toContain("ce-plan");
|
||||
});
|
||||
|
||||
it("PASSING: all seven CE stages resolve when requested after install", () => {
|
||||
const installRoot = join(tmp, ".fusion-ce-skills");
|
||||
materializeInstalledSkills(installRoot, CE_STAGES);
|
||||
|
||||
const resolved = resolveSessionFor({
|
||||
projectRootDir,
|
||||
agentDir,
|
||||
discoveredSkillPaths: [installRoot],
|
||||
requestedSkillNames: [...CE_STAGES],
|
||||
});
|
||||
for (const s of CE_STAGES) {
|
||||
expect(resolved).toContain(s);
|
||||
}
|
||||
});
|
||||
});
|
||||
3
plugins/fusion-plugin-compound-engineering/.gitignore
vendored
Normal file
3
plugins/fusion-plugin-compound-engineering/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Runtime, plugin-local install target for bundled ce-* skills (U2).
|
||||
# Populated by installBundledCeSkills() on plugin load; never committed.
|
||||
.fusion-ce-skills/
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import manifest from "../../manifest.json";
|
||||
import plugin from "../index.js";
|
||||
import { COMPOUND_ENGINEERING_SKILLS } from "../skills.js";
|
||||
|
||||
describe("compound engineering plugin manifest", () => {
|
||||
it("exports expected plugin id", () => {
|
||||
@@ -30,8 +31,33 @@ describe("compound engineering plugin manifest", () => {
|
||||
expect(manifest.dashboardViews).toEqual(plugin.dashboardViews);
|
||||
});
|
||||
|
||||
it("ships an empty hooks/routes scaffold (U1)", () => {
|
||||
expect(plugin.hooks).toEqual({});
|
||||
it("ships an empty routes scaffold (U1)", () => {
|
||||
expect(plugin.routes).toEqual([]);
|
||||
});
|
||||
|
||||
it("registers the bundled CE pipeline-stage skills on plugin and manifest (U2)", () => {
|
||||
const expectedIds = [
|
||||
"ce-strategy",
|
||||
"ce-ideate",
|
||||
"ce-brainstorm",
|
||||
"ce-plan",
|
||||
"ce-work",
|
||||
"ce-code-review",
|
||||
"ce-compound",
|
||||
];
|
||||
expect(COMPOUND_ENGINEERING_SKILLS.map((s) => s.skillId)).toEqual(expectedIds);
|
||||
expect(plugin.skills).toBe(COMPOUND_ENGINEERING_SKILLS);
|
||||
// Manifest mirrors agent-browser: { skillId, name } projection.
|
||||
expect(plugin.manifest.skills).toEqual(
|
||||
COMPOUND_ENGINEERING_SKILLS.map((s) => ({ skillId: s.skillId, name: s.name })),
|
||||
);
|
||||
// Each contribution points at a plugin-root-relative bundled SKILL.md.
|
||||
for (const s of COMPOUND_ENGINEERING_SKILLS) {
|
||||
expect(s.skillFiles).toEqual([`skills/${s.skillId}/SKILL.md`]);
|
||||
}
|
||||
});
|
||||
|
||||
it("registers an onLoad hook that installs bundled skills (U2)", () => {
|
||||
expect(typeof plugin.hooks?.onLoad).toBe("function");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
assertPluginLocalTarget,
|
||||
installBundledCeSkills,
|
||||
isPluginLocalPath,
|
||||
resolveBundledSkillsRoot,
|
||||
} from "../skill-installation.js";
|
||||
import { COMPOUND_ENGINEERING_SKILLS } from "../skills.js";
|
||||
|
||||
describe("compound engineering bundled skill install", () => {
|
||||
let tmp: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tmp = mkdtempSync(join(tmpdir(), "ce-skill-install-"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("installs every bundled CE skill into the plugin-local target", () => {
|
||||
const targetRoot = join(tmp, "plugin-local", ".fusion-ce-skills");
|
||||
const { results } = installBundledCeSkills({ targetRoot });
|
||||
|
||||
for (const skill of COMPOUND_ENGINEERING_SKILLS) {
|
||||
const r = results.find((x) => x.skillId === skill.skillId)!;
|
||||
expect(r.outcome).toBe("installed");
|
||||
const skillMd = join(targetRoot, skill.skillId, "SKILL.md");
|
||||
expect(existsSync(skillMd)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("is idempotent: a second run with the target present is a skip-if-exists no-op", () => {
|
||||
const targetRoot = join(tmp, ".fusion-ce-skills");
|
||||
const first = installBundledCeSkills({ targetRoot });
|
||||
expect(first.results.every((r) => r.outcome === "installed")).toBe(true);
|
||||
|
||||
// Tamper with an installed file; skip-if-exists must NOT overwrite it.
|
||||
const sentinelPath = join(targetRoot, "ce-plan", "SKILL.md");
|
||||
writeFileSync(sentinelPath, "SENTINEL");
|
||||
|
||||
const second = installBundledCeSkills({ targetRoot });
|
||||
expect(second.results.every((r) => r.outcome === "skipped")).toBe(true);
|
||||
expect(readFileSync(sentinelPath, "utf-8")).toBe("SENTINEL");
|
||||
});
|
||||
|
||||
// ── AE2: isolation — a global compound-engineering install is untouched ──
|
||||
it("AE2: never writes outside the plugin-local target when a global install exists", () => {
|
||||
// Seed a fake global compound-engineering install under a fake HOME.
|
||||
const fakeHome = join(tmp, "home");
|
||||
const globalSkillsDir = join(fakeHome, ".claude", "skills", "ce-plan");
|
||||
mkdirSync(globalSkillsDir, { recursive: true });
|
||||
const globalSkillMd = join(globalSkillsDir, "SKILL.md");
|
||||
writeFileSync(globalSkillMd, "GLOBAL-ORIGINAL");
|
||||
const beforeContent = readFileSync(globalSkillMd, "utf-8");
|
||||
const beforeMtime = statSync(globalSkillMd).mtimeMs;
|
||||
|
||||
const targetRoot = join(tmp, "plugin-local", ".fusion-ce-skills");
|
||||
const { targetRoot: usedTarget, results } = installBundledCeSkills({ targetRoot });
|
||||
|
||||
// The install target is provably plugin-local, never the global dir.
|
||||
expect(usedTarget.includes(join(".claude", "skills"))).toBe(false);
|
||||
expect(isPluginLocalPath(usedTarget)).toBe(true);
|
||||
for (const r of results) {
|
||||
expect(r.targetDir.includes(join(".claude", "skills"))).toBe(false);
|
||||
}
|
||||
|
||||
// The global install is byte-for-byte and mtime untouched.
|
||||
expect(readFileSync(globalSkillMd, "utf-8")).toBe(beforeContent);
|
||||
expect(statSync(globalSkillMd).mtimeMs).toBe(beforeMtime);
|
||||
});
|
||||
|
||||
it("AE2 guard: refuses to install into a global client skills directory", () => {
|
||||
const globalTarget = join(tmp, "home", ".claude", "skills");
|
||||
expect(() => assertPluginLocalTarget(globalTarget)).toThrow(/plugin-local/i);
|
||||
expect(() => installBundledCeSkills({ targetRoot: globalTarget })).toThrow(/plugin-local/i);
|
||||
expect(isPluginLocalPath(globalTarget)).toBe(false);
|
||||
});
|
||||
|
||||
// ── Edge: malformed/missing SKILL.md surfaces a clear error ──
|
||||
it("edge: a missing/malformed bundled SKILL.md surfaces a clear load error, not a silent skip", () => {
|
||||
// Point at an empty source root so every skill's source dir is missing.
|
||||
const emptySource = join(tmp, "empty-source");
|
||||
mkdirSync(emptySource, { recursive: true });
|
||||
const targetRoot = join(tmp, ".fusion-ce-skills");
|
||||
|
||||
const { results } = installBundledCeSkills({ targetRoot, sourceRoot: emptySource });
|
||||
for (const r of results) {
|
||||
expect(r.outcome).toBe("error");
|
||||
expect(r.reason).toMatch(/missing|SKILL\.md/i);
|
||||
}
|
||||
|
||||
// Now a malformed SKILL.md (no frontmatter name) for one skill.
|
||||
const malformedSource = join(tmp, "malformed-source");
|
||||
const planDir = join(malformedSource, "ce-plan");
|
||||
mkdirSync(planDir, { recursive: true });
|
||||
writeFileSync(join(planDir, "SKILL.md"), "no frontmatter here\n");
|
||||
const res2 = installBundledCeSkills({ targetRoot: join(tmp, "t2"), sourceRoot: malformedSource });
|
||||
const plan = res2.results.find((r) => r.skillId === "ce-plan")!;
|
||||
expect(plan.outcome).toBe("error");
|
||||
expect(plan.reason).toMatch(/frontmatter 'name:'/i);
|
||||
});
|
||||
|
||||
it("bundled source root resolves and contains all SKILL.md files", () => {
|
||||
const root = resolveBundledSkillsRoot();
|
||||
expect(existsSync(root)).toBe(true);
|
||||
for (const skill of COMPOUND_ENGINEERING_SKILLS) {
|
||||
expect(existsSync(join(root, skill.skillId, "SKILL.md"))).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,15 @@
|
||||
import { definePlugin } from "@fusion/plugin-sdk";
|
||||
import { COMPOUND_ENGINEERING_SKILLS } from "./skills.js";
|
||||
import { installBundledCeSkills } from "./skill-installation.js";
|
||||
|
||||
export { CompoundEngineeringDashboardView } from "./dashboard-view.js";
|
||||
export { COMPOUND_ENGINEERING_SKILLS } from "./skills.js";
|
||||
export {
|
||||
installBundledCeSkills,
|
||||
resolveBundledSkillsRoot,
|
||||
resolveDefaultInstallTargetRoot,
|
||||
isPluginLocalPath,
|
||||
} from "./skill-installation.js";
|
||||
|
||||
const plugin = definePlugin({
|
||||
manifest: {
|
||||
@@ -10,9 +19,38 @@ const plugin = definePlugin({
|
||||
description: "A dedicated dashboard surface for compound-engineering artifacts and interactive ce-* sessions.",
|
||||
author: "Fusion Team",
|
||||
fusionVersion: ">=0.1.0",
|
||||
skills: COMPOUND_ENGINEERING_SKILLS.map((s) => ({ skillId: s.skillId, name: s.name })),
|
||||
},
|
||||
state: "installed",
|
||||
hooks: {},
|
||||
skills: COMPOUND_ENGINEERING_SKILLS,
|
||||
hooks: {
|
||||
// Install the bundled, pinned ce-* SKILL.md files into a plugin-local,
|
||||
// discoverable directory on load. The engine ingests
|
||||
// PluginSkillContribution only as a name; physical discovery requires the
|
||||
// files to exist on a path it scans (U2 finding). Install is idempotent
|
||||
// (skip-if-exists) and guarded to never touch a global ~/.claude/skills.
|
||||
onLoad: async (ctx) => {
|
||||
try {
|
||||
const { targetRoot, results } = installBundledCeSkills();
|
||||
const installed = results.filter((r) => r.outcome === "installed").length;
|
||||
const errored = results.filter((r) => r.outcome === "error");
|
||||
if (errored.length > 0) {
|
||||
ctx.logger.warn(
|
||||
`Compound Engineering: ${errored.length} skill(s) failed to install: ${errored
|
||||
.map((e) => `${e.skillId} (${e.reason})`)
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
ctx.logger.info(
|
||||
`Compound Engineering skills ready — installed=${installed} target=${targetRoot}`,
|
||||
);
|
||||
ctx.emitEvent("compound-engineering:skills-installed", { targetRoot, results });
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
ctx.logger.error(`Compound Engineering skill install failed: ${message}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
routes: [],
|
||||
dashboardViews: [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
||||
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { COMPOUND_ENGINEERING_SKILLS } from "./skills.js";
|
||||
|
||||
/**
|
||||
* Physical install of the bundled Compound Engineering skills.
|
||||
*
|
||||
* EMPIRICAL FINDING (U2): the engine never ingests
|
||||
* `PluginSkillContribution.skillFiles` into the set of skills that
|
||||
* pi-coding-agent's `DefaultResourceLoader`/`loadSkills` discovers. The
|
||||
* contribution only contributes a *name* to `requestedSkillNames`, which the
|
||||
* skill-resolver then tries to MATCH against skills already discovered from
|
||||
* disk. If no `SKILL.md` for that name was discovered, the requested name
|
||||
* resolves to nothing. Therefore a physical install into a discoverable,
|
||||
* PLUGIN-LOCAL skills directory is required.
|
||||
*
|
||||
* This mirrors the cpSync + skip-if-exists pattern of
|
||||
* `installBundledFusionSkill` (packages/cli) but the target is ALWAYS
|
||||
* plugin-local — it MUST NOT be a global `<home>/.claude/skills` path (R12/AE2).
|
||||
* The installed directory is intended to be wired into a session via
|
||||
* `additionalSkillPaths` (engine-side, in later units), keeping discovery
|
||||
* scoped to the plugin and never clobbering a user's global install.
|
||||
*/
|
||||
|
||||
export type CeSkillInstallOutcome = "installed" | "skipped" | "error";
|
||||
|
||||
export interface CeSkillInstallResult {
|
||||
skillId: string;
|
||||
sourceDir: string;
|
||||
targetDir: string;
|
||||
outcome: CeSkillInstallOutcome;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface InstallBundledCeSkillsResult {
|
||||
targetRoot: string;
|
||||
results: CeSkillInstallResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute path to the plugin's bundled `src/skills` directory (the pinned
|
||||
* source of truth). Resolved relative to this module so it is correct whether
|
||||
* running from `src` (tests/dev) or `dist` (build output).
|
||||
*/
|
||||
export function resolveBundledSkillsRoot(): string {
|
||||
const here = fileURLToPath(import.meta.url);
|
||||
// src/skill-installation.ts -> src/skills ; dist/skill-installation.js -> the
|
||||
// bundled skills live next to source under src/skills, so when running from
|
||||
// dist we walk up one and into src/skills.
|
||||
const dir = dirname(here);
|
||||
const local = resolve(dir, "skills");
|
||||
if (existsSync(local)) return local;
|
||||
return resolve(dir, "..", "src", "skills");
|
||||
}
|
||||
|
||||
/**
|
||||
* Default plugin-local install target. Lives under the plugin package root
|
||||
* (`.fusion-ce-skills/`), which is ALWAYS plugin-local and never a global
|
||||
* client skills directory. Callers may override via `targetRoot` (e.g. tests),
|
||||
* but a guard rejects any target inside a global ".claude"/".codex"/".gemini"
|
||||
* skills tree.
|
||||
*/
|
||||
export function resolveDefaultInstallTargetRoot(): string {
|
||||
const here = fileURLToPath(import.meta.url);
|
||||
// <pkg>/(src|dist)/skill-installation.* -> <pkg>/.fusion-ce-skills
|
||||
return resolve(dirname(here), "..", ".fusion-ce-skills");
|
||||
}
|
||||
|
||||
const GLOBAL_SKILL_DIR_PATTERN = /[\\/]\.(claude|codex|gemini)[\\/]skills([\\/]|$)/;
|
||||
|
||||
/**
|
||||
* Guard: refuse to install into a global client skills directory.
|
||||
* This is the AE2 isolation invariant — the global compound-engineering install
|
||||
* (if present) must be provably untouched.
|
||||
*/
|
||||
export function assertPluginLocalTarget(targetRoot: string): void {
|
||||
const normalized = resolve(targetRoot);
|
||||
if (GLOBAL_SKILL_DIR_PATTERN.test(normalized + sep)) {
|
||||
throw new Error(
|
||||
`Refusing to install Compound Engineering skills into a global client skills directory: ${normalized}. ` +
|
||||
`Install target MUST be plugin-local (never <home>/.claude|.codex|.gemini/skills).`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a bundled SKILL.md exists and has a non-empty frontmatter
|
||||
* `name:`. A malformed/missing file surfaces a clear error instead of being
|
||||
* silently skipped.
|
||||
*/
|
||||
function assertValidSkillSource(skillId: string, sourceDir: string): void {
|
||||
if (!existsSync(sourceDir)) {
|
||||
throw new Error(`Bundled skill source directory missing for '${skillId}': ${sourceDir}`);
|
||||
}
|
||||
const skillMd = join(sourceDir, "SKILL.md");
|
||||
if (!existsSync(skillMd)) {
|
||||
throw new Error(`Bundled skill '${skillId}' has no SKILL.md at ${skillMd}`);
|
||||
}
|
||||
const content = readFileSync(skillMd, "utf-8");
|
||||
if (!/^---[\s\S]*?\bname\s*:\s*\S/m.test(content)) {
|
||||
throw new Error(
|
||||
`Bundled skill '${skillId}' SKILL.md at ${skillMd} is missing a frontmatter 'name:' field`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface InstallBundledCeSkillsOptions {
|
||||
/** Override the install target root (must be plugin-local). */
|
||||
targetRoot?: string;
|
||||
/** Override the bundled source root (tests). */
|
||||
sourceRoot?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy each bundled CE skill directory into the plugin-local install target.
|
||||
* Idempotent: existing per-skill target dirs are preserved (skip-if-exists).
|
||||
*/
|
||||
export function installBundledCeSkills(
|
||||
options: InstallBundledCeSkillsOptions = {},
|
||||
): InstallBundledCeSkillsResult {
|
||||
const targetRoot = options.targetRoot
|
||||
? resolve(options.targetRoot)
|
||||
: resolveDefaultInstallTargetRoot();
|
||||
assertPluginLocalTarget(targetRoot);
|
||||
|
||||
const sourceRoot = options.sourceRoot ? resolve(options.sourceRoot) : resolveBundledSkillsRoot();
|
||||
|
||||
const results = COMPOUND_ENGINEERING_SKILLS.map<CeSkillInstallResult>((skill) => {
|
||||
const sourceDir = join(sourceRoot, skill.skillId);
|
||||
const targetDir = join(targetRoot, skill.skillId);
|
||||
try {
|
||||
assertValidSkillSource(skill.skillId, sourceDir);
|
||||
|
||||
if (existsSync(targetDir)) {
|
||||
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "skipped", reason: "existing install preserved" };
|
||||
}
|
||||
|
||||
mkdirSync(targetRoot, { recursive: true });
|
||||
cpSync(sourceDir, targetDir, { recursive: true });
|
||||
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "installed" };
|
||||
} catch (error) {
|
||||
return {
|
||||
skillId: skill.skillId,
|
||||
sourceDir,
|
||||
targetDir,
|
||||
outcome: "error",
|
||||
reason: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return { targetRoot, results };
|
||||
}
|
||||
|
||||
/** True if the given path is absolute and not inside a global client skills dir. */
|
||||
export function isPluginLocalPath(p: string): boolean {
|
||||
return isAbsolute(p) && !GLOBAL_SKILL_DIR_PATTERN.test(resolve(p) + sep);
|
||||
}
|
||||
79
plugins/fusion-plugin-compound-engineering/src/skills.ts
Normal file
79
plugins/fusion-plugin-compound-engineering/src/skills.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import type { PluginSkillContribution } from "@fusion/plugin-sdk";
|
||||
|
||||
/**
|
||||
* Compound Engineering pipeline-stage skills, bundled (pinned) inside the plugin.
|
||||
*
|
||||
* Each entry's `skillFiles` is plugin-root-relative and points at a `SKILL.md`
|
||||
* physically shipped under `src/skills/<skillId>/`. The bundled copy is a pinned
|
||||
* snapshot (KTD5) — never a symlink to the global compound-engineering cache —
|
||||
* so registering it can never clobber a user's global install (R12).
|
||||
*
|
||||
* The frontmatter `name` in each bundled SKILL.md equals the directory name
|
||||
* (e.g. `ce-brainstorm`), so `skillId === name` here. pi-coding-agent's
|
||||
* `loadSkills` derives `Skill.name` from that frontmatter, which is what the
|
||||
* engine skill-resolver matches against.
|
||||
*/
|
||||
export const COMPOUND_ENGINEERING_SKILLS: PluginSkillContribution[] = [
|
||||
{
|
||||
skillId: "ce-strategy",
|
||||
name: "ce-strategy",
|
||||
description:
|
||||
"Create or maintain STRATEGY.md — the product's target problem, approach, users, key metrics, and tracks of work.",
|
||||
skillFiles: ["skills/ce-strategy/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["strategy", "roadmap", "what are we working on", "set up the strategy doc"],
|
||||
},
|
||||
{
|
||||
skillId: "ce-ideate",
|
||||
name: "ce-ideate",
|
||||
description:
|
||||
"Generate and critically evaluate grounded ideas about a topic before committing to one direction.",
|
||||
skillFiles: ["skills/ce-ideate/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["ideate", "give me ideas", "what should I improve", "surprise me"],
|
||||
},
|
||||
{
|
||||
skillId: "ce-brainstorm",
|
||||
name: "ce-brainstorm",
|
||||
description:
|
||||
"Explore requirements and approaches through collaborative dialogue, then write a right-sized requirements document.",
|
||||
skillFiles: ["skills/ce-brainstorm/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["brainstorm", "what should we build", "help me think through"],
|
||||
},
|
||||
{
|
||||
skillId: "ce-plan",
|
||||
name: "ce-plan",
|
||||
description:
|
||||
"Create structured plans for multi-step tasks and optionally deepen existing plans via sub-agent review.",
|
||||
skillFiles: ["skills/ce-plan/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["plan this", "create a plan", "break this down", "deepen the plan"],
|
||||
},
|
||||
{
|
||||
skillId: "ce-work",
|
||||
name: "ce-work",
|
||||
description: "Execute work efficiently while maintaining quality and finishing features.",
|
||||
skillFiles: ["skills/ce-work/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["do the work", "implement", "execute the plan", "finish this feature"],
|
||||
},
|
||||
{
|
||||
skillId: "ce-code-review",
|
||||
name: "ce-code-review",
|
||||
description:
|
||||
"Structured code review using tiered persona agents, confidence-gated findings, and a merge/dedup pipeline.",
|
||||
skillFiles: ["skills/ce-code-review/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["code review", "review this change", "review before PR"],
|
||||
},
|
||||
{
|
||||
skillId: "ce-compound",
|
||||
name: "ce-compound",
|
||||
description:
|
||||
"Document a recently solved problem to compound the team's knowledge or the project's shared CONCEPTS.md vocabulary.",
|
||||
skillFiles: ["skills/ce-compound/SKILL.md"],
|
||||
enabled: true,
|
||||
triggerPatterns: ["compound this", "document this learning", "capture this solution"],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,283 @@
|
||||
---
|
||||
name: ce-brainstorm
|
||||
description: 'Explore requirements and approaches through collaborative dialogue, then write a right-sized requirements document. Use when the user says "let''s brainstorm", "what should we build", or "help me think through X", presents a vague or ambitious feature request, or seems unsure about scope or direction -- even without explicitly asking to brainstorm.'
|
||||
argument-hint: "[feature idea or problem to explore] [output:html]"
|
||||
---
|
||||
|
||||
# Brainstorm a Feature or Improvement
|
||||
|
||||
**Note: The current year is 2026.** Use this when dating requirements documents.
|
||||
|
||||
Brainstorming helps answer **WHAT** to build through collaborative dialogue. It precedes `/ce-plan`, which answers **HOW** to build it.
|
||||
|
||||
The durable output of this workflow is a **requirements document**. In other workflows this might be called a lightweight PRD or feature brief. In compound engineering, keep the workflow name `brainstorm`, but make the written artifact strong enough that planning does not need to invent product behavior, scope boundaries, or success criteria.
|
||||
|
||||
This skill does not implement code. It explores, clarifies, and documents decisions for later planning or execution.
|
||||
|
||||
**IMPORTANT: All file references in generated documents must use repo-relative paths (e.g., `src/models/user.rb`), never absolute paths. Absolute paths break portability across machines, worktrees, and teammates.**
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Assess scope first** - Match the amount of ceremony to the size and ambiguity of the work.
|
||||
2. **Be a thinking partner** - Suggest alternatives, challenge assumptions, and explore what-ifs instead of only extracting requirements.
|
||||
3. **Resolve product decisions here** - User-facing behavior, scope boundaries, and success criteria belong in this workflow. Detailed implementation belongs in planning.
|
||||
4. **Keep implementation out of the requirements doc by default** - Do not include libraries, schemas, endpoints, file layouts, or code-level design unless the brainstorm itself is inherently about a technical or architectural change.
|
||||
5. **Right-size the artifact** - Simple work gets a compact requirements document or brief alignment. Larger work gets a fuller document. Do not add ceremony that does not help planning.
|
||||
6. **Apply YAGNI to carrying cost, not coding effort** - Prefer the simplest approach that delivers meaningful value. Avoid speculative complexity and hypothetical future-proofing, but low-cost polish or delight is worth including when its ongoing cost is small and easy to maintain.
|
||||
|
||||
## Interaction Rules
|
||||
|
||||
These rules apply to every brainstorm, including the universal (non-software) flow routed to `references/universal-brainstorming.md`.
|
||||
|
||||
1. **Ask one question at a time** - One question per turn, even when sub-questions feel related. Stacking several questions in a single message produces diluted answers; pick the single most useful one and ask it.
|
||||
2. **Prefer single-select multiple choice** - Use single-select when choosing one direction, one priority, or one next step.
|
||||
3. **Use multi-select rarely and intentionally** - Use it only for compatible sets such as goals, constraints, non-goals, or success criteria that can all coexist. If prioritization matters, follow up by asking which selected item is primary.
|
||||
4. **Default to the platform's blocking question tool** - Use `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). These tools include a free-text fallback (e.g., "Other" in Claude Code), so options scaffold the answer without confining it — well-chosen options surface dimensions the user may not have separated, and pick-plus-optional-note is lower activation energy than composing prose from scratch. This default holds for opening and elicitation questions too, not only narrowing. Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
5. **Use an open-ended question only when the question is genuinely open** - Drop the blocking tool only when (a) the answer is inherently narrative ("walk me through how you got here"), (b) the question is diagnostic or introspective and presented options would unintentionally influence the user's answer (e.g., "what concerns you most?" — a 4-option menu would nudge them toward those axes rather than the ones actually on their mind), or (c) you cannot write 3-4 genuinely distinct, plausibly-correct options that cover the space without padding or strawmen. The test: if you'd be straining to fill the option slots, the question is open — ask it open-ended. Rule 1 still applies: still one question per turn.
|
||||
6. **Open-ended questions earn their place only when they're specific enough to elicit a substantive answer** - Apply Rule 5 silently: just ask the question, do not narrate the form choice. The question itself must give the user something concrete to anchor on. Good: *"What's the most concrete thing someone's already done about this — paid for it, built a workaround, quit a tool over it?"* (this is one of Phase 1.2's rigor probes — it earns its open-endedness by naming what counts as an answer). Too thin: *"What's your take?"* (nothing to bite into; user defaults to a one-liner that wastes the open question). Avoid (a) narrating the form choice ("the most useful question I can ask here is..."), (b) framings that imply a short answer ("briefly", "in one sentence"), (c) yes/no traps, and (d) AI-slop warmth wrappers ("take it wherever feels relevant").
|
||||
|
||||
## Output Guidance
|
||||
|
||||
- **Keep outputs concise** - Prefer short sections, brief bullets, and only enough detail to support the next decision.
|
||||
- **Use repo-relative paths** - When referencing files, use paths relative to the repo root (e.g., `src/models/user.rb`), never absolute paths. Absolute paths make documents non-portable across machines and teammates.
|
||||
|
||||
## Feature Description
|
||||
|
||||
<feature_description> #$ARGUMENTS </feature_description>
|
||||
|
||||
**If the feature description above is empty, ask the user:** "What would you like to explore? Please describe the feature, problem, or improvement you're thinking about."
|
||||
|
||||
Do not proceed until you have a feature description from the user.
|
||||
|
||||
## Execution Flow
|
||||
|
||||
### Phase 0: Resume, Assess, and Route
|
||||
|
||||
#### 0.0 Resolve Output Mode
|
||||
|
||||
Determine `OUTPUT_FORMAT` before any other phase fires. Output mode is **exclusive** — the requirements doc is written as either markdown (`.md`) OR HTML (`.html`), never both. Precedence: CLI arg > config > default (`md`), with a hard pipeline-mode override.
|
||||
|
||||
**Read config (pre-resolved at skill load):**
|
||||
!`cat "$(git rev-parse --show-toplevel 2>/dev/null)/.compound-engineering/config.local.yaml" 2>/dev/null || echo '__NO_CONFIG__'`
|
||||
|
||||
Resolution steps:
|
||||
|
||||
1. **CLI arg.** Scan `$ARGUMENTS` for a token starting with the literal prefix `output:`. If found, strip it from arguments before treating the remainder as the feature description, and match its value case-insensitively against `md` and `html`.
|
||||
- `output:` alone (no value) → no-op, fall through to step 2.
|
||||
- `output:<unknown>` (e.g., `output:pdf`) → drop the token, fall through to step 2, and remember to emit a one-line note above the post-generation menu after final resolution: `Ignored unknown output: value '<value>' — using <resolved_format> instead.` where `<resolved_format>` is the value `OUTPUT_FORMAT` actually resolved to after steps 2-4. Do not hardcode `md` in the note — that misleads users when config has set HTML.
|
||||
2. **Config.** If step 1 did not resolve and the pre-resolved YAML above has an **active (non-commented)** `brainstorm_output:` key whose value matches `md` or `html` (case-insensitive), use it. Missing, invalid, or commented values fall through silently. Critical: lines starting with `#` are YAML comments and must be ignored — the shipped config template includes commented examples like `# brainstorm_output: html` to document the option, and matching those as active settings would silently force HTML mode on every run without the user having opted in.
|
||||
3. **Default.** Otherwise `OUTPUT_FORMAT=md`.
|
||||
4. **Pipeline override.** When invoked from LFG or any `disable-model-invocation` context, force `OUTPUT_FORMAT=md` regardless of steps 1-3. Downstream consumers (`ce-plan`, `ce-work`) parse markdown reliably; HTML in pipeline runs is unnecessary friction.
|
||||
|
||||
**Token-parsing convention:** only literal-prefix flag tokens (`output:`, `mode:`, `delegate:` where applicable) are consumed and stripped. Other `<word>:<word>` tokens — including conventional commit prefixes like `feat:`, `fix:`, `chore:` that may appear inside a feature description — pass through verbatim.
|
||||
|
||||
**Load the format-rendering reference based on the resolved value.** Section content is the same in either format; presentation differs. Both rendering references are paired with `references/brainstorm-sections.md`, which describes what the brainstorm contains regardless of format.
|
||||
|
||||
- When `OUTPUT_FORMAT=md`, read `references/markdown-rendering.md` for format principles.
|
||||
- When `OUTPUT_FORMAT=html`, read `references/html-rendering.md` for format principles.
|
||||
|
||||
The `output:` preference does NOT auto-propagate to `ce-plan` on handoff — ce-plan re-resolves its own `plan_output` config independently. Asymmetric output (`requirements.html` + `plan.md`) is acceptable; users who want HTML for both set both keys in `.compound-engineering/config.local.yaml`.
|
||||
|
||||
#### 0.1 Resume Existing Work When Appropriate
|
||||
|
||||
If the user references an existing brainstorm topic or document, or there is an obvious recent matching `*-requirements.{md,html}` file in `docs/brainstorms/`:
|
||||
- Read the document
|
||||
- Confirm with the user before resuming: "Found an existing requirements doc for [topic]. Should I continue from this, or start fresh?"
|
||||
- If resuming, summarize the current state briefly, continue from its existing decisions and outstanding questions, and update the existing document instead of creating a duplicate
|
||||
- **Resume preserves the existing artifact's format, except pipeline mode.** Write back in whatever format the existing artifact uses — markdown if the existing file is `.md`, HTML if it is `.html`. Explicit `output:` arguments on this run override (e.g., resuming an `.html` doc with `output:md` switches the artifact to markdown). Pipeline mode (LFG, any `disable-model-invocation` context) always wins per Phase 0.0: even when resuming an existing `.html` brainstorm, pipeline runs force `OUTPUT_FORMAT=md` so downstream automation receives the markdown shape it expects. The resume rewrites the markdown file at the parallel path and the original `.html` is left in place untouched.
|
||||
|
||||
#### 0.1b Classify Task Domain
|
||||
|
||||
Before proceeding to Phase 0.2, classify whether this is a software task. The key question is: **does the task involve building, modifying, or architecting software?** -- not whether the task *mentions* software topics.
|
||||
|
||||
**Software** (continue to Phase 0.2) -- the task references code, repositories, APIs, databases, or asks to build/modify/debug/deploy software.
|
||||
|
||||
**Non-software brainstorming** (route to universal brainstorming) -- BOTH conditions must be true:
|
||||
- None of the software signals above are present
|
||||
- The task describes something the user wants to explore, decide, or think through in a non-software domain
|
||||
|
||||
**Neither** (respond directly, skip all brainstorming phases) -- the input is a quick-help request, error message, factual question, or single-step task that doesn't need a brainstorm.
|
||||
|
||||
**If non-software brainstorming is detected:** Read `references/universal-brainstorming.md` and use those facilitation principles. Skip Phases 0.2–4 below — the **Core Principles and Interaction Rules above still apply unchanged**, including one-question-per-turn and the default to the platform's blocking question tool.
|
||||
|
||||
#### 0.2 Assess Whether Brainstorming Is Needed
|
||||
|
||||
**Clear requirements indicators:**
|
||||
- Specific acceptance criteria provided
|
||||
- Referenced existing patterns to follow
|
||||
- Described exact expected behavior
|
||||
- Constrained, well-defined scope
|
||||
|
||||
**If requirements are already clear:**
|
||||
Keep the interaction brief. Confirm understanding and present concise next-step options rather than forcing a long brainstorm. Only write a short requirements document when a durable handoff to planning or later review would be valuable. Skip Phase 1.1 and 1.2 entirely — go straight to Phase 1.3 or Phase 2.5 in announce-mode (synthesis emitted for visibility, no blocking confirmation), then to Phase 3.
|
||||
|
||||
#### 0.3 Assess Scope
|
||||
|
||||
Use the feature description plus a light repo scan to classify the work:
|
||||
- **Lightweight** - small, well-bounded, low ambiguity
|
||||
- **Standard** - normal feature or bounded refactor with some decisions to make
|
||||
- **Deep** - cross-cutting, strategic, or highly ambiguous
|
||||
|
||||
If the scope is unclear, ask one targeted question to disambiguate and then proceed.
|
||||
|
||||
**Deep sub-mode: feature vs product.** For Deep scope, also classify whether the brainstorm must establish product shape or inherit it:
|
||||
|
||||
- **Deep — feature** (default): existing product shape anchors decisions. Primary actors, core outcome, positioning, and primary flows are already established in the product or repo. The brainstorm extends or refines within that shape.
|
||||
- **Deep — product**: the brainstorm must establish product shape rather than inherit it. Primary actors, core outcome, positioning against adjacent products, or primary end-to-end flows are materially unresolved. Existing code lowers the odds of product-tier but does not by itself rule it out — a half-built tool with ambiguous shape is still product-tier.
|
||||
|
||||
Product-tier triggers additional Phase 1.2 questions and additional sections in the requirements document. Feature-tier uses the current Deep behavior unchanged.
|
||||
|
||||
### Phase 1: Understand the Idea
|
||||
|
||||
#### 1.1 Existing Context Scan
|
||||
|
||||
Scan the repo before substantive brainstorming. Match depth to scope:
|
||||
|
||||
**Lightweight** — Search for the topic, check if something similar already exists, and move on.
|
||||
|
||||
**Standard and Deep** — Two passes:
|
||||
|
||||
*Constraint Check* — Check project instruction files (`AGENTS.md`, and `CLAUDE.md` only if retained as compatibility context) for workflow, product, or scope constraints that affect the brainstorm. Also read `STRATEGY.md` if it exists — the product's target problem, approach, persona, and active tracks are direct input to what this brainstorm should deliver and should shape scope, success criteria, and which approaches are aligned vs out-of-scope. Also read `CONCEPTS.md` at repo root if it exists — the project's authoritative vocabulary. Use these names in dialogue, approaches, and the requirements doc; map user-offered synonyms back. If any of these add nothing, move on.
|
||||
|
||||
*Topic Scan* — Search for relevant terms. Read the most relevant existing artifact if one exists (brainstorm, plan, spec, skill, feature doc). Skim adjacent examples covering similar behavior.
|
||||
|
||||
If nothing obvious appears after a short scan, say so and continue. Two rules govern technical depth during the scan:
|
||||
|
||||
1. **Verify before claiming** — When the brainstorm touches checkable infrastructure (database tables, routes, config files, dependencies, model definitions), read the relevant source files to confirm what actually exists. Any claim that something is absent — a missing table, an endpoint that doesn't exist, a dependency not in the Gemfile, a config option with no current support — must be verified against the codebase first; if not verified, label it as an unverified assumption. This applies to every brainstorm regardless of topic.
|
||||
|
||||
2. **Defer design decisions to planning** — Implementation details like schemas, migration strategies, endpoint structure, or deployment topology belong in planning, not here — unless the brainstorm is itself about a technical or architectural decision, in which case those details are the subject of the brainstorm and should be explored.
|
||||
|
||||
**Slack context** (opt-in, Standard and Deep only) — never auto-dispatch. Route by condition:
|
||||
|
||||
- **Tools available + user asked**: Dispatch `ce-slack-researcher` with a brief summary of the brainstorm topic alongside Phase 1.1 work. Incorporate findings into constraint and context awareness.
|
||||
- **Tools available + user didn't ask**: Note in output: "Slack tools detected. Ask me to search Slack for organizational context at any point, or include it in your next prompt."
|
||||
- **No tools + user asked**: Note in output: "Slack context was requested but no Slack tools are available. Install and authenticate the Slack plugin to enable organizational context search."
|
||||
|
||||
#### 1.2 Product Pressure Test
|
||||
|
||||
Before generating approaches, scan the user's opening for rigor gaps. Match depth to scope.
|
||||
|
||||
This is agent-internal analysis, not a user-facing checklist. Read the opening, note which gaps actually exist, and raise only those as questions during Phase 1.3 — folded into the normal flow of dialogue, not fired as a pre-flight gauntlet. A fuzzy opening may earn three or four probes; a concrete, well-framed one may earn zero because no scope-appropriate gaps were found.
|
||||
|
||||
**Lightweight:**
|
||||
- Is this solving the real user problem?
|
||||
- Are we duplicating something that already covers this?
|
||||
- Is there a clearly better framing with near-zero extra cost?
|
||||
|
||||
**Standard — scan for these gaps:**
|
||||
|
||||
- **Evidence gap.** The opening asserts want or need, but doesn't point to anything the would-be user has already done — time spent, money paid, workarounds built — that would make the want observable. When present, ask for the most concrete thing someone has already done about this.
|
||||
|
||||
- **Specificity gap.** The opening describes the beneficiary at a level of abstraction where the agent couldn't design without silently inventing who they are and what changes for them. When present, ask the user to name a specific person or narrow segment, and what changes for that person when this ships.
|
||||
|
||||
- **Counterfactual gap.** The opening doesn't make visible what users do today when this problem arises, nor what changes if nothing ships. When present, ask what the current workaround is, even if it's messy — and what it costs them.
|
||||
|
||||
- **Attachment gap.** The opening treats a particular solution shape as the thing being built, rather than the value that shape is supposed to deliver, and hasn't been examined against smaller forms that might deliver the same value. When present, ask what the smallest version that still delivers real value would look like.
|
||||
|
||||
Plus these synthesis questions — not gap lenses, product-judgment the agent weighs in its own reasoning:
|
||||
- Is there a nearby framing that creates more user value without more carrying cost? If so, what complexity does it add?
|
||||
- Given the current project state, user goal, and constraints, what is the single highest-leverage move right now: the request as framed, a reframing, one adjacent addition, a simplification, or doing nothing?
|
||||
|
||||
Favor moves that compound value, reduce future carrying cost, or make the product meaningfully more useful or compelling. Use the result to sharpen the conversation, not to bulldoze the user's intent.
|
||||
|
||||
**Deep** — Standard lenses and synthesis questions plus:
|
||||
- Is this a local patch, or does it move the broader system toward where it wants to be?
|
||||
|
||||
**Deep — product** — Deep plus:
|
||||
|
||||
- **Durability gap.** The opening's value proposition rests on a current state of the world that may shift in predictable ways within the horizon the user cares about. When present, ask how the idea fares under the most plausible near-term shifts — and push past rising-tide answers every competitor could make.
|
||||
|
||||
- What adjacent product could we accidentally build instead, and why is that the wrong one?
|
||||
- What would have to be true in the world for this to fail?
|
||||
|
||||
These questions force an explicit product thesis and feed the Scope Boundaries subsections ("Deferred for later" and "Outside this product's identity") and Dependencies / Assumptions in the requirements document.
|
||||
|
||||
#### 1.3 Collaborative Dialogue
|
||||
|
||||
Follow the Interaction Rules above. Use the platform's blocking question tool when available.
|
||||
|
||||
**Guidelines:**
|
||||
- Ask what the user is already thinking before offering your own ideas. This surfaces hidden context and prevents fixation on AI-generated framings.
|
||||
- Start broad (problem, users, value) then narrow (constraints, exclusions, edge cases)
|
||||
- **Rigor probes fire before Phase 2 and are open-ended, not menus.** Narrowing is legitimate, but Phase 1 cannot end with un-probed rigor gaps. Each scope-appropriate gap from Phase 1.2 fires as a **separate** direct open-ended probe — one probe satisfies one gap, not multiple. Standard brainstorms scan four gap lenses (evidence, specificity, counterfactual, attachment); Deep-product adds durability (five total), but only the gaps actually present in the opening must be probed. Surface those probes progressively across the conversation — interleaving with narrowing moves is fine, as long as every scope-appropriate gap that was found in Phase 1.2 has been probed open-ended before Phase 2. Rigor probes map to Interaction Rule 5(b): a 4-option menu signals which kinds of evidence count and lets the user pick rather than produce. Open-ended questions force them to produce real observation or surface their uncertainty. Examples (one per gap): *evidence — "What's the most concrete thing someone's already done about this — paid, built a workaround, quit a tool over it?"* / *specificity — "Can you name a team you've actually watched hit this, or are you reasoning?"* / *counterfactual — "What do teams do today when this breaks — who reconciles?"* / *attachment — "Before we move to shapes or approaches — what's the smallest version that would still prove the bet right, and what's excluded?"* — **attachment is the final rigor probe before Phase 2 when the attachment gap is present. Fire it regardless of whether a specific shape has emerged through narrowing; its job is to pressure-test the user's implicit framing of the product before Phase 2 inherits it** / *durability — "Under the most plausible near-term shifts, how does this bet hold?"* If the answer reveals genuine uncertainty, record it as an explicit assumption in the requirements document rather than skipping the probe.
|
||||
- Clarify the problem frame, validate assumptions, and ask about success criteria
|
||||
- Make requirements concrete enough that planning will not need to invent behavior
|
||||
- Surface dependencies or prerequisites only when they materially affect scope
|
||||
- Resolve product decisions here; leave technical implementation choices for planning
|
||||
- Bring ideas, alternatives, and challenges instead of only interviewing
|
||||
|
||||
**Before exiting Phase 1.3: integration check.** Mentally combine what the user has said so far and surface any non-obvious consequences the dialogue hasn't probed. If user-stated X plus user-stated Y plus your-default-Z produces a downstream effect the user is unlikely to have tracked through one-question-at-a-time dialogue ("if mute lives on the rule AND we don't warn on delete, then rule-delete silently loses pause state"), probe it now while you're still in dialogue. One probe per genuine combination effect, asked open-ended, same discipline as rigor probes. Phase 2.5's call-outs are a safety net for residuals (silent agent inferences, pre-loaded contexts with no dialogue) — NOT a punt list for consequences you could have asked about now.
|
||||
|
||||
**Exit condition:** Continue until the idea is clear AND no integration-check questions are pending, OR the user explicitly wants to proceed.
|
||||
|
||||
### Phase 2: Explore Approaches
|
||||
|
||||
If multiple plausible directions remain, propose **2-3 concrete approaches** based on research and conversation. Otherwise state the recommended direction directly.
|
||||
|
||||
Use at least one non-obvious angle — inversion (what if we did the opposite?), constraint removal (what if X weren't a limitation?), or analogy from how another domain solves this. The first approaches that come to mind are usually variations on the same axis.
|
||||
|
||||
Present approaches first, then evaluate. Let the user see all options before hearing which one is recommended — leading with a recommendation before the user has seen alternatives anchors the conversation prematurely.
|
||||
|
||||
When useful, include one deliberately higher-upside alternative:
|
||||
- Identify what adjacent addition or reframing would most increase usefulness, compounding value, or durability without disproportionate carrying cost. Present it as a challenger option alongside the baseline, not as the default. Omit it when the work is already obviously over-scoped or the baseline request is clearly the right move.
|
||||
|
||||
At product tier, alternatives should differ on *what* is built (product shape, actor set, positioning), not *how* it is built. Implementation-variant alternatives belong at feature tier.
|
||||
|
||||
For each approach, provide:
|
||||
- Brief description (2-3 sentences)
|
||||
- Pros and cons
|
||||
- Key risks or unknowns
|
||||
- When it's best suited
|
||||
|
||||
**Approach granularity: mechanism / product shape, not architecture.** Approach descriptions name mechanism-level distinctions ("pause as a rule property" vs "pause as an event filter" vs "pause as a separate entity") and product-relevant trade-offs (plan-tier coupling, complexity surface, migration difficulty). They do NOT name implementation specifics — column names, table names, file paths, service classes, JSON shapes, exact method names. Those are ce-plan's job. Bringing architecture forward at brainstorm time forces the user to make architectural decisions on ce-brainstorm's intentionally-shallow research, and the synthesis at Phase 2.5 then has to filter out the leak.
|
||||
|
||||
After presenting all approaches, state your recommendation and explain why. Prefer simpler solutions when added complexity creates real carrying cost, but do not reject low-cost, high-value polish just because it is not strictly necessary.
|
||||
|
||||
If one approach is clearly best and alternatives are not meaningful, skip the menu and state the recommendation directly.
|
||||
|
||||
If relevant, call out whether the choice is:
|
||||
- Reuse an existing pattern
|
||||
- Extend an existing capability
|
||||
- Build something net new
|
||||
|
||||
### Phase 2.5: Synthesis Summary
|
||||
|
||||
**STOP. Before composing the synthesis, read `references/synthesis-summary.md`.** The two-stage shape (internal three-bucket draft → chat-time scoping synthesis), the Path A / Path B gate, the four scoping synthesis sections with their keep tests, the tier-aware bullet budget with re-cut rule, anti-pattern guidance, soft-cut behavior, self-redirect support, and internal-draft routing into doc body sections all live there. Composing a synthesis without these rules loaded reliably produces malformed output — pasting the full internal three-bucket draft verbatim into chat, implementation-detail leakage into the scoping synthesis, the proposal-pitch anti-pattern. **Each scoping synthesis bullet must pass the affirmability test (can the user evaluate this without reading code?) AND the detail test (1–2 lines max, conversational not documentary); over-share and over-detail are the failure modes to avoid.** This is not optional supplementary reading; it is the source of truth for how the phase behaves.
|
||||
|
||||
Surface a scoping synthesis to the user before Phase 3 writes the requirements doc — the user's last opportunity to correct scope before the artifact lands. The scoping synthesis is shaped like what two product collaborators would confirm before writing a PRD, not like a comprehensive audit or a one-line preview.
|
||||
|
||||
Fires for **all tiers** including Lightweight. Skip Phase 2.5 entirely on the Phase 0.1b non-software (universal-brainstorming) route.
|
||||
|
||||
**Path A vs Path B:** the scoping synthesis shape depends on TWO signals — whether any blocking question fired AND what tier Phase 0.3 classified the scope as.
|
||||
|
||||
- **Path A — no blocking questions fired AND tier is Lightweight**: announce-mode. Emit "What we're building" prose only (1–3 sentences), then proceed to Phase 3 doc-write in the same turn. No other sections, no confirmation question. Do NOT end the turn waiting for acknowledgment. The user can revise after the doc lands if the shape is wrong — Lightweight Path A docs are short, post-hoc revision is cheap.
|
||||
- **Path B — at least one blocking question fired, OR tier is Standard / Deep-feature / Deep-product**: full tier-aware scoping synthesis with confirmation gate. Two scenarios fire Path B: (a) the user invested answer-time during dialogue, or (b) the user pre-loaded substantive scope content (Phase 0.2 fast-path with a richly-specified opening prompt). Either way, the substance earns a real checkpoint. Confirmation is unconditional even when zero call-outs survive the keep test.
|
||||
|
||||
**Why the tier guard on Path A**: Phase 0.2's fast path serves two very different cases — a tight one-liner that needs no dialogue ("fix the typo on line 47") and a richly pre-loaded brainstorm context that ALSO needs no dialogue because the user pre-stated everything. Without the tier guard, both route to Path A and the pre-loaded case gets a 1-sentence checkpoint for what may be 20+ items worth of scope. Tier-classifying Phase 0.3 distinguishes the two — pre-loaded substance makes the tier Standard or Deep, which then routes to Path B.
|
||||
|
||||
### Phase 3: Capture the Requirements
|
||||
|
||||
Write or update a requirements document only when the conversation produced durable decisions worth preserving — see `references/brainstorm-sections.md` "Decide whether a doc is warranted at all" for the criteria and the bug-fix stress test. Skip document creation when the user only needs brief alignment and the decisions can flow downstream (ce-plan, commit message, docs/solutions/) without a brainstorm artifact in the middle.
|
||||
|
||||
When a doc is warranted, compose it using:
|
||||
|
||||
- `references/brainstorm-sections.md` — section contract (outcomes, hard floor, include-when-material catalog, agency rules, ID conventions).
|
||||
- The format-specific rendering reference loaded at Phase 0.0 (`markdown-rendering.md` OR `html-rendering.md`) — how the resolved format presents the sections.
|
||||
|
||||
Write to `docs/brainstorms/YYYY-MM-DD-<topic>-requirements.<md|html>` — extension follows `OUTPUT_FORMAT`. Confirm with the absolute path so the reference is clickable.
|
||||
|
||||
#### Vocabulary Capture — after the requirements doc (only if CONCEPTS.md already exists)
|
||||
|
||||
**Skip this step entirely if `CONCEPTS.md` does not exist at repo root** — creation is owned by ce-compound and ce-compound-refresh.
|
||||
|
||||
Run this **after** the approaches, the scope synthesis, and the requirements doc — that is where the canonical term often gets chosen or corrected, so capturing during early dialogue (before this point) would miss the final resolved name. If it exists, scan the full dialogue and the requirements doc for **resolved** domain terms — terms where the conversation actively pinned down a precise local meaning, not terms merely mentioned in passing. **Resolved means the definition is settled, not still under discussion.** Provisional terms that may still revise stay in the conversation only.
|
||||
|
||||
For each resolved term: if missing, add it; if present but new precision surfaced, refine it; if already consistent, no action.
|
||||
|
||||
**Domain entities, named processes, and status concepts with project-specific meaning only.** Not file paths, class names, function signatures, or implementation decisions — `CONCEPTS.md` is a glossary, not a spec or catch-all.
|
||||
|
||||
Follow the format set by existing entries. Apply edits silently. (If Phase 3 skipped the doc, still run this against the resolved dialogue.)
|
||||
|
||||
### Phase 4: Handoff
|
||||
|
||||
Present next-step options and execute the user's selection. Read `references/handoff.md` for the option logic, dispatch instructions, and closing summary format.
|
||||
@@ -0,0 +1,263 @@
|
||||
# Brainstorm Sections
|
||||
|
||||
This reference describes what makes a great brainstorm requirements document.
|
||||
It does NOT prescribe how the doc looks on the page — rendering is handled by
|
||||
the format-specific references (`markdown-rendering.md`, `html-rendering.md`).
|
||||
|
||||
## The outcome
|
||||
|
||||
A great brainstorm produces a doc that enables three audiences to act:
|
||||
|
||||
- **The planning agent** (`ce-plan` or a human) produces an implementation
|
||||
plan without inventing user behavior, scope boundaries, or success
|
||||
criteria — the brainstorm answered those.
|
||||
- **The reviewer** sees the framing choices, distinguishes pinned from open,
|
||||
and catches scope gaps before planning.
|
||||
- **The future reader** traces why the proposed thing matters, who it's for,
|
||||
and what success looks like.
|
||||
|
||||
Sections earn their place by serving one of these audiences. Omit padding.
|
||||
|
||||
## Decide whether a doc is warranted at all
|
||||
|
||||
Brainstorm dialogue does not always need to produce a durable document.
|
||||
Skip document creation when **both** hold:
|
||||
|
||||
- The user only needs brief alignment — no exploration produced novel scope,
|
||||
framing, or decisions worth preserving in IDed shape.
|
||||
- Any durable decisions made during the dialogue can flow naturally to
|
||||
downstream artifacts (`ce-plan`, the commit message, `docs/solutions/`)
|
||||
without a brainstorm doc as an intermediary.
|
||||
|
||||
The trigger for creating a doc is when the dialogue surfaced enough
|
||||
structural decisions, scope boundaries, or acceptance criteria that
|
||||
downstream consumers (planner, reviewer, future reader) need them in a
|
||||
durable, IDed form — not just as conversational artifacts.
|
||||
|
||||
**Stress test:** a brainstorm about a tiny bug fix where the user asks "fix
|
||||
this with a null check or with upstream validation?" and the agent confirms
|
||||
"upstream validation, here's why" doesn't need a brainstorm doc. The
|
||||
decision flows to `ce-plan` (or directly to commit message, or to
|
||||
`docs/solutions/` if it's a pattern worth carrying) without a brainstorm
|
||||
artifact in the middle.
|
||||
|
||||
Conversely, a brainstorm about a multi-actor feature with contested scope
|
||||
and several behavioral conditions probably does need a doc — the planning
|
||||
agent needs the structured content the dialogue produced.
|
||||
|
||||
## Match depth to content
|
||||
|
||||
When a doc IS warranted, depth matches what the dialogue produced. A
|
||||
brainstorm with sparse content produces a sparse doc; one with rich content
|
||||
produces a rich doc. Don't add ceremony to make a slim brainstorm look
|
||||
substantial.
|
||||
|
||||
## Hard floor
|
||||
|
||||
When a doc is warranted, these are present.
|
||||
|
||||
- **Summary** — what is being proposed, in 1-3 lines. Forward-looking.
|
||||
Orients the reader before they invest in detail.
|
||||
- **Requirements** (with stable R-IDs) — what must be true about the
|
||||
proposed thing. For very sparse brainstorms (≤3 simple items where the
|
||||
bullets ARE the summary), plain bullets without IDs are acceptable; the
|
||||
trigger for R-IDs is whether downstream consumers will reference them.
|
||||
When requirements span distinct concerns (e.g., "Packaging" /
|
||||
"Migration and compatibility" / "Contributor workflow"), group them
|
||||
under bold inline headers within the Requirements section — group by
|
||||
capability or concern, not by the order requirements were discussed.
|
||||
The trigger is distinct concerns, not item count — even four
|
||||
requirements benefit if they cover three different topics. Skip
|
||||
grouping only when all requirements are genuinely about the same thing;
|
||||
a long flat list is a smell that subgroups were missed. R-IDs stay
|
||||
continuous across groups (R1, R2 in the first group; R3, R4 in the
|
||||
second; never restart at R1 per group).
|
||||
|
||||
## Include when material
|
||||
|
||||
The agent decides per brainstorm whether each section carries information
|
||||
that isn't covered elsewhere. Filling a section with placeholder prose is
|
||||
worse than omitting it.
|
||||
|
||||
- **Problem Frame** — include when motivation isn't obvious from Summary
|
||||
alone (the *why* needs paragraphs, not a sentence). Backward-looking /
|
||||
situational. Does NOT restate the proposal; the remedy lives in Summary.
|
||||
|
||||
- **Key Decisions** — include when the brainstorm produced opinionated
|
||||
framing choices (defaults, scope narrowings, foundational technical picks)
|
||||
that constrain Requirements / Flows / Scope below. Each entry names the
|
||||
decision in bold with prose rationale. Sits high in the rendered doc so
|
||||
readers encounter the framing choices before descending into detail.
|
||||
|
||||
- **Actors** — include when the proposed thing has multi-party behavior
|
||||
(multiple humans, agents, or systems meaningfully involved). Skip for
|
||||
non-behavioral brainstorms (naming briefs, data-shape briefs, pure
|
||||
research, decision frameworks).
|
||||
|
||||
- **Key Flows** — include when the proposed thing has multi-step behavior.
|
||||
Expected by default for behavioral brainstorms unless the proposed thing
|
||||
is genuinely non-flow-shaped (pure API surface, policy, artifact output)
|
||||
and Actors / Requirements / Scope Boundaries / Acceptance Examples
|
||||
together prevent downstream invention of paths. When omitting from a
|
||||
behavioral brainstorm, note the reason in the doc.
|
||||
|
||||
- **Visualizations** — include a diagram when the brainstorm contains a
|
||||
diagram-shaped concept that a picture carries faster than prose. Common
|
||||
shapes: a data-shape transformation (before/after schema or field
|
||||
mapping), a source-of-truth fan-out (one authority feeding many derived
|
||||
surfaces), state-or-lifecycle logic, a multi-step flow, or a quantitative
|
||||
comparison. A diagram is cross-cutting, not a section of its own — it sits
|
||||
next to the Key Decision, Requirements group, or Flow it illustrates. The
|
||||
named test: *does the picture let a reader grasp the concept faster than
|
||||
the paragraph alone?* If yes, add it; if the prose already conveys it at a
|
||||
glance, skip it. One diagram per load-bearing concept — don't add visuals
|
||||
for ceremony. This affordance is the conceptual-diagram path; it is
|
||||
distinct from the wireframe affordance (a wireframe is for visual-product
|
||||
UI and does not apply to non-visual systems like data models or agent
|
||||
workflows, but a conceptual diagram does).
|
||||
|
||||
**Diagrams complement prose; they never replace it.** A diagram is an
|
||||
on-ramp to the prose it illustrates, not a substitute. The IDed prose
|
||||
(Requirements, Key Decisions, Acceptance Examples) stays complete and
|
||||
standalone — a reader who ignores every diagram still gets the full
|
||||
content in text, and a downstream agent that reads the artifact as linear
|
||||
text is never left with a relationship that exists only in an SVG. Adding
|
||||
a before/after diagram is not license to thin the requirement or decision
|
||||
prose it depicts.
|
||||
|
||||
- **Acceptance Examples** — include when any requirement has a
|
||||
state-dependent or conditional shape ("When X, Y") where prose alone leaves
|
||||
ambiguity about edge cases. **Always include AEs covering
|
||||
behavioral-conditional requirements** — that's where the ambiguity bites
|
||||
hardest. Skip when all requirements are unconditional and unambiguous.
|
||||
|
||||
- **Success Criteria** — include when there are quality / metric / handoff
|
||||
signals that Requirements don't already carry: quantitative metrics ("p95
|
||||
latency under 200ms"), qualitative criteria ("the agent's output reads as
|
||||
one voice"), process / handoff quality ("ce-doc-review can act on this
|
||||
without follow-ups"). Skip when Requirements ARE the success criteria
|
||||
(every R is "done when the R is true").
|
||||
|
||||
- **Scope Boundaries** — include when scope is contested or there are
|
||||
tempting non-goals worth naming explicitly. When the brainstorm is about
|
||||
positioning a product against adjacent ones the team could have built but
|
||||
is rejecting, split into "Deferred for later" (eventually but not v1) and
|
||||
"Outside this product's identity" (positioning decision). Otherwise, a
|
||||
single list is fine.
|
||||
|
||||
- **Dependencies / Assumptions** — include when material upstream
|
||||
dependencies exist or when load-bearing assumptions need to be surfaced.
|
||||
|
||||
- **Outstanding Questions** — include when there are unresolved items.
|
||||
Distinguish "Resolve Before Planning" (blocks planning) from "Deferred to
|
||||
Planning" (answered during planning or codebase exploration).
|
||||
|
||||
- **Sources / Research** — surface research that orients the planner or
|
||||
justifies framing choices. The test: *"if I were the planner reading this
|
||||
cold, would this breadcrumb help me make better choices?"* Yes → surface
|
||||
(code locations, external docs, RFCs, constraints, prior plans — the
|
||||
category is inclusive, not enumerated). Process exhaust (reading the
|
||||
user's prompt, glancing at obvious files) → omit.
|
||||
|
||||
## Agent agency
|
||||
|
||||
The catalog is a floor, not a ceiling. When the brainstorm's content doesn't
|
||||
fit any catalog section, introduce a new one — don't force the content into
|
||||
a section it doesn't belong in. Content drives section choices, not vice
|
||||
versa.
|
||||
|
||||
The agent also picks per artifact:
|
||||
|
||||
- Whether Acceptance Examples render as a separate section or embed in each
|
||||
requirement
|
||||
- How much depth each present section gets
|
||||
|
||||
(Requirements grouping is covered above in the Hard Floor item — group by
|
||||
concern by default, rendering a flat list only when all requirements are
|
||||
about the same thing, with continuous R-IDs across groups.)
|
||||
|
||||
## Brainstorm metadata fields
|
||||
|
||||
Every brainstorm carries a small set of stable metadata fields that
|
||||
downstream tooling depends on. The contract is format-independent: in
|
||||
markdown these fields appear as YAML frontmatter at the top of the file; in
|
||||
HTML they appear as visible header text (typically a `<dl>` of `<dt>`/`<dd>`
|
||||
pairs or a stats strip). Field names and semantics are the same across both
|
||||
formats so consumers can locate them without knowing which format produced
|
||||
the brainstorm.
|
||||
|
||||
### Required
|
||||
|
||||
- **`date`** — creation date in ISO 8601 (`YYYY-MM-DD`), ASCII digits only.
|
||||
Used in the filename (`docs/brainstorms/YYYY-MM-DD-<topic>-requirements.<md|html>`).
|
||||
- **`topic`** — kebab-case slug identifying the brainstorm subject (e.g.,
|
||||
`surface-scope-earlier`, `demo-reel-local-save`). Used in the filename
|
||||
alongside `date` and as the resume-detection key when `ce-brainstorm`'s
|
||||
Phase 0.1 scans `docs/brainstorms/` for an existing artifact to continue.
|
||||
|
||||
### Status flip does not apply to brainstorm
|
||||
|
||||
Unlike plans, brainstorm artifacts have no `status` field — there is no
|
||||
`active → completed` lifecycle. A brainstorm is a one-time output that
|
||||
downstream consumers (`ce-plan`, `ce-doc-review`) reference via the plan's
|
||||
`origin:` field. The `<span class="status">` HTML hook described in
|
||||
`html-rendering.md` is a plan-side mechanic and does not render on
|
||||
brainstorm artifacts.
|
||||
|
||||
### Field-name stability
|
||||
|
||||
Field names are stable across brainstorm revisions — never rename a field
|
||||
or repurpose its semantics. Agents composing new brainstorms MUST use these
|
||||
exact names; adding new fields is fine, but renaming `topic` to `subject`
|
||||
or `date` to `created` breaks filename construction and resume detection.
|
||||
|
||||
## ID and content rules
|
||||
|
||||
Same shape as plan rules.
|
||||
|
||||
- **Stable IDs.** R-IDs (Requirements), A-IDs (if Actors fire), F-IDs (if
|
||||
Flows fire), AE-IDs (if Acceptance Examples fire). No other ID namespaces.
|
||||
- **Plain prefix.** `R1.`, `A1.`, `F1.`, `AE1.` as bullet prefixes. Do not
|
||||
bold; the prefix is visually distinctive on its own.
|
||||
- **Bold leader labels** inside Flows and Acceptance Examples
|
||||
(`**Trigger:**`, `**Covers R4, R8.**`) provide structure without deeper
|
||||
heading levels.
|
||||
- **Repo-relative paths.** Always. Never absolute paths.
|
||||
- **No process exhaust.** No "captured at Phase X" notes, no `## Next Steps`
|
||||
pointing to ce-plan, no italic provenance lines. Engineering process
|
||||
metadata belongs in commit messages and tool output, not the artifact.
|
||||
- **No implementation details by default.** Libraries, schemas, endpoints,
|
||||
file layouts, code structure stay out unless the brainstorm itself is
|
||||
inherently about a technical or architectural change and those details are
|
||||
the subject of the decision.
|
||||
|
||||
## Discipline: Summary vs Problem Frame
|
||||
|
||||
When both sections are present, they earn separate sections only by holding
|
||||
to different purposes:
|
||||
|
||||
| Section | Question it answers | Time direction | Length |
|
||||
|---|---|---|---|
|
||||
| `## Summary` | What is this doc proposing? | Forward-looking | 1-3 lines |
|
||||
| `## Problem Frame` | Why does this proposal exist? | Backward-looking / situational | Paragraphs |
|
||||
|
||||
- **Summary doesn't need problem context.** A reader scanning Summary gets
|
||||
the proposal at a glance.
|
||||
- **Problem Frame doesn't restate the proposal.** It establishes the
|
||||
situation, the specific moment of pain, and the cost shape — then stops.
|
||||
The remedy lives in Summary; restating it in Problem Frame is the
|
||||
duplication that makes the two sections feel redundant.
|
||||
|
||||
## Rendering
|
||||
|
||||
The format-specific references describe how to render these sections in each
|
||||
output format:
|
||||
|
||||
- **Markdown rendering:** `references/markdown-rendering.md`
|
||||
- **HTML rendering:** `references/html-rendering.md`
|
||||
|
||||
This reference (`brainstorm-sections.md`) is about WHAT the brainstorm
|
||||
contains; rendering references are about HOW each format presents it. The
|
||||
brainstorm is written in one format — markdown OR HTML, never both — based
|
||||
on the resolved output mode. The section catalog is the same regardless of
|
||||
format.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Handoff
|
||||
|
||||
This content is loaded when Phase 4 begins — after the requirements document is written.
|
||||
|
||||
---
|
||||
|
||||
#### 4.1 Present Next-Step Options
|
||||
|
||||
The Phase 4 menu's visible option count varies by state: no requirements doc hides the review and Proof options, `OUTPUT_FORMAT=html` also hides the review option (ce-doc-review is markdown-only today), unresolved `Resolve Before Planning` hides `Plan implementation` and `Build it now`, a failing direct-to-work gate hides `Build it now`. Count the visible options for the current state and choose the rendering mode accordingly:
|
||||
|
||||
- **4 or fewer visible:** use the platform's blocking question tool (`AskUserQuestion` in Claude Code — call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded; `request_user_input` in Codex; `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)). This is the default.
|
||||
- **5 or more visible:** render as a numbered list in chat. This is the narrow option-overflow fallback; trimming would hide legitimate choices (plan, review, Proof, build, refine, pause are all distinct destinations). Include a hint that free-form input is accepted ("Pick a number or describe what you want.") so the numbered list retains the blocking tool's open-endedness.
|
||||
|
||||
Never silently skip the question.
|
||||
|
||||
If `Resolve Before Planning` contains any items:
|
||||
- Ask the blocking questions now, one at a time, by default
|
||||
- If the user explicitly wants to proceed anyway, first convert each remaining item into an explicit decision, assumption, or `Deferred to Planning` question
|
||||
- If the user chooses to pause instead, present the handoff as paused or blocked rather than complete
|
||||
- Do not offer the `Plan implementation` or `Build it now` options while `Resolve Before Planning` remains non-empty
|
||||
|
||||
In both preambles below, the "Pick a number or describe what you want." hint applies only in numbered-list mode. When using the blocking tool, omit that line and pass the remaining stem as the question.
|
||||
|
||||
**Path format:** Use absolute paths for chat-output file references — relative paths are not auto-linked as clickable in most terminals.
|
||||
|
||||
**Preamble when no blocking questions remain:**
|
||||
|
||||
```
|
||||
Brainstorm complete.
|
||||
|
||||
Requirements doc: <absolute path to requirements doc> # omit line if no doc was created
|
||||
|
||||
What would you like to do next? (Pick a number or describe what you want.)
|
||||
```
|
||||
|
||||
**Preamble when blocking questions remain and user wants to pause:**
|
||||
|
||||
```
|
||||
Brainstorm paused. Planning is blocked until the remaining questions are resolved.
|
||||
|
||||
Requirements doc: <absolute path to requirements doc> # omit line if no doc was created
|
||||
|
||||
What would you like to do next? (Pick a number or describe what you want.)
|
||||
```
|
||||
|
||||
Present only the options that apply. Renumber so visible options stay contiguous starting at 1.
|
||||
|
||||
1. **Plan implementation with `ce-plan` (Recommended)** - Move to `ce-plan` for structured implementation planning. Shown only when `Resolve Before Planning` is empty.
|
||||
2. **Agent review of requirements doc with `ce-doc-review`** - Dispatch reviewer agents to check the doc for coherence, feasibility, scope, and other persona-specific issues; auto-apply safe fixes; route remaining findings interactively. Shown only when a requirements document exists **and `OUTPUT_FORMAT=md`** — ce-doc-review's walkthrough applies markdown-only mutations (`##`/`###` heading inserts, single-file markdown edits via apply-set) and would corrupt an HTML artifact, so HTML brainstorms skip this option until ce-doc-review gains HTML-aware mutation support. Under HTML mode, surface a one-line note above the menu: `Agent review unavailable in output:html mode — ce-doc-review is markdown-only today. Switch to output:md if you want a review pass.`
|
||||
3. **Open in Proof — review and comment to iterate with the agent** - Open the doc in Every's Proof editor, iterate with the agent via comments, or copy a link to share with others. Shown only when a requirements document exists. **Render only when `OUTPUT_FORMAT=md`** (Proof operates on markdown and cannot ingest HTML).
|
||||
3. **Open in browser** — open the HTML requirements file locally for review and sharing. Shown only when a requirements document exists. **Render only when `OUTPUT_FORMAT=html`.** Replaces "Open in Proof" at the same slot under exclusive output mode — the doc is either markdown OR HTML, never both, so exactly one of the two labels applies per run.
|
||||
4. **Build it now with `ce-work` (skip planning)** - Skip planning and move to `ce-work`; suited to lightweight, well-defined changes. Shown only when `Resolve Before Planning` is empty **and** scope is lightweight, success criteria are clear, scope boundaries are clear, and no meaningful technical or research questions remain (the "direct-to-work gate").
|
||||
5. **More clarifying questions to sharpen the doc** - Keep refining scope, edge cases, constraints, and preferences through further dialogue. Always shown.
|
||||
6. **Done for now** - Pause; the requirements doc is saved and can be resumed later. Always shown.
|
||||
|
||||
**Post-review nudge (subsequent rounds only):** If the user has already run `ce-doc-review` this session and residual P0/P1 findings remain unaddressed, add a one-line prose nudge adjacent to the menu (e.g., "Document review flagged 2 P1 findings you may want to address — pick \"Agent review of requirements doc\" to run another pass."). Reference the option by label, not number: the menu renumbers when `Resolve Before Planning` hides `Plan implementation` and `Build it now`, so a hardcoded option number can point users at the wrong action. Do not add a separate menu option; reuse the existing agent-review option. Suppress this nudge when `OUTPUT_FORMAT=html` — the agent-review option is hidden in that mode, so the nudge would point users at a missing action.
|
||||
|
||||
#### 4.2 Handle the Selected Option
|
||||
|
||||
Selections may be the literal option label (when the user types the label or a close paraphrase) or the option number. Match numbers against the currently-rendered (post-trim) list. Free-form input that doesn't match an option or describe an alternative action should be treated as clarification — ask a follow-up rather than guessing.
|
||||
|
||||
**If user selects "Plan implementation with `ce-plan` (Recommended)":**
|
||||
|
||||
Immediately load the `ce-plan` skill in the current session. Pass the requirements document path when one exists; otherwise pass a concise summary of the finalized brainstorm decisions. Do not print the closing summary first.
|
||||
|
||||
**If user selects "Agent review of requirements doc with `ce-doc-review`":**
|
||||
|
||||
Load the `ce-doc-review` skill, passing the requirements document path as the argument. When ce-doc-review returns "Review complete", return to the Phase 4 options and re-render the menu (the doc may have changed, so re-evaluate `Resolve Before Planning`, direct-to-work gate, and residual findings). If residual P0/P1 findings remain unaddressed, include the post-review nudge above the menu. Do not show the closing summary yet.
|
||||
|
||||
**If user selects "Build it now with `ce-work` (skip planning)":**
|
||||
|
||||
Immediately load the `ce-work` skill in the current session using the finalized brainstorm output as context. If a compact requirements document exists, pass its path. Do not print the closing summary first.
|
||||
|
||||
**If user selects "More clarifying questions to sharpen the doc":** Return to Phase 1.3 (Collaborative Dialogue) and continue asking the user clarifying questions one at a time to further refine scope, edge cases, constraints, and preferences. Continue until the user is satisfied, then return to Phase 4. Do not show the closing summary yet.
|
||||
|
||||
**If user selects "Open in Proof — review and comment to iterate with the agent":**
|
||||
|
||||
Load the `ce-proof` skill in HITL-review mode with:
|
||||
|
||||
- **source file:** `docs/brainstorms/YYYY-MM-DD-<topic>-requirements.md`
|
||||
- **doc title:** `Requirements: <topic title>`
|
||||
- **identity:** `ai:compound-engineering` / `Compound Engineering`
|
||||
- **recommended next step:** `ce-plan` (shown in the ce-proof skill's final terminal output)
|
||||
|
||||
Follow `references/hitl-review.md` in the ce-proof skill. It uploads the doc, prompts the user for review in Proof's web UI, ingests filtered comment threads, applies agreed edits through the current Proof edit APIs, replies/resolves in-thread, and syncs the final markdown back to the source file atomically on proceed.
|
||||
|
||||
When the ce-proof skill returns control:
|
||||
|
||||
- `status: proceeded` with `localSynced: true` → the requirements doc on disk now reflects the review. Return to the Phase 4 options and re-render the menu (the doc may have changed substantially during review, so option eligibility can shift — re-evaluate `Resolve Before Planning`, direct-to-work gate, and residual ce-doc-review findings against the updated doc).
|
||||
- `status: proceeded` with `localSynced: false` → the reviewed version lives in Proof at `docUrl` but the local copy is stale. Offer to pull the Proof doc to `localPath` using the ce-proof skill's Pull workflow. Re-render the Phase 4 menu after the pull completes (or is declined). If the pull was declined, include a one-line note above the menu that `<localPath>` is stale vs. Proof — otherwise `Plan implementation` / `Build it now` / `Agent review of requirements doc` will silently read the pre-review copy.
|
||||
- `status: done_for_now` → the doc on disk may be stale if the user edited in Proof before leaving. Offer to pull the Proof doc to `localPath` so the local requirements file stays in sync, then return to the Phase 4 options. If the pull was declined, include the stale-local note above the menu. `done_for_now` means the user stopped the HITL loop without syncing — it does not mean they ended the whole brainstorm.
|
||||
- `status: aborted` → fall back to the Phase 4 options without changes.
|
||||
|
||||
If the initial upload fails (network error, Proof API down), retry once after a short wait. If it still fails, tell the user the upload didn't succeed and briefly explain why, then return to the Phase 4 options — don't leave them wondering why the option did nothing.
|
||||
|
||||
**If user selects "Open in browser":** Display the absolute path to the `.html` requirements file so the user can open it locally. Where the platform exposes a browser-opening primitive (e.g., `open` on macOS, `xdg-open` on Linux, `start` on Windows), the agent may invoke it directly; otherwise print the absolute path and let the user open it. After the path is displayed (or the browser is opened), return to the Phase 4 options so the user can pick a follow-up action.
|
||||
|
||||
**If user selects "Done for now":** Display the closing summary (see 4.3) and end the turn.
|
||||
|
||||
#### 4.3 Closing Summary
|
||||
|
||||
Use the closing summary only when this run of the workflow is ending or handing off, not when returning to the Phase 4 options.
|
||||
|
||||
In both templates below, substitute `<absolute path to requirements doc>` with the actual file path written this run — `.md` for `OUTPUT_FORMAT=md`, `.html` for `OUTPUT_FORMAT=html`. Do not emit a hardcoded `.md` path when the artifact is HTML, or the closing summary will point users at a file that was never written.
|
||||
|
||||
When complete and ready for planning, display:
|
||||
|
||||
```text
|
||||
Brainstorm complete!
|
||||
|
||||
Requirements doc: <absolute path to requirements doc> # omit line if no doc was created
|
||||
|
||||
Key decisions:
|
||||
- [Decision 1]
|
||||
- [Decision 2]
|
||||
|
||||
Recommended next step: `ce-plan`
|
||||
```
|
||||
|
||||
If the user pauses with `Resolve Before Planning` still populated, display:
|
||||
|
||||
```text
|
||||
Brainstorm paused.
|
||||
|
||||
Requirements doc: <absolute path to requirements doc> # omit line if no doc was created
|
||||
|
||||
Planning is blocked by:
|
||||
- [Blocking question 1]
|
||||
- [Blocking question 2]
|
||||
|
||||
Resume with `ce-brainstorm` when ready to resolve these before planning.
|
||||
```
|
||||
@@ -0,0 +1,538 @@
|
||||
# HTML Rendering
|
||||
|
||||
This is a format-rendering reference — it describes how to render any
|
||||
artifact in HTML, independent of which skill is producing it.
|
||||
|
||||
It is paired with a section contract (`plan-sections.md`,
|
||||
`brainstorm-sections.md`, etc.) that describes *what* the artifact contains.
|
||||
This reference describes *how* HTML specifically presents it. The same
|
||||
content rendered by different skills shares the same HTML principles.
|
||||
|
||||
The HTML artifact is the *only* artifact the skill produces for that run —
|
||||
output mode is exclusive (markdown OR HTML, never both). Downstream
|
||||
consumers that read HTML today (`ce-work`, human readers) do so directly;
|
||||
the agent-consumability rules below make that work. `ce-doc-review` is
|
||||
*not* currently an HTML consumer — its mutation mechanics are markdown-only,
|
||||
so the ce-plan handoff gates the 5.3.8 doc-review pass to `OUTPUT_FORMAT=md`
|
||||
runs and skips it for HTML.
|
||||
|
||||
## Hard invariants
|
||||
|
||||
These hold regardless of which skill produced the artifact.
|
||||
|
||||
- **Single self-contained HTML5 file.** No companion `.css`, `.js`, or
|
||||
`.svg` files. CSS lives in `<style>`. SVG lives inline. Images are
|
||||
base64 data URIs or inline SVG. The one permitted exception is a
|
||||
`<link rel="stylesheet">` to a CDN webfont CSS endpoint (Google Fonts,
|
||||
Bunny Fonts, etc.), paired with an offline-readable fallback font stack
|
||||
so the doc remains readable if the CDN is unreachable.
|
||||
- **All metadata appears as visible text — single source of truth.**
|
||||
The artifact's metadata (title, type, status, date, etc. — exact
|
||||
fields per-skill, defined in the section contract) renders as visible
|
||||
HTML elements that downstream agents and humans read. No hidden
|
||||
machine-readable copy in any form: no `<script type="application/json">`
|
||||
frontmatter block, no `data-*` attribute mirror, and no
|
||||
`<meta name="status">` / `<meta name="created">` / `<meta name="origin">`
|
||||
in `<head>` duplicating the same values that appear in the visible
|
||||
header. One representation for each value — drift across two copies is
|
||||
the failure this rule prevents.
|
||||
|
||||
The text-and-attribute redundancy in `<time datetime="2026-05-12">2026-05-12</time>`
|
||||
is acceptable because the attribute is a parser hint, not a hidden copy.
|
||||
- **Editable status renders as `<span class="status">{value}</span>`.**
|
||||
Downstream tooling (`ce-work` shipping flip, future HTML-aware
|
||||
consumers) finds and rewrites status by selector. Embedding the
|
||||
status value inside a header `<dl>` cell (`<dt>Status</dt><dd>active</dd>`),
|
||||
inside a `<meta>` tag, or as visible text without the `class="status"`
|
||||
hook all break the flip mechanic — the consumer either can't locate
|
||||
the value or can't disambiguate it from prose. The status span may
|
||||
sit anywhere in the doc (inside the header metadata, in a stats
|
||||
strip, in a hero banner); placement is a visual choice, the selector
|
||||
shape is the contract.
|
||||
- **Stable IDs as anchor IDs AND visible text.** Every ID-bearing item
|
||||
(R-IDs, U-IDs, A-IDs, F-IDs, AE-IDs, KTDs) gets `id="r1"` on its
|
||||
element AND appears as visible text inside the element (e.g., the
|
||||
text "R1." inside the table cell or heading). Downstream agents find
|
||||
the ID in source the same way they find it in markdown.
|
||||
- **Source / composition signal.** A visible footer at the bottom of
|
||||
the doc names the composition timestamp and the source identifier
|
||||
(the user prompt context, the upstream brainstorm doc when one
|
||||
exists, or just the composing skill name when there's no external
|
||||
source). Example shape:
|
||||
`<footer class="composition-signal">Composed 2026-05-17T14:23Z by ce-plan from <code>docs/brainstorms/...-requirements.md</code></footer>`.
|
||||
Under exclusive output mode this signal is the artifact's own
|
||||
provenance — there's no markdown sibling to reference. Omitting it
|
||||
leaves readers unable to tell how stale the rendering is.
|
||||
- **ASCII identifiers.** Class names, element IDs, data attribute names
|
||||
are ASCII-only.
|
||||
|
||||
## Precedence stack for style preferences
|
||||
|
||||
Honor user style preferences in this order (highest to lowest):
|
||||
|
||||
1. **In-session conversation** — explicit direction the user gave this run.
|
||||
2. **Preferred stylesheet reference** named in loaded agent-instruction
|
||||
context (typically `AGENTS.md` / `CLAUDE.md`, but scan loaded context;
|
||||
don't enumerate locations). The reference may be a file path
|
||||
(`docs/style.css`), a URL, a named library ("Tailwind"), or a style
|
||||
brand ("Stripe docs"). Agent-instruction files carry deliberate
|
||||
agent-aware preferences, so this tier sits above DESIGN.md.
|
||||
3. **DESIGN.md** discovered on the filesystem (see "DESIGN.md discovery"
|
||||
below).
|
||||
4. **Fallback default** — the opinionated palette / typography choices the
|
||||
agent makes when no preference exists.
|
||||
|
||||
### Active-recall at compose time
|
||||
|
||||
Before writing the CSS, scan loaded context for any stylesheet reference
|
||||
the user has indicated for documents like this. If found and inlinable
|
||||
(short local file, fetchable URL within budget), inline it into `<style>`.
|
||||
If found but not inlinable (large framework, paywalled stylesheet, named
|
||||
system without a fetchable source), compose CSS in its spirit — typography,
|
||||
color, density cues drawn from the named system. Only fall back to the
|
||||
default style when no preference signal exists.
|
||||
|
||||
The single-file invariant is preserved either way. External
|
||||
`<link rel="stylesheet">` is permitted only for CDN webfont CSS (with the
|
||||
offline fallback font stack); never link to an external stylesheet
|
||||
carrying layout, color, or typography rules the doc cannot read offline.
|
||||
|
||||
### DESIGN.md discovery
|
||||
|
||||
When tier 3 of the precedence stack applies, look for a DESIGN.md file in
|
||||
these locations, first match wins:
|
||||
|
||||
1. Worktree root (resolve via `git rev-parse --show-toplevel`).
|
||||
2. `docs/DESIGN.md`.
|
||||
3. `.compound-engineering/DESIGN.md`.
|
||||
|
||||
Read once at compose time. Absent → fall through to the fallback default.
|
||||
|
||||
Worktree-root only — do not fall through to a main checkout. Users
|
||||
working from a worktree who want HTML defaults can add DESIGN.md to the
|
||||
worktree.
|
||||
|
||||
**DESIGN.md is a partial override, not all-or-nothing.** Real
|
||||
DESIGN.md files vary widely: some are token tables, some are CSS
|
||||
variables, some are prose; most cover a subset of what HTML composition
|
||||
needs. Apply the tokens that fit a long-form text doc — typography roles,
|
||||
text colors, contrast targets, border-radius scale, elevation primitives,
|
||||
muted-vs-accent split. Skip the rest. Three specific failure modes to
|
||||
defend against:
|
||||
|
||||
- **Scope mismatch (product UI vs doc surface).** A DESIGN.md aimed at
|
||||
product marketing or app UI may name page-surface colors, button
|
||||
states, input borders, or hero backgrounds that are tied to *that*
|
||||
surface, not to a generic doc. Page-surface colors are the canonical
|
||||
trap — `--surface: #c0f0fb` belongs on the product's marketing page,
|
||||
not on every plan or requirements doc the team writes. Extract the
|
||||
principle (the design language uses a tinted surface) rather than the
|
||||
literal value when the token is product-UI-scoped. Apply literal
|
||||
values only when the token is generic enough to transfer (text color,
|
||||
type scale ratio, radius scale, contrast ratio).
|
||||
- **Partial coverage.** When DESIGN.md defines some categories but not
|
||||
others (e.g., colors but no spacing scale, typography but no
|
||||
elevation), use DESIGN.md for what it covers and the fallback default
|
||||
for what it doesn't. Do not require DESIGN.md to be complete before
|
||||
honoring it.
|
||||
- **Named font without a fetchable source.** When DESIGN.md names a
|
||||
font (e.g., "Signifier", "Every") without a CDN URL or local
|
||||
`@font-face` source the agent can inline, treat the name as a hint
|
||||
about the design intent, not a literal directive. Emit a system-font
|
||||
stack in the same family (serif vs sans vs mono) and pick a weight
|
||||
that matches the intent. The single-file invariant still holds; do
|
||||
not link to an external stylesheet to fetch the named font.
|
||||
- **Typography-scale mismatch.** DESIGN.md typography tokens are often
|
||||
sized for product UI — marketing pages, app screens, hero sections —
|
||||
with body text at 18-20px and headings at 32-52px. A long-form doc
|
||||
surface needs body at ~14-16px and headings at ~1.2-1.6× body. When
|
||||
the DESIGN.md size scale looks product-scaled, use the **family**,
|
||||
**weight**, and **OpenType feature** assignments (these carry the
|
||||
design language) and pick the agent's own **size scale** for the doc
|
||||
surface. Apply DESIGN.md sizes literally only when the tokens are
|
||||
clearly doc-scaled — body tokens at 14-16px, headings under ~32px.
|
||||
|
||||
## Format principles
|
||||
|
||||
These shape what "good" HTML looks like; the agent applies them per
|
||||
artifact based on content.
|
||||
|
||||
### Readable measure, not full bleed
|
||||
|
||||
Long-form text is unreadable at full viewport width — past ~80 characters
|
||||
per line the eye loses the return sweep and scanning slows. As a
|
||||
fallback-default (precedence tier 4, overridden by in-session direction or
|
||||
DESIGN.md), center the document in a content container and hold prose to a
|
||||
comfortable measure.
|
||||
|
||||
- **Page container.** A centered column with a max-width in the ~820-960px
|
||||
band (`margin-inline: auto`) keeps the doc off the far edges of wide
|
||||
monitors while leaving room for the format's richer shapes.
|
||||
- **Prose measure.** Hold running paragraphs to roughly 65-80 characters
|
||||
(`max-width: ~70ch` on text blocks). The named test: read a paragraph at
|
||||
full window width on a wide display — if the return sweep to the next
|
||||
line is effortful, the measure is too wide.
|
||||
- **Let wide content break out.** Tables, diagrams, and side-by-side
|
||||
columns may use the full container width (or wider) when the content
|
||||
needs it — the measure constraint is for prose, not for everything.
|
||||
|
||||
Express the constraint in `ch`/`rem` rather than a single hardcoded pixel
|
||||
value so it survives font-size and DESIGN.md overrides. DESIGN.md or an
|
||||
in-session instruction overrides these values; this is the fallback when no
|
||||
layout preference exists.
|
||||
|
||||
### Markdown source is content, not design
|
||||
|
||||
When markdown (or markdown-shaped chat context) is part of the input, use
|
||||
it for semantic content — what the doc is about, what sections exist,
|
||||
what facts each section establishes. Do NOT treat its bullet-vs-table
|
||||
presentation choices as authoritative; re-choose the rendering per
|
||||
content shape in HTML's richer affordance space. If the markdown rendered
|
||||
13 requirements as a bulleted list, that does NOT mean HTML must render
|
||||
them as a list — ask whether 13 items sharing `ID + body` shape deserve
|
||||
a table.
|
||||
|
||||
### Prose is authoritative
|
||||
|
||||
When a visualization disagrees with the surrounding prose, the prose
|
||||
governs. If they diverge, the visualization is wrong.
|
||||
|
||||
### Hyperlink the reference index
|
||||
|
||||
When the doc has a Sources & References (or equivalent reference-index)
|
||||
section, hyperlink each entry to its canonical destination so readers
|
||||
can open it directly. A long bare-text list of paths and ticket IDs is
|
||||
the format's biggest unforced UX miss — the reader has to copy-paste
|
||||
every entry into a browser or IDE.
|
||||
|
||||
Resolve the repo's GitHub URL once at compose time:
|
||||
|
||||
```bash
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
Apply linking to three reference shapes:
|
||||
|
||||
- **Repo-relative code/doc paths** (`services/foo.ts`,
|
||||
`docs/solutions/bar.md`) → `<repo-url>/blob/main/<path>`.
|
||||
- **Named GitHub PRs/issues** (`PR #636`, `issue #1048`) →
|
||||
`<repo-url>/pull/636` or `<repo-url>/issues/1048`.
|
||||
- **Named external trackers** (Linear `ESP-1705`, Jira `PROJ-123`) →
|
||||
link only when the workspace URL is established in loaded context
|
||||
(e.g., a `linear.app/<workspace>/...` URL appeared earlier in the
|
||||
session or in `AGENTS.md`); otherwise leave as text.
|
||||
|
||||
**Do not invent URLs.** If `origin` isn't a GitHub URL (GitLab,
|
||||
Bitbucket, internal host) and the equivalent main-tree URL pattern
|
||||
isn't obvious, leave entries as `<code>` text. If the external
|
||||
tracker workspace isn't established, leave as text. A broken or
|
||||
guessed link is worse than no link.
|
||||
|
||||
**Scope: reference index only, not inline prose.** Inline `<code>`
|
||||
mentions of paths or PRs inside paragraph prose stay as code or text.
|
||||
Linking every mention would clutter; readers expect clickable jumps
|
||||
where the doc presents itself as a reference index.
|
||||
|
||||
### Text contrast is local
|
||||
|
||||
Every text-on-background pairing must hold up on its own. A color that
|
||||
works for prose on the page background does not automatically work for
|
||||
a small label inside a tinted container. The most common violation:
|
||||
applying a generic "muted" text variable (calibrated for prose-on-bg) to
|
||||
secondary text inside an accent-soft / warn-soft / info-soft container.
|
||||
|
||||
Test by reading each filled shape's labels at the rendered scale. If the
|
||||
subtitle or secondary text feels washed-out against the fill, the choice
|
||||
is wrong for that local context — pick a color from the same family as
|
||||
the fill (accent-text for accent-soft, etc.) or drop the muting entirely
|
||||
and rely on font-size and weight for hierarchy.
|
||||
|
||||
### Body bold not colored by default
|
||||
|
||||
Reserve accent text color for status chips, ID chips, links, and section
|
||||
borders. Do NOT color `<strong>` in body content by default. Bold weight
|
||||
already carries emphasis; applying accent color to every `<strong>` in a
|
||||
long list overwhelms the eye, especially in dark mode. CSS should leave
|
||||
`strong` at `color: inherit` unless a specific surface (status pill, ID
|
||||
chip) is being styled.
|
||||
|
||||
### No JS framework runtimes
|
||||
|
||||
A small inline `<script>` for active-section TOC tracking or anchor-
|
||||
permalink behavior is acceptable. React, Vue, Svelte, or any framework
|
||||
runtime is not. The single-file invariant doesn't permit framework
|
||||
bundles, and the artifact's longevity doesn't warrant a build dependency.
|
||||
|
||||
## Section anatomy
|
||||
|
||||
How section types commonly render in HTML. These are patterns, not
|
||||
contracts — the agent picks shapes that fit the content.
|
||||
|
||||
- **Summary / Problem Frame** — semantic `<section>` with prose
|
||||
paragraphs. Optionally precede with an eyebrow label (small-caps tag
|
||||
above the title) for editorial polish.
|
||||
- **Requirements** — `<table>` is the default at 5+ uniform items;
|
||||
bullets at smaller counts. Concern-grouping takes precedence over the
|
||||
flat-table default: when requirements span distinct concerns, group them
|
||||
under bold inline headers (or per-group sections) first, then apply the
|
||||
5+ table default *within* each group rather than flattening the whole
|
||||
section into one table. Each row has the R-ID as visible text in
|
||||
its own column. Consider adding a "covered by" column for reverse
|
||||
traceability when ID-anchored items have downstream references in
|
||||
the same doc.
|
||||
- **Implementation Units** — repeating `<article>` cards with a stable
|
||||
ID chip (visible "U1" text), a metadata strip (`<dl>` with field
|
||||
labels and values for Goal, Files, Dependencies), and secondary
|
||||
content (Approach, Test Scenarios, Verification, Patterns to Follow)
|
||||
inside `<details>` collapsibles, **default-closed**. At 3+ units the
|
||||
default-closed rule is load-bearing — rendering all units fully
|
||||
expanded turns the doc into one continuous scroll where the reader
|
||||
can't see the unit list at a glance. The metadata strip is the
|
||||
primary always-visible surface; subsection labels (`<summary>`) are
|
||||
clickable affordances for readers to expand on demand. A single unit
|
||||
with no secondary content can skip `<details>` entirely; the rule
|
||||
fires when content exists to hide. The `<dl>` strip is for *descriptive*
|
||||
fields (Goal, Files, Dependencies). A *directive* field — `Execution
|
||||
note` is the canonical case, carrying a procedural instruction the
|
||||
implementer must act on (e.g. "start with a failing integration test") —
|
||||
does not belong in the strip, where it renders as a passive pair styled
|
||||
like a date and gets skimmed past. Render it as an advisory callout (see
|
||||
Tinted callout cards) so its visual weight matches its actionability. The
|
||||
test: descriptive value -> metadata pair; something the reader must act
|
||||
on -> callout.
|
||||
- **Key Technical Decisions** — repeating cards with the decision ID,
|
||||
bold decision title (often with inline code for technical
|
||||
identifiers), and prose rationale. Flat cards (not collapsibles) —
|
||||
these are reference material readers scan, not drill into.
|
||||
- **Risks** — color-coded cards with status eyebrow (e.g., "RISK ·
|
||||
MITIGATED" / "OPEN · DEFERRED FOLLOW-UP") and prose body. Color of
|
||||
the left-border or accent communicates status at a glance.
|
||||
- **Scope Boundaries** — callout cards with color-coded left borders
|
||||
(in-scope vs deferred vs outside) when the distinction is meaningful.
|
||||
|
||||
The agent picks more elaborate or simpler shapes based on what each
|
||||
specific artifact's content needs.
|
||||
|
||||
## Diagrams
|
||||
|
||||
When the section contract calls for a diagram (architecture, sequence,
|
||||
flowchart, state machine, swim lane, data-flow, quantitative
|
||||
comparison), HTML renders it as **inline SVG**. The agent picks the
|
||||
shape that conveys the content fastest — there is no fixed catalog of
|
||||
"approved" diagram types. If the content is quantitative comparison
|
||||
across categories, a bar chart is the right shape; if it's component
|
||||
relationships, a topology diagram; if it's process flow across
|
||||
participants, a swim lane; etc.
|
||||
|
||||
**Conceptual diagrams are not wireframes.** The wireframe affordance below
|
||||
is scoped to brainstorm requirements docs about *visual products* and is
|
||||
excluded for non-visual systems. That exclusion is about wireframes only —
|
||||
a brainstorm about a data model, schema, agent workflow, or migration is
|
||||
still free to use a conceptual diagram (a before/after field map, a
|
||||
source-of-truth fan-out, a state diagram). Don't let the wireframe
|
||||
exclusion suppress a conceptual diagram the content warrants.
|
||||
|
||||
**Diagrams complement prose; they never replace it.** A diagram is an
|
||||
accelerant placed next to the prose it illustrates, not a substitute. The
|
||||
IDed prose stays complete and standalone — a reader who ignores every
|
||||
diagram still gets the full content in text, and a text-reading downstream
|
||||
agent (which does not parse SVG geometry) is never left with a relationship
|
||||
that exists only in the picture. This extends the prose-is-authoritative
|
||||
rule above: prose governs not only on disagreement but on completeness, so
|
||||
adding a diagram is not license to thin the prose it depicts.
|
||||
|
||||
### Layout legibility for hand-authored SVG
|
||||
|
||||
The agent designs SVG coordinates without rendering — layouts that look
|
||||
fine in source can collide in practice. Before emitting, trace each
|
||||
labeled arrow and each text label:
|
||||
|
||||
- **No arrow path passes through a text label.** If an arrow line or
|
||||
curve crosses a label's bounding box, the text reads as struck-through
|
||||
and the arrow reads as terminating at the wrong element. Fix by
|
||||
re-routing the arrow, moving the label, or applying
|
||||
`paint-order: stroke fill` with a stroke color matching the diagram
|
||||
background to halo the label. The halo width is a judgment call:
|
||||
narrow enough not to bleed into glyph strokes (a halo whose width
|
||||
approaches the glyph's own stroke width muddies the text color), wide
|
||||
enough to mask underlying arrows (at least the arrow's stroke width
|
||||
plus a hairline). Verify by inspecting rendered text at the target
|
||||
font size — if glyphs look thicker or more colored-toward-halo than
|
||||
the same text outside the diagram, the halo is too wide.
|
||||
- **Arrow labels sit adjacent to the arrow's midpoint** (typically
|
||||
within ~10-15px above or beside the line they describe). A label
|
||||
floating at the diagram's edge that readers have to trace back to an
|
||||
arrow is broken — readers will misread.
|
||||
- **Avoid long curves that traverse the diagram** to connect a
|
||||
component on one side to one on the other. If A and D need a labeled
|
||||
connection across a multi-component layout, prefer reordering boxes
|
||||
so A and D are adjacent, numbered step badges next to each
|
||||
participant that the caption ties together, or a short
|
||||
labeled-channel notation — rather than one curve crossing multiple
|
||||
unrelated elements.
|
||||
- **Differentiate diagram shapes by geometry first, by fill semantics
|
||||
second.** Geometry (diamond = decision, rect = step, oval =
|
||||
start/end, parallelogram = data) carries the role unambiguously.
|
||||
Fill semantics (accent-soft for highlighted path, warn-soft for
|
||||
fallthrough) carry meaning. Resist introducing additional neutral-tint
|
||||
tiers (a slightly-lighter grey to mark "decision shapes are different
|
||||
from boxes") — when geometry already differentiates, an additional
|
||||
luminance tier adds no information and creates fragility: small RGB
|
||||
deltas survive native browser rendering but can be flattened or
|
||||
inverted inconsistently by dark-mode extensions, accessibility
|
||||
plugins, or printing.
|
||||
|
||||
### Plan architecture diagrams are not directional sketches
|
||||
|
||||
Do not add hedging captions or section preambles to plan SVG diagrams —
|
||||
phrases like "directional guidance for review, not implementation
|
||||
specification" do not belong on plan diagrams or on unit-card
|
||||
technical-design subsections. Plan diagrams render the same authoritative
|
||||
content as the surrounding prose; the prose-is-authoritative rule
|
||||
already governs disagreement. Hedging language is reserved for the
|
||||
wireframe affordance below, which carries a *required* directional
|
||||
caption because the wireframe is explicitly NOT a spec.
|
||||
|
||||
## Wireframe mockups (requirements docs only)
|
||||
|
||||
When a brainstorm requirements document describes a user-facing visual
|
||||
surface (UI feature, screen layout, screen flow, component placement),
|
||||
the HTML rendering may include a wireframe mockup. This affordance applies
|
||||
ONLY to brainstorm requirements docs that describe visual products — not
|
||||
to plan artifacts, and not to brainstorms about non-visual systems (API
|
||||
design, agent workflows, infrastructure).
|
||||
|
||||
When a wireframe is included:
|
||||
|
||||
- **Fidelity ceiling: wireframe, not mockup.** Gray boxes for layout
|
||||
regions, text labels for content placeholders, intentional placeholder
|
||||
copy (`[Product name]`, `[CTA label]`, `[user avatar]`). No
|
||||
pixel-perfect colors, no exact typography choices, no specific
|
||||
component-library references. The wireframe communicates spatial
|
||||
arrangement and structure, not visual style.
|
||||
- **Static only.** Inline SVG or simple HTML/CSS for layout. No JS
|
||||
interaction, no working form fields, no state changes, no live data.
|
||||
- **Anti-padding.** One wireframe per distinct visual concept.
|
||||
- **Mandatory directional caption.** Every wireframe carries an explicit
|
||||
"directional, not the spec" note adjacent to it. Required wording (or
|
||||
close paraphrase): *"Directional only — illustrates the intended
|
||||
user-facing shape. Exact colors, spacing, copy, and component choices
|
||||
are placeholders for review, not requirements."*
|
||||
|
||||
Without this caption the wireframe risks being read as a binding visual
|
||||
spec, which the affordance is explicitly designed to avoid.
|
||||
|
||||
## Affordance idioms
|
||||
|
||||
Common HTML affordances the agent can reach for when content benefits.
|
||||
These are examples, not requirements — the agent picks what each
|
||||
artifact's content warrants. Other affordances not listed here are
|
||||
fine when the content suggests them.
|
||||
|
||||
- **Sticky TOC sidebar with active-section indicator** — available when
|
||||
the agent judges navigation will materially help and the
|
||||
implementation is reliable: two-column layout on desktop, collapsed
|
||||
to top-of-page on mobile, paired with a small inline
|
||||
`IntersectionObserver` script that toggles `.active` on the matching
|
||||
nav anchor. Trade-off: a broken sticky TOC (layout collisions,
|
||||
active-section state drift, dark-mode CSS issues) is worse than a
|
||||
static top-of-doc TOC. For most long docs, default-closed `<details>`
|
||||
on repeating cards (see Implementation Units anatomy) already cuts
|
||||
the visible scroll length enough that a static TOC works — reach for
|
||||
sticky only when collapsibles alone don't solve the navigation
|
||||
problem.
|
||||
- **Within-section sub-nav** for sections containing 6+ repeating cards
|
||||
(Implementation Units, KTDs, Risks at large counts). A short list of
|
||||
card-anchor links (`<ul>` of `<a href="#u1">U1. ...</a>`) rendered at
|
||||
the top of the section gives readers a jump table — no JS needed.
|
||||
Lower-complexity alternative to the sticky TOC for the specific case
|
||||
of long card sections.
|
||||
- **Eyebrow labels** (small-caps tag above section titles) for
|
||||
editorial polish, especially when section titles are narrative
|
||||
rather than literal.
|
||||
- **Stats strip** at the top of the doc when the artifact has 3+
|
||||
quantifiable signals worth surfacing at a glance.
|
||||
- **`<details>` + `<summary>`** for collapsible secondary content
|
||||
inside repeating cards. All collapsibles start closed — `open`
|
||||
attribute should not appear on any `<details>` inside repeating
|
||||
cards by default.
|
||||
- **Side-by-side columns** for parallel content (Request / Response,
|
||||
Before / After, Two alternatives).
|
||||
- **Tinted callout cards** for content that is "different in kind"
|
||||
(Deferred, Open Questions, advisory notes, unit-level execution notes)
|
||||
— color-coded left borders communicate kind at a glance.
|
||||
|
||||
## Agent-consumability rules
|
||||
|
||||
Downstream agents that read HTML today (`ce-work`, future consumers) read
|
||||
the HTML file as text linearly, not via DOM extraction. `ce-doc-review` is
|
||||
not a current HTML consumer (see opening note). Compose so semantic
|
||||
understanding is reachable in source:
|
||||
|
||||
- **Use semantic HTML over `<div>` soup.** `<article>` per unit card,
|
||||
`<dl>` for metadata pairs, `<table>` for tabular content, `<details>`
|
||||
/ `<summary>` for collapsibles, `<section>` for top-level doc
|
||||
sections. Structure markers carry meaning to a text-reading agent.
|
||||
- **Render field labels as visible text, not as attributes.** Emit
|
||||
`<dt>GOAL</dt><dd>...</dd>`, not `<dd data-field="goal">...</dd>`.
|
||||
The label is the semantic anchor.
|
||||
- **Keep U-IDs, R-IDs, and similar as visible text** in headings and
|
||||
table cells, not only as `id=""` attributes. The agent finds "U1." in
|
||||
source the same way it finds "U1." in markdown.
|
||||
- **Match section heading vocabulary to what the section contract
|
||||
defines.** When the section contract says "Implementation Units," the
|
||||
HTML heading is "Implementation Units" — not "How we'll build it,"
|
||||
even if the narrative version reads better. Section heading
|
||||
vocabulary is the contract downstream consumers grep for. (Editorial
|
||||
re-titles can appear as eyebrow labels, sub-headings, or visual
|
||||
framing — but the load-bearing section heading matches the contract
|
||||
name.)
|
||||
- **All semantic content lives in actual HTML text.** No CSS `::before
|
||||
{ content: "..." }` carrying meaning, no background images as
|
||||
content, no semantic info that only renders. Whatever the agent sees
|
||||
in source is what it knows.
|
||||
- **Stable structure is the public API.** Element types, the ID and
|
||||
label scheme, and the field-label vocabulary do not break across
|
||||
versions. Visual styling can change freely.
|
||||
|
||||
## Post-compose audit
|
||||
|
||||
Before returning the artifact, scan it for common slips:
|
||||
|
||||
- **Single self-contained file.** No companion `.css` / `.js` / `.svg`.
|
||||
- **No hidden machine-readable metadata copy.** No
|
||||
`<script type="application/json">` frontmatter block, no `data-*`
|
||||
attributes mirroring visible values, **no `<meta name="status">` /
|
||||
`<meta name="created">` / `<meta name="origin">` etc. in `<head>`
|
||||
duplicating the visible header**. Metadata lives in visible text;
|
||||
one source of truth per value.
|
||||
- **Status renders as `<span class="status">{value}</span>`** so
|
||||
downstream tooling can flip `active → completed` by selector.
|
||||
- **All stable IDs** appear as both `id=""` and visible text.
|
||||
- **Section heading vocabulary** matches the section contract names
|
||||
(downstream agents grep these).
|
||||
- **Source / composition signal** is present as a visible footer at
|
||||
the bottom of the doc (composition timestamp + source identifier).
|
||||
- **Repeating cards with 3+ instances put secondary content inside
|
||||
default-closed `<details>`.** Fully-expanded unit cards in a long
|
||||
Implementation Units section is a failure mode — the reader can't see
|
||||
the unit list at a glance. Verify by skimming the rendered units:
|
||||
each `<article>` should render as its ID + title + metadata strip
|
||||
with collapsibles below, not as one long block.
|
||||
- **Within-section sub-nav** is present for sections with 6+ repeating
|
||||
cards.
|
||||
- **Body `<strong>`** is not colored with accent palette.
|
||||
- **`<details>`** inside repeating cards have no `open` attribute.
|
||||
- **Diagram labels** are legible — no arrow paths crossing text,
|
||||
halo width appropriate for font size.
|
||||
- **Diagrams complement prose, not replace it.** Every relationship a
|
||||
diagram conveys is also present in the surrounding IDed prose; no
|
||||
content lives only in an SVG.
|
||||
- **No JS framework runtimes** included. Small inline `<script>` for
|
||||
active-section TOC tracking or anchor-permalink behavior is the only
|
||||
acceptable JS.
|
||||
- **Each heading level** is visually distinct from others and from
|
||||
inline bold.
|
||||
- **No template placeholders** (`{skill}`, `<value>`, `[plan title]`)
|
||||
leaked into output.
|
||||
- **No process exhaust** callouts in the artifact.
|
||||
@@ -0,0 +1,207 @@
|
||||
# Markdown Rendering
|
||||
|
||||
This is a format-rendering reference — it describes how to render any
|
||||
artifact in markdown, independent of which skill is producing it.
|
||||
|
||||
It is paired with a section contract (`plan-sections.md`,
|
||||
`brainstorm-sections.md`, etc.) that describes *what* the artifact contains.
|
||||
This reference describes *how* markdown specifically presents it. The same
|
||||
content rendered by different skills shares the same markdown principles.
|
||||
|
||||
## Hard invariants
|
||||
|
||||
These hold regardless of which skill produced the artifact.
|
||||
|
||||
- **YAML frontmatter at the top of the file.** Standard `---` delimited block
|
||||
containing the artifact's stable metadata (title, status, date, type, etc.
|
||||
— exact fields are per-skill, defined in the section contract). Editable
|
||||
in place; tools and agents that do status flips (`active → completed`)
|
||||
update the YAML directly.
|
||||
- **ASCII identifiers in anchors.** Markdown headings auto-generate anchors
|
||||
from the heading text. Keep headings ASCII so anchors are predictable
|
||||
(`#implementation-units`, not `#implementación-units`).
|
||||
- **Repo-relative paths for file references.** Always. Never absolute paths
|
||||
— they break portability across machines, worktrees, teammates.
|
||||
- **No HTML mixed in.** Keep the markdown pure. No `<div>`, no `<details>`,
|
||||
no inline `<style>`. If a layout idea only works as HTML, defer it to the
|
||||
HTML rendering. Markdown stays markdown.
|
||||
|
||||
## Format principles
|
||||
|
||||
These shape what "good" markdown looks like; the agent applies them per
|
||||
artifact based on content shape.
|
||||
|
||||
### ID prefix format
|
||||
|
||||
Stable IDs (R, U, A, F, AE, KTD) appear as plain prefixes at the start of
|
||||
the bullet or heading — do NOT bold the prefix. The prefix is visually
|
||||
distinctive on its own; bolding it inflates visual noise.
|
||||
|
||||
```markdown
|
||||
- R1. The plan returns paginated sessions. ← right
|
||||
- **R1.** The plan returns paginated sessions. ← wrong (bolded prefix)
|
||||
```
|
||||
|
||||
Same applies to unit headings: `### U1. Cloak detection in preflight contract`.
|
||||
|
||||
### Content shape: prose vs bullets vs tables
|
||||
|
||||
The same content can be rendered three ways; the agent picks per content
|
||||
shape, not by template default.
|
||||
|
||||
- **Prose** when the content has narrative flow (motivation, decision
|
||||
rationale, problem framing). Bullets fragment narrative into
|
||||
disconnected pieces.
|
||||
- **Bullets** when items share a parallel shape but each carries enough
|
||||
prose to not fit a table cell.
|
||||
- **Tables** when 5+ items share uniform structure (`ID + body`,
|
||||
`name + value`, `decision + rationale`, `risk + mitigation`). Tables
|
||||
scan faster at that scale and unlock additional columns (status,
|
||||
traceability, severity) that bullets can't accommodate cleanly.
|
||||
|
||||
The test: which shape would a reader scan fastest for this content? If
|
||||
items have parallel structure and 5+ instances, table. If items are 3-5
|
||||
and each has a few lines of prose, bullets. If the content is a single
|
||||
narrative thought, prose.
|
||||
|
||||
### Bold leader labels within bullets
|
||||
|
||||
When a bullet has substructure that benefits from named fields (Key Flows
|
||||
with Trigger / Actors / Steps / Outcome, Acceptance Examples with Covers
|
||||
/ Given / When / Then), use bold leader labels at the start of nested
|
||||
bullets — not deeper heading levels.
|
||||
|
||||
```markdown
|
||||
- F1. Anonymous capture
|
||||
- **Trigger:** Agent enters Step 2a with no session.
|
||||
- **Actors:** A1, A2
|
||||
- **Steps:** Preflight detects cloak; agent launches; capture proceeds.
|
||||
- **Covered by:** R1, R2, R5
|
||||
```
|
||||
|
||||
This gives the bullet structure without needing H4/H5 headings that would
|
||||
clutter the doc and break TOC generation.
|
||||
|
||||
### Section separators
|
||||
|
||||
For substantial artifacts, use horizontal rules (`---`) between top-level
|
||||
H2 sections. Omit for short docs where separators would dominate.
|
||||
|
||||
### Tables for genuinely comparative info only
|
||||
|
||||
Use tables for the uniform-shape case in "Content shape" above. Don't use
|
||||
tables to render content lists that are really bullets — markdown tables
|
||||
are noisier in raw form and worse for diffs.
|
||||
|
||||
## Section anatomy
|
||||
|
||||
How section types commonly render in markdown. These are patterns, not
|
||||
contracts — the agent picks the shape that fits the content.
|
||||
|
||||
- **Summary / Problem Frame** — prose paragraphs.
|
||||
- **Requirements** — bullets with `R<N>.` prefix. When requirements span
|
||||
more than one concern, grouping under bold inline headers is the default
|
||||
shape, not optional polish (group by capability, not by discussion order);
|
||||
render a flat list only when every requirement is about the same thing.
|
||||
When requirements have status, traceability, or severity that warrant
|
||||
additional columns, escalate to a table.
|
||||
- **Implementation Units** — H3 heading per unit with `U<N>.` prefix.
|
||||
Fields (Goal, Files, Patterns, Test Scenarios, Verification) render as
|
||||
bullets with bold leader labels, or as sub-headings if the field has
|
||||
multi-paragraph content.
|
||||
- **Key Technical Decisions** — bullets with bold decision name + prose
|
||||
rationale, or numbered KTD-N pattern when traceability matters.
|
||||
- **Key Flows / Acceptance Examples** — bullets with bold leader labels
|
||||
(Trigger / Actors / Steps / Outcome / Covers / Given-When-Then).
|
||||
- **Scope Boundaries** — bullets, optionally split into "Deferred for
|
||||
later" / "Outside this product's identity" sub-headings when the
|
||||
positioning distinction matters.
|
||||
|
||||
The agent picks more elaborate or simpler shapes based on what each
|
||||
specific artifact's content needs.
|
||||
|
||||
## Diagrams
|
||||
|
||||
When the section contract calls for a diagram (architecture, sequence,
|
||||
flowchart, state machine, swim lane, data-flow), markdown renders it as
|
||||
a fenced mermaid block:
|
||||
|
||||
```markdown
|
||||
` ``mermaid
|
||||
flowchart TB
|
||||
A[Start] --> B{Decision}
|
||||
B -->|yes| C[Action]
|
||||
B -->|no| D[Other action]
|
||||
` ``
|
||||
```
|
||||
|
||||
(`TB` direction default — keeps diagrams narrow in source view and in
|
||||
narrow rendered viewports.)
|
||||
|
||||
Markdown's diagram affordances are limited compared to HTML. For
|
||||
quantitative comparisons (bar charts, scatter plots) markdown has no
|
||||
native equivalent — use a table with the data and let prose or caption
|
||||
carry the interpretation. The richer visualization happens in the HTML
|
||||
rendering.
|
||||
|
||||
## Inline code and code blocks
|
||||
|
||||
- **Inline code** for identifiers (variable names, function names,
|
||||
flag names, file paths, IDs that aren't section anchors).
|
||||
- **Fenced code blocks** with language tag for code, shell commands,
|
||||
API request/response samples. Always specify the language for syntax
|
||||
highlighting and accessibility.
|
||||
|
||||
```markdown
|
||||
The flag `--cdp-url` accepts a URL.
|
||||
|
||||
` ``bash
|
||||
browser-use --cdp-url http://localhost:9222
|
||||
` ``
|
||||
```
|
||||
|
||||
## No process exhaust
|
||||
|
||||
Engineering process metadata stays out of the artifact:
|
||||
|
||||
- No "captured at Phase X" notes
|
||||
- No `## Next Steps` pointing to the next skill
|
||||
- No italic provenance lines ("*Brainstorm completed 2026-05-13*")
|
||||
- No engineering-flow shepherding ("Now read this file:", "Next, run that
|
||||
command:")
|
||||
|
||||
This information belongs in commit messages, tool output, and agent
|
||||
transcripts — not in the artifact a reader returns to weeks later.
|
||||
|
||||
## Frontmatter shape
|
||||
|
||||
Per-skill frontmatter fields are defined in each skill's section contract
|
||||
(`plan-sections.md` lists plan frontmatter; `brainstorm-sections.md` lists
|
||||
brainstorm frontmatter). Common rules:
|
||||
|
||||
- YAML at the top of the file, delimited by `---` on its own line above
|
||||
and below.
|
||||
- Field names in lowercase snake_case (`status`, `created_at`, not
|
||||
`Status`, `CreatedAt`).
|
||||
- **Status lifecycle is per-contract.** When the section contract
|
||||
defines a `status` field with a lifecycle (plans use
|
||||
`active → completed`, flipped by ce-work at shipping time via direct
|
||||
YAML edit), it is editable in place. When the section contract does
|
||||
not define a status lifecycle (brainstorms, for example, have no
|
||||
`active → completed` flip — they are upstream of plans and
|
||||
referenced via the plan's `origin:`), do not introduce one.
|
||||
- Stable across artifact revisions — never rename or repurpose a field.
|
||||
|
||||
## Post-write audit
|
||||
|
||||
Before declaring the markdown file written, scan it for these common
|
||||
slips:
|
||||
|
||||
- All stable IDs are plain-prefix format, not bolded.
|
||||
- No HTML elements mixed in.
|
||||
- All file paths are repo-relative.
|
||||
- Horizontal rule separators between H2s (for Standard / Deep artifacts).
|
||||
- No process exhaust (Phase X notes, Next Steps pointers, provenance
|
||||
lines).
|
||||
- Tables only where 5+ uniform-shape items justify them.
|
||||
- Frontmatter has all the per-skill required fields with reasonable values.
|
||||
@@ -0,0 +1,271 @@
|
||||
# Synthesis Summary
|
||||
|
||||
**Synthesis ≠ requirements doc.** The synthesis is NOT a preview, draft, or substitute for the requirements doc — it's the scope checkpoint that doc-write consumes as input. The requirements doc itself is written in Phase 3 from the confirmed synthesis. Both the synthesis and the requirements doc stay scope-only — implementation detail (file paths, code shapes, exact error wording) is downstream (ce-plan's job), not the requirements doc.
|
||||
|
||||
**Two-stage shape: internal draft, then chat-time scoping synthesis.** The synthesis is composed in two stages. Stage 1 is an internal three-bucket draft (Stated / Inferred / Out of scope) the agent uses to think comprehensively about scope. Stage 2 is the scoping synthesis presented to the user — shaped like what two product collaborators would confirm before writing a PRD, not like a comprehensive audit and not like a one-line preview. The user only sees stage 2. The internal draft still informs the doc body via the doc-shape routing below; it just doesn't reach the user verbatim. This split exists because the comprehensive audit shape produced too much detail for the user to actually weigh in on, even when the granularity rules were followed.
|
||||
|
||||
**Three-bucket structure is the internal draft, not the user-facing artifact.** It does its scope-thinking job during stage 1 and dissolves when Phase 3 writes the doc: Stated content informs Requirements, Inferred content informs Key Decisions, Out-of-scope content informs Scope Boundaries. The doc has no parallel `## Synthesis` section — only the scoping synthesis prose embeds, as `## Summary`. See "Doc shape after confirmation" below for the routing.
|
||||
|
||||
This content is loaded when Phase 2.5 fires — after Phase 2 (approaches chosen) and before Phase 3 (write requirements doc). The synthesis is the user's last opportunity to correct the agent's interpretation before the doc lands. It serves two purposes: synthesis confirmation (the user agreed to many individual things in dialogue but never saw the whole) and a transition checkpoint ("about to write a doc").
|
||||
|
||||
Fires for **all tiers** including Lightweight. Skip Phase 2.5 entirely on the Phase 0.1b non-software (universal-brainstorming) route. The skill is interactive by design — brainstorming requires dialogue with a synchronous user. There is no non-interactive mode; if an automated workflow needs a requirements doc without dialogue, the right move is to write the doc from context directly, not to invoke `ce-brainstorm`.
|
||||
|
||||
---
|
||||
|
||||
## Stage 1: internal three-bucket draft
|
||||
|
||||
The internal draft is structured in three labeled buckets. Items may appear in two buckets when meaningfully both — flag the inclusion-then-exclusion as Inferred so the reasoning is captured.
|
||||
|
||||
- **Stated** — what the user said directly (in the original prompt, prior conversation, dialogue answers, approach selection in Phase 2). Items here have explicit user-language anchors.
|
||||
- **Inferred** — what the agent assumed to fill gaps. Scope boundaries the user never explicitly named, success criteria extrapolated from intent, technical assumptions made because the brief interview didn't probe them. The Inferred bucket is the most actionable surface for correction — items here are the agent's bets.
|
||||
- **Out of scope** — deliberately excluded items. Adjacent work the agent considered but decided not to include, refactors, nice-to-haves, future-work items. Making exclusions explicit lets the agent spot anything that should actually be included.
|
||||
|
||||
This draft is internal. Do not paste it verbatim into chat. Compose it as a thinking step, then derive stage 2 from it.
|
||||
|
||||
---
|
||||
|
||||
## Stage 2: the chat-time scoping synthesis
|
||||
|
||||
The scoping synthesis is what the user actually sees. It reflects the dialogue's substance back so the user can pattern-match — long enough to serve a multi-turn conversation, short enough to be high-impact only. The reference shape is what two product collaborators would say to each other after a real discussion: "OK, so we're doing X, with Y trade-off, deferring Z, and one thing I want to double-check is W. Sound right?"
|
||||
|
||||
The scoping synthesis has up to four named sections, each **render-conditional** on having something to say. Empty sections are omitted, not padded.
|
||||
|
||||
1. **What we're building** (always present) — 1–3 sentences. The shape that emerged from dialogue, forward-looking, plain words. Not a transcript of "you said X."
|
||||
2. **Key trade-offs** (conditional) — 1–3 bullets, each with a brief why. Render only when real trade-offs were made in dialogue.
|
||||
3. **What's not in scope** (conditional) — 1–3 bullets, or fold into a single sentence. Render only when deferred items would surprise a downstream reader if absent.
|
||||
4. **Call outs** (conditional) — 0–3 bullets. Residual forks the dialogue didn't resolve: post-dialogue consequences (combining user answers surfaced something they couldn't see during Q&A), silent agent inferences, or — in pre-loaded contexts with no dialogue — scope bets the user is seeing for the first time. **Not "questions the agent could have asked during Phase 1.3 but didn't"** — if a call-out reads like a missed dialogue question, Phase 1.3's integration check failed; flag the gap rather than padding the section.
|
||||
|
||||
Each section answers a different question:
|
||||
|
||||
- **What's being built?** → shape
|
||||
- **What did we trade off?** → explicit choices made in conversation
|
||||
- **What did we cut?** → deferred items a reader would expect to see acknowledged
|
||||
- **Where might you redirect?** → residual forks: post-dialogue consequences, silent inferences, late-cycle bets
|
||||
|
||||
Then the confirmation: *"Confirm and I'll write the requirements doc next, drawing on our dialogue and this synthesis. Or tell me what to change."* The phrasing sets the expectation that confirm → doc-write, so the user knows what's about to happen and can interrupt without ambiguity.
|
||||
|
||||
### Path A vs Path B: the gate that fires the confirmation question
|
||||
|
||||
Phase 2.5 has two presentation modes, gated by **two signals**: (1) did any blocking question fire before Phase 2.5? AND (2) what tier did Phase 0.3 classify the scope as? Blocking questions include Phase 0.3 scope disambiguation, Phase 1.3 collaborative dialogue probes, and Phase 2 approach selection (when a menu fires). Internal classification, Phase 1.1 scan, and Phase 1.2 pressure test are not blocking questions — they don't count.
|
||||
|
||||
- **Path A — no blocking questions fired AND tier is Lightweight**: announce-mode. Emit "What we're building" prose only (no other sections, no confirmation question), then proceed to Phase 3 doc-write in the same turn. Do NOT end the turn waiting for acknowledgment. The user can revise after the doc lands if the shape is wrong — Lightweight Path A docs are short, post-hoc revision is cheap.
|
||||
- **Path B — at least one blocking question fired, OR tier is Standard / Deep-feature / Deep-product**: full tier-aware scoping synthesis with confirmation gate. Two scenarios fire Path B: (a) the user invested answer-time during dialogue, or (b) the user pre-loaded substantive scope content (Phase 0.2 fast-path with a richly-specified opening prompt). Either way, the substance earns a real checkpoint. The confirmation question is unconditional even when zero call-outs survive the keep test.
|
||||
|
||||
**Why the tier guard exists.** Phase 0.2's fast path is designed for two very different cases — a tight one-line prompt that needs no dialogue ("fix the typo on line 47"), and a richly pre-loaded brainstorm context that ALSO needs no dialogue because the user pre-stated everything (e.g., handing off accumulated decisions from a prior session for a brainstorm doc backfill). Without a tier guard, both route to Path A, and the richly-loaded case gets a 1-sentence checkpoint for what may be 20+ items worth of scope. Tier-classifying Phase 0.3 distinguishes these cases — pre-loaded substance makes the tier Standard or Deep, which then routes to Path B and produces the full scoping synthesis the substance deserves. Do not simplify the gate back to a single "no questions fired" signal — that was a real defect that produced one-sentence syntheses on Deep-tier pre-loads.
|
||||
|
||||
Path A maps to the existing "announce-mode" concept on the Phase 0.2 fast path, but only when the substance genuinely warrants 1–3 sentences. Path B is the default for every other interactive invocation.
|
||||
|
||||
### Keep tests per section
|
||||
|
||||
Each conditional section has its own keep test. Sections are render-conditional — an empty section is omitted, not padded with weak items.
|
||||
|
||||
**Trade-offs keep test:** would the user be surprised if I didn't surface this acknowledgment? Real trade-offs are choices the user explicitly weighed alternatives on in dialogue, or structural choices the agent made that the user would expect to see named. Mechanical or inevitable choices (e.g., "uses the existing rule entity") fail the test and dissolve into the doc body without surfacing.
|
||||
|
||||
**Deferred keep test:** is a reasonable downstream reader likely to ask "why isn't X here?" Items the user explicitly deferred, or items adjacent enough that a reader will look for them. Mechanical excludes (e.g., "no rate limiting because it's not in scope") fail and stay in the internal draft only.
|
||||
|
||||
**Call-outs keep test (the affirmability test):** would the user need to read code to evaluate this? If yes, it is doc-body content — cut. If no, apply the keep test — one of the following must be true:
|
||||
|
||||
- **Real scope fork** — another reasonable agent might choose a different scope on this dimension (who the primary actor is, whether case X is in/out, in scope vs deferred)
|
||||
- **Non-obvious scope inclusion** — a behavior the agent assumed is in scope that the user might want excluded
|
||||
- **Non-obvious scope exclusion** — an item the agent moved to deferred that the user might want in scope
|
||||
- **Cheap-now-expensive-later correction** — a scope bet that's cheap to fix now but expensive after the requirements doc lands and ce-plan consumes it
|
||||
- **Non-obvious consequence of multi-turn answers** — a downstream effect of combining user-stated answers that the user is unlikely to have tracked through dialogue. Surfaced forward-looking ("X means Y for the doc"), not retrospectively ("you said X"). This category is the multi-turn-dialogue reason call-outs exist at all in ce-brainstorm; do not filter these as "already implied by Stated"
|
||||
|
||||
Cut anything that doesn't match a keep-test category, including:
|
||||
|
||||
- Mechanical items where there is no real alternative
|
||||
- Implementation choices that will be settled during planning
|
||||
- Items already implied by the scoping synthesis prose
|
||||
- Re-statements of Q&A turns ("you said you wanted X") — that's transcript, not a call-out
|
||||
- Re-statements of the Phase 2 approach the user already picked
|
||||
|
||||
### Total bullet budget across sections 2–4
|
||||
|
||||
The cap is heuristic, not law. The real discipline is each section's keep test on each candidate. Typical bounds by tier, counting bullets across Trade-offs + Deferred + Call outs combined:
|
||||
|
||||
| Tier | Typical total | Hard ceiling |
|
||||
|---|---|---|
|
||||
| Lightweight | 0–1 | 2 |
|
||||
| Standard | 2–4 | 5 |
|
||||
| Deep — feature | 3–5 | 7 |
|
||||
| Deep — product | 4–7 | 9 |
|
||||
|
||||
**Above the hard ceiling, the synthesis is misshapen — do not raise the cap, re-cut at a higher level of abstraction.** Almost always, multiple bullets within a section are sub-decisions of one larger named decision. Collapse related bullets into a single one named at the level the user actually weighs in on.
|
||||
|
||||
A useful test: read the bullets aloud. If two or more sound like "and also" extensions of the same idea, they belong as one.
|
||||
|
||||
**Path A fires only for Lightweight tier with no blocking questions. Path B is the default for Standard, Deep-feature, and Deep-product regardless of question signal — substance earns the checkpoint, not interaction history.** Zero call-outs on Path B is normal for Lightweight, sometimes for Standard, almost never for Deep. If a Deep scoping synthesis produces zero call-outs after rich content (whether from dialogue or pre-loaded context), double-check the agent hasn't filtered consequence-class call-outs as "already implied."
|
||||
|
||||
### Detail level: conversational, not documentary
|
||||
|
||||
Each bullet is **1 line ideally, 2 lines maximum**. The reference shape is what two collaborators would say to each other in conversation, not what a requirements doc would say in its body. The synthesis is a forcing function for shape confirmation; the requirements doc is where the substance lives. If a bullet reads like a doc paragraph, it's wrong-shaped — the agent has compressed horizontally (fewer bullets) without compressing vertically (less per bullet), and the cap is meaningless if individual bullets bloat to fill it.
|
||||
|
||||
Two tests:
|
||||
|
||||
- **Read-aloud test**: would two product collaborators *say* this bullet, or would they *write* it in a spec? Say = right. Write = re-cut to a sentence or cut.
|
||||
- **Single-sentence test**: can the bullet land in one sentence? If it needs semicolons stringing clauses or a list within the bullet, it's probably two decisions sharing a bullet — split (and re-cut for count) or cut to the higher-level one.
|
||||
|
||||
Bad vs good — detail level:
|
||||
|
||||
| Too detailed (wrong) | Conversational (right) |
|
||||
|---|---|
|
||||
| Per-channel mute scoped to notification rules; mute applies to all events through that rule including @mentions, DMs forwarded as notifications, and bot messages; persists 24h with extension | Per-channel over per-user — support team isn't a single user |
|
||||
| Rule-delete loss path is silent and could surprise users who configured extended mutes; consider a confirmation dialog, soft-delete with state preservation, or a 7-day undo window | Rule-delete silently loses pause state — confirm no warning needed |
|
||||
|
||||
The "What we're building" prose obeys the same discipline: 1–3 sentences describing the shape, not an enumeration of requirements. If the prose lists what's in / what's out / what's how, it has become a doc preview — cut to shape only.
|
||||
|
||||
### Anti-patterns
|
||||
|
||||
Each anti-pattern below produces a bullet that fails its section's keep test, or a scoping synthesis that drifts back toward the comprehensive-audit failure mode.
|
||||
|
||||
- **Naming implementation detail in any bullet**: file paths, module names, exact JSON keys, HTTP status codes, error message wording, SQL syntax. The synthesis is scope-only; implementation is ce-plan's job. These granularity rules apply to every bullet in every section.
|
||||
- **Re-stating a Q&A turn verbatim** ("you said you wanted X"): transcript, not scoping synthesis. Reframe forward-looking ("X means Y for the doc") or cut.
|
||||
- **Re-stating the Phase 2 approach the user already picked**: the approach was chosen before Phase 2.5 — its mention belongs in one sentence of "What we're building," not as a call-out.
|
||||
- **Padding a section to meet a bullet count**: render-conditional means empty is allowed. Omit the section entirely rather than fill it with weak items.
|
||||
- **Pasting the three-bucket internal draft verbatim into chat**: that was the old shape and the volume problem it produced is why stage 2 exists. Compose internally, derive scoping synthesis sections, present compressed.
|
||||
- **Floating questions adjacent to stage 2**: if a question genuinely cannot be defaulted, pause synthesis and resolve it before presenting. Pick the question shape that matches: a blocking multiple-choice tool when options are bounded and meaningfully distinct, open-ended when option sets would unintentionally influence the user's answer per Interaction Rule 5(a). Integrate the answer, then present the scoping synthesis. Never present the scoping synthesis with adjacent floating questions — that gives the user no clear resolution path.
|
||||
|
||||
---
|
||||
|
||||
## Prompt templates
|
||||
|
||||
This is directional guidance — adjust phrasing to fit dialogue context. Open-ended feedback per Interaction Rule 5(a) (an option menu would unintentionally influence the user toward the parts the menu lists, away from anything else they might want to change).
|
||||
|
||||
**Prose discipline for "What we're building" (required):** forward-looking (what *will* be in the doc), not retrospective (what's been discussed). Lead with the actual thing being built in plain words. No qualifiers ("comprehensive," "thoughtful," "substantive"). No re-stating dialogue context the user just lived through. If the work can't be said in 1–3 sentences without filler, the synthesis isn't ready yet.
|
||||
|
||||
### Path B template (questions were asked)
|
||||
|
||||
```
|
||||
Based on our dialogue, here's the scope I'm proposing for the requirements doc:
|
||||
|
||||
**What we're building:** [1–3 sentences — the shape that emerged from dialogue, forward-looking, plain words]
|
||||
|
||||
**Key trade-offs:** [render only when real trade-offs exist]
|
||||
- [explicit choice + brief why]
|
||||
- [explicit choice + brief why]
|
||||
|
||||
**What's not in scope:** [render only when deferred items would surprise a reader]
|
||||
- [deferred item]
|
||||
- [deferred item]
|
||||
|
||||
**Call outs:** [render only when one or more survived the keep test]
|
||||
- [scope-level fork or non-obvious consequence the user can affirm or redirect]
|
||||
- [same]
|
||||
|
||||
Confirm and I'll write the requirements doc next, drawing on our dialogue and this synthesis. Or tell me what to change — even something I captured correctly earlier is fair game to revise (you may have changed your mind or want to correct an unstated assumption).
|
||||
```
|
||||
|
||||
### Path A template (no questions were asked — typically Phase 0.2 short-circuit)
|
||||
|
||||
```
|
||||
Proposing: [1–3 line shape — what the doc will say in plain words].
|
||||
|
||||
No open decisions — writing the requirements doc now. Interrupt if the shape is wrong.
|
||||
```
|
||||
|
||||
Proceed to Phase 3 doc-write in the same turn — do NOT end the turn waiting for an acknowledgment. The "interrupt if wrong" affordance means the user can revise after the doc lands, not before. Lightweight Path A docs are short, so post-hoc revision is cheap.
|
||||
|
||||
Ask the user open-ended on Path B (no `AskUserQuestion` menu). The justification is Interaction Rule 5(a) in SKILL.md — an option menu would unintentionally influence the user's feedback toward the parts the menu lists.
|
||||
|
||||
### Worked example: compression from internal draft to scoping synthesis (Standard tier)
|
||||
|
||||
For a notification-mute feature where the internal draft had 5 Stated items, 4 Inferred items, and 3 Out-of-scope items, the compressed Stage 2 looks like:
|
||||
|
||||
```
|
||||
Based on our dialogue, here's the scope I'm proposing for the requirements doc:
|
||||
|
||||
**What we're building:** Per-channel mute on notification rules, with a 24h preset for the support team's 3 AM ping problem. Mute lives on the rule itself and survives rule edits.
|
||||
|
||||
**Key trade-offs:**
|
||||
- Per-channel over per-user — support team isn't a single user
|
||||
- Mute on the rule, not a separate entity — pause state survives edits
|
||||
|
||||
**What's not in scope:**
|
||||
- Presence-based mute and quiet-hours schedules — deferred for later
|
||||
- Cross-rule mute groups — would force a rule-grouping concept we don't have
|
||||
|
||||
**Call outs:**
|
||||
- Rule-delete silently loses pause state — confirm no warning needed
|
||||
|
||||
Confirm and I'll write the requirements doc next, drawing on our dialogue and this synthesis. Or tell me what to change.
|
||||
```
|
||||
|
||||
What got cut from the 12-item internal draft and why:
|
||||
|
||||
- Stated items already covered by the "What we're building" prose dissolved silently
|
||||
- "Use existing rule entity" — mechanical, no real trade-off
|
||||
- "Use Postgres for persistence" — implementation detail (ce-plan's job), failed granularity rules
|
||||
- One Out-of-scope item ("no rate limiting") — mechanical exclude, no reader would ask about it
|
||||
- Three Inferred items rolled into the Trade-offs section as the explicit choices behind them
|
||||
|
||||
What survived: a scoping synthesis with substance proportional to the dialogue, bounded at the Standard ceiling of 5 bullets across the three conditional sections — any more would have triggered a re-cut at higher abstraction.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight re-review
|
||||
|
||||
Before emitting the scoping synthesis, re-read the draft as a user would read it. Two failure modes to catch:
|
||||
|
||||
- **The scoping synthesis reads like a requirements-doc preview.** Prose enumerates what's in/out, bullets are documentary instead of conversational. The synthesis is a shape-confirmation checkpoint, not a doc preview — if it reads as preview, Phase 2.5 and Phase 3 have collapsed into one step. Revise to conversational shape, or accept that the requirements doc itself will contain the detail and the synthesis should be lighter.
|
||||
- **The bullet count fits the cap but each bullet is over-detailed.** Hitting 5 bullets in Standard while each bullet is a paragraph means the agent met the count cap by compressing horizontally (fewer bullets) without compressing vertically (less per bullet). The cap is meaningless if individual bullets bloat to fill it. Re-cut to sentence-level bullets.
|
||||
|
||||
This is one mental act — re-read as the user — not a checklist to mechanically run. The forcing function is putting yourself in the user's reading shoes briefly, with explicit attention to detail level alongside the keep tests. Revise before emitting if either failure mode fires.
|
||||
|
||||
---
|
||||
|
||||
## Re-present after revision; write only on confirm
|
||||
|
||||
A revision is not a confirmation. After any user revision (even a trivially-understood swap like "move deferred item X back into scope"), integrate the change, re-present the revised scoping synthesis with the change reflected, and wait for explicit confirmation before writing the doc. The loop is:
|
||||
|
||||
1. Present scoping synthesis → user responds
|
||||
2. User confirms → write the doc
|
||||
3. User revises → integrate, re-present revised scoping synthesis, return to step 1
|
||||
|
||||
Doc-write fires only on explicit confirm or after the soft-cut blocking question's "proceed" option (see below). The confirmation step is what makes the scoping synthesis **confirmed** rather than "agent's last proposal" — never write immediately after a revision, even when the revision is small enough that the agent feels it understood.
|
||||
|
||||
---
|
||||
|
||||
## Soft-cut on circularity (not iteration count)
|
||||
|
||||
Track which scoping synthesis items the user touched per round. The soft-cut blocking question fires **only when the same item is revised twice** (or a third-round revision targets an item already revised in round two). New-item revisions across rounds proceed without limit — revising different aspects of a wrong scoping synthesis is exactly what the mechanism should support.
|
||||
|
||||
**Identity across rounds is by decision dimension, not surface wording or section.** A revision may cause stage 2 to re-derive — the same underlying decision can come back rephrased, merged with another bullet, or moved to a different section (e.g., what was a Trade-off in round one becomes a Call-out in round two after the user pushed back). "Same item" means the same underlying decision regardless of which section currently holds it. When a re-cut collapses multiple prior bullets into one, the new combined bullet inherits the "touched" status of any of its constituents — soft-cut fires if any underlying decision was already revised once before.
|
||||
|
||||
When the soft-cut fires, use the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi) with two options:
|
||||
|
||||
- `Proceed and write the requirements doc`
|
||||
- `Hold off — keep discussing before the doc`
|
||||
|
||||
Fall back to a numbered list in chat only when no blocking tool exists or the call errors. Never silently skip.
|
||||
|
||||
---
|
||||
|
||||
## Self-redirect
|
||||
|
||||
If the user response indicates they're in the wrong skill or want a different workflow (e.g., "this is too small, just /ce-work it" or "this needs more thought, let me brainstorm differently"):
|
||||
|
||||
- Stop ce-brainstorm
|
||||
- Suggest the alternative skill the user appears to want (e.g., `/ce-work`, `/ce-debug`)
|
||||
- Offer to load it in-session
|
||||
- Do not push back or argue — the user's redirect signal is the deliberate choice
|
||||
|
||||
This support exists because the scoping synthesis is an honest checkpoint. If the user discovers the skill choice was wrong by reading the scoping synthesis, redirecting is the right move.
|
||||
|
||||
---
|
||||
|
||||
## Doc shape after confirmation
|
||||
|
||||
After user confirmation (or after the soft-cut decision proceeds), Phase 3 writes the requirements doc. The internal draft does NOT carry into the doc as a `## Synthesis` section. Only the "What we're building" prose embeds, as `## Summary` at the top. Internal-draft content dissolves into the doc's body sections:
|
||||
|
||||
| Internal-draft element | Where it goes in the doc |
|
||||
|---|---|
|
||||
| "What we're building" prose | `## Summary` (1–3 lines, forward-looking, what's proposed) |
|
||||
| Stated bullets | `## Requirements` (numbered R-IDs, full detail) and where relevant `## Problem Frame` for narrative context |
|
||||
| Inferred bullets | `## Key Decisions` (with rationale) — bets the user accepted in dialogue become decisions in the doc. |
|
||||
| Out-of-scope bullets | `## Scope Boundaries` |
|
||||
|
||||
The chat-time Trade-offs section dissolves into `## Key Decisions` (the explicit choices acknowledged in chat become documented decisions). The chat-time What's-not-in-scope section dissolves into `## Scope Boundaries`.
|
||||
|
||||
No italic capture-context note (e.g., "Captured at Phase 2.5..."). It would leak engineering process into an artifact whose readers do not need that signal.
|
||||
|
||||
The doc's `## Summary` and `## Problem Frame` must serve distinct purposes — see `references/brainstorm-sections.md` "Discipline: Summary vs Problem Frame" for the rules.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Universal Brainstorming Facilitator
|
||||
|
||||
This file is loaded when ce-brainstorm detects a non-software task (Phase 0). It replaces the software-specific brainstorming phases (Phases 0.2 through 4) with facilitation principles for any domain. The Core Principles and **Interaction Rules** in the parent `ce-brainstorm/SKILL.md` still apply unchanged — including one-question-per-turn and the default to the platform's blocking question tool. This file extends those rules with universal-domain facilitation guidance; it does not relax them.
|
||||
|
||||
---
|
||||
|
||||
## Your role
|
||||
|
||||
Be a thinking partner, not an answer machine. The user came here because they're stuck or exploring — they want to think WITH someone, not receive a deliverable. Resist the urge to generate a complete solution immediately. A premature answer anchors the conversation and kills exploration.
|
||||
|
||||
**Match the tone to the stakes.** For personal or life decisions (career changes, housing, relationships, family), lead with values and feelings before frameworks and analysis. Ask what matters to them, not just what the options are. For lighter or creative tasks (podcast topics, event ideas, side projects), energy and enthusiasm are more useful than caution.
|
||||
|
||||
## Asking questions
|
||||
|
||||
"Thinking partner" framing does not mean "conversational prose." The parent skill's Interaction Rules apply in full: one question per turn, and default to the platform's blocking question tool (with its free-text fallback) even for opening and elicitation.
|
||||
|
||||
"What's prompting this?", "what matters most here?", and "what have you ruled out?" feel open-ended and conversational, but that's not a reason to skip the tool. The free-text option preserves flexibility while a well-crafted option set teaches the user the dimensions they might not have separated. Pick-plus-optional-note is lower activation energy than composing prose from scratch — especially for emotional or values-laden topics where prose can feel like an essay prompt.
|
||||
|
||||
Drop the blocking tool only when (a) the answer is inherently narrative ("walk me through how you got here"), (b) the question is diagnostic or introspective and presented options would unintentionally influence the user's answer, or (c) you cannot write 3-4 genuinely distinct, plausibly-correct options that cover the space without padding. If you'd be straining to fill the option slots, the question is open — ask it open-ended (see Interaction Rule 6 in SKILL.md for how to phrase open-ended questions so they earn their place).
|
||||
|
||||
## How to start
|
||||
|
||||
**Assess scope first.** Not every brainstorm needs deep exploration:
|
||||
- **Quick** (user has a clear goal, just needs a sounding board): Confirm understanding, offer a few targeted suggestions or reactions, done in 2-3 exchanges.
|
||||
- **Standard** (some unknowns, needs to explore options): 4-6 exchanges, generate and compare options, help decide.
|
||||
- **Full** (vague goal, lots of uncertainty, or high-stakes decision): Deep exploration, many exchanges, structured convergence.
|
||||
|
||||
**Ask what they're already thinking.** Before offering ideas, find out what the user has considered, tried, or rejected. This prevents fixation on AI-generated ideas and surfaces hidden constraints.
|
||||
|
||||
**When the user represents a group** (couple, family, team) — surface whose preferences are in play and where they diverge. The brainstorm shifts from "help you decide" to "help you find alignment." Ask about each person's priorities, not just the speaker's.
|
||||
|
||||
**Understand before generating.** Spend time on the problem before jumping to solutions. "What would success look like?" and "What have you already ruled out?" reveal more than "Here are 10 ideas."
|
||||
|
||||
## How to explore and generate
|
||||
|
||||
**Use diverse angles to avoid repetitive ideas.** When generating options, vary your approach across exchanges:
|
||||
- Inversion: "What if you did the opposite of the obvious choice?"
|
||||
- Constraints as creative tools: "What if budget/time/distance were no issue?" then "What if you had to do it for free?"
|
||||
- Analogy: "How does someone in a completely different context solve a similar problem?"
|
||||
- What the user hasn't considered: introduce lateral ideas from unexpected directions
|
||||
|
||||
**Separate generation from evaluation.** When exploring options, don't critique them in the same breath. Generate first, evaluate later. Make the transition explicit when it's time to narrow.
|
||||
|
||||
**Offer options to react to when the user is stuck.** People who can't generate from scratch can often evaluate presented options. Use multi-select questions to gather preferences efficiently. Always include a skip option for users who want to move faster.
|
||||
|
||||
**Keep presented options to 3-5 at any decision point.** More causes analysis paralysis.
|
||||
|
||||
## How to converge
|
||||
|
||||
When the conversation has enough material to narrow — reflect back what you've heard. Name the user's priorities as they've emerged through the conversation (what excited them, what they rejected, what they asked about). Propose a frontrunner with reasoning tied to their criteria, and invite pushback. Keep final options to 3-5 max. Don't force a final decision if the user isn't there yet — clarity on direction is a valid outcome.
|
||||
|
||||
## When to wrap up
|
||||
|
||||
**Always synthesize a summary in the chat.** Before offering any next steps, reflect back what emerged: key decisions, the direction chosen, open threads, and any assumptions made. This is the primary output of the brainstorm — the user should be able to read the summary and know what they landed on.
|
||||
|
||||
**Then offer next steps** using the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
**Question:** "Brainstorm wrapped. What would you like to do next?"
|
||||
|
||||
- **Create a plan** → hand off to `/ce-plan` with the decided goal and constraints
|
||||
- **Save summary to disk** → write the summary as a markdown file in the current working directory
|
||||
- **Open in Proof (web app) — review and comment to iterate with the agent** → load the `ce-proof` skill to open the doc in Every's Proof editor, iterate with the agent via comments, or copy a link to share with others
|
||||
- **Done** → the conversation was the value, no artifact needed
|
||||
@@ -0,0 +1,898 @@
|
||||
---
|
||||
name: ce-code-review
|
||||
description: "Structured code review using tiered persona agents, confidence-gated findings, and a merge/dedup pipeline. Use when reviewing code changes before creating a PR."
|
||||
argument-hint: "[blank to review current branch, or provide PR link]"
|
||||
---
|
||||
|
||||
# Code Review
|
||||
|
||||
Reviews code changes using dynamically selected reviewer personas. Spawns parallel sub-agents that return structured JSON, then merges and deduplicates findings into a single report.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Before creating a PR
|
||||
- After completing a task during iterative implementation
|
||||
- When feedback is needed on any code changes
|
||||
- Can be invoked standalone
|
||||
- Can run as a read-only or autofix review step inside larger workflows
|
||||
|
||||
## Argument Parsing
|
||||
|
||||
Parse `$ARGUMENTS` for the following optional tokens. Strip each recognized token before interpreting the remainder as the PR number, GitHub URL, or branch name.
|
||||
|
||||
| Token | Example | Effect |
|
||||
|-------|---------|--------|
|
||||
| `mode:autofix` | `mode:autofix` | Select autofix mode (see Mode Detection below) |
|
||||
| `mode:report-only` | `mode:report-only` | Select report-only mode |
|
||||
| `mode:headless` | `mode:headless` | Select headless mode for programmatic callers (see Mode Detection below) |
|
||||
| `base:<sha-or-ref>` | `base:abc1234` or `base:origin/main` | Skip scope detection — use this as the diff base directly |
|
||||
| `plan:<path>` | `plan:docs/plans/2026-03-25-001-feat-foo-plan.md` | Load this plan for requirements verification |
|
||||
|
||||
All tokens are optional. Each one present means one less thing to infer. When absent, fall back to existing behavior for that stage.
|
||||
|
||||
**Conflicting mode flags:** If multiple mode tokens appear in arguments, stop and do not dispatch agents. If `mode:headless` is one of the conflicting tokens, emit the headless error envelope: `Review failed (headless mode). Reason: conflicting mode flags — <mode_a> and <mode_b> cannot be combined.` Otherwise emit the generic form: `Review failed. Reason: conflicting mode flags — <mode_a> and <mode_b> cannot be combined.`
|
||||
|
||||
## Quick Review Short-Circuit
|
||||
|
||||
If `$ARGUMENTS` indicates the user wants a quick, fast, or light code review, do not dispatch the multi-agent flow.
|
||||
|
||||
**Announce the chosen path** before any other work (Quick review vs Multi-agent review).
|
||||
|
||||
Programmatic callers (when `mode:autofix`, `mode:report-only`, or `mode:headless` is present) skip this announcement -- the orchestrator owns user-facing messaging.
|
||||
|
||||
Sequence:
|
||||
|
||||
1. **Run the harness's built-in code review.** If `$ARGUMENTS` contained a review target (PR number, GitHub URL, or branch name) after stripping recognized tokens, forward that target to the built-in. If no target was provided, run the bare command and let the built-in default to the current branch.
|
||||
- If you are Claude Code, run the `/review` tool, passing the target if present (e.g., `/review 123`, `/review <PR-URL>`, `/review <branch>`); otherwise run bare `/review`.
|
||||
- If you are Gemini, run a quick code review against the resolved target (or the current branch when none was provided).
|
||||
- For all other coding harnesses, run your built-in code review tool, forwarding the target when its syntax accepts one.
|
||||
|
||||
Then stop. Do not dispatch the multi-agent reviewer pipeline.
|
||||
|
||||
2. **Exemption -- no built-in code review exists.** If the current harness has no built-in code review command or skill, do not short-circuit. Continue into the full multi-agent review described in the rest of this skill (Tier 2).
|
||||
|
||||
3. **Programmatic callers bypass this short-circuit.** When `mode:autofix`, `mode:report-only`, or `mode:headless` is present, ignore quick intent and run the full multi-agent review. Skill-to-skill callers that want the lightweight pass should invoke `/review` (or the harness equivalent) directly rather than route through this short-circuit.
|
||||
|
||||
## Mode Detection
|
||||
|
||||
| Mode | When | Behavior |
|
||||
|------|------|----------|
|
||||
| **Interactive** (default) | No mode token present | Review, apply safe_auto fixes automatically, present findings, ask for policy decisions on gated/manual findings, and optionally continue into fix/push/PR next steps |
|
||||
| **Autofix** | `mode:autofix` in arguments | No user interaction. Review, apply only policy-allowed `safe_auto` fixes, re-review in bounded rounds, write a run artifact capturing residual downstream work |
|
||||
| **Report-only** | `mode:report-only` in arguments | Strictly read-only. Review and report only, then stop with no edits, artifacts, commits, pushes, or PR actions |
|
||||
| **Headless** | `mode:headless` in arguments | Programmatic mode for skill-to-skill invocation. Apply `safe_auto` fixes silently (single pass), return all other findings as structured text output, write run artifacts, and return "Review complete" signal. No interactive prompts. |
|
||||
|
||||
### Autofix mode rules
|
||||
|
||||
- **Skip all user questions.** Never pause for approval or clarification once scope has been established.
|
||||
- **Apply only `safe_auto -> review-fixer` findings.** Leave `gated_auto`, `manual`, `human`, and `release` work unresolved.
|
||||
- **Write a run artifact** under `/tmp/compound-engineering/ce-code-review/<run-id>/` summarizing findings, applied fixes, residual actionable work, and advisory outputs. Orchestrators read this artifact to route residual `downstream-resolver` findings; the skill itself does not file tickets or prompt the user in autofix.
|
||||
- **Emit a compact Residual Actionable Work summary in the autofix return** listing each residual `downstream-resolver` finding with its stable `#`, severity, file:line, title, and autofix_class. Structure the summary as two separate contiguous sections: applied `safe_auto` fixes first, then residual non-auto findings. Within the residual section, reuse each finding's stable `#` from Stage 5 -- never renumber. Include the run-artifact path. Callers read this summary directly without parsing the artifact. When no residuals exist, state `Residual actionable work: none.` explicitly.
|
||||
- **Never commit, push, or create a PR** from autofix mode. Parent workflows own those decisions.
|
||||
|
||||
### Report-only mode rules
|
||||
|
||||
- **Skip all user questions.** Infer intent conservatively if the diff metadata is thin.
|
||||
- **Never edit files or externalize work.** Do not write `/tmp/compound-engineering/ce-code-review/<run-id>/`, do not file tickets, and do not commit, push, or create a PR.
|
||||
- **Safe for parallel read-only verification.** `mode:report-only` is the only mode that is safe to run concurrently with browser testing on the same checkout.
|
||||
- **Do not switch the shared checkout.** If the caller passes an explicit PR or branch target, `mode:report-only` must run in an isolated checkout/worktree or stop instead of running `gh pr checkout` / `git checkout`.
|
||||
- **Do not overlap mutating review with browser testing on the same checkout.** If a future orchestrator wants fixes, run the mutating review phase after browser testing or in an isolated checkout/worktree.
|
||||
|
||||
### Headless mode rules
|
||||
|
||||
- **Skip all user questions.** Never use the platform question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)) or other interactive prompts. Infer intent conservatively if the diff metadata is thin.
|
||||
- **Require a determinable diff scope.** If headless mode cannot determine a diff scope (no branch, PR, or `base:` ref determinable without user interaction), emit `Review failed (headless mode). Reason: no diff scope detected. Re-invoke with a branch name, PR number, or base:<ref>.` and stop without dispatching agents.
|
||||
- **Apply only `safe_auto -> review-fixer` findings in a single pass.** No bounded re-review rounds. Leave `gated_auto`, `manual`, `human`, and `release` work unresolved and return them in the structured output.
|
||||
- **Return all non-auto findings as structured text output.** Use the headless output envelope format (see Stage 6 below) preserving severity, autofix_class, owner, requires_verification, confidence, pre_existing, and suggested_fix per finding. Enrich with detail-tier fields (why_it_matters, evidence[]) from the per-agent artifact files on disk (see Detail enrichment in Stage 6).
|
||||
- **Write a run artifact** under `/tmp/compound-engineering/ce-code-review/<run-id>/` summarizing findings, applied fixes, and advisory outputs. Include the artifact path in the structured output.
|
||||
- **Do not file tickets or externalize work.** The caller receives structured findings and routes downstream work itself.
|
||||
- **Do not switch the shared checkout.** If the caller passes an explicit PR or branch target, `mode:headless` must run in an isolated checkout/worktree or stop instead of running `gh pr checkout` / `git checkout`. When stopping, emit `Review failed (headless mode). Reason: cannot switch shared checkout. Re-invoke with base:<ref> to review the current checkout, or run from an isolated worktree.`
|
||||
- **Not safe for concurrent use on a shared checkout.** Unlike `mode:report-only`, headless mutates files (applies `safe_auto` fixes). Callers must not run headless concurrently with other mutating operations on the same checkout.
|
||||
- **Never commit, push, or create a PR** from headless mode. The caller owns those decisions.
|
||||
- **End with "Review complete" as the terminal signal** so callers can detect completion. If all reviewers fail or time out, emit `Code review degraded (headless mode). Reason: 0 of N reviewers returned results.` followed by "Review complete".
|
||||
|
||||
### Interactive mode rules
|
||||
|
||||
- **Pre-load the platform question tool before any question fires.** In Claude Code, `AskUserQuestion` is a deferred tool — its schema is not available at session start. At the start of Interactive-mode work (before Stage 2 intent-ambiguity questions, the After-Review routing question, walk-through per-finding questions, bulk-preview Proceed/Cancel, and tracker-defer failure sub-questions), call `ToolSearch` with query `select:AskUserQuestion` to load the schema. Load it **once, eagerly, at the top of the Interactive flow** — do not wait for the first question site and do not decide it on a per-site basis. On Codex, Gemini, and Pi this preload step does not apply.
|
||||
- **The numbered-list fallback only applies when the harness genuinely lacks a blocking question tool** — `ToolSearch` returns no match, the tool call explicitly fails, or the runtime mode does not expose it (e.g., Codex edit modes where `request_user_input` is unavailable). A pending schema load is not a fallback trigger; call `ToolSearch` first per the pre-load rule. Rendering a question as narrative text because the tool feels inconvenient, because the model is in report-formatting mode, or because the instruction was buried in a long skill is a bug. A question that calls for a user decision must either fire the tool or fall back loudly.
|
||||
|
||||
## Severity Scale
|
||||
|
||||
All reviewers use P0-P3:
|
||||
|
||||
| Level | Meaning | Action |
|
||||
|-------|---------|--------|
|
||||
| **P0** | Critical breakage, exploitable vulnerability, data loss/corruption | Must fix before merge |
|
||||
| **P1** | High-impact defect likely hit in normal usage, breaking contract | Should fix |
|
||||
| **P2** | Moderate issue with meaningful downside (edge case, perf regression, maintainability trap) | Fix if straightforward |
|
||||
| **P3** | Low-impact, narrow scope, minor improvement | User's discretion |
|
||||
|
||||
## Action Routing
|
||||
|
||||
Severity answers **urgency**. Routing answers **who acts next** and **whether this skill may mutate the checkout**.
|
||||
|
||||
| `autofix_class` | Default owner | Meaning |
|
||||
|-----------------|---------------|---------|
|
||||
| `safe_auto` | `review-fixer` | Local, deterministic fix suitable for the in-skill fixer when the current mode allows mutation |
|
||||
| `gated_auto` | `downstream-resolver` or `human` | Concrete fix exists, but it changes behavior, contracts, permissions, or another sensitive boundary that should not be auto-applied by default |
|
||||
| `manual` | `downstream-resolver` or `human` | Actionable work that should be handed off rather than fixed in-skill |
|
||||
| `advisory` | `human` or `release` | Report-only output such as learnings, rollout notes, or residual risk |
|
||||
|
||||
Routing rules:
|
||||
|
||||
- **Synthesis owns the final route.** Persona-provided routing metadata is input, not the last word.
|
||||
- **Choose the more conservative route on disagreement.** A merged finding may move from `safe_auto` to `gated_auto` or `manual`, but never the other way without stronger evidence.
|
||||
- **Only `safe_auto -> review-fixer` enters the in-skill fixer queue automatically.**
|
||||
- **`requires_verification: true` means a fix is not complete without targeted tests, a focused re-review, or operational validation.**
|
||||
|
||||
## Reviewers
|
||||
|
||||
14 reviewer personas in layered conditionals, plus CE-specific agents. See the persona catalog included below for the full catalog.
|
||||
|
||||
**Always-on (every review):**
|
||||
|
||||
| Agent | Focus |
|
||||
|-------|-------|
|
||||
| `ce-correctness-reviewer` | Logic errors, edge cases, state bugs, error propagation |
|
||||
| `ce-testing-reviewer` | Coverage gaps, weak assertions, brittle tests |
|
||||
| `ce-maintainability-reviewer` | Structural quality, complexity deletion, 1k-line regressions, coupling, type-boundary leaks, dead code, abstraction debt |
|
||||
| `ce-project-standards-reviewer` | CLAUDE.md and AGENTS.md compliance -- frontmatter, references, naming, portability |
|
||||
| `ce-agent-native-reviewer` | Verify new features are agent-accessible |
|
||||
| `ce-learnings-researcher` | Search docs/solutions/ for past issues related to this PR |
|
||||
|
||||
**Cross-cutting conditional (selected per diff):**
|
||||
|
||||
| Agent | Select when diff touches... |
|
||||
|-------|---------------------------|
|
||||
| `ce-security-reviewer` | Auth, public endpoints, user input, permissions |
|
||||
| `ce-performance-reviewer` | DB queries, data transforms, caching, async |
|
||||
| `ce-api-contract-reviewer` | Routes, serializers, type signatures, versioning |
|
||||
| `ce-data-migration-reviewer` | Migration files, schema dumps (`db/schema.rb`, `structure.sql`), backfills, data-transform scripts — **not** model/query-only changes without migration artifacts |
|
||||
| `ce-reliability-reviewer` | Error handling, retries, timeouts, background jobs |
|
||||
| `ce-adversarial-reviewer` | Diff >=50 changed non-test/non-generated/non-lockfile lines, or auth, payments, data mutations, external APIs |
|
||||
| `ce-previous-comments-reviewer` | Reviewing a PR that has existing review comments or threads |
|
||||
|
||||
**Stack-specific conditional (selected per diff):**
|
||||
|
||||
| Agent | Select when diff touches... |
|
||||
|-------|---------------------------|
|
||||
| `ce-julik-frontend-races-reviewer` | Stimulus/Turbo controllers, DOM events, timers, animations, or async UI flows |
|
||||
| `ce-swift-ios-reviewer` | Swift files, SwiftUI views, UIKit controllers, entitlements, privacy manifests, Core Data models, SPM manifests, storyboards/XIBs, or semantic build-setting/target/signing changes in .pbxproj |
|
||||
|
||||
**CE conditional (migration-specific):**
|
||||
|
||||
| Agent | Select when diff includes migration files |
|
||||
|-------|------------------------------------------|
|
||||
| `ce-deployment-verification-agent` | Produces deployment checklist with SQL verification queries and rollback procedures |
|
||||
|
||||
Schema drift detection is folded into `ce-data-migration-reviewer` (Step 0) and surfaces as P1 findings — not a separate agent or report section.
|
||||
|
||||
## Review Scope
|
||||
|
||||
Every review spawns all 4 always-on personas plus the 2 CE always-on agents, then adds whichever cross-cutting and stack-specific conditionals fit the diff. The model naturally right-sizes: a small config change triggers 0 conditionals = 6 reviewers. A Rails auth feature might trigger security + reliability + adversarial = 9 reviewers.
|
||||
|
||||
## Protected Artifacts
|
||||
|
||||
The following paths are compound-engineering pipeline artifacts and must never be flagged for deletion, removal, or gitignore by any reviewer:
|
||||
|
||||
- `docs/brainstorms/*` -- requirements documents created by ce-brainstorm
|
||||
- `docs/plans/*.md` -- plan files created by ce-plan (decision artifacts; execution progress is derived from git, not stored in plan bodies)
|
||||
- `docs/solutions/*.md` -- solution documents created during the pipeline
|
||||
|
||||
If a reviewer flags any file in these directories for cleanup or removal, discard that finding during synthesis.
|
||||
|
||||
## How to Run
|
||||
|
||||
### Stage 1: Determine scope
|
||||
|
||||
Compute the diff range, file list, and diff. Minimize permission prompts by combining into as few commands as possible.
|
||||
|
||||
**If `base:` argument is provided (fast path):**
|
||||
|
||||
The caller already knows the diff base. Skip all base-branch detection, remote resolution, and merge-base computation. Use the provided value directly:
|
||||
|
||||
```
|
||||
BASE_ARG="{base_arg}"
|
||||
BASE=$(git merge-base HEAD "$BASE_ARG" 2>/dev/null) || BASE="$BASE_ARG"
|
||||
```
|
||||
|
||||
Then produce the same output as the other paths:
|
||||
|
||||
```
|
||||
echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard
|
||||
```
|
||||
|
||||
This path works with any ref — a SHA, `origin/main`, a branch name. Automated callers (ce-work, lfg, slfg) should prefer this to avoid the detection overhead. **Do not combine `base:` with a PR number or branch target.** If both are present, stop with an error: "Cannot use `base:` with a PR number or branch target — `base:` implies the current checkout is already the correct branch. Pass `base:` alone, or pass the target alone and let scope detection resolve the base." This avoids scope/intent mismatches where the diff base comes from one source but the code and metadata come from another.
|
||||
|
||||
**If a PR number or GitHub URL is provided as an argument:**
|
||||
|
||||
If `mode:report-only` or `mode:headless` is active, do **not** run `gh pr checkout <number-or-url>` on the shared checkout. For `mode:report-only`, tell the caller: "mode:report-only cannot switch the shared checkout to review a PR target. Run it from an isolated worktree/checkout for that PR, or run report-only with no target argument on the already checked out branch." For `mode:headless`, emit `Review failed (headless mode). Reason: cannot switch shared checkout. Re-invoke with base:<ref> to review the current checkout, or run from an isolated worktree.` Stop here unless the review is already running in an isolated checkout.
|
||||
|
||||
**Skip-condition pre-check.** Before checkout or scope detection, run a PR-state probe to decide whether the review should proceed:
|
||||
|
||||
```
|
||||
gh pr view <number-or-url> --json state,title,body,files
|
||||
```
|
||||
|
||||
Apply skip rules in order:
|
||||
|
||||
- `state` is `CLOSED` or `MERGED` -> stop with message `PR is closed/merged; not reviewing.`
|
||||
- **Trivial-PR judgment**: spawn a lightweight sub-agent (use `model: haiku` in Claude Code; gpt-5.4-nano or equivalent in Codex) with the PR title, body, and changed file paths. The agent's task: "Is this an automated or trivial PR that does not warrant a code review? Consider: dependency lock-file or manifest-only bumps, automated release commits, chore version increments with no substantive code changes. When in doubt, answer no — false negatives (skipped reviews that should have run) are more costly than false positives (unnecessary reviews)." If the judgment returns yes: stop with message `PR appears to be a trivial automated PR; not reviewing. Run without a PR argument to review the current branch, or pass base:<ref> if review is intended.`
|
||||
|
||||
When any skip rule fires, emit the message and stop without dispatching reviewers, switching the checkout, or running scope detection. **Standalone branch mode and `base:` mode are unaffected** -- they always run the full review. **Draft PRs are reviewed normally** -- draft status is not a skip condition; early feedback on in-progress work is valuable.
|
||||
|
||||
If no skip rule fires, proceed to the checkout logic below.
|
||||
|
||||
First, verify the worktree is clean before switching branches:
|
||||
|
||||
```
|
||||
git status --porcelain
|
||||
```
|
||||
|
||||
If the output is non-empty, inform the user: "You have uncommitted changes on the current branch. Stash or commit them before reviewing a PR, or use standalone mode (no argument) to review the current branch as-is." Do not proceed with checkout until the worktree is clean.
|
||||
|
||||
Then check out the PR branch so persona agents can read the actual code (not the current checkout):
|
||||
|
||||
```
|
||||
gh pr checkout <number-or-url>
|
||||
```
|
||||
|
||||
Then fetch PR metadata. Capture the base branch name and the PR base repository identity, not just the branch name. Project `reviews` and `comments` to a `hasPriorComments` boolean via `--jq` -- counting only, not materializing review or comment bodies into the orchestrator's context. The reviews filter excludes approval-state submissions with empty bodies (approvals are not feedback to verify), so PRs with only approval clicks correctly fall through the gate. Stage 3 uses `hasPriorComments` to decide whether to spawn `previous-comments`:
|
||||
|
||||
```
|
||||
gh pr view <number-or-url> --json title,body,baseRefName,headRefName,url,reviews,comments --jq '{title, body, baseRefName, headRefName, url, hasPriorComments: ((.reviews | map(select(.state != "APPROVED" or .body != "")) | length) > 0 or (.comments | length) > 0)}'
|
||||
```
|
||||
|
||||
Use the repository portion of the returned PR URL as `<base-repo>` (for example, `EveryInc/compound-engineering-plugin` from `https://github.com/EveryInc/compound-engineering-plugin/pull/348`).
|
||||
|
||||
Then compute a local diff against the PR's base branch so re-reviews also include local fix commits and uncommitted edits. Substitute the PR base branch from metadata (shown here as `<base>`) and the PR base repository identity derived from the PR URL (shown here as `<base-repo>`). Resolve the base ref from the PR's actual base repository, not by assuming `origin` points at that repo:
|
||||
|
||||
```
|
||||
PR_BASE_REMOTE=$(git remote -v | awk 'index($2, "github.com:<base-repo>") || index($2, "github.com/<base-repo>") {print $1; exit}')
|
||||
if [ -n "$PR_BASE_REMOTE" ]; then PR_BASE_REMOTE_REF="$PR_BASE_REMOTE/<base>"; else PR_BASE_REMOTE_REF=""; fi
|
||||
PR_BASE_REF=$(git rev-parse --verify "$PR_BASE_REMOTE_REF" 2>/dev/null || git rev-parse --verify <base> 2>/dev/null || true)
|
||||
if [ -z "$PR_BASE_REF" ]; then
|
||||
if [ -n "$PR_BASE_REMOTE_REF" ]; then
|
||||
git fetch --no-tags "$PR_BASE_REMOTE" <base>:refs/remotes/"$PR_BASE_REMOTE"/<base> 2>/dev/null || git fetch --no-tags "$PR_BASE_REMOTE" <base> 2>/dev/null || true
|
||||
PR_BASE_REF=$(git rev-parse --verify "$PR_BASE_REMOTE_REF" 2>/dev/null || git rev-parse --verify <base> 2>/dev/null || true)
|
||||
else
|
||||
if git fetch --no-tags https://github.com/<base-repo>.git <base> 2>/dev/null; then
|
||||
PR_BASE_REF=$(git rev-parse --verify FETCH_HEAD 2>/dev/null || true)
|
||||
fi
|
||||
if [ -z "$PR_BASE_REF" ]; then PR_BASE_REF=$(git rev-parse --verify <base> 2>/dev/null || true); fi
|
||||
fi
|
||||
fi
|
||||
if [ -n "$PR_BASE_REF" ]; then BASE=$(git merge-base HEAD "$PR_BASE_REF" 2>/dev/null) || BASE=""; else BASE=""; fi
|
||||
```
|
||||
|
||||
```
|
||||
if [ -n "$BASE" ]; then echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard; else echo "ERROR: Unable to resolve PR base branch <base> locally. Fetch the base branch and rerun so the review scope stays aligned with the PR."; fi
|
||||
```
|
||||
|
||||
Extract PR title/body, base branch, and PR URL from `gh pr view`, then extract the base marker, file list, diff content, and `UNTRACKED:` list from the local command. Do not use `gh pr diff` as the review scope after checkout -- it only reflects the remote PR state and will miss local fix commits until they are pushed. If the base ref still cannot be resolved from the PR's actual base repository after the fetch attempt, stop instead of falling back to `git diff HEAD`; a PR review without the PR base branch is incomplete.
|
||||
|
||||
**If a branch name is provided as an argument:**
|
||||
|
||||
Check out the named branch, then diff it against the base branch. Substitute the provided branch name (shown here as `<branch>`).
|
||||
|
||||
If `mode:report-only` or `mode:headless` is active, do **not** run `git checkout <branch>` on the shared checkout. For `mode:report-only`, tell the caller: "mode:report-only cannot switch the shared checkout to review another branch. Run it from an isolated worktree/checkout for `<branch>`, or run report-only on the current checkout with no target argument." For `mode:headless`, emit `Review failed (headless mode). Reason: cannot switch shared checkout. Re-invoke with base:<ref> to review the current checkout, or run from an isolated worktree.` Stop here unless the review is already running in an isolated checkout.
|
||||
|
||||
First, verify the worktree is clean before switching branches:
|
||||
|
||||
```
|
||||
git status --porcelain
|
||||
```
|
||||
|
||||
If the output is non-empty, inform the user: "You have uncommitted changes on the current branch. Stash or commit them before reviewing another branch, or provide a PR number instead." Do not proceed with checkout until the worktree is clean.
|
||||
|
||||
```
|
||||
git checkout <branch>
|
||||
```
|
||||
|
||||
Then detect the review base branch and compute the merge-base.
|
||||
|
||||
**If a PR exists for `<branch>`** (check with `gh pr view <branch> --json baseRefName,url`): reuse PR mode's `PR_BASE_REMOTE` block above. Use `baseRefName` as `<base>` and derive `<base-repo>` from the PR URL (e.g., `EveryInc/foo` from `https://github.com/EveryInc/foo/pull/123`). The block already sets `$BASE` to the merge-base SHA — `origin` may point at the user's fork, which is why naive `origin/<base>` is unsafe and the fork-safe block is required.
|
||||
|
||||
**If no PR exists**: derive the default branch. Primary source is `git symbolic-ref --quiet --short refs/remotes/origin/HEAD | sed 's#^origin/##'`; fall back to `gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`, then to the first of `main`/`master`/`develop`/`trunk` that exists as `origin/<name>` or bare `<name>` locally. Compute `BASE=$(git merge-base HEAD <base-ref>)`, where `<base-ref>` is `origin/<base-branch>` when available, otherwise the bare local `<base-branch>` (covers single-branch clones, missing origin remote, and unfetched defaults). If `BASE` is empty and the clone is shallow (`git rev-parse --is-shallow-repository`), run `git fetch --unshallow origin` and retry.
|
||||
|
||||
If no base can be resolved, **stop**. Do not fall back to `git diff HEAD` — a branch review without the base would only show uncommitted changes and silently miss all committed work.
|
||||
|
||||
On success, produce the diff:
|
||||
|
||||
```
|
||||
echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard
|
||||
```
|
||||
|
||||
You may still fetch additional PR metadata with `gh pr view` for title, body, linked issues, and a projected `hasPriorComments` boolean (use the same `--jq` shape from PR mode above so the gate ignores approval-only reviews and stays consistent across modes). Do not fail if no PR exists -- leave `hasPriorComments=false`.
|
||||
|
||||
**If no argument (standalone on current branch):**
|
||||
|
||||
Apply the same base-detection logic as branch mode above, using the current branch (i.e., `gh pr view --json baseRefName,url` with no argument defaults to the current branch).
|
||||
|
||||
If no base can be resolved, **stop**. Do not fall back to `git diff HEAD` — a standalone review without the base would only show uncommitted changes and silently miss all committed work on the branch.
|
||||
|
||||
On success, produce the diff:
|
||||
|
||||
```
|
||||
echo "BASE:$BASE" && echo "FILES:" && git diff --name-only $BASE && echo "DIFF:" && git diff -U10 $BASE && echo "UNTRACKED:" && git ls-files --others --exclude-standard
|
||||
```
|
||||
|
||||
Using `git diff $BASE` (without `..HEAD`) diffs the merge-base against the working tree, which includes committed, staged, and unstaged changes together.
|
||||
|
||||
**Untracked file handling:** Always inspect the `UNTRACKED:` list, even when `FILES:`/`DIFF:` are non-empty. Untracked files are outside review scope until staged. If the list is non-empty, tell the user which files are excluded. If any of them should be reviewed, stop and tell the user to `git add` them first and rerun. Only continue when the user is intentionally reviewing tracked changes only. In `mode:headless` or `mode:autofix`, do not stop to ask — proceed with tracked changes only and note the excluded untracked files in the Coverage section of the output.
|
||||
|
||||
### Stage 2: Intent discovery
|
||||
|
||||
Understand what the change is trying to accomplish. The source of intent depends on which Stage 1 path was taken:
|
||||
|
||||
**PR/URL mode:** Use the PR title, body, and linked issues from `gh pr view` metadata. Supplement with commit messages from the PR if the body is sparse.
|
||||
|
||||
**Branch mode:** Run `git log --oneline ${BASE}..<branch>` using the resolved merge-base from Stage 1.
|
||||
|
||||
**Standalone (current branch):** Run:
|
||||
|
||||
```
|
||||
echo "BRANCH:" && git rev-parse --abbrev-ref HEAD && echo "COMMITS:" && git log --oneline ${BASE}..HEAD
|
||||
```
|
||||
|
||||
Combined with conversation context (plan section summary, PR description), write a 2-3 line intent summary:
|
||||
|
||||
```
|
||||
Intent: Simplify tax calculation by replacing the multi-tier rate lookup
|
||||
with a flat-rate computation. Must not regress edge cases in tax-exempt handling.
|
||||
```
|
||||
|
||||
Pass this to every reviewer in their spawn prompt. Intent shapes *how hard each reviewer looks*, not which reviewers are selected.
|
||||
|
||||
**When intent is ambiguous:**
|
||||
|
||||
- **Interactive mode:** Ask one question using the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)): "What is the primary goal of these changes?" Do not spawn reviewers until intent is established. **Claude Code only:** if `AskUserQuestion` has not yet been loaded this session (per the Interactive mode rules pre-load), call `ToolSearch` with query `select:AskUserQuestion` first before asking. Fall back to numbered options in chat only when the harness genuinely lacks a blocking tool or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
- **Autofix/report-only/headless modes:** Infer intent conservatively from the branch name, diff, PR metadata, and caller context. Note the uncertainty in Coverage or Verdict reasoning instead of blocking.
|
||||
|
||||
### Stage 2b: Plan discovery (requirements verification)
|
||||
|
||||
Locate the plan document so Stage 6 can verify requirements completeness. Check these sources in priority order — stop at the first hit:
|
||||
|
||||
1. **`plan:` argument.** If the caller passed a plan path, use it directly. Read the file to confirm it exists.
|
||||
2. **PR body.** If PR metadata was fetched in Stage 1, scan the body for paths matching `docs/plans/*.md`. If exactly one match is found and the file exists, use it as `plan_source: explicit`. If multiple plan paths appear, treat as ambiguous — demote to `plan_source: inferred` for the most recent match that exists on disk, or skip if none exist or none clearly relate to the PR title/intent. Always verify the selected file exists before using it — stale or copied plan links in PR descriptions are common.
|
||||
3. **Auto-discover.** Extract 2-3 keywords from the branch name (e.g., `feat/onboarding-skill` -> `onboarding`, `skill`). Glob `docs/plans/*` and filter filenames containing those keywords. If exactly one match, use it. If multiple matches or the match looks ambiguous (e.g., generic keywords like `review`, `fix`, `update` that could hit many plans), **skip auto-discovery** — a wrong plan is worse than no plan. If zero matches, skip.
|
||||
|
||||
**Confidence tagging:** Record how the plan was found:
|
||||
- `plan:` argument -> `plan_source: explicit` (high confidence)
|
||||
- Single unambiguous PR body match -> `plan_source: explicit` (high confidence)
|
||||
- Multiple/ambiguous PR body matches -> `plan_source: inferred` (lower confidence)
|
||||
- Auto-discover with single unambiguous match -> `plan_source: inferred` (lower confidence)
|
||||
|
||||
If a plan is found, read its **Requirements** section — `## Requirements` in current plans, `## Requirements Trace` in legacy ones — and the R-IDs (R1, R2, etc.) listed there, plus **Implementation Units** (current numeric subsections such as `### U1.`, `### U2.`, or `### Unit 1:` under `## Implementation Units`; legacy bullet or checkbox unit entries under that section also count). Store the extracted requirements list and `plan_source` for Stage 6. Do not block the review if no plan is found — requirements verification is additive, not required.
|
||||
|
||||
### Stage 3: Select reviewers
|
||||
|
||||
Read the diff and file list from Stage 1. The 4 always-on personas and 2 CE always-on agents are automatic. For each cross-cutting and stack-specific conditional persona in the persona catalog included below, decide whether the diff warrants it. This is agent judgment, not keyword matching.
|
||||
|
||||
**File-type awareness for conditional selection:** Instruction-prose files (Markdown skill definitions, JSON schemas, config files) are product code but do not benefit from runtime-focused reviewers. The adversarial reviewer's techniques (race conditions, cascade failures, abuse cases) target executable code behavior. For diffs that only change instruction-prose files, skip adversarial unless the prose describes auth, payment, or data-mutation behavior. Count only executable code lines toward line-count thresholds.
|
||||
|
||||
**`previous-comments` is PR-only AND comment-gated.** Only select this persona when both conditions hold:
|
||||
|
||||
1. Stage 1 gathered PR metadata (PR number or URL was provided as an argument, or `gh pr view` returned metadata for the current branch).
|
||||
2. `hasPriorComments` from Stage 1 is true (the PR has at least one review submission or issue comment).
|
||||
|
||||
Skip it for standalone branch reviews with no associated PR, and skip it for PRs with no prior feedback yet -- there is nothing for the persona to verify, and a spawned subagent that returns empty findings still costs the full subagent startup overhead (persona spec, diff, schema, plus its own gh calls).
|
||||
|
||||
Stack-specific personas are additive when runtime behavior warrants them. A Hotwire UI change may warrant `julik-frontend-races`; a TypeScript API diff may warrant `api-contract` and `reliability`. Structural and maintainability concerns are handled by the always-on `maintainability` persona — do not spawn extra reviewers for convention or philosophy passes.
|
||||
|
||||
**`data-migration` spawn gate.** Select `ce-data-migration-reviewer` only when the diff includes at least one migration or schema artifact: `db/migrate/*`, `db/schema.rb`, `db/structure.sql`, Alembic/Flyway/Liquibase migration paths, or explicit backfill/data-transform scripts (rake tasks, one-off data migration classes). **Do not spawn** for model-only changes, query-only refactors, serializers/controllers that reference columns without a migration or schema dump in the diff, or migration tests alone.
|
||||
|
||||
For `ce-deployment-verification-agent`, use the same migration-artifact gate when the change is risky (destructive DDL, backfills, NOT NULL without default, column renames/drops).
|
||||
|
||||
Announce the team before spawning:
|
||||
|
||||
```
|
||||
Review team:
|
||||
- correctness (always)
|
||||
- testing (always)
|
||||
- maintainability (always)
|
||||
- project-standards (always)
|
||||
- ce-agent-native-reviewer (always)
|
||||
- ce-learnings-researcher (always)
|
||||
- security -- new endpoint in routes.rb accepts user-provided redirect URL
|
||||
- julik-frontend-races -- Stimulus controller with async DOM updates
|
||||
- data-migration -- adds migration 20260303_add_index_to_orders
|
||||
- ce-deployment-verification-agent -- destructive migration with backfill
|
||||
```
|
||||
|
||||
This is progress reporting, not a blocking confirmation.
|
||||
|
||||
### Stage 3b: Discover project standards paths
|
||||
|
||||
Before spawning sub-agents, find the file paths (not contents) of all relevant standards files for the `project-standards` persona. Use the native file-search/glob tool to locate:
|
||||
|
||||
1. Use the native file-search tool (e.g., Glob in Claude Code) to find all `**/CLAUDE.md` and `**/AGENTS.md` in the repo.
|
||||
2. Filter to those whose directory is an ancestor of at least one changed file. A standards file governs all files below it (e.g., `plugins/compound-engineering/AGENTS.md` applies to everything under `plugins/compound-engineering/`).
|
||||
|
||||
Pass the resulting path list to the `project-standards` persona inside a `<standards-paths>` block in its review context (see Stage 4). The persona reads the files itself, targeting only the sections relevant to the changed file types. This keeps the orchestrator's work cheap (path discovery only) and avoids bloating the subagent prompt with content the reviewer may not fully need.
|
||||
|
||||
### Stage 4: Spawn sub-agents
|
||||
|
||||
#### Model tiering
|
||||
|
||||
Three reviewers inherit the session model with no override: `ce-correctness-reviewer`, `ce-security-reviewer`, and `ce-adversarial-reviewer`. These perform the highest-stakes analysis — logic bugs, security vulnerabilities, adversarial failure scenarios — and should run at whatever capability level the user has configured. If the user is on Opus, these get Opus.
|
||||
|
||||
All other persona sub-agents and CE agents use the platform's mid-tier model to reduce cost and latency. See the Spawning subsection below for the exact dispatch-time override — the imperative lives there so it lands at the point of action when spawning many agents in parallel.
|
||||
|
||||
The orchestrator (this skill) also inherits the session model; it handles intent discovery, reviewer selection, finding merge/dedup, and synthesis -- tasks that benefit from the same reasoning capability the user configured.
|
||||
|
||||
#### Run ID
|
||||
|
||||
Generate a unique run identifier before dispatching any agents. This ID scopes all agent artifact files and the post-review run artifact to the same directory.
|
||||
|
||||
```bash
|
||||
RUN_ID=$(date +%Y%m%d-%H%M%S)-$(head -c4 /dev/urandom | od -An -tx1 | tr -d ' ')
|
||||
mkdir -p "/tmp/compound-engineering/ce-code-review/$RUN_ID"
|
||||
```
|
||||
|
||||
Pass `{run_id}` to every persona sub-agent so they can write their full analysis to `/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json`.
|
||||
|
||||
**Report-only mode:** Skip run-id generation and directory creation. Do not pass `{run_id}` to agents. Agents return compact JSON only with no file write, consistent with report-only's no-write contract.
|
||||
|
||||
#### Spawning
|
||||
|
||||
Omit the `mode` parameter when dispatching sub-agents so the user's configured permission settings apply. Do not pass `mode: "auto"`.
|
||||
|
||||
**Model override at dispatch time.** Pass the platform's mid-tier model on every dispatch except `ce-correctness-reviewer`, `ce-security-reviewer`, and `ce-adversarial-reviewer`, which inherit the session model (per the Model tiering subsection above). In Claude Code, add `model: "sonnet"` to the `Agent` tool call. In Codex, pass the equivalent mid-tier on `spawn_agent` (e.g., `gpt-5.4-mini` as of April 2026). In Pi, pass the equivalent on `subagent` via the `pi-subagents` extension. On platforms where the dispatch primitive has no model-override parameter or the available model names are unknown, omit the override — a working review on the parent model beats a broken dispatch on an unrecognized name. Check this on every Agent / `spawn_agent` / `subagent` call in the parallel dispatch; omitting it on Opus sessions silently 3-4x's the cost of a review.
|
||||
|
||||
**Bounded parallel dispatch.** Respect the current harness's active-subagent limit. Queue selected reviewers, dispatch only as many as the harness accepts, and fill freed slots as reviewers complete. Treat active-agent/thread/concurrency-limit spawn errors as backpressure, not reviewer failure: leave the reviewer queued and retry after a slot frees. Record a reviewer as failed only after a successful dispatch times out/fails, or when dispatch fails for a non-capacity reason.
|
||||
|
||||
Spawn each selected persona reviewer using the subagent template included below. Each persona sub-agent receives:
|
||||
|
||||
1. Their persona file content (identity, failure modes, calibration, suppress conditions)
|
||||
2. Shared diff-scope rules from the diff-scope reference included below
|
||||
3. The JSON output contract from the findings schema included below
|
||||
4. PR metadata: title, body, and URL when reviewing a PR (empty string otherwise). Passed in a `<pr-context>` block so reviewers can verify code against stated intent
|
||||
5. Review context: intent summary, file list, diff
|
||||
6. Run ID and reviewer name for the artifact file path
|
||||
7. **For `project-standards` only:** the standards file path list from Stage 3b, wrapped in a `<standards-paths>` block appended to the review context
|
||||
8. **For `data-migration` only:** the resolved review base ref from Stage 1 (`BASE:` marker), wrapped in `<review-base>` inside the review context so schema drift checks never assume `main`
|
||||
|
||||
Persona sub-agents are **read-only** with respect to the project: they review and return structured JSON. They do not edit project files or propose refactors. The one permitted write is saving their full analysis to the run-artifact path specified in the output contract (under `/tmp/compound-engineering/ce-code-review/<run-id>/`).
|
||||
|
||||
Read-only here means **non-mutating**, not "no shell access." Reviewer sub-agents may use non-mutating inspection commands when needed to gather evidence or verify scope, including read-oriented `git` / `gh` usage such as `git diff`, `git show`, `git blame`, `git log`, and `gh pr view`. They must not edit project files, change branches, commit, push, create PRs, or otherwise mutate the checkout or repository state.
|
||||
|
||||
Each persona sub-agent writes full JSON (all schema fields) to `/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json` and returns compact JSON with merge-tier fields only:
|
||||
|
||||
```json
|
||||
{
|
||||
"reviewer": "security",
|
||||
"findings": [
|
||||
{
|
||||
"title": "User-supplied ID in account lookup without ownership check",
|
||||
"severity": "P0",
|
||||
"file": "orders_controller.rb",
|
||||
"line": 42,
|
||||
"confidence": 100,
|
||||
"autofix_class": "gated_auto",
|
||||
"owner": "downstream-resolver",
|
||||
"requires_verification": true,
|
||||
"pre_existing": false,
|
||||
"suggested_fix": "Add current_user.owns?(account) guard before lookup"
|
||||
}
|
||||
],
|
||||
"residual_risks": [...],
|
||||
"testing_gaps": [...]
|
||||
}
|
||||
```
|
||||
|
||||
Detail-tier fields (`why_it_matters`, `evidence`) are in the artifact file only. `suggested_fix` is optional in both tiers -- included in compact returns when present so the orchestrator has fix context for auto-apply decisions. If the file write fails, the compact return still provides everything the merge needs.
|
||||
|
||||
**CE always-on agents** (ce-agent-native-reviewer, ce-learnings-researcher) are dispatched as standard Agent calls through the same bounded parallel scheduler as the persona agents. Give them the same review context bundle the personas receive: entry mode, any PR metadata gathered in Stage 1, intent summary, review base branch name when known, `BASE:` marker, file list, diff, and `UNTRACKED:` scope notes. Do not invoke them with a generic "review this" prompt. Their output is unstructured and synthesized separately in Stage 6.
|
||||
|
||||
**CE conditional agents** (`ce-deployment-verification-agent` only) are dispatched as standard Agent calls through the same bounded parallel scheduler when the migration-artifact gate applies. Pass the same review context bundle plus the applicability reason (for example, which migration files triggered the agent). Their output is unstructured and must be preserved for Stage 6 synthesis just like the CE always-on agents. Schema drift is handled by the `data-migration` persona as structured findings — not here.
|
||||
|
||||
### Stage 5: Merge findings
|
||||
|
||||
Convert multiple reviewer compact JSON returns into one deduplicated, confidence-gated finding set. The compact returns contain merge-tier fields (title, severity, file, line, confidence, autofix_class, owner, requires_verification, pre_existing) plus the optional suggested_fix. Detail-tier fields (why_it_matters, evidence) are on disk in the per-agent artifact files and are not loaded at this stage.
|
||||
|
||||
`confidence` is one of 5 discrete anchors (`0`, `25`, `50`, `75`, `100`) with behavioral definitions in the findings schema. Synthesis treats anchors as integers; do not coerce to floats.
|
||||
|
||||
1. **Validate.** Check each compact return for required top-level and per-finding fields, plus value constraints. Drop malformed returns or findings. Record the drop count.
|
||||
- **Top-level required:** reviewer (string), findings (array), residual_risks (array), testing_gaps (array). Drop the entire return if any are missing or wrong type.
|
||||
- **Per-finding required:** title, severity, file, line, confidence, autofix_class, owner, requires_verification, pre_existing
|
||||
- **Value constraints:**
|
||||
- severity: P0 | P1 | P2 | P3
|
||||
- autofix_class: safe_auto | gated_auto | manual | advisory
|
||||
- owner: review-fixer | downstream-resolver | human | release
|
||||
- confidence: integer in {0, 25, 50, 75, 100}
|
||||
- line: positive integer
|
||||
- pre_existing, requires_verification: boolean
|
||||
- Do not validate against the full schema here -- the full schema (including why_it_matters and evidence) applies to the artifact files on disk, not the compact returns.
|
||||
2. **Deduplicate.** Compute fingerprint: `normalize(file) + line_bucket(line, +/-3) + normalize(title)`. When fingerprints match, merge: keep highest severity, keep highest anchor, note which reviewers flagged it. Dedup runs over the full validated set (including anchor 50) so cross-reviewer promotion in step 3 can lift matching anchor-50 findings into the actionable tier.
|
||||
3. **Cross-reviewer agreement.** When 2+ independent reviewers flag the same issue (same fingerprint), promote the merged finding by one anchor step: `50 -> 75`, `75 -> 100`, `100 -> 100`. Cross-reviewer corroboration is a stronger signal than any single reviewer's anchor; the promotion routes a previously-soft finding into the actionable tier or strengthens its already-actionable position. Note the agreement in the Reviewer column of the output (e.g., "security, correctness").
|
||||
4. **Separate pre-existing.** Pull out findings with `pre_existing: true` into a separate list.
|
||||
5. **Resolve disagreements.** When reviewers flag the same code region but disagree on severity, autofix_class, or owner, annotate the Reviewer column with the disagreement (e.g., "security (P0), correctness (P1) -- kept P0"). This transparency helps the user understand why a finding was routed the way it was.
|
||||
6. **Normalize routing.** For each merged finding, set the final `autofix_class`, `owner`, and `requires_verification`. If reviewers disagree, keep the most conservative route. Synthesis may narrow a finding from `safe_auto` to `gated_auto` or `manual`, but must not widen it without new evidence.
|
||||
6b. **Derive the recommended action.** Interactive mode's walk-through and best-judgment paths present a per-finding recommended action (Apply / Defer / Skip / Acknowledge). The recommendation is derived from the normalized `autofix_class` and the presence of `suggested_fix` using this mapping:
|
||||
|
||||
| `autofix_class` | `suggested_fix` present? | Recommended action |
|
||||
|-----------------|--------------------------|--------------------|
|
||||
| `safe_auto` | (auto-applied before the routing question; not surfaced to best-judgment/walk-through) | Apply |
|
||||
| `gated_auto` | yes | Apply |
|
||||
| `gated_auto` | no | Defer |
|
||||
| `manual` | **yes** | **Apply** |
|
||||
| `manual` | no | Defer |
|
||||
| `advisory` | n/a | Acknowledge |
|
||||
|
||||
The presence of `suggested_fix` is the authoritative signal that the agent can act on the finding. A `manual` finding *with* a `suggested_fix` recommends Apply because the persona has committed to a concrete fix shape grounded in review context (per the subagent template's suggested_fix rule). A `manual` finding *without* a `suggested_fix` recommends Defer because the persona signaled that the fix genuinely needs cross-team input or business-rule context the reviewer cannot provide. `autofix_class` itself is not collapsed by this mapping — the report still records what the persona thought (`manual` vs `gated_auto`), and the distinction matters for downstream surfaces like the unified completion report.
|
||||
|
||||
**Cross-reviewer tie-break.** When contributing reviewers implied different actions for the same merged finding, synthesis picks the most conservative using the order `Skip > Defer > Apply > Acknowledge`. This rule fires only on multi-reviewer disagreement; the per-finding mapping above is the single-reviewer default. Tie-break guarantees that identical review artifacts produce the same recommendation deterministically, so best-judgment results are auditable after the fact and the walk-through's recommendation is stable across re-runs. The user may still override per finding via the walk-through's options; this rule only determines what gets labeled "recommended."
|
||||
6c. **Mode-aware demotion of weak general-quality findings.** Some persona output is real signal but does not warrant primary-findings attention. Reroute it to the existing soft buckets so the primary findings table stays focused on actionable issues.
|
||||
|
||||
A finding qualifies for demotion when **all** of these hold:
|
||||
- Severity is P2 or P3 (P0 and P1 always stay in primary findings)
|
||||
- `autofix_class` is `advisory` (concrete-fix findings stay in primary)
|
||||
- **All** contributing reviewers are `testing` or `maintainability` — if any other persona also flagged this finding, cross-reviewer corroboration is present and the finding stays in primary findings regardless of its severity or advisory status (expand the weak-signal list later only with evidence)
|
||||
|
||||
When a finding qualifies, route by mode:
|
||||
- **Interactive and report-only modes:** Move the finding out of the primary findings set. If the contributing reviewer is `testing`, append `<file:line> -- <title>` to `testing_gaps`. If `maintainability`, append the same to `residual_risks`. Record the demotion count for Coverage. The finding does not appear in the Stage 6 findings table. (Use title only -- the compact return omits `why_it_matters`, and report-only mode skips artifact files entirely. Soft-bucket entries are FYI items; readers who want depth can open the per-agent artifact when one exists.)
|
||||
- **Headless and autofix modes:** Suppress the finding entirely. Record the suppressed count in Coverage as "mode-aware demotion suppressions" so the user can see what was filtered.
|
||||
|
||||
Demotion is intentionally narrow. The conservative scope (testing/maintainability + P2/P3 + advisory) is the starting point; do not expand the rule by guessing which other personas overproduce noise. If real review runs show another persona consistently emitting weak signal, expand with evidence.
|
||||
|
||||
7. **Confidence gate.** After dedup, promotion, and demotion have shaped the primary set, suppress remaining findings below anchor 75. Exception: P0 findings at anchor 50+ survive the gate -- critical-but-uncertain issues must not be silently dropped. Record the suppressed count by anchor (so Coverage can report "N findings suppressed at anchor 50, M at anchor 25"). The gate runs late deliberately: anchor-50 findings need a chance to be promoted by step 3 (cross-reviewer corroboration) or rerouted by step 6c (mode-aware demotion to soft buckets) before any drop decision.
|
||||
8. **Partition the work.** Build three sets:
|
||||
- in-skill fixer queue: only `safe_auto -> review-fixer`
|
||||
- residual actionable queue: unresolved `gated_auto` or `manual` findings whose owner is `downstream-resolver`
|
||||
- report-only queue: `advisory` findings plus anything owned by `human` or `release`
|
||||
9. **Sort and number.** Order by severity (P0 first) -> anchor (descending) -> file path -> line number, then assign monotonically increasing `#` values across the full primary finding set in that sorted order. Do not restart numbering inside each severity table or autofix/routing bucket. If later sections repeat a finding (for example Residual Actionable Work after `safe_auto` fixes are applied), reuse the same stable `#` so users -- and downstream skills like `ce-resolve-pr-feedback` -- can reference findings by `#` after the autofix loop rewrites the report. Renumbering after autofix invalidates any prior reference: copied snippets, follow-up prompts citing `#3`, or tickets filed against an earlier render.
|
||||
10. **Collect coverage data.** Union residual_risks and testing_gaps across reviewers.
|
||||
11. **Preserve CE agent artifacts.** Keep the learnings, agent-native, and deployment-verification outputs alongside the merged finding set. Do not drop unstructured agent output just because it does not match the persona JSON schema. Schema drift from `data-migration` is already in the merged finding set.
|
||||
|
||||
### Stage 5b: Validation pass (externalizing modes only)
|
||||
|
||||
Independent verification gate. Spawn one validator sub-agent per surviving finding using `references/validator-template.md`. The validator's job is to re-check the finding against the diff and surrounding code with no commitment to the original persona's analysis. Findings the validator rejects are dropped; findings the validator confirms flow through unchanged.
|
||||
|
||||
**When this stage runs:**
|
||||
|
||||
| Mode | Runs Stage 5b? | Where |
|
||||
|------|---------------|-------|
|
||||
| `headless` | Yes, eagerly | Between Stage 5 and Stage 6 |
|
||||
| `autofix` | Yes, eagerly | Between Stage 5 and Stage 6 |
|
||||
| `interactive`, walk-through routing (option A) — per-finding phase | No -- the user is the per-finding validator | n/a |
|
||||
| `interactive`, walk-through routing (option A) — best-judgment-the-rest handoff | No -- the best-judgment path dispatches the fixer immediately; the fixer's apply/fail outcome is the validation | n/a |
|
||||
| `interactive`, best-judgment routing (option B) | No -- the best-judgment path dispatches the fixer immediately; the fixer's apply/fail outcome is the validation | n/a |
|
||||
| `interactive`, File-tickets routing (option C) | Yes, on all pending findings | Before tracker dispatch |
|
||||
| `interactive`, Report-only routing (option D) | No -- nothing is being externalized | n/a |
|
||||
| `report-only` | No -- read-only mode externalizes nothing | n/a |
|
||||
|
||||
The best-judgment path skips Stage 5b deliberately. Running per-finding validators before the fixer dispatches is duplicate research — the fixer naturally re-checks each finding when applying or proposing the fix, and items where the cited evidence no longer matches the code (the false-positive case Stage 5b would catch) are routed to the `failed` bucket during the fix attempt itself. The user reviews via diff and the post-run failure-handling question (see Step 2 Interactive option B), not via a pre-dispatch validator gate.
|
||||
|
||||
When Stage 5b does not run, the merged finding set from Stage 5 flows through to Stage 6 unchanged. When it runs, the steps below execute on the relevant set.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Select findings to validate.**
|
||||
- **headless/autofix:** All survivors of Stage 5.
|
||||
- **interactive File-tickets (option C):** All pending findings regardless of recommended action. Option C externalizes every finding as a ticket, so every finding needs validation.
|
||||
2. **Apply dispatch budget cap.** If the selected set exceeds 15 findings, validate the highest-severity 15 (P0 first, then P1, then P2, then P3, breaking ties by anchor descending). Drop the remainder and record the over-budget count for the Coverage section. The blunt drop is intentional; a review producing 15+ surviving findings is already in territory where a second wave would not change the user's triage approach.
|
||||
3. **Spawn validators with bounded parallelism.** One sub-agent per finding, dispatched independently using the validator template and the same bounded scheduler from Stage 4. Each validator receives:
|
||||
- The finding's title, severity, file, line, suggested_fix, original reviewer name, and confidence anchor
|
||||
- `why_it_matters` when available — loaded from the per-agent artifact file at `/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json`; omit when the file is absent or the artifact write failed. The validator proceeds without it, using the diff and cited code directly.
|
||||
- The full diff
|
||||
- Read-tool access to inspect the cited code, callers, guards, framework defaults, and git blame
|
||||
4. **Collect verdicts.** Each validator returns `{ "validated": true | false, "reason": "<one sentence>" }`.
|
||||
- `validated: true` -> finding survives unchanged into the next phase (Stage 6 for headless/autofix, dispatch for interactive)
|
||||
- `validated: false` -> finding is dropped; record the validator's reason in Coverage
|
||||
- Validator failure (timeout, dispatch error, malformed JSON) -> drop the finding with reason "validator failed"; conservative bias is correct
|
||||
5. **Use mid-tier model for validators.** Same model class (sonnet) the persona reviewers use. Validators are read-only — same constraints as persona reviewers. They may use non-mutating inspection commands (Read, Grep, Glob, git blame, gh).
|
||||
6. **Record metrics for Coverage.** Total dispatched, validated true count, validated false count (with reasons), failures, and over-budget drops.
|
||||
|
||||
**Why per-finding bounded dispatch (not batched):** Independence is the point. A single batched validator looking at all findings together pattern-matches across them and recreates the persona-bias problem. Per-finding dispatch preserves fresh context while the scheduler respects harness limits. Per-file batching is a plausible future optimization for reviews with many findings clustered in few files; not implemented today.
|
||||
|
||||
### Stage 6: Synthesize and present
|
||||
|
||||
Assemble the final report using **pipe-delimited markdown tables for findings** from the review output template included below. The table format is mandatory for finding rows in interactive mode — do not render findings as freeform text blocks or horizontal-rule-separated prose. Other report sections (Applied Fixes, Learnings, Coverage, etc.) use bullet lists and the `---` separator before the verdict, as shown in the template.
|
||||
|
||||
1. **Header.** Scope, intent, mode, reviewer team with per-conditional justifications.
|
||||
2. **Findings.** Rendered as pipe-delimited tables grouped by severity (`### P0 -- Critical`, `### P1 -- High`, `### P2 -- Moderate`, `### P3 -- Low`). Each finding row shows `#`, file, issue, reviewer(s), confidence, and synthesized route. Omit empty severity levels. Never render findings as freeform text blocks or numbered lists. Finding numbers come from the stable assignment in Stage 5 -- never re-derive them per severity table.
|
||||
3. **Requirements Completeness.** Include only when a plan was found in Stage 2b. For each requirement (R1, R2, etc.) and implementation unit in the plan, report whether corresponding work appears in the diff. Use a simple checklist: met / not addressed / partially addressed. Routing depends on `plan_source`:
|
||||
- **`explicit`** (caller-provided or PR body): Flag unaddressed requirements or implementation units as P1 findings with `autofix_class: manual`, `owner: downstream-resolver`. These enter the residual actionable queue.
|
||||
- **`inferred`** (auto-discovered): Flag unaddressed requirements or implementation units as P3 findings with `autofix_class: advisory`, `owner: human`. These stay in the report only — no autonomous follow-up. An inferred plan match is a hint, not a contract.
|
||||
Omit this section entirely when no plan was found — do not mention the absence of a plan.
|
||||
4. **Applied Fixes.** Include only if a fix phase ran in this invocation.
|
||||
5. **Residual Actionable Work.** Include when unresolved actionable findings were handed off or should be handed off.
|
||||
6. **Pre-existing.** Separate section, does not count toward verdict.
|
||||
7. **Learnings & Past Solutions.** Surface ce-learnings-researcher results: if past solutions are relevant, flag them as "Known Pattern" with links to docs/solutions/ files.
|
||||
8. **Agent-Native Gaps.** Surface ce-agent-native-reviewer results. Omit section if no gaps found.
|
||||
9. **Deployment Notes.** If ce-deployment-verification-agent ran, surface the key Go/No-Go items: blocking pre-deploy checks, the most important verification queries, rollback caveats, and monitoring focus areas. Keep the checklist actionable rather than dropping it into Coverage. Schema drift appears in the findings tables as `data-migration` P1 rows — do not add a separate Schema Drift section.
|
||||
10. **Coverage.** Suppressed count by anchor (e.g., "N findings suppressed at anchor 50, M at anchor 25"), mode-aware demotion count (interactive/report-only) or suppression count (headless/autofix), validator drop count and reasons (when Stage 5b ran), validator over-budget drops (when the 15-cap fired), residual risks, testing gaps, failed/timed-out reviewers, and any intent uncertainty carried by non-interactive modes.
|
||||
11. **Verdict.** Ready to merge / Ready with fixes / Not ready. Fix order if applicable. When an `explicit` plan has unaddressed requirements or implementation units, the verdict must reflect it — a PR that's code-clean but missing planned requirements is "Not ready" unless the omission is intentional. When an `inferred` plan has unaddressed requirements or implementation units, note it in the verdict reasoning but do not block on it alone.
|
||||
|
||||
Do not include time estimates.
|
||||
|
||||
**Format verification:** Before delivering the report, verify the findings sections use pipe-delimited table rows (`| # | File | Issue | ... |`) not freeform text. If you catch yourself rendering findings as prose blocks separated by horizontal rules or bullet points, stop and reformat into tables.
|
||||
|
||||
### Headless output format
|
||||
|
||||
In `mode:headless`, replace the interactive pipe-delimited table report with a structured text envelope. The envelope follows the same structural pattern as document-review's headless output (completion header, metadata block, findings grouped by autofix_class, trailing sections) while using ce-code-review's own section headings and per-finding fields.
|
||||
|
||||
```
|
||||
Code review complete (headless mode).
|
||||
|
||||
Scope: <scope-line>
|
||||
Intent: <intent-summary>
|
||||
Reviewers: <reviewer-list with conditional justifications>
|
||||
Verdict: <Ready to merge | Ready with fixes | Not ready>
|
||||
Artifact: /tmp/compound-engineering/ce-code-review/<run-id>/
|
||||
|
||||
Applied N safe_auto fixes.
|
||||
|
||||
Gated-auto findings (concrete fix, changes behavior/contracts):
|
||||
|
||||
[P1][gated_auto -> downstream-resolver][needs-verification] File: <file:line> -- <title> (<reviewer>, confidence <N>)
|
||||
Why: <why_it_matters>
|
||||
Suggested fix: <suggested_fix or "none">
|
||||
Evidence: <evidence[0]>
|
||||
Evidence: <evidence[1]>
|
||||
|
||||
Manual findings (actionable, needs handoff):
|
||||
|
||||
[P1][manual -> downstream-resolver] File: <file:line> -- <title> (<reviewer>, confidence <N>)
|
||||
Why: <why_it_matters>
|
||||
Evidence: <evidence[0]>
|
||||
|
||||
Advisory findings (report-only):
|
||||
|
||||
[P2][advisory -> human] File: <file:line> -- <title> (<reviewer>, confidence <N>)
|
||||
Why: <why_it_matters>
|
||||
|
||||
Pre-existing issues:
|
||||
[P2][gated_auto -> downstream-resolver] File: <file:line> -- <title> (<reviewer>, confidence <N>)
|
||||
Why: <why_it_matters>
|
||||
|
||||
Residual risks:
|
||||
- <risk>
|
||||
|
||||
Learnings & Past Solutions:
|
||||
- <learning>
|
||||
|
||||
Agent-Native Gaps:
|
||||
- <gap description>
|
||||
|
||||
Deployment Notes:
|
||||
- <deployment note>
|
||||
|
||||
Testing gaps:
|
||||
- <gap>
|
||||
|
||||
Coverage:
|
||||
- Suppressed: <N> findings below anchor 75 (P0 at anchor 50+ retained)
|
||||
- Mode-aware demotion suppressions: <N> findings suppressed (testing/maintainability advisory P2-P3)
|
||||
- Validator drops: <N> findings rejected by Stage 5b validator
|
||||
- <file:line> -- <reason>
|
||||
- Validator over-budget drops: <N> findings exceeded the 15-cap and were not validated
|
||||
- Untracked files excluded: <file1>, <file2>
|
||||
- Failed reviewers: <reviewer>
|
||||
|
||||
Review complete
|
||||
```
|
||||
|
||||
**Detail enrichment (headless only):** The headless envelope includes `Why:`, `Evidence:`, and `Suggested fix:` lines. After merge (Stage 5), read the per-agent artifact files from `/tmp/compound-engineering/ce-code-review/{run_id}/` for only the findings that survived dedup and confidence gating.
|
||||
- **Field tiers:** `Why:` and `Evidence:` are detail-tier -- load from per-agent artifact files. `Suggested fix:` is merge-tier -- use it directly from the compact return without artifact lookup.
|
||||
- **Artifact matching:** For each surviving finding, look up its detail-tier fields in the artifact files of the contributing reviewers. Match on `file + line_bucket(line, +/-3)` (the same tolerance used in Stage 5 dedup) within each contributing reviewer's artifact. When multiple artifact entries fall within the line bucket, apply `normalize(title)` to both the merged finding's title and each candidate entry's title as a tie-breaker.
|
||||
- **Reviewer order:** Try contributing reviewers in the order they appear in the merged finding's reviewer list; use the first match.
|
||||
- **No-match fallback:** If no artifact file contains a match (all writes failed, or the finding was synthesized during merge), omit the `Why:` and `Evidence:` lines for that finding and note the gap in Coverage. The `Suggested fix:` line can still be populated from the compact return since it is merge-tier.
|
||||
|
||||
**Formatting rules:**
|
||||
- The `[needs-verification]` marker appears only on findings where `requires_verification: true`.
|
||||
- The `Artifact:` line gives callers the path to the full run artifact for machine-readable access to the complete findings schema. The text envelope is the primary handoff; the artifact is for debugging and full-fidelity access.
|
||||
- Findings with `owner: release` appear in the Advisory section (they are operational/rollout items, not code fixes).
|
||||
- Findings with `pre_existing: true` appear in the Pre-existing section regardless of autofix_class.
|
||||
- The Verdict appears in the metadata header (deliberately reordered from the interactive format where it appears at the bottom) so programmatic callers get the verdict first.
|
||||
- Omit any section with zero items.
|
||||
- If all reviewers fail or time out, emit `Code review degraded (headless mode). Reason: 0 of N reviewers returned results.` followed by "Review complete".
|
||||
- End with "Review complete" as the terminal signal so callers can detect completion.
|
||||
|
||||
## Quality Gates
|
||||
|
||||
Before delivering the review, verify:
|
||||
|
||||
1. **Every finding is actionable.** Re-read each finding. If it says "consider", "might want to", or "could be improved" without a concrete fix, rewrite it with a specific action. Vague findings waste engineering time.
|
||||
2. **No false positives from skimming.** For each finding, verify the surrounding code was actually read. Check that the "bug" isn't handled elsewhere in the same function, that the "unused import" isn't used in a type annotation, that the "missing null check" isn't guarded by the caller.
|
||||
3. **Severity is calibrated.** A style nit is never P0. A SQL injection is never P3. Re-check every severity assignment.
|
||||
4. **Line numbers are accurate.** Verify each cited line number against the file content. A finding pointing to the wrong line is worse than no finding.
|
||||
5. **Protected artifacts are respected.** Discard any findings that recommend deleting or gitignoring files in `docs/brainstorms/`, `docs/plans/`, or `docs/solutions/`.
|
||||
6. **Findings don't duplicate linter output.** Don't flag things the project's linter/formatter would catch (missing semicolons, wrong indentation). Focus on semantic issues.
|
||||
|
||||
## Language-Aware Conditionals
|
||||
|
||||
This skill uses stack-specific reviewer agents when the diff touches runtime behavior those stacks specialize in (async UI races, iOS/Swift lifecycle). Structural quality — complexity deletion, 1k-line regressions, spaghetti growth, type-boundary leaks — lives in the always-on `ce-maintainability-reviewer`. Do not spawn extra reviewers for language conventions, philosophy, or "strict bar" passes; that signal is folded into maintainability.
|
||||
|
||||
Do not spawn stack reviewers mechanically from file extensions alone. The trigger is meaningful changed behavior in that stack's runtime domain.
|
||||
|
||||
## After Review
|
||||
|
||||
### Mode-Driven Post-Review Flow
|
||||
|
||||
After presenting findings and verdict (Stage 6), route the next steps by mode. Review and synthesis stay the same in every mode; only mutation and handoff behavior changes.
|
||||
|
||||
#### Step 1: Build the action sets
|
||||
|
||||
- **Clean review** means zero findings after suppression and pre-existing separation. Skip the fix/handoff phase when the review is clean.
|
||||
- **Fixer queue:** final findings routed to `safe_auto -> review-fixer`.
|
||||
- **Residual actionable queue:** unresolved `gated_auto` or `manual` findings whose final owner is `downstream-resolver`.
|
||||
- **Report-only queue:** `advisory` findings and any outputs owned by `human` or `release`.
|
||||
- **Never convert advisory-only outputs into fix work or ticket handoff.** Deployment notes, residual risks, and release-owned items stay in the report.
|
||||
|
||||
#### Step 2: Choose policy by mode
|
||||
|
||||
**Interactive mode**
|
||||
|
||||
- Apply `safe_auto -> review-fixer` findings automatically without asking. These are safe by definition.
|
||||
- **Zero-remaining case:** if no `gated_auto` or `manual` findings remain after the `safe_auto` pass, skip the routing question entirely. Emit a one-line completion summary phrased so advisory and pre-existing findings (which are not handled by this flow) are not implied to be cleared. When no advisory or pre-existing findings remain in the report, `All findings resolved — N safe_auto fixes applied.` is accurate. When advisory and/or pre-existing findings do remain, use the qualified form `All actionable findings resolved — N safe_auto fixes applied. (K advisory, J pre-existing findings remain in the report.)`, omitting any zero-count clause. Follow the summary with the existing end-of-review verdict, then proceed to Step 5 per the gating rule there.
|
||||
- **Tracker pre-detection:** before rendering the routing question, consult `references/tracker-defer.md` for the session's tracker tuple `{ tracker_name, confidence, named_sink_available, any_sink_available }`. The probe runs at most once per session and is cached for the rest of the run. `named_sink_available` drives the option C label (inline tracker name only when the named sink can actually be invoked). `any_sink_available` drives whether option C is offered at all (it can still be offered when the named tracker is unreachable but GitHub Issues via `gh` works).
|
||||
- **Verify question-tool pre-load (checklist, Claude Code only).** Before firing the routing question in Claude Code, confirm `AskUserQuestion` is loaded (per Interactive mode rules at the top of this skill). If not yet loaded this session, call `ToolSearch` with query `select:AskUserQuestion` now. Do not proceed to the routing question without this verification. Rendering the question as narrative text because the schema isn't loaded yet is a bug, not a valid fallback. On Codex, Gemini, and Pi this checklist does not apply — there is no `ToolSearch` preload step to perform. (If `request_user_input` is unavailable in the current Codex runtime mode, use the numbered-list fallback described below.)
|
||||
- **Routing question.** Ask using the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)). Stem: `What should the agent do with the remaining N findings?` — use third-person voice referring to "the agent", not first-person "me" / "I". Options:
|
||||
|
||||
```
|
||||
(A) Review each finding one by one — accept the recommendation or choose another action
|
||||
(B) Auto-resolve with best judgment — apply per-finding fixes the agent can defend, surface the rest
|
||||
(C) File a [TRACKER] ticket per finding without applying fixes
|
||||
(D) Report only — take no further action
|
||||
```
|
||||
|
||||
Render option C per `references/tracker-defer.md`: when `confidence = high` AND `named_sink_available = true`, replace `[TRACKER]` with the concrete name and keep the full label (e.g., `File a Linear ticket per finding without applying fixes`). When `any_sink_available = true` but either `confidence = low` or `named_sink_available = false` (GitHub Issues via `gh` is working as the fallback), use the generic label `File an issue per finding without applying fixes` — this is a whole-label substitution, not a `[TRACKER]` token swap. When `any_sink_available = false`, **omit option C entirely** and add one line to the stem explaining that no issue tracker is configured for this checkout (Linear, GitHub Issues, etc., were probed and unavailable). Phrase it for a developer audience — avoid `tracker sink` jargon, and avoid `platform` since the missing piece is per-project, not per-agent-platform. The three remaining options (A, B, D) survive.
|
||||
|
||||
The numbered-list text fallback applies when `ToolSearch` explicitly returns no match for the platform's question tool or the tool call errors (including Codex runtime modes where `request_user_input` is unavailable). It does not apply when the agent simply hasn't loaded the tool yet — in that case, load it now (see the verification checklist above). When the fallback applies, present the options as a numbered list and wait for the user's reply — never silently skip the question.
|
||||
|
||||
- **Dispatch on selection.** Route by the option letter (A / B / C / D), not by the rendered label string. The option-C label varies by tracker-detection confidence (`File a [TRACKER] ticket per finding without applying fixes` for a named tracker, `File an issue per finding without applying fixes` as the generic fallback, or omitted entirely when no sink is available — see `references/tracker-defer.md`), and options A / B / D have a single canonical label each. The letter is the stable dispatch signal; the canonical labels below are shown for documentation only. A low-confidence run that rendered option C as the generic label routes to the same branch as a high-confidence run that rendered it with the named tracker.
|
||||
- (A) `Review each finding one by one` — **before presenting the first finding, read `references/walkthrough.md` in full.** It is the canonical spec for the per-finding presentation format and the option menu. Do not improvise from memory; do not paraphrase the format; do not invent custom option variants. Then enter the per-finding walk-through loop. Decision handling:
|
||||
- When the user picks `Apply`, queue the fix for end-of-loop dispatch — do not apply it immediately.
|
||||
- When the user picks `Defer`, file the ticket inline via `references/tracker-defer.md`.
|
||||
- When the user picks `Skip` or `Acknowledge`, record the decision as no-action.
|
||||
- When the user picks the option to auto-resolve the rest, exit the loop and dispatch **one** fixer pass on the union of (queued Apply set ∪ remaining undecided findings) — there is no second end-of-loop dispatch in this branch, so the "one fixer, consistent tree" contract holds.
|
||||
|
||||
When the user works through every finding without invoking the auto-resolve-the-rest option, dispatch one fixer subagent for the queued Apply set at end of loop (Step 3). Emit the unified completion report after dispatch.
|
||||
- (B) `Auto-resolve with best judgment — apply per-finding fixes the agent can defend, surface the rest` — dispatch the fixer subagent (Step 3) immediately on the full pending action set (`gated_auto` + `manual` + `advisory`). No Stage 5b validator pre-pass. No bulk-preview approval gate. The fixer applies items with concrete `suggested_fix`, no-ops on advisory items, and routes items where the fix cannot be applied cleanly (or where the cited evidence no longer matches the code) to a `failed` bucket with a one-line reason.
|
||||
|
||||
**After the fixer returns, the order is:**
|
||||
1. **If `failed` is empty:** emit the unified completion report and proceed to Step 5 per its gating rule. No question fires.
|
||||
2. **If `failed` is non-empty:** fire the post-run failure-handling question *first* — emitting the report before the user resolves the failed bucket would produce a stale or duplicated report, since `File tickets` and `Walk through` both change the final action state. Stem: `N findings could not be auto-resolved. What should the agent do with them?` Three options:
|
||||
- `File tickets for these` — route the failed set through `references/tracker-defer.md` Interactive mode. Omit this option when the cached tracker-detection tuple reports `any_sink_available = false`, and append one line to the stem explaining that no issue tracker is configured for this checkout (Linear, GitHub Issues, etc., were probed and unavailable). Phrase it for a developer audience — avoid `tracker sink` jargon, and avoid `platform` since the missing piece is per-project, not per-agent-platform.
|
||||
- `Walk through these one at a time` — re-enter the walk-through loop scoped to the failed set. Each finding's recommended action is recomputed via the Stage 5 step 6b mapping: items that have a `suggested_fix` recommend Apply (and join the in-memory Apply set if the user picks Apply, dispatching at end-of-walk-through to a focused fixer pass on those items only); items without a `suggested_fix` recommend Defer (Apply is not offered for them; menu is Defer / Skip / `Auto-resolve with best judgment on the rest`).
|
||||
- `Ignore — leave them in the report` — record the failed list as residual actionable work in the report. No further action.
|
||||
|
||||
After the user's choice executes (tickets filed, walk-through completed, or ignore recorded), emit the unified completion report. The report reflects the final state including any tickets filed or additional fixes applied during walk-through re-entry.
|
||||
|
||||
Numbered-list fallback applies when `ToolSearch` explicitly returns no match or the tool call errors (Codex edit modes without `request_user_input`) — never silently skip the question.
|
||||
|
||||
- (C) `File a [TRACKER] ticket per finding without applying fixes` (or the generic `File an issue per finding without applying fixes` when the named-tracker label is not used) — first run Stage 5b validation on every pending finding. Drop validator-rejected findings with their reasons recorded in Coverage. Then load `references/bulk-preview.md` with every surviving finding in the file-tickets bucket. On `Proceed`, route every finding through `references/tracker-defer.md`; no fixes are applied. On `Cancel`, return to this routing question. Emit the unified completion report.
|
||||
- (D) `Report only — take no further action` — do not enter any dispatch phase. Emit the completion report, then proceed to Step 5 per its gating rule (`fixes_applied_count > 0` from earlier `safe_auto` passes). If no fixes were applied this run, stop after the report.
|
||||
|
||||
- The walk-through's completion report, the best-judgment / File-tickets completion report, and the zero-remaining completion summary all follow the unified completion-report structure documented in `references/walkthrough.md`. Use the same structure across every terminal path.
|
||||
|
||||
**Autofix mode**
|
||||
|
||||
- Ask no questions.
|
||||
- Apply only the `safe_auto -> review-fixer` queue.
|
||||
- Leave `gated_auto`, `manual`, `human`, and `release` items unresolved.
|
||||
- Prepare residual work only for unresolved actionable findings whose final owner is `downstream-resolver`.
|
||||
|
||||
**Report-only mode**
|
||||
|
||||
- Ask no questions.
|
||||
- Do not build a fixer queue.
|
||||
- Do not write run artifacts.
|
||||
- Stop after Stage 6. Everything remains in the report.
|
||||
|
||||
**Headless mode**
|
||||
|
||||
- Ask no questions.
|
||||
- Apply only the `safe_auto -> review-fixer` queue in a single pass. Do not enter the bounded re-review loop (Step 3). Spawn one fixer subagent, apply fixes, then proceed directly to Step 4.
|
||||
- Leave `gated_auto`, `manual`, `human`, and `release` items unresolved — they appear in the structured text output.
|
||||
- Output the headless output envelope (see Stage 6) instead of the interactive report.
|
||||
- Write a run artifact (Step 4). Do not file tickets or externalize work — the caller owns that.
|
||||
- Stop after the structured text output and "Review complete" signal. No commit/push/PR.
|
||||
|
||||
#### Step 3: Apply fixes with one fixer
|
||||
|
||||
- Spawn exactly one fixer subagent for the current fixer queue in the current checkout. That fixer applies all approved changes and runs the relevant targeted tests in one pass against a consistent tree.
|
||||
- Do not fan out multiple fixers against the same checkout. Parallel fixers require isolated worktrees/branches and deliberate mergeback.
|
||||
- Do not start a mutating review round concurrently with browser testing on the same checkout. Future orchestrators that want both must either run `mode:report-only` during the parallel phase or isolate the mutating review in its own checkout/worktree.
|
||||
|
||||
**Queue contract by caller path:**
|
||||
|
||||
The fixer accepts two queue shapes depending on which caller invoked it:
|
||||
|
||||
- **Homogeneous queue (autofix, headless, walk-through Apply set):** every item is `safe_auto -> review-fixer` (autofix, headless), or every item carries a concrete `suggested_fix` (walk-through Apply set, where the user picked Apply on each finding). The fixer applies each item. **Defensive backstop for the walk-through Apply set:** the walk-through suppresses the Apply option for findings without a `suggested_fix` (see `references/walkthrough.md` adaptations) and the post-run failure-handling re-entry suppresses it as well, so this queue should not contain such items in normal runs. If one slips through, route it to `failed` with reason `no fix proposed by reviewer` rather than attempting an undefined apply — mirroring the heterogeneous queue's handling. Autofix and headless callers are unaffected; they only ever process `safe_auto` items.
|
||||
- **Heterogeneous queue (best-judgment path — interactive option B and walk-through's `Auto-resolve with best judgment on the rest`):** the queue mixes `gated_auto`, `manual`, and `advisory` findings. Each item carries: `autofix_class`, `severity`, `file:line`, `title`, `suggested_fix` (may be null), `why_it_matters`, and `evidence`. The fixer routes each item to one of four buckets — the routing categories are fixed; the failure *reason string* should be specific enough that the post-run question's framing (`N findings could not be auto-resolved...`) reads meaningfully to the user. Use the category's default phrasing below when nothing more specific applies; prefer richer, finding-specific reasons that capture *why this particular item didn't land* (e.g., `needs intent confirmation; was the field narrowing deliberate, or do clients still need the full payload?` is more useful than the generic default).
|
||||
- **`safe_auto` / `gated_auto` / `manual` with `suggested_fix`:** light evidence-match check (verify the cited code at `file:line` still resembles the persona's evidence — concretely: at least one identifier or distinctive token from the evidence appears at the cited location, and the line has not been deleted). If the check passes, attempt to apply the fix. On clean apply, route to `applied`. On fix-application failure (line moved, conflicting edit, syntax issue), route to `failed` with a concrete reason — default phrasing `fix did not apply cleanly: <error>` when no richer description fits.
|
||||
- **`gated_auto` or `manual` without `suggested_fix`:** route to `failed` — default phrasing `no fix proposed by reviewer` when no richer description fits. For `manual` this signal indicates the persona judged the finding to need cross-team input or context outside the review; a richer reason naming the specific decision (intent ambiguity, contract decision, design choice) is more useful when the persona's `why_it_matters` or `evidence` makes that clear. For `gated_auto` this is a defensive case (the persona shouldn't normally produce `gated_auto` without a concrete fix) — surface it in `failed` rather than skipping it, to preserve the apply-or-fail contract.
|
||||
- **Advisory items (`autofix_class: advisory`):** no-op. Route to `advisory` (recorded as acknowledged).
|
||||
- **Evidence-match check fails:** route to `failed` — default phrasing `evidence no longer matches code at <file:line>` when no richer description fits. This is the false-positive case — the finding cited something that has since changed or was already handled.
|
||||
|
||||
**Best-judgment path is single-pass.** No `max_rounds: 2` re-review loop. After the fixer returns, the orchestrator follows Step 2 Interactive option B's post-fixer ordering: when the `failed` bucket is empty, emit the unified completion report directly; when it is non-empty, fire the post-run failure-handling question first, execute the user's choice, then emit the unified completion report so it reflects the final action state.
|
||||
|
||||
**Other paths retain the bounded-rounds loop.** For autofix and the walk-through Apply set, re-review only the changed scope after fixes land, bound the loop with `max_rounds: 2`, and if issues remain after the second round, hand them off as residual work or report them as unresolved.
|
||||
|
||||
**Verification.** If any applied finding has `requires_verification: true`, the fixer runs the targeted verification (focused tests or operational checks) for that item before declaring it `applied`. Verification failure routes the item to `failed` — default phrasing `verification failed: <test-name>` when no richer description fits (e.g., `verification failed: payment_spec timed out after 30s` is more useful than the bare default). This applies on every path.
|
||||
|
||||
**Fixer return shape (best-judgment path).** The fixer returns the partition `{applied, failed, advisory}` where each entry includes the finding identifier, original `autofix_class`, `severity`, `file:line`, and (for `failed`) a one-line reason. The orchestrator uses this partition to assemble the unified completion report and gate the post-run failure-handling question.
|
||||
|
||||
#### Step 4: Emit artifacts and downstream handoff
|
||||
|
||||
- In interactive, autofix, and headless modes, write a per-run artifact under `/tmp/compound-engineering/ce-code-review/<run-id>/` containing:
|
||||
- synthesized findings (merged output from Stage 5)
|
||||
- applied fixes
|
||||
- residual actionable work
|
||||
- advisory-only outputs
|
||||
Per-agent full-detail JSON files (`{reviewer_name}.json`) are already present in this directory from Stage 4 dispatch.
|
||||
- Also write `metadata.json` alongside the findings so downstream skills (e.g., `ce-polish-beta`) can verify the artifact matches the current branch and HEAD. Minimum fields:
|
||||
```json
|
||||
{
|
||||
"run_id": "<run-id>",
|
||||
"branch": "<git branch --show-current at dispatch time>",
|
||||
"head_sha": "<git rev-parse HEAD at dispatch time>",
|
||||
"verdict": "<Ready to merge | Ready with fixes | Not ready>",
|
||||
"completed_at": "<ISO 8601 UTC timestamp>"
|
||||
}
|
||||
```
|
||||
Capture `branch` and `head_sha` at dispatch time (before any autofixes land), and write the file after the verdict is finalized. This file is additive -- pre-existing artifacts that predate this field are still valid, and downstream skills fall back to file mtime when it is missing.
|
||||
- In autofix mode, the run artifact is the handoff. Orchestrators read the artifact's residual actionable work and route it as appropriate. The skill itself does not file tickets or prompt the user in autofix.
|
||||
- Interactive mode may offer to externalize residual actionable work via `references/tracker-defer.md` (named tracker -> GitHub Issues via `gh`), but it is not required to finish the review.
|
||||
|
||||
#### Step 5: Final next steps
|
||||
|
||||
**Interactive mode only.** After the fix-review cycle completes (clean verdict or the user chose to stop), offer next steps based on the entry mode. Reuse the resolved review base/default branch from Stage 1 when known; do not hard-code only `main`/`master`.
|
||||
|
||||
**The gate is total fixes applied this run, not routing option.** Track `fixes_applied_count` across the whole Interactive invocation. This counter includes both the `safe_auto` fixes applied automatically before the routing question (see Step 2 Interactive mode) AND any Apply decisions executed by routing option A (walk-through) or option B (best-judgment). Routing options C (File tickets) and D (Report only) add zero to this counter; neither does a walk-through that ends with only Skip / Defer / Acknowledge, and neither does a best-judgment dispatch whose findings were all routed to `failed` or `advisory`.
|
||||
|
||||
Step 5 runs only when `fixes_applied_count > 0`. If the counter is zero — no `safe_auto` fixes were applied AND the routing path produced no additional Apply — skip Step 5 entirely and exit after the completion report. Asking "push fixes?" when nothing changed in the working tree is incoherent.
|
||||
|
||||
Common outcomes:
|
||||
|
||||
- `safe_auto` produced fixes AND the user picked any routing option → Step 5 runs (counter > 0 from the safe_auto pass alone).
|
||||
- No `safe_auto` fixes AND the user picked option C or D → Step 5 skipped.
|
||||
- No `safe_auto` fixes AND walk-through / best-judgment finished with zero Applies → Step 5 skipped.
|
||||
- Zero-remaining case (no `gated_auto` / `manual` after `safe_auto`) with at least one `safe_auto` fix → Step 5 runs; the routing question was never asked but the counter is > 0.
|
||||
|
||||
- **PR mode (entered via PR number/URL):**
|
||||
- **Push fixes** -- push commits to the existing PR branch
|
||||
- **Exit** -- done for now
|
||||
- **Branch mode (feature branch with no PR, and not the resolved review base/default branch):**
|
||||
- **Create a PR (Recommended)** -- push and open a pull request
|
||||
- **Continue without PR** -- stay on the branch
|
||||
- **Exit** -- done for now
|
||||
- **On the resolved review base/default branch:**
|
||||
- **Continue** -- proceed with next steps
|
||||
- **Exit** -- done for now
|
||||
|
||||
If "Create a PR": first publish the branch with `git push --set-upstream origin HEAD`, then use `gh pr create` with a title and summary derived from the branch changes.
|
||||
If "Push fixes": push the branch with `git push` to update the existing PR.
|
||||
|
||||
**Autofix, report-only, and headless modes:** stop after the report, artifact emission, and residual-work handoff. Do not commit, push, or create a PR.
|
||||
|
||||
## Fallback
|
||||
|
||||
If the platform doesn't support parallel sub-agents, run reviewers sequentially. If the platform supports sub-agents but caps active concurrency, use the bounded queueing rules in Stage 4 rather than treating cap-related spawn failures as reviewer failures. Everything else (stages, output format, merge pipeline) stays the same.
|
||||
|
||||
---
|
||||
|
||||
## Included References
|
||||
|
||||
### Persona Catalog
|
||||
|
||||
@./references/persona-catalog.md
|
||||
|
||||
### Subagent Template
|
||||
|
||||
@./references/subagent-template.md
|
||||
|
||||
### Diff Scope Rules
|
||||
|
||||
@./references/diff-scope.md
|
||||
|
||||
### Findings Schema
|
||||
|
||||
@./references/findings-schema.json
|
||||
|
||||
### Review Output Template
|
||||
|
||||
@./references/review-output-template.md
|
||||
@@ -0,0 +1,112 @@
|
||||
# Bulk Action Preview
|
||||
|
||||
This reference defines the compact plan preview that Interactive mode shows before the file-tickets routing option (option C) executes. The preview gives the user a single-screen view of what the agent is about to do, with exactly two options to Proceed or Cancel.
|
||||
|
||||
Interactive mode only. Option C only.
|
||||
|
||||
The best-judgment path (routing option B and the walk-through's `Auto-resolve with best judgment on the rest`) does **not** use the bulk preview. The best-judgment path dispatches the fixer immediately and surfaces failures in a post-run question, per the `(B)` handler in `SKILL.md` Step 2 Interactive mode. Filing tickets is the one bulk action that benefits from a preview because filing produces durable external state that is expensive to undo — applying local fixes on uncommitted edits is not.
|
||||
|
||||
---
|
||||
|
||||
## When the preview fires
|
||||
|
||||
One call site:
|
||||
|
||||
- **Routing option C (top-level File tickets)** — after the user picks `File a [TRACKER] ticket per finding without applying fixes` but before any ticket is filed. Scope: every pending `gated_auto` / `manual` finding. Every finding appears under `Filing [TRACKER] tickets (N):` regardless of the agent's natural recommendation, because option C is batch-defer.
|
||||
|
||||
The user confirms with `Proceed` or backs out with `Cancel`. No per-item decisions inside the preview — per-item decisioning is the walk-through's role (option A).
|
||||
|
||||
---
|
||||
|
||||
## Preview structure
|
||||
|
||||
The preview is grouped by the action the agent intends to take. Bucket headers appear only when their bucket is non-empty.
|
||||
|
||||
```
|
||||
<Path label> — <scope summary>[ (tracker: <name>)]:
|
||||
|
||||
Applying (N):
|
||||
[P0] <file>:<line> — <one-line plain-English summary>
|
||||
[P1] <file>:<line> — <one-line plain-English summary>
|
||||
|
||||
Filing [TRACKER] tickets (N):
|
||||
[P2] <file>:<line> — <one-line plain-English summary>
|
||||
|
||||
Skipping (N):
|
||||
[P2] <file>:<line> — <one-line plain-English summary>
|
||||
|
||||
Acknowledging (N):
|
||||
[P3] <file>:<line> — <one-line plain-English summary>
|
||||
```
|
||||
|
||||
Worked example, for routing option C (file tickets):
|
||||
|
||||
```
|
||||
File plan — 8 findings as Linear tickets:
|
||||
|
||||
Filing Linear tickets (8):
|
||||
[P0] orders_controller.rb:42 — Missing ownership guard on order lookup
|
||||
[P1] webhook_handler.rb:120 — Unhandled error swallowed in webhook
|
||||
[P2] user_serializer.rb:14 — internal_id leaks in serialized response
|
||||
[P2] billing_service.rb:230 — N+1 on refund batch
|
||||
[P2] session_helper.rb:12 — Session reset behavior unclear
|
||||
[P2] report_worker.rb:55 — Worker timeout under heavy load
|
||||
[P3] string_utils.rb:8 — Ambiguous helper name
|
||||
[P3] readme.md:14 — Documentation gap
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scope summary wording
|
||||
|
||||
- **Routing option C (top-level File tickets):** header reads `File plan — N findings as [TRACKER] tickets:`. Every finding lands in the `Filing [TRACKER] tickets (N):` bucket. Option C is batch-defer — no Apply / Skip / Acknowledge buckets render in the preview, since every finding is being filed.
|
||||
|
||||
When the detected tracker is low-confidence or generic (see `tracker-defer.md`), the `(tracker: <name>)` annotation is omitted from the header and the `Filing [TRACKER] tickets` bucket header uses the generic form (`Filing tickets (N):`).
|
||||
|
||||
---
|
||||
|
||||
## Per-finding line format
|
||||
|
||||
Each line uses the compressed form of the framing-quality bar from the plan (R22-R25 — observable-behavior-first, no function / variable names unless needed to locate). The one-line summary is drawn from the persona-produced `why_it_matters` by taking the first sentence (and, when the first sentence is too long for the preview width, paraphrasing it tightly to fit).
|
||||
|
||||
- **Shape:** `[<severity>] <file>:<line> — <one-line summary>`
|
||||
- **Width target:** keep lines near 80 columns so the preview renders cleanly in narrow terminals. Truncate with ellipsis when necessary.
|
||||
- **No function / variable names inline** unless the reader needs them to locate the issue.
|
||||
- **Advisory bucket phrasing:** the `Acknowledging (N):` bucket describes the advisory content in one line. No "fix" phrase — advisory findings have no concrete fix.
|
||||
|
||||
When no `why_it_matters` is available for a finding (e.g., Unit 2's template upgrade hasn't fully propagated through the persona run, or the artifact file was unreadable), fall back to the finding's title directly. Note the gap in the completion report's Coverage section if it affects more than a few findings in the same run.
|
||||
|
||||
---
|
||||
|
||||
## Question and options
|
||||
|
||||
After the preview body is rendered, ask the user using the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)). In Claude Code, the tool should already be loaded from the Interactive-mode pre-load step — if it isn't, call `ToolSearch` with query `select:AskUserQuestion` now. The text fallback below applies only when the harness genuinely lacks a blocking tool — `ToolSearch` returns no match, the tool call explicitly fails, or the runtime mode does not expose it (e.g., Codex edit modes without `request_user_input`). A pending schema load is not a fallback trigger. Never silently skip the question.
|
||||
|
||||
Stem: `The agent is about to file the tickets above. Proceed?`
|
||||
|
||||
Options (exactly two):
|
||||
- `Proceed` — file every ticket in the preview
|
||||
- `Cancel` — do nothing, return to the routing question
|
||||
|
||||
Only when `ToolSearch` explicitly returns no match or the tool call errors — or on a platform with no blocking question tool — fall back to presenting numbered options and waiting for the user's next reply.
|
||||
|
||||
---
|
||||
|
||||
## Cancel semantics
|
||||
|
||||
`Cancel` returns the user to the routing question (the four-option menu in `SKILL.md` Step 2 Interactive mode). No tickets are filed; no state is recorded. The session's cached tracker-detection tuple is preserved.
|
||||
|
||||
---
|
||||
|
||||
## Proceed semantics
|
||||
|
||||
When the user picks `Proceed`, every finding in the preview routes through `references/tracker-defer.md` for ticket creation. No fixes are applied. After all tickets have been filed (or failed), emit the unified completion report (see `references/walkthrough.md`).
|
||||
|
||||
Failure during `Proceed` (e.g., ticket creation fails for one finding during a batch Defer) follows the failure path defined in `tracker-defer.md` — surface the failure inline with Retry / Fallback / Skip, continue with the rest of the plan, and capture the failure in the completion report's failure section.
|
||||
|
||||
---
|
||||
|
||||
## Edge cases
|
||||
|
||||
- **N=1 preview (only one finding in scope):** the preview still renders with a single-line bucket. `Proceed` / `Cancel` still apply.
|
||||
- **No tracker available:** option C is not offered upstream (see `tracker-defer.md` no sink handling). The bulk preview is therefore never invoked when `any_sink_available` is false.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Diff Scope Rules
|
||||
|
||||
These rules apply to every reviewer. They define what is "your code to review" versus pre-existing context.
|
||||
|
||||
## Scope Discovery
|
||||
|
||||
Determine the diff to review using this priority order:
|
||||
|
||||
1. **User-specified scope.** If the caller passed `BASE:`, `FILES:`, or `DIFF:` markers, use that scope exactly.
|
||||
2. **Working copy changes.** If there are unstaged or staged changes (`git diff HEAD` is non-empty), review those.
|
||||
3. **Unpushed commits vs base branch.** If the working copy is clean, review `git diff $(git merge-base HEAD <base>)..HEAD` where `<base>` is the default branch (main or master).
|
||||
|
||||
The scope step in the SKILL.md handles discovery and passes you the resolved diff. You do not need to run git commands yourself.
|
||||
|
||||
## Finding Classification Tiers
|
||||
|
||||
Every finding you report falls into one of three tiers based on its relationship to the diff:
|
||||
|
||||
### Primary (directly changed code)
|
||||
|
||||
Lines added or modified in the diff. This is your main focus. Report findings against these lines at full confidence.
|
||||
|
||||
### Secondary (immediately surrounding code)
|
||||
|
||||
Unchanged code within the same function, method, or block as a changed line. If a change introduces a bug that's only visible by reading the surrounding context, report it -- but note that the issue exists in the interaction between new and existing code.
|
||||
|
||||
### Pre-existing (unrelated to this diff)
|
||||
|
||||
Issues in unchanged code that the diff didn't touch and doesn't interact with. Mark these as `"pre_existing": true` in your output. They're reported separately and don't count toward the review verdict.
|
||||
|
||||
**The rule:** If you'd flag the same issue on an identical diff that didn't include the surrounding file, it's pre-existing. If the diff makes the issue *newly relevant* (e.g., a new caller hits an existing buggy function), it's secondary.
|
||||
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Code Review Findings",
|
||||
"description": "Structured output schema for code review sub-agents",
|
||||
"type": "object",
|
||||
"required": ["reviewer", "findings", "residual_risks", "testing_gaps"],
|
||||
"properties": {
|
||||
"reviewer": {
|
||||
"type": "string",
|
||||
"description": "Persona name that produced this output (e.g., 'correctness', 'security')"
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"description": "List of code review findings. Empty array if no issues found.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"severity",
|
||||
"file",
|
||||
"line",
|
||||
"why_it_matters",
|
||||
"autofix_class",
|
||||
"owner",
|
||||
"requires_verification",
|
||||
"confidence",
|
||||
"evidence",
|
||||
"pre_existing"
|
||||
],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Short, specific issue title. 10 words or fewer.",
|
||||
"maxLength": 100
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["P0", "P1", "P2", "P3"],
|
||||
"description": "Issue severity level"
|
||||
},
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "Relative file path from repository root"
|
||||
},
|
||||
"line": {
|
||||
"type": "integer",
|
||||
"description": "Primary line number of the issue",
|
||||
"minimum": 1
|
||||
},
|
||||
"why_it_matters": {
|
||||
"type": "string",
|
||||
"description": "Impact and failure mode -- not 'what is wrong' but 'what breaks'"
|
||||
},
|
||||
"autofix_class": {
|
||||
"type": "string",
|
||||
"enum": ["safe_auto", "gated_auto", "manual", "advisory"],
|
||||
"description": "Routing class for downstream fixer dispatch. safe_auto = local mechanical fix the fixer applies without approval (test: a one-sentence fix with no 'depends on' clauses, AND no change to function signature, public-API/error contract, security posture, or permission model; for helper extraction, naming/placement must follow mechanically from the shared shape). gated_auto = concrete fix that changes contracts/permissions or whose placement requires a design conversation; needs user approval before apply. manual = actionable work needing design decisions; usually paired with a suggested_fix the user can confirm. advisory = report-only, no code change. The wrong-side cost is symmetric -- bias toward safe_auto when the rubric permits, since misclassifying mechanical fixes as gated_auto makes users triage findings the fixer could have applied."
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"enum": ["review-fixer", "downstream-resolver", "human", "release"],
|
||||
"description": "Who should own the next action for this finding after synthesis"
|
||||
},
|
||||
"requires_verification": {
|
||||
"type": "boolean",
|
||||
"description": "Whether any fix for this finding must be re-verified with targeted tests or a follow-up review pass"
|
||||
},
|
||||
"suggested_fix": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Concrete minimal fix the reviewer can defend from the diff and surrounding code. Propose one whenever any defensible code change is reachable from review context (parallel patterns, framework conventions, or the cited code itself). Imperfect information is not grounds for omission -- propose the most defensible default given what you can see, name any assumption you are making, and let the user override. 'I need <specific input> to commit' is a soft punt: the right question is 'what code change would I propose if I had to choose now?' and propose that, with the assumption named. Omit only when there is genuinely no code-level change to propose -- e.g., the finding is a question rather than a fix ('what is the intended SLA here?'), or the resolution is purely an organizational action with no code component (legal sign-off, business policy decision). These cases are rare in code review. A bad suggestion is still worse than none, but a soft punt is the failure mode this field is designed to prevent."
|
||||
},
|
||||
"confidence": {
|
||||
"type": "integer",
|
||||
"enum": [0, 25, 50, 75, 100],
|
||||
"description": "Anchored confidence score. Use exactly one of 0, 25, 50, 75, 100. Each anchor has a behavioral criterion the reviewer must honestly self-apply. 0: Not confident. This is a false positive that does not stand up to light scrutiny, or a pre-existing issue this PR did not introduce. 25: Somewhat confident. Might be a real issue but could also be a false positive; the reviewer could not verify from the diff and surrounding code alone. 50: Moderately confident. The reviewer verified this is a real issue but it may be a nitpick, narrow edge case, or have minimal practical impact. Relative to the diff's other concerns, it is not very important. Style preferences and subjective improvements land here. 75: Highly confident. The reviewer double-checked the diff and confirmed the issue will affect users, downstream callers, or runtime behavior in normal usage. The bug, vulnerability, or contract violation is clearly present and actionable. 100: Absolutely certain. The issue is verifiable from the code itself -- compile error, type mismatch, definitive logic bug, or an explicit project-standards violation with a quotable rule. No interpretation required."
|
||||
},
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"description": "Code-grounded evidence: snippets, line references, or pattern descriptions. At least 1 item.",
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1
|
||||
},
|
||||
"pre_existing": {
|
||||
"type": "boolean",
|
||||
"description": "True if this issue exists in unchanged code unrelated to the current diff"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"residual_risks": {
|
||||
"type": "array",
|
||||
"description": "Risks the reviewer noticed but could not confirm as findings",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"testing_gaps": {
|
||||
"type": "array",
|
||||
"description": "Missing test coverage the reviewer identified",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
},
|
||||
|
||||
"_meta": {
|
||||
"confidence_anchors": {
|
||||
"description": "Confidence is one of 5 discrete anchors (0, 25, 50, 75, 100), each tied to a behavioral criterion the reviewer can honestly self-apply. Float values (e.g., 0.73) are not valid -- the model cannot meaningfully calibrate at finer granularity, and discrete anchors prevent false-precision gaming.",
|
||||
"0": "False positive or pre-existing -- do not report",
|
||||
"25": "Speculative; could not verify -- do not report",
|
||||
"50": "Verified real but minor or stylistic -- report only when P0 or when synthesis routes to advisory/soft buckets",
|
||||
"75": "Highly confident, will affect users or runtime in normal usage -- report",
|
||||
"100": "Verifiable from code alone (compile error, type mismatch, definitive logic bug, quoted standards violation) -- report"
|
||||
},
|
||||
"confidence_thresholds": {
|
||||
"suppress": "Below anchor 75 -- do not report. Exception: P0 findings at anchor 50+ may be reported (critical-but-uncertain issues must not be silently dropped).",
|
||||
"report": "Anchor 75 or 100 -- include with full evidence."
|
||||
},
|
||||
"severity_definitions": {
|
||||
"P0": "Critical breakage, exploitable vulnerability, data loss/corruption. Must fix before merge.",
|
||||
"P1": "High-impact defect likely hit in normal usage, breaking contract. Should fix.",
|
||||
"P2": "Moderate issue with meaningful downside (edge case, perf regression, maintainability trap). Fix if straightforward.",
|
||||
"P3": "Low-impact, narrow scope, minor improvement. User's discretion."
|
||||
},
|
||||
"autofix_classes": {
|
||||
"safe_auto": "Local, deterministic code or test fix suitable for the in-skill fixer. Examples: extract duplicated helper, add missing nil check, fix off-by-one, add missing test, remove dead code. Do not default to advisory when a concrete safe fix exists.",
|
||||
"gated_auto": "Concrete fix exists, but it changes behavior, permissions, contracts, or other sensitive areas that deserve explicit approval. Examples: add auth to unprotected endpoint, change API response shape.",
|
||||
"manual": "Actionable issue that requires design decisions or cross-cutting changes. Examples: redesign data model, add pagination strategy, choose between architectural approaches.",
|
||||
"advisory": "Informational or operational item that should be surfaced in the report only. Examples: design asymmetry the PR improves but does not fully resolve, residual risk notes, deployment considerations."
|
||||
},
|
||||
"owners": {
|
||||
"review-fixer": "The in-skill fixer can own this when policy allows.",
|
||||
"downstream-resolver": "Turn this into residual work for later resolution.",
|
||||
"human": "A person must make a judgment call before code changes should continue.",
|
||||
"release": "Operational or rollout follow-up; do not convert into code-fix work automatically."
|
||||
},
|
||||
"return_tiers": {
|
||||
"description": "Finding fields are split into two tiers. The full schema (with all required fields) applies to the artifact file on disk. The compact return to the orchestrator omits detail-tier fields. Both are valid uses of this schema in different contexts.",
|
||||
"merge_tier": "Returned to orchestrator: title, severity, file, line, confidence, autofix_class, owner, requires_verification, pre_existing, suggested_fix (optional). Plus top-level reviewer, residual_risks, testing_gaps.",
|
||||
"detail_tier": "Required in artifact file, omitted from compact return: why_it_matters, evidence. The artifact file must pass full schema validation including all required fields. Headless output depends on why_it_matters and evidence being present in the artifact."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
# Persona Catalog
|
||||
|
||||
14 reviewer personas organized into always-on, cross-cutting conditional, and stack-specific conditional layers, plus CE-specific agents. The orchestrator uses this catalog to select which reviewers to spawn for each review.
|
||||
|
||||
## Always-on (4 personas + 2 CE agents)
|
||||
|
||||
Spawned on every review regardless of diff content.
|
||||
|
||||
**Persona agents (structured JSON output):**
|
||||
|
||||
| Persona | Agent | Focus |
|
||||
|---------|-------|-------|
|
||||
| `correctness` | `ce-correctness-reviewer` | Logic errors, edge cases, state bugs, error propagation, intent compliance |
|
||||
| `testing` | `ce-testing-reviewer` | Coverage gaps, weak assertions, brittle tests, missing edge case tests |
|
||||
| `maintainability` | `ce-maintainability-reviewer` | Structural quality, complexity deletion, 1k-line regressions, coupling, type-boundary leaks, dead code, premature abstraction |
|
||||
| `project-standards` | `ce-project-standards-reviewer` | CLAUDE.md and AGENTS.md compliance -- frontmatter, references, naming, cross-platform portability, tool selection |
|
||||
|
||||
**CE agents (unstructured output, synthesized separately):**
|
||||
|
||||
| Agent | Focus |
|
||||
|-------|-------|
|
||||
| `ce-agent-native-reviewer` | Verify new features are agent-accessible |
|
||||
| `ce-learnings-researcher` | Search docs/solutions/ for past issues related to this PR's modules and patterns |
|
||||
|
||||
## Conditional (7 personas)
|
||||
|
||||
Spawned when the orchestrator identifies relevant patterns in the diff. The orchestrator reads the full diff and reasons about selection -- this is agent judgment, not keyword matching.
|
||||
|
||||
| Persona | Agent | Select when diff touches... |
|
||||
|---------|-------|---------------------------|
|
||||
| `security` | `ce-security-reviewer` | Auth middleware, public endpoints, user input handling, permission checks, secrets management |
|
||||
| `performance` | `ce-performance-reviewer` | Database queries, ORM calls, loop-heavy data transforms, caching layers, async/concurrent code |
|
||||
| `api-contract` | `ce-api-contract-reviewer` | Route definitions, serializer/interface changes, event schemas, exported type signatures, API versioning |
|
||||
| `data-migration` | `ce-data-migration-reviewer` | Migration files, schema dumps (`db/schema.rb`, `structure.sql`), backfill scripts, data transformations — **not** model/query-only changes without migration artifacts |
|
||||
| `reliability` | `ce-reliability-reviewer` | Error handling, retry logic, circuit breakers, timeouts, background jobs, async handlers, health checks |
|
||||
| `adversarial` | `ce-adversarial-reviewer` | Diff has >=50 changed non-test, non-generated, non-lockfile lines, OR touches auth, payments, data mutations, external API integrations, or other high-risk domains |
|
||||
| `previous-comments` | `ce-previous-comments-reviewer` | **PR-only AND comment-gated.** Reviewing a PR that has existing review comments or review threads from prior review rounds. Skip entirely when no PR metadata was gathered in Stage 1, OR when Stage 1's `hasPriorComments` flag is false (no `reviews` and no `comments` on the PR). |
|
||||
|
||||
## Stack-Specific Conditional (2 personas)
|
||||
|
||||
These reviewers cover runtime behavior the always-on personas do not specialize in. Structural and maintainability concerns live in the always-on `maintainability` persona — do not spawn extra stack reviewers for philosophy or convention-only passes.
|
||||
|
||||
| Persona | Agent | Select when diff touches... |
|
||||
|---------|-------|---------------------------|
|
||||
| `julik-frontend-races` | `ce-julik-frontend-races-reviewer` | Stimulus/Turbo controllers, DOM event wiring, timers, async UI flows, animations, or frontend state transitions with race potential |
|
||||
| `swift-ios` | `ce-swift-ios-reviewer` | Swift files, SwiftUI views, UIKit controllers, `.entitlements`, `PrivacyInfo.xcprivacy`, `.xcdatamodeld`, `Package.swift`, `Package.resolved`, storyboards, XIBs, or semantic build-setting / target-membership / code-signing changes in `.pbxproj` |
|
||||
|
||||
## CE Conditional Agents (migration-specific)
|
||||
|
||||
Spawn `ce-deployment-verification-agent` when the migration-artifact gate applies **and** the change is risky (destructive DDL, backfills, NOT NULL without default, column renames/drops). Schema drift and migration safety live in the `data-migration` persona — not separate CE agents.
|
||||
|
||||
| Agent | Focus |
|
||||
|-------|-------|
|
||||
| `ce-deployment-verification-agent` | Go/No-Go deployment checklist with SQL verification queries and rollback procedures |
|
||||
|
||||
## Selection rules
|
||||
|
||||
1. **Always spawn all 4 always-on personas** plus the 2 CE always-on agents.
|
||||
2. **For each cross-cutting conditional persona**, the orchestrator reads the diff and decides whether the persona's domain is relevant. This is a judgment call, not a keyword match.
|
||||
3. **For each stack-specific conditional persona**, use file types and changed patterns as a starting point, then decide whether the diff actually introduces meaningful work for that reviewer. Do not spawn language-specific reviewers just because one config or generated file happens to match the extension.
|
||||
4. **For `data-migration`**, spawn only when the diff includes migration or schema artifacts (`db/migrate/*`, `db/schema.rb`, `db/structure.sql`, Alembic/Flyway/Liquibase paths, or explicit backfill/data-transform scripts). Do **not** spawn for model-only or query-only changes without those files.
|
||||
5. **For CE conditional agents**, spawn `ce-deployment-verification-agent` when the migration-artifact gate applies and the change is risky (see above).
|
||||
6. **Announce the team** before spawning with a one-line justification per conditional reviewer selected.
|
||||
@@ -0,0 +1,147 @@
|
||||
# Code Review Output Template
|
||||
|
||||
Use this **exact format** when presenting synthesized review findings. Findings are grouped by severity, not by reviewer.
|
||||
|
||||
**IMPORTANT:** Use pipe-delimited markdown tables (`| col | col |`). Do NOT use ASCII box-drawing characters.
|
||||
|
||||
**IMPORTANT:** Escape literal pipe characters in table cells. Any `|` that appears inside a finding title, issue description, code snippet, regex pattern, or delimited-string example (e.g. cache key examples like `userName + "|" + groups`) must be written as `\|` so column boundaries are determined only by unescaped pipes. Unescaped pipes split the cell across columns and corrupt the row's `Reviewer`, `Confidence`, and `Route` values.
|
||||
|
||||
## Example
|
||||
|
||||
```markdown
|
||||
## Code Review Results
|
||||
|
||||
**Scope:** merge-base with the review base branch -> working tree (14 files, 342 lines)
|
||||
**Intent:** Add order export endpoint with CSV and JSON format support
|
||||
**Mode:** autofix
|
||||
|
||||
**Reviewers:** correctness, testing, maintainability, security, api-contract
|
||||
- security -- new public endpoint accepts user-provided format parameter
|
||||
- api-contract -- new /api/orders/export route with response schema
|
||||
|
||||
### P0 -- Critical
|
||||
|
||||
| # | File | Issue | Reviewer | Confidence | Route |
|
||||
|---|------|-------|----------|------------|-------|
|
||||
| 1 | `orders_controller.rb:42` | User-supplied ID in account lookup without ownership check | security | 100 | `gated_auto -> downstream-resolver` |
|
||||
|
||||
### P1 -- High
|
||||
|
||||
| # | File | Issue | Reviewer | Confidence | Route |
|
||||
|---|------|-------|----------|------------|-------|
|
||||
| 2 | `export_service.rb:87` | Loads all orders into memory -- unbounded for large accounts | performance | 100 | `safe_auto -> review-fixer` |
|
||||
| 3 | `export_service.rb:91` | No pagination -- response size grows linearly with order count | api-contract, performance | 75 | `manual -> downstream-resolver` |
|
||||
|
||||
### P2 -- Moderate
|
||||
|
||||
| # | File | Issue | Reviewer | Confidence | Route |
|
||||
|---|------|-------|----------|------------|-------|
|
||||
| 4 | `export_service.rb:45` | Missing error handling for CSV serialization failure | correctness | 75 | `safe_auto -> review-fixer` |
|
||||
|
||||
### P3 -- Low
|
||||
|
||||
| # | File | Issue | Reviewer | Confidence | Route |
|
||||
|---|------|-------|----------|------------|-------|
|
||||
| 5 | `export_helper.rb:12` | Format detection could use early return instead of nested conditional | maintainability | 75 | `advisory -> human` |
|
||||
|
||||
### Applied Fixes
|
||||
|
||||
- `safe_auto`: Added bounded export pagination guard and CSV serialization failure test coverage in this run
|
||||
|
||||
### Residual Actionable Work
|
||||
|
||||
| # | File | Issue | Route | Next Step |
|
||||
|---|------|-------|-------|-----------|
|
||||
| 1 | `orders_controller.rb:42` | Ownership check missing on export lookup | `gated_auto -> downstream-resolver` | Defer via tracker (requires explicit approval before behavior change) |
|
||||
| 3 | `export_service.rb:91` | Pagination contract needs a broader API decision | `manual -> downstream-resolver` | Defer via tracker with contract and client impact details |
|
||||
|
||||
### Pre-existing Issues
|
||||
|
||||
| # | File | Issue | Reviewer |
|
||||
|---|------|-------|----------|
|
||||
| 1 | `orders_controller.rb:12` | Broad rescue masking failed permission check | correctness |
|
||||
|
||||
### Learnings & Past Solutions
|
||||
|
||||
- [Known Pattern] `docs/solutions/export-pagination.md` -- previous export pagination fix applies to this endpoint
|
||||
|
||||
### Agent-Native Gaps
|
||||
|
||||
- New export endpoint has no CLI/agent equivalent -- agent users cannot trigger exports
|
||||
|
||||
### Deployment Notes
|
||||
|
||||
- Pre-deploy: capture baseline row counts before enabling the export backfill
|
||||
- Verify: `SELECT COUNT(*) FROM exports WHERE status IS NULL;` should stay at `0`
|
||||
- Rollback: keep the old export path available until the backfill has been validated
|
||||
|
||||
### Coverage
|
||||
|
||||
- Suppressed: 2 findings below anchor 75 (1 at anchor 50, 1 at anchor 25)
|
||||
- Residual risks: No rate limiting on export endpoint
|
||||
- Testing gaps: No test for concurrent export requests
|
||||
|
||||
---
|
||||
|
||||
> **Verdict:** Ready with fixes
|
||||
>
|
||||
> **Reasoning:** 1 critical auth bypass must be fixed. The memory/pagination issues (P1) should be addressed for production safety.
|
||||
>
|
||||
> **Fix order:** P0 auth bypass -> P1 memory/pagination -> P2 error handling if straightforward
|
||||
```
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
Do NOT produce output like this. The following is wrong:
|
||||
|
||||
```markdown
|
||||
Findings
|
||||
|
||||
Sev: P1
|
||||
File: foo.go:42
|
||||
Issue: Some problem description
|
||||
Reviewer(s): adversarial
|
||||
Confidence: 75
|
||||
Route: advisory -> human
|
||||
────────────────────────────────────────
|
||||
Sev: P2
|
||||
File: bar.go:99
|
||||
Issue: Another problem
|
||||
```
|
||||
|
||||
This fails because: no pipe-delimited tables, no severity-grouped `###` headers, uses box-drawing horizontal rules, no numbered findings, no `## Code Review Results` title, and the verdict is not in a blockquote. Always use the table format from the example above.
|
||||
|
||||
## Formatting Rules
|
||||
|
||||
- **Pipe-delimited markdown tables** for findings -- never ASCII box-drawing characters or per-finding horizontal-rule separators between entries (the report-level `---` before the verdict is still required)
|
||||
- **Escape literal `|` in table cells** -- any `|` inside a finding title, issue description, code snippet, regex pattern, or delimited-string example must be written as `\|`. Unescaped pipes are parsed as column separators and corrupt the row's `Reviewer`, `Confidence`, and `Route` columns. Applies especially to cache-key delimiter examples, regex alternations, and logical-OR operators quoted inside findings.
|
||||
- **Severity-grouped sections** -- `### P0 -- Critical`, `### P1 -- High`, `### P2 -- Moderate`, `### P3 -- Low`. Omit empty severity levels.
|
||||
- **Stable sequential finding numbers** -- assign finding numbers once after sorting, continue them across severity sections, and reuse those same numbers when findings are repeated in Residual Actionable Work. Do not restart at `1` for each severity or route bucket.
|
||||
- **Always include file:line location** for code review issues
|
||||
- **Reviewer column** shows which persona(s) flagged the issue. Multiple reviewers = cross-reviewer agreement.
|
||||
- **Confidence column** shows the finding's anchor as an integer (`50`, `75`, or `100`). Never render as a float.
|
||||
- **Route column** shows the synthesized handling decision as ``<autofix_class> -> <owner>``.
|
||||
- **Header includes** scope, intent, and reviewer team with per-conditional justifications
|
||||
- **Mode line** -- include `interactive`, `autofix`, `report-only`, or `headless`
|
||||
- **Applied Fixes section** -- include only when a fix phase ran in this review invocation
|
||||
- **Residual Actionable Work section** -- include only when unresolved actionable findings were handed off for later work
|
||||
- **Pre-existing section** -- separate table, no confidence column (these are informational)
|
||||
- **Learnings & Past Solutions section** -- results from ce-learnings-researcher, with links to docs/solutions/ files
|
||||
- **Agent-Native Gaps section** -- results from ce-agent-native-reviewer. Omit if no gaps found.
|
||||
- **Deployment Notes section** -- key checklist items from ce-deployment-verification-agent. Omit if the agent did not run. Schema drift surfaces as `data-migration` findings — no separate section.
|
||||
- **Coverage section** -- suppressed count, residual risks, testing gaps, failed reviewers
|
||||
- **Summary uses blockquotes** for verdict, reasoning, and fix order
|
||||
- **Horizontal rule** (`---`) separates findings from verdict
|
||||
- **`###` headers** for each section -- never plain text headers
|
||||
|
||||
## Headless Mode Format
|
||||
|
||||
In `mode:headless`, replace the interactive pipe-delimited table report with a structured text envelope. The headless format is defined in the `### Headless output format` section of SKILL.md. Key differences from the interactive format:
|
||||
|
||||
- **No pipe-delimited tables.** Findings use `[severity][autofix_class -> owner] File: <file:line> -- <title>` line format with indented Why/Evidence/Suggested fix lines.
|
||||
- **Findings grouped by autofix_class** (gated-auto, manual, advisory) instead of severity. Within each group, findings are sorted by severity.
|
||||
- **Verdict in header** (top of output) instead of bottom, so programmatic callers get it first.
|
||||
- **`Artifact:` line** in metadata header gives callers the path to the full run artifact.
|
||||
- **`[needs-verification]` marker** on findings where `requires_verification: true`.
|
||||
- **Evidence lines** included per finding.
|
||||
- **Completion signal:** "Review complete" as the final line.
|
||||
@@ -0,0 +1,200 @@
|
||||
# Sub-agent Prompt Template
|
||||
|
||||
This template is used by the orchestrator to spawn each reviewer sub-agent. Variable substitution slots are filled at spawn time.
|
||||
|
||||
---
|
||||
|
||||
## Template
|
||||
|
||||
```
|
||||
You are a specialist code reviewer.
|
||||
|
||||
<persona>
|
||||
{persona_file}
|
||||
</persona>
|
||||
|
||||
<scope-rules>
|
||||
{diff_scope_rules}
|
||||
</scope-rules>
|
||||
|
||||
<output-contract>
|
||||
You produce up to two outputs depending on whether a run ID was provided:
|
||||
|
||||
1. **Artifact file (when run ID is present).** If a Run ID appears in <review-context> below, WRITE your full analysis (all schema fields, including why_it_matters, evidence, and suggested_fix) as JSON to:
|
||||
/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json
|
||||
This is the ONE write operation you are permitted to make. Use the platform's file-write tool.
|
||||
If the write fails, continue -- the compact return still provides everything the merge needs.
|
||||
If no Run ID is provided (the field is empty or absent), skip this step entirely -- do not attempt any file write.
|
||||
|
||||
2. **Compact return (always).** RETURN compact JSON to the parent with ONLY merge-tier fields per finding:
|
||||
title, severity, file, line, confidence, autofix_class, owner, requires_verification, pre_existing, suggested_fix.
|
||||
Do NOT include why_it_matters or evidence in the returned JSON.
|
||||
Include reviewer, residual_risks, and testing_gaps at the top level.
|
||||
|
||||
The full file preserves detail for downstream consumers (headless output, debugging).
|
||||
The compact return keeps the orchestrator's context lean for merge and synthesis.
|
||||
|
||||
The schema below describes the **full artifact file format** (all fields required). For the compact return, follow the field list above -- omit why_it_matters and evidence even though the schema marks them as required.
|
||||
|
||||
{schema}
|
||||
|
||||
**Schema conformance — hard constraints (use these exact values; validation rejects anything else):**
|
||||
|
||||
- `severity`: one of `"P0"`, `"P1"`, `"P2"`, `"P3"` — use these exact strings. Do NOT use `"high"`, `"medium"`, `"low"`, `"critical"`, or any other vocabulary, even if your persona's prose discusses priorities in those terms conceptually.
|
||||
- `autofix_class`: one of `"safe_auto"`, `"gated_auto"`, `"manual"`, `"advisory"`.
|
||||
- `owner`: one of `"review-fixer"`, `"downstream-resolver"`, `"human"`, `"release"`.
|
||||
- `evidence`: an ARRAY of strings with at least one element. A single string value is a validation failure — wrap every quote in `["..."]` even when there is only one.
|
||||
- `pre_existing`: boolean, never null.
|
||||
- `requires_verification`: boolean, never null.
|
||||
- `confidence`: one of exactly `0`, `25`, `50`, `75`, or `100` — a discrete anchor, NOT a continuous number. Any other value (e.g., `72`, `0.85`, `"high"`) is a validation failure. Pick the anchor whose behavioral criterion you can honestly self-apply to this finding (see "Confidence rubric" below).
|
||||
|
||||
If your persona description uses severity vocabulary like "high-priority" or "critical" in its rubric text, translate to the P0-P3 scale at emit time. "Critical / must-fix" → P0, "important / should-fix" → P1, "worth-noting / could-fix" → P2, "low-signal" → P3. Same for priorities described qualitatively in your analysis — map to P0-P3 on the way out.
|
||||
|
||||
**Confidence rubric — use these exact behavioral anchors.** Pick the single anchor whose criterion you can honestly self-apply. Do not pick a value between anchors; only `0`, `25`, `50`, `75`, and `100` are valid. The rubric is anchored on behavior you performed, not on a vague sense of certainty — if you cannot truthfully attach the behavioral claim to the finding, step down to the next anchor.
|
||||
|
||||
- **`0` — Not confident at all.** A false positive that does not stand up to light scrutiny, or a pre-existing issue this PR did not introduce. **Do not emit — suppress silently.** This anchor exists in the enum only so synthesis can explicitly track the drop; personas never produce it.
|
||||
- **`25` — Somewhat confident.** Might be a real issue but could also be a false positive; you could not verify from the diff and surrounding code alone. **Do not emit — suppress silently.** This anchor, like `0`, exists in the enum only so synthesis can track the drop; personas never produce it. If your domain is genuinely uncertain, either gather more evidence (read related files, check call sites, inspect git blame) until you can honestly anchor at `50` or higher, or suppress entirely.
|
||||
- **`50` — Moderately confident.** You verified this is a real issue but it is a nitpick, narrow edge case, or has minimal practical impact. Style preferences and subjective improvements land here. Surfaces only when synthesis routes weak findings to advisory / residual_risks / testing_gaps soft buckets, or when the finding is P0 (critical-but-uncertain issues are not silently dropped).
|
||||
- **`75` — Highly confident.** You double-checked the diff and surrounding code and confirmed the issue will affect users, downstream callers, or runtime behavior in normal usage. The bug, vulnerability, or contract violation is clearly present and actionable.
|
||||
|
||||
**Anchor `75` requires naming a concrete observable consequence** — a wrong result, an unhandled error path, a contract mismatch, a security exposure, missing coverage that a real test scenario would surface. "This could be cleaner" or "I would have written this differently" do not meet this bar — they are advisory observations and land at anchor `50`. When in doubt between `50` and `75`, ask: "will a user, caller, or operator concretely encounter this in normal usage, or is this my opinion about the code's quality?" The former is `75`; the latter is `50`.
|
||||
- **`100` — Absolutely certain.** The issue is verifiable from the code itself — compile error, type mismatch, definitive logic bug (off-by-one in a tested algorithm, wrong return type, swapped arguments), or an explicit project-standards violation with a quotable rule. No interpretation required.
|
||||
|
||||
Anchor and severity are independent axes. A P2 finding can be anchor `100` if the evidence is airtight; a P0 finding can be anchor `50` if it is an important concern you could not fully verify. Anchor gates where the finding surfaces (drop / soft bucket / actionable); severity orders it within the actionable surface.
|
||||
|
||||
Synthesis suppresses anchors `0` and `25` silently. Anchor `50` is dropped from primary findings unless the severity is P0 (P0+50 survives) or synthesis routes it to a soft bucket (testing_gaps, residual_risks, advisory) per mode-aware demotion. Anchors `75` and `100` enter the actionable tier.
|
||||
|
||||
Example of a schema-valid finding (all required fields, correct enum values, correct array shape):
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "User-supplied ID in account lookup without ownership check",
|
||||
"severity": "P0",
|
||||
"file": "app/controllers/orders_controller.rb",
|
||||
"line": 42,
|
||||
"why_it_matters": "Any signed-in user can read another user's orders by pasting the target account ID into the URL. The controller looks up the account and returns its orders without verifying the current user owns it. The shipments controller already uses a current_user.owns?(account) guard for the same attack class; matching that pattern fixes this finding.",
|
||||
"autofix_class": "gated_auto",
|
||||
"owner": "downstream-resolver",
|
||||
"requires_verification": true,
|
||||
"suggested_fix": "Add current_user.owns?(account) guard before lookup, matching the pattern in shipments_controller.rb",
|
||||
"confidence": 100,
|
||||
"evidence": [
|
||||
"orders_controller.rb:42 -- account = Account.find(params[:account_id])",
|
||||
"shipments_controller.rb:38 -- raise NotAuthorized unless current_user.owns?(account)"
|
||||
],
|
||||
"pre_existing": false
|
||||
}
|
||||
```
|
||||
|
||||
The `confidence: 100` is justified because the issue is verifiable from the code alone — the controller fetches by user-supplied ID and returns data without any guard, and the parallel pattern in shipments_controller.rb confirms the project's own convention is being violated.
|
||||
|
||||
Writing `why_it_matters` (required field, every finding):
|
||||
|
||||
The `why_it_matters` field is how the reader — a developer triaging findings, a ticket-body reader months later, or a downstream automated surface — understands the problem without re-reading the file. Treat it as the most important prose field in your output; every downstream surface (walk-through questions, bulk-action previews, ticket bodies, headless output) depends on it being good.
|
||||
|
||||
- **Lead with observable behavior.** Describe what the bug does from the outside — what a user, attacker, operator, or downstream caller experiences. Do not lead with code structure ("The function X does Y..."). Start with the effect ("Any signed-in user can read another user's orders..."). Function and variable names appear later, only when the reader needs them to locate the issue.
|
||||
- **Explain why the fix resolves the problem.** If you include a `suggested_fix`, the `why_it_matters` should make clear why that specific fix addresses the root cause. When a similar pattern exists elsewhere in the codebase (an existing guard, an established convention, a parallel handler), reference it so the recommendation is grounded in the project's own conventions rather than theoretical best practice.
|
||||
- **Keep it tight.** Approximately 2-4 sentences plus the minimum code quoted inline to ground the point. Longer framings are a regression — downstream surfaces have narrow display budgets, and verbose `why_it_matters` content gets truncated or skimmed.
|
||||
- **Always produce substantive content.** `why_it_matters` is required by the schema. Empty strings, nulls, and single-phrase entries are validation failures. If you found something worth flagging at anchor `50` or higher, you can explain it — the field exists because every finding needs a reason.
|
||||
|
||||
Illustrative pair — same finding, weak vs. strong framing:
|
||||
|
||||
```
|
||||
WEAK (code-citation first; fails the observable-behavior rule):
|
||||
orders_controller.rb:42 has a missing authorization check.
|
||||
Add current_user.owns?(account) guard before the query.
|
||||
|
||||
STRONG (observable behavior first, grounded fix reasoning):
|
||||
Any signed-in user can read another user's orders by pasting the
|
||||
target account ID into the URL. The controller looks up the account
|
||||
and returns its orders without verifying the current user owns it.
|
||||
Adding a one-line ownership guard before the lookup matches the
|
||||
pattern already used in the shipments controller for the same attack.
|
||||
```
|
||||
|
||||
False-positive categories to actively suppress. Do NOT emit a finding when any of these apply — not even at anchor `25` or `50`. These are not edge cases you should route to soft buckets; they are non-findings.
|
||||
|
||||
- **Pre-existing issues unrelated to this diff.** Mark `pre_existing: true` only for unchanged code the diff does not interact with. If the diff makes a previously-dormant issue newly relevant (e.g., changes a caller in a way that exposes a bug downstream), it is a secondary finding, not pre-existing. PR-comment and headless externalization filter pre-existing entirely; interactive review surfaces them in a separate section.
|
||||
- **Pedantic style nitpicks that a linter or formatter would catch.** Missing semicolons, indentation, import ordering, unused-variable warnings the project's tooling already catches. Style belongs to the toolchain.
|
||||
- **Code that looks wrong but is intentional.** Check comments, commit messages, PR description, or surrounding code for evidence of intent before flagging. A persona-flagged "missing null check" guarded by an upstream `.present?` call is a false positive.
|
||||
- **Issues already handled elsewhere.** Check callers, guards, middleware, framework defaults, and parallel handlers before flagging. If a controller's input is already validated by a parent middleware, the controller-level check the persona wants to add is redundant.
|
||||
- **Suggestions that restate what the code already does in different words.** "Consider extracting this into a helper" when the code is already a small helper, "consider adding a guard" when a guard one line up already enforces it.
|
||||
- **Generic "consider adding" advice without a concrete failure mode.** If you cannot name what breaks, the finding is not actionable. Either find the failure mode or suppress.
|
||||
- **Issues with a relevant lint-ignore comment.** Code that carries an explicit lint disable comment for the rule you are about to flag (`eslint-disable-next-line no-unused-vars`, `# rubocop:disable Style/StringLiterals`, `# noqa: E501`, etc.) — suppress unless the suppression itself violates a project-standards rule that explicitly forbids disabling that lint for this code shape. The author already chose to suppress; re-flagging it via a different reviewer creates noise and ignores their decision.
|
||||
- **General code-quality concerns not codified in CLAUDE.md / AGENTS.md.** "This file is getting long," "this method has too many parameters," "this is hard to read" — without a project-standards rule to anchor the concern, these are subjective and waste reviewer time. If the project explicitly bans long files or sets a parameter-count limit in its standards, that is a project-standards finding; otherwise suppress.
|
||||
- **Speculative future-work concerns with no current signal.** "This might break under load," "what if the requirements change," "this could be hard to test later" — not findings unless the diff introduces concrete evidence the concern is reachable now.
|
||||
|
||||
**Advisory observations — route to advisory autofix_class, do not force a decision.** If the honest answer to "what actually breaks if we do not fix this?" is "nothing breaks, but…", the finding is advisory. Set `autofix_class: advisory` and `confidence: 50` so synthesis routes the finding to a soft bucket rather than surfacing it as a primary action item. Do not suppress — the observation may have value; it just does not warrant user judgment. Typical advisory shapes: design asymmetry the PR improves but does not fully resolve, opportunity to consolidate two similar helpers when neither is broken, residual risk worth noting in the report.
|
||||
|
||||
**Precedence over the false-positive catalog.** The false-positive catalog above is stricter than the advisory rule — if a shape matches the FP catalog, it is a non-finding and must be suppressed entirely. Do NOT route it to anchor `50` / advisory. The advisory rule applies only to shapes that are NOT in the FP catalog.
|
||||
|
||||
Rules:
|
||||
- You are a leaf reviewer inside an already-running compound-engineering review workflow. Do not invoke compound-engineering skills or agents unless this template explicitly instructs you to. Perform your analysis directly and return findings in the required output format only.
|
||||
- Suppress any finding you cannot honestly anchor at `50` or higher (the actionable floor is `50`; anchors `0` and `25` are suppressed by synthesis anyway, so emitting them only adds noise). If your persona's domain description sets a stricter floor (e.g., anchor `75` minimum), honor it.
|
||||
- Every finding in the full artifact file MUST include at least one evidence item grounded in the actual code. The compact return omits evidence -- the evidence requirement applies to the disk artifact only.
|
||||
- Set `pre_existing` to true ONLY for issues in unchanged code that are unrelated to this diff. If the diff makes the issue newly relevant, it is NOT pre-existing.
|
||||
- You are operationally read-only. The one permitted exception is writing your full analysis to the `.context/` artifact path when a run ID is provided. You may also use non-mutating inspection commands, including read-oriented `git` / `gh` commands, to gather evidence. Do not edit project files, change branches, commit, push, create PRs, or otherwise mutate the checkout or repository state.
|
||||
- Set `autofix_class` accurately. The classification governs whether the fixer applies the change automatically (`safe_auto`) or surfaces it for explicit review (`gated_auto` / `manual` / `advisory`). **The wrong-side cost is symmetric:** classifying a contract-change as `safe_auto` produces an unwanted edit; classifying a mechanical fix as `gated_auto` makes the user manually triage findings the fixer could have applied. Bias toward `safe_auto` when the rubric permits it. Use this decision guide:
|
||||
- `safe_auto`: The fix is local and deterministic — the fixer can apply it mechanically. **The test:** you can articulate the fix in one sentence with no "depends on" clauses, AND applying it doesn't change any of {function signature, public-API/response contract, error contract, security posture, permission model}. Examples: extracting a duplicated helper, adding a missing nil/null guard inside an internal function, fixing an off-by-one when the parallel pattern is in scope, adding a missing test for an existing public method, removing dead code, removing an unused import.
|
||||
|
||||
**Boundary cases that often feel risky but are still `safe_auto`:**
|
||||
- A nil guard that turns a crash into a nil-return is `safe_auto` when the function is internal and no public-API/error contract is documented. The contract is the function body itself — adding a precondition check isn't a behavior change worth gating.
|
||||
- An off-by-one fix is `safe_auto` when the corrected behavior is verifiable from a parallel pattern visible in the surrounding code or from explicit documentation. Matching an established pattern isn't a design decision.
|
||||
- Dead-code removal is `safe_auto` when the code's deadness is signaled in scope: no callers reachable from the diff, in-file comment says "superseded" / "unused" / "no callers", or the surrounding refactor obviously displaces it. "Someone might want this someday" isn't a design call the reviewer is empowered to make.
|
||||
- Helper extraction is `safe_auto` when the duplication is identical, all callers update in lockstep within the same diff, and the consolidation point is mechanical (a shared method on the same class, or a new helper named after the shared shape). Cross-file extraction qualifies when both files ship in the same diff and the shared shape dictates the name. The discriminator is whether **naming or placement requires a design conversation** ("service object vs concern? where does it live in the layering?"). If yes, gated_auto. If the name follows mechanically from the body, safe_auto.
|
||||
|
||||
- `gated_auto`: A concrete fix exists but applying it changes a contract, permission, or module boundary in a way the user should approve before it lands. Examples: adding authentication to an unprotected endpoint, changing a public API response shape (even by narrowing fields), switching from soft-delete to hard-delete, modifying error-handling in ways downstream callers can observe.
|
||||
- `manual`: Actionable work that requires design decisions or cross-cutting changes. Examples: redesigning a data model, choosing between two equally-defensible architectural approaches, adding pagination to an unbounded query when no parallel pattern exists. **Pair `manual` with a concrete `suggested_fix` whenever you can defend one from the diff and surrounding code** — see the suggested_fix rule below. Omit `suggested_fix` only when the fix genuinely requires cross-team input, business context, or research outside this review.
|
||||
- `advisory`: Report-only items that should not become code-fix work. Examples: noting a design asymmetry the PR improves but doesn't fully resolve, flagging a residual risk, deployment notes.
|
||||
|
||||
Do not default to `advisory` when uncertain — if a concrete fix is obvious, classify it as `safe_auto` or `gated_auto`. Do not default to `gated_auto` when the fix is mechanical but the change feels substantive — apply the safe_auto test above. The "feels risky" reflex is exactly the asymmetry this rubric is designed to neutralize.
|
||||
- Set `owner` to the default next actor for this finding: `review-fixer`, `downstream-resolver`, `human`, or `release`.
|
||||
- Set `requires_verification` to true whenever the likely fix needs targeted tests, a focused re-review, or operational validation before it should be trusted.
|
||||
- **Propose a `suggested_fix` whenever any defensible code change is reachable from the diff and surrounding code.** This is the persona's commitment that "I, the reviewer with the diff and evidence in front of me, can articulate what the fix looks like." The suggested fix becomes the authoritative signal that downstream surfaces use to decide whether the agent can act on the finding. Three rules:
|
||||
- **Defensible from review context:** the fix should be reachable from the diff, the cited code, parallel patterns elsewhere in the repo, or framework conventions you can verify. If you cannot ground the fix in evidence the reader can check, omit it.
|
||||
- **Concrete, not generic:** "add a guard before the query" with the specific guard named is concrete; "consider adding validation" is generic. Generic advice is suppressed by the false-positive catalog above.
|
||||
- **Imperfect information is not grounds for omission.** When you don't have full context for the optimal fix, propose the most defensible default and name the assumption. Do not omit because "the right answer depends on X" — name the assumption you're making, propose the default, and let the user override.
|
||||
Examples of imperfect-info findings that should still get a `suggested_fix`:
|
||||
- Pagination strategy unclear → propose offset pagination matching the existing pattern at `file:line`, with assumption named. If product needs cursor-based, the user can switch.
|
||||
- Rate limit value uncertain → propose the value that matches existing rate limits in the project, with assumption named. The user can tune.
|
||||
- Auth model unknown → propose authentication via the existing middleware pattern at `file:line`, with assumption named. If a different service owns the auth flow, the user can route through it.
|
||||
The "I need `<specific input>` before I can commit" framing is a soft punt. The question to ask instead is "what code change would I propose if I had to choose now?" — and propose that, with the assumption named so the user can correct it.
|
||||
- **Genuinely-omit cases are rare.** Omit `suggested_fix` only when there is no code-level change to propose — for example:
|
||||
- The finding is a question, not a fix request: "What is the intended SLA here?" with no clear default to assume.
|
||||
- The resolution is purely organizational with no code component: legal sign-off, business policy decision, or a process change that doesn't touch code.
|
||||
These shapes are the exception, not the norm. Most "manual" findings in code review have a defensible code-level proposal even when context is incomplete. A `manual` finding without `suggested_fix` routes to the best-judgment path's `failed` bucket with reason "no fix proposed by reviewer" — owning that omission is the persona's responsibility.
|
||||
A bad fix suggestion is still worse than none — the false-positive catalog and grounding rule above prevent that. The bias is toward proposing when you can; the omission case is narrow.
|
||||
- If you find no issues, return an empty findings array. Still populate residual_risks and testing_gaps if applicable.
|
||||
- **Intent verification:** Compare the code changes against the stated intent (and PR title/body when available). If the code does something the intent does not describe, or fails to do something the intent promises, flag it as a finding. Mismatches between stated intent and actual code are high-value findings.
|
||||
</output-contract>
|
||||
|
||||
<pr-context>
|
||||
{pr_metadata}
|
||||
</pr-context>
|
||||
|
||||
<review-context>
|
||||
Run ID: {run_id}
|
||||
Reviewer name: {reviewer_name}
|
||||
|
||||
Intent: {intent_summary}
|
||||
|
||||
Changed files: {file_list}
|
||||
|
||||
Diff:
|
||||
{diff}
|
||||
</review-context>
|
||||
```
|
||||
|
||||
## Variable Reference
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| `{persona_file}` | Agent markdown file content | The full persona definition (identity, failure modes, calibration, suppress conditions) |
|
||||
| `{diff_scope_rules}` | `references/diff-scope.md` content | Primary/secondary/pre-existing tier rules |
|
||||
| `{schema}` | `references/findings-schema.json` content | The JSON schema reviewers must conform to |
|
||||
| `{intent_summary}` | Stage 2 output | 2-3 line description of what the change is trying to accomplish |
|
||||
| `{pr_metadata}` | Stage 1 output | PR title, body, and URL when reviewing a PR. Empty string when reviewing a branch or standalone checkout |
|
||||
| `{file_list}` | Stage 1 output | List of changed files from the scope step |
|
||||
| `{diff}` | Stage 1 output | The actual diff content to review |
|
||||
| `{run_id}` | Stage 4 output | Unique review run identifier for the artifact directory |
|
||||
| `{reviewer_name}` | Stage 3 output | Persona or agent name used as the artifact filename stem |
|
||||
@@ -0,0 +1,149 @@
|
||||
# Tracker Detection and Defer Execution
|
||||
|
||||
This reference covers how Defer actions file tickets in the project's tracker. It is loaded by `SKILL.md` when Interactive mode's routing question needs to decide whether to offer option C (File tickets), when the walk-through's Defer option executes, and when the bulk-preview of option C is shown. It is also loaded by autonomous callers (e.g., `lfg`) that need to file residual actionable findings without user prompts — see Execution Modes below.
|
||||
|
||||
---
|
||||
|
||||
## Execution Modes
|
||||
|
||||
Tracker-defer has two execution modes. The caller selects one; the detection, fallback chain, and ticket composition are shared.
|
||||
|
||||
### Interactive mode (default)
|
||||
|
||||
Used by `ce-code-review` Interactive mode's routing question, walk-through Defer actions, and bulk-preview option C. All user-facing prompts fire:
|
||||
|
||||
- First Defer of the session with a generic (non-named) label confirms the effective tracker choice.
|
||||
- Execution failures prompt with Retry / Fall back to next sink / Convert to Skip.
|
||||
- Labels in the routing question reflect `named_sink_available` (name the tracker) vs fallback generics.
|
||||
|
||||
### Non-interactive mode
|
||||
|
||||
Used by autonomous callers like `lfg` that must not prompt. All blocking questions are skipped; the fallback chain is executed silently in order. Behavior:
|
||||
|
||||
- No confirmation on the first generic-label Defer; proceed directly.
|
||||
- On execution failure, automatically fall to the next tier without prompting. Record the failure.
|
||||
- On total chain exhaustion (every tier failed or no sink available), return findings in the `no_sink` bucket so the caller can route them to another surface (e.g., inline them in a PR description).
|
||||
- Return a structured result: `{ filed: [{ finding_id, tracker, url }], failed: [{ finding_id, tracker, reason }], no_sink: [{ finding_id, title, severity, file, line }] }`.
|
||||
|
||||
The caller decides how to surface the result to the user. The non-interactive mode treats "no sink available" as a data-producing outcome, not a prompt trigger.
|
||||
|
||||
---
|
||||
|
||||
## Detection
|
||||
|
||||
The agent determines the project's tracker from whatever documentation is obvious. Primary sources: `CLAUDE.md` and `AGENTS.md` at the repo root and in relevant subdirectories. Supplementary signals (when primary documentation is ambiguous): `CONTRIBUTING.md`, `README.md`, PR templates under `.github/`, visible tracker URLs in the repo.
|
||||
|
||||
A tracker can be surfaced via MCP tool (e.g., a Linear MCP server), CLI (e.g., `gh`), or direct API. All are acceptable. The detection output is a tuple with two availability flags — one for the named tracker specifically (drives label confidence in Interactive mode) and one for the full fallback chain (drives whether Defer is offered at all):
|
||||
|
||||
```
|
||||
{ tracker_name, confidence, named_sink_available, any_sink_available }
|
||||
```
|
||||
|
||||
Where:
|
||||
- `tracker_name` — human-readable name ("Linear", "GitHub Issues", "Jira"), or `null` when detection cannot identify a specific tracker
|
||||
- `confidence` — `high` when the tracker is named explicitly in documentation (or via a linked URL to a specific project/workspace) and is unambiguously the project's canonical tracker; `low` when the signal is thin, conflicting, or implied only
|
||||
- `named_sink_available` — `true` only when the agent can actually invoke the detected tracker (MCP tool is loaded, CLI is authenticated, or API credentials are in environment); `false` when the tracker is documented but no tool reaches it, or when no tracker is found at all. Drives label confidence: inline tracker naming requires this to be `true`.
|
||||
- `any_sink_available` — `true` when any tier in the fallback chain (named tracker or GitHub Issues via `gh`) can be invoked this session. Drives whether Defer is offered in Interactive mode, and drives the `no_sink` bucket in Non-interactive mode.
|
||||
|
||||
Detection is reasoning-based. Do not maintain an enumerated checklist of files to read. Read the obvious sources and form a confident conclusion; when the obvious sources don't resolve, the label falls back to generic wording and the agent confirms with the user before executing (Interactive mode only).
|
||||
|
||||
---
|
||||
|
||||
## Probe timing and caching
|
||||
|
||||
Availability probes run **at most once per session** and **only when Defer execution is imminent**. Never speculatively at review start, never per-Defer, never per-walk-through-finding. The cached tuple is reused for every Defer action in the same run.
|
||||
|
||||
Typical probe sequence:
|
||||
|
||||
1. Read `CLAUDE.md` / `AGENTS.md` for tracker references. If nothing found, set `tracker_name = null`, `confidence = low`.
|
||||
2. **Probe the named tracker when one was found.** For GitHub Issues, run `gh auth status` and `gh repo view --json hasIssuesEnabled`. For Linear or other MCP-backed trackers, verify the relevant MCP tool is loaded and responsive. For API-backed trackers, verify credentials in environment. Set `named_sink_available` from the probe result.
|
||||
3. **Probe the GitHub Issues fallback to compute `any_sink_available`.** Even when the named tracker was found and probed, `gh` matters for the `no_sink` bucket decision so that a run with no documented tracker but working `gh` still offers Defer.
|
||||
- If `named_sink_available = true`: `any_sink_available = true` (no further probes needed).
|
||||
- Otherwise, probe GitHub Issues via `gh auth status` + `gh repo view --json hasIssuesEnabled` (skip if already probed in step 2). If it works, `any_sink_available = true`.
|
||||
- Otherwise, `any_sink_available = false`.
|
||||
|
||||
When Interactive mode's routing question is skipped entirely (R2 zero-findings case), no probes run. When the cached tuple is reused across a session, any `named_sink_available = true` from the session's first probe stays cached — do not re-probe per Defer.
|
||||
|
||||
---
|
||||
|
||||
## Label logic (Interactive mode)
|
||||
|
||||
- When `confidence = high` AND `named_sink_available = true`: the routing question's option C and the walk-through's per-finding Defer option both include the tracker name verbatim. Example: `File a Linear ticket per finding`, `Defer — file a Linear ticket`.
|
||||
- When `any_sink_available = true` but either `confidence = low` or `named_sink_available = false` (a fallback tier is working instead): the labels read generically — `File an issue per finding`, `Defer — file a ticket`. Before executing the first Defer of the session, the agent confirms the effective tracker choice with the user using the platform's blocking question tool.
|
||||
- When `any_sink_available = false`: option C is omitted from the routing question, option B (Defer) is omitted from the walk-through per-finding options, and the agent tells the user why in the routing question's stem.
|
||||
|
||||
Non-interactive mode skips label decisions entirely — it acts silently on the detected sink.
|
||||
|
||||
---
|
||||
|
||||
## Fallback chain
|
||||
|
||||
When the named tracker is unavailable or no tracker is named, fall back in this order. Prefer the project's detected tracker; use `gh` only when no named tracker was found or the named one is unreachable.
|
||||
|
||||
1. **Named tracker** (MCP tool, CLI, or API the agent can invoke directly, identified via Detection above)
|
||||
2. **GitHub Issues via `gh`** — when `gh auth status` succeeds and the current repo has issues enabled (`gh repo view --json hasIssuesEnabled` returns `true`)
|
||||
3. **No sink** — findings remain in the review report's residual-work section (Interactive mode) or are returned in the `no_sink` bucket for the caller to route (Non-interactive mode). The agent does not re-display them through a transient surface.
|
||||
|
||||
Previously this chain included a third in-session fallback tier. That tier was removed because in-session tasks do not survive past the session and therefore do not meet the "durable filing" intent of a Defer action. When no durable tracker exists, the correct behavior is to leave findings in the report (Interactive) or return them to the caller (Non-interactive).
|
||||
|
||||
---
|
||||
|
||||
## Ticket composition
|
||||
|
||||
Every Defer action creates a ticket with the following content, adapted to the tracker's capabilities:
|
||||
|
||||
- **Title:** the merged finding's `title` (schema-capped at 10 words).
|
||||
- **Body:**
|
||||
- Plain-English problem statement — reads the persona-produced `why_it_matters` from the contributing reviewer's artifact file at `/tmp/compound-engineering/ce-code-review/<run-id>/{reviewer}.json`, using the same `file + line_bucket(line, +/-3) + normalize(title)` matching headless mode uses (see SKILL.md Stage 6 detail enrichment). Falls back to the merged finding's `title`, `severity`, `file`, and `suggested_fix` (when present) when no artifact match is available — these fields are guaranteed in the merge-tier compact return.
|
||||
- Suggested fix (when present in the finding's `suggested_fix`).
|
||||
- Evidence (direct quotes from the reviewer's artifact).
|
||||
- Metadata block: `Severity: <level>`, `Confidence: <score>`, `Reviewer(s): <list>`, `Finding ID: <fingerprint>`.
|
||||
- **Labels** (when the tracker supports labels): severity tag (`P0`, `P1`, `P2`, `P3`) and, when the tracker convention supports it, a category label sourced from the reviewer name.
|
||||
- **Length cap:** when the composed body would exceed a tracker's body length limit, truncate with `... (continued in ce-code-review run artifact: /tmp/compound-engineering/ce-code-review/<run-id>/)` and include the finding_id in both the truncated body and the metadata block so the artifact is discoverable.
|
||||
|
||||
The finding_id is a stable fingerprint composed as `normalize(file) + line_bucket(line, +/-3) + normalize(title)` — the same fingerprint used by the merge pipeline.
|
||||
|
||||
---
|
||||
|
||||
## Failure path
|
||||
|
||||
When ticket creation fails at execution (API error, auth expiry mid-session, rate limit, malformed body rejected, 4xx/5xx response):
|
||||
|
||||
**Interactive mode:** surface the failure inline and ask the user using the platform's blocking question tool.
|
||||
|
||||
Stem:
|
||||
> Defer failed: <tracker name> returned <error summary>. How should the agent handle this finding?
|
||||
|
||||
Options:
|
||||
- `Retry on <tracker>` — re-attempt the same tracker once more (useful for transient errors)
|
||||
- `Fall back to next sink` — move this finding's Defer to the next tier in the fallback chain (e.g., from Linear to GitHub Issues)
|
||||
- `Convert to Skip — record the failure` — abandon this Defer, note the failure in the completion report's failure section, and continue the walk-through or bulk flow
|
||||
|
||||
**Non-interactive mode:** do not prompt. Automatically fall through to the next tier. If every tier fails, record the finding in the `failed` bucket of the structured return and continue. If the chain exhausts with no sink ever available, the finding ends up in the `no_sink` bucket.
|
||||
|
||||
When a high-confidence named tracker fails at execution, the cached `named_sink_available` is set to `false` for the rest of the session. Subsequent Defer actions fall straight through to the next tier without retrying a confirmed-broken sink. `any_sink_available` is only downgraded to `false` when every tier has been confirmed broken — a failed Linear call that succeeds via `gh` keeps `any_sink_available = true`.
|
||||
|
||||
Only when `ToolSearch` explicitly returns no match or the tool call errors — or on a platform with no blocking question tool — fall back to numbered options and waiting for the user's reply (Interactive mode only).
|
||||
|
||||
---
|
||||
|
||||
## Per-tracker behavior
|
||||
|
||||
Concrete behavior per tracker at execution time. The agent may invoke any of these through the appropriate interface (MCP, CLI, or API) — the choice depends on what is available in the current environment.
|
||||
|
||||
| Tracker | Interface | Invocation sketch | Body format | Labels |
|
||||
|---------|-----------|-------------------|-------------|--------|
|
||||
| Linear | MCP (preferred) or API | Create issue in the project/workspace identified by documentation; assign to the reporter if the MCP tool exposes user context | Markdown | Severity priority field if the MCP exposes it; otherwise include severity in body |
|
||||
| GitHub Issues | `gh issue create` | Repo defaults to the current repo. Use `--label` for severity tag when labels exist; omit `--label` if the repo has no label fixture. Fall back to a label-less issue on first failure. | Markdown | `--label P0` / `--label P1` / etc. when labels exist |
|
||||
| Jira | MCP or API | Create issue in the project identified by documentation; Jira's markdown dialect differs from GitHub's — use plain text in the body when MCP does not handle conversion | Plain text when MCP does not handle markdown | Severity priority field |
|
||||
| No sink available | — | Interactive: Defer option omitted, findings remain in the report's residual-work section. Non-interactive: findings returned in the `no_sink` bucket for caller routing. | — | — |
|
||||
|
||||
When uncertain, prefer "drop with explicit user-facing notice" over "pass through silently and hope." A Defer that produces no durable artifact and no user message is data loss.
|
||||
|
||||
---
|
||||
|
||||
## Cross-platform notes
|
||||
|
||||
The question-tool name varies by platform. In Interactive mode, use the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)). In Claude Code the tool should already be loaded from the Interactive-mode pre-load step — if it isn't, call `ToolSearch` with query `select:AskUserQuestion` now. Fall back to numbered options in chat only when the harness genuinely lacks a blocking tool — `ToolSearch` returns no match, the tool call explicitly fails, or the runtime mode does not expose it (e.g., Codex edit modes without `request_user_input`). A pending schema load is not a fallback trigger. Never silently skip the question.
|
||||
|
||||
Non-interactive mode is platform-agnostic: it never prompts, so the platform's question tool is not relevant.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Validator Sub-agent Prompt Template
|
||||
|
||||
This template is used by Stage 5b to spawn one validator sub-agent per surviving finding before externalization. The validator's job is **independent re-verification**, not re-reasoning. It is a fresh second opinion, not a critic of the original persona's analysis.
|
||||
|
||||
---
|
||||
|
||||
## Template
|
||||
|
||||
```
|
||||
You are an independent validator for a code review finding. Another reviewer flagged the issue described below. Your job is to verify whether the finding holds up under fresh inspection.
|
||||
|
||||
You have no commitment to the original finding. If it is wrong, say so. False positives are common; do not feel pressure to confirm.
|
||||
|
||||
<finding-to-validate>
|
||||
Title: {finding_title}
|
||||
Severity: {finding_severity}
|
||||
File: {finding_file}
|
||||
Line: {finding_line}
|
||||
|
||||
Why it matters (the original reviewer's framing):
|
||||
{finding_why_it_matters}
|
||||
|
||||
Suggested fix (if any):
|
||||
{finding_suggested_fix}
|
||||
|
||||
Original reviewer: {finding_reviewer}
|
||||
Confidence anchor: {finding_confidence}
|
||||
</finding-to-validate>
|
||||
|
||||
<diff>
|
||||
{diff}
|
||||
</diff>
|
||||
|
||||
<scope-context>
|
||||
The diff above is the full change being reviewed. The finding is about file {finding_file} around line {finding_line}. Use read tools (Read, Grep, Glob, git blame) to inspect the cited code and its callers, guards, middleware, or framework defaults that might handle the concern elsewhere.
|
||||
</scope-context>
|
||||
|
||||
Your task is to answer three questions:
|
||||
|
||||
1. **Is the issue real in the code as written?** Read the cited file and surrounding code. If the code does not actually have the problem the finding describes, the finding is invalid. Common false-positive shapes:
|
||||
- The persona missed an existing guard / null check / validation that handles the case
|
||||
- The persona misread types or signatures
|
||||
- The persona flagged a pattern that is intentional in this codebase (check comments, parallel handlers, project conventions)
|
||||
|
||||
2. **Is the issue introduced by THIS diff?** Use git blame or diff inspection. If the cited line predates this PR's commits and the diff does not interact with it (does not call into it, does not change its callers in a way that newly exposes the issue), the finding is pre-existing — not validated for externalization regardless of whether it is a real issue.
|
||||
|
||||
3. **Is the issue not handled elsewhere?** Look for guards in callers, middleware in the request chain, framework defaults, type system constraints, or parallel handlers that already address the concern. If the issue is functionally prevented by surrounding infrastructure, the finding is invalid.
|
||||
|
||||
Return ONLY this JSON, no prose:
|
||||
|
||||
```json
|
||||
{
|
||||
"validated": true | false,
|
||||
"reason": "<one sentence explaining the verdict>"
|
||||
}
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- `{ "validated": true, "reason": "Cited line is new in this diff and lacks the ownership guard used by parallel controllers." }`
|
||||
- `{ "validated": false, "reason": "Line 87 already guards user.email with .present? check; the null deref the finding describes cannot occur." }`
|
||||
- `{ "validated": false, "reason": "Cited line dates to 2024-08 (pre-existing); diff does not modify or interact with it." }`
|
||||
- `{ "validated": false, "reason": "Framework handles the timeout case via Faraday default; no application-level retry needed." }`
|
||||
|
||||
Rules:
|
||||
- Be honest. If the original reviewer was right, validate. If they were wrong, reject. Conservative bias is preferred — when in doubt, reject.
|
||||
- Do not invent new findings. Your scope is this one finding; surface anything else as a no-vote with reason.
|
||||
- Do not edit, commit, push, or modify any files. You are operationally read-only.
|
||||
- If you cannot read the cited file, return `{ "validated": false, "reason": "Could not access file path to verify." }` rather than guessing.
|
||||
- Return JSON only. No prose, no markdown, no explanation outside the JSON object.
|
||||
```
|
||||
|
||||
## Variable Reference
|
||||
|
||||
| Variable | Source | Description |
|
||||
|----------|--------|-------------|
|
||||
| `{finding_title}` | Stage 5 merged finding | The persona's title for the issue |
|
||||
| `{finding_severity}` | Stage 5 merged finding | P0 / P1 / P2 / P3 |
|
||||
| `{finding_file}` | Stage 5 merged finding | Repo-relative file path |
|
||||
| `{finding_line}` | Stage 5 merged finding | Primary line number |
|
||||
| `{finding_why_it_matters}` | Per-agent artifact file (detail tier) | Loaded from disk for this validation; required for the validator to understand the finding |
|
||||
| `{finding_suggested_fix}` | Stage 5 merged finding (optional) | Pass empty string if not present |
|
||||
| `{finding_reviewer}` | Stage 5 merged finding | Original persona name (informational; helps validator interpret the framing) |
|
||||
| `{finding_confidence}` | Stage 5 merged finding | The persona's anchor (informational) |
|
||||
| `{diff}` | Stage 1 output | Full diff for context |
|
||||
@@ -0,0 +1,249 @@
|
||||
# Per-finding Walk-through
|
||||
|
||||
This reference defines Interactive mode's per-finding walk-through — the path the user enters by picking option A (`Review each finding one by one — accept the recommendation or choose another action`) from the routing question. It also covers the unified completion report that every terminal path (walk-through, best-judgment, File tickets, zero findings) emits.
|
||||
|
||||
Interactive mode only.
|
||||
|
||||
---
|
||||
|
||||
## Entry
|
||||
|
||||
The walk-through receives, from the orchestrator:
|
||||
|
||||
- The merged findings list in severity order (P0 → P1 → P2 → P3), filtered to `gated_auto` and `manual` findings that survived the Stage 5 anchor gate (anchor 75+, with P0 escape at anchor 50). Advisory findings are included when they were surfaced to this phase (advisory findings normally live in the report-only queue, but when the review flow routes them here for acknowledgment they take the advisory variant below).
|
||||
- The cached tracker-detection tuple from `tracker-defer.md` (`{ tracker_name, confidence, named_sink_available, any_sink_available }`). `any_sink_available` determines whether the Defer option is offered; `named_sink_available` + `confidence` determine whether the label names the tracker inline.
|
||||
- The run id for artifact lookups.
|
||||
|
||||
Each finding's recommended action has already been normalized by Stage 5 (step 7b — tie-break on action). The walk-through surfaces that recommendation to the user but does not recompute it.
|
||||
|
||||
---
|
||||
|
||||
## Per-finding presentation
|
||||
|
||||
Each finding is presented in two parts: a **terminal output block** carrying the explanation, and a **question** via the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)) carrying the decision. Never merge the two — the terminal block uses markdown; the question uses plain text.
|
||||
|
||||
In Claude Code the tool should already be loaded from the Interactive-mode pre-load step in `SKILL.md` — if it isn't, call `ToolSearch` with query `select:AskUserQuestion` now. Fall back to presenting the per-finding options as a numbered list only when the harness genuinely lacks a blocking tool — `ToolSearch` returns no match, the tool call explicitly fails, or the runtime mode does not expose it (e.g., Codex edit modes without `request_user_input`). A pending schema load is not a fallback trigger. Never silently skip the question.
|
||||
|
||||
### Terminal output block (print before firing the question)
|
||||
|
||||
Render as markdown. Labels on their own line, blank lines between sections:
|
||||
|
||||
```
|
||||
## Finding {N} of {M} — {severity} {plain-English title}
|
||||
|
||||
{file}:{line}
|
||||
|
||||
**What's wrong**
|
||||
|
||||
{plain-English problem statement from why_it_matters}
|
||||
|
||||
**Proposed fix**
|
||||
|
||||
{suggested_fix — rendered per the substitution rules below: prose-first, intent-language}
|
||||
|
||||
**Why it works**
|
||||
|
||||
{short reasoning, grounded in a codebase pattern when available}
|
||||
|
||||
{R15 conflict context line, when applicable}
|
||||
```
|
||||
|
||||
Substitutions:
|
||||
|
||||
- **`{plain-English title}`:** a 3-8 word summary suitable as a heading. Derived from the merged finding's `title` field but rephrased so it reads as observable behavior (e.g., "Path traversal in loadUserFromCache" rather than "Missing userId validation on line 36").
|
||||
- **`why_it_matters`:** read the contributing reviewer's artifact file at `/tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json` using the same `file + line_bucket(line, +/-3) + normalize(title)` matching that headless mode uses (see `SKILL.md` Stage 6 detail enrichment). When multiple reviewers flagged the merged finding, try them in the order they appear in the merged finding's reviewer list. Use the first match.
|
||||
- **`suggested_fix`:** from the merged finding's `suggested_fix` field. Render as prose describing **intent**, not as syntax. The fixer subagent owns the exact code — the walk-through just needs enough for the user to trust or reject the action. Rules:
|
||||
- **Default — one sentence describing the effect.** What does the fix achieve, and where does it live? Prefer intent language over quoted code.
|
||||
- ✅ `Throw on non-2xx response before parsing JSON.`
|
||||
- ✅ `` Replace `==` with `===` on line 42. ``
|
||||
- ✅ `` Add a `response.ok` check after the fetch and throw on non-2xx. ``
|
||||
- ✅ `Extract the request-building logic into a helper and call it from both sites.`
|
||||
- ❌ `` Add `if (!response.ok) throw new Error(`HTTP ${response.status}`);` after the `await fetch(...)` call, before `response.json()`. `` — nested backticks, multiple code spans, full statement quoted; renders broken in terminal.
|
||||
- **Code-span budget: at most 2 inline backtick spans per sentence, each a single identifier, operator, or short phrase** (e.g., `` `response.ok` ``, `` `===` ``, `` `fetchUserById` ``). Never embed full statements, template literals, or code requiring nested backticks. If the intent can't be stated within that budget, the prose is too close to syntax — restate at a higher level, or switch to summary + artifact pointer.
|
||||
- **Always leave a space before and after every backtick span.** Without it, the terminal's markdown renderer eats the delimiters and runs the words together.
|
||||
- **Raw code block — only for short (≤5 line) genuinely additive new code** where no before-state exists (new file, new function, new guard at the top of an empty body). Above 5 lines, switch to summary + pointer.
|
||||
- **Summary + artifact pointer** — when prose can't capture the fix: one-sentence transformation + key symbol/location + `Full fix: /tmp/compound-engineering/ce-code-review/{run_id}/{reviewer_name}.json → findings[].suggested_fix`.
|
||||
- **No diff blocks.** Modifications to existing code render as prose.
|
||||
- **`Why it works`:** grounded reasoning that, where possible, references a similar pattern already used elsewhere in the codebase (e.g., "matches the format-validation pattern already used at src/cli/io.ts:41"). One to three sentences.
|
||||
- **R15 conflict context line (when applicable):** when contributing reviewers implied different actions for this finding and Stage 5 step 7b broke the tie, surface that briefly. Example: `Correctness recommends Apply; Testing recommends Skip (low confidence). Agent's recommendation: Skip.` The orchestrator's recommendation — the post-tie-break value — is what the menu labels "recommended."
|
||||
|
||||
When no artifact match exists for the finding (merge-synthesized finding, or the persona's artifact write failed), the terminal block degrades to the heading + `suggested_fix` only (omit the `What's wrong` and `Why it works` sections) and records the gap for the Coverage section of the completion report.
|
||||
|
||||
### Question stem (short, decision-focused)
|
||||
|
||||
After the terminal block renders, fire the platform's blocking question tool with a compact two-line stem:
|
||||
|
||||
```
|
||||
Finding {N} of {M} — {severity} {short handle}.
|
||||
{Action framing in a phrase}?
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- **Short handle:** matches the `{plain-English title}` from the terminal block heading.
|
||||
- **Action framing:** one phrase describing what the *single recommended action* does, as a yes/no question. Examples: `Apply the format-validation + path.resolve guard?`, `Skip the fix since the fixture is being deleted?`, `Defer and file a rotation ticket?`.
|
||||
|
||||
Never enumerate alternatives in the stem. One recommendation as a yes/no — the option list carries the alternatives. When the recommendation is close, surface the disagreement in the R15 conflict context line, not as a multi-option stem.
|
||||
|
||||
Example (recommendation = Apply):
|
||||
|
||||
```
|
||||
Finding 3 of 8 — P1 path traversal in loadUserFromCache.
|
||||
Apply the format-validation + path.resolve guard?
|
||||
```
|
||||
|
||||
Example (recommendation = Skip because content context overrides default):
|
||||
|
||||
```
|
||||
Finding 1 of 9 — P0 hardcoded admin token.
|
||||
Skip the fix since the fixture is being deleted?
|
||||
(Security recommends Apply; file context recommends Skip. Agent's recommendation: Skip.)
|
||||
```
|
||||
|
||||
Never embed code blocks, diff syntax, or the full fix/reasoning in the stem.
|
||||
|
||||
### Confirmation between findings
|
||||
|
||||
After the user answers and before printing the next finding's terminal block, emit a one-line confirmation of the action taken. Examples: `→ Applied. Fix staged at src/utils/api-client.ts:36-37.`, `→ Deferred. Ticket filed: <url>.`, `→ Skipped.`, `→ Acknowledged.`
|
||||
|
||||
### Options (four, or adapted as noted)
|
||||
|
||||
Fixed order. Never reorder:
|
||||
|
||||
```
|
||||
1. Apply the proposed fix
|
||||
2. Defer — file a [TRACKER] ticket
|
||||
3. Skip — don't apply, don't track
|
||||
4. Auto-resolve with best judgment on the rest
|
||||
```
|
||||
|
||||
Render the `[TRACKER]` label per `tracker-defer.md`: when `confidence = high` AND `named_sink_available = true`, replace `[TRACKER]` with the concrete tracker name (e.g., `Defer — file a Linear ticket`). When `any_sink_available = true` but either `confidence = low` or `named_sink_available = false`, use the generic whole label `Defer — file a ticket` — whole-label substitution, not a `[TRACKER]` token swap.
|
||||
|
||||
**Mark the post-tie-break recommendation with `(recommended)` on its option label.** Required, not optional. Any of the four options can carry it:
|
||||
|
||||
```
|
||||
1. Apply the proposed fix (recommended)
|
||||
2. Defer — file a ticket
|
||||
3. Skip — don't apply, don't track
|
||||
4. Auto-resolve with best judgment on the rest
|
||||
```
|
||||
|
||||
```
|
||||
1. Apply the proposed fix
|
||||
2. Defer — file a ticket
|
||||
3. Skip — don't apply, don't track (recommended)
|
||||
4. Auto-resolve with best judgment on the rest
|
||||
```
|
||||
|
||||
When reviewers disagreed or content context cuts against the default, still mark one option — whichever Stage 5 step 7b produced — and surface the disagreement in the R15 conflict context line.
|
||||
|
||||
### Adaptations
|
||||
|
||||
- **No `suggested_fix` (Apply suppressed):** when the finding has no concrete `suggested_fix` (`gated_auto` or `manual` with `suggested_fix == null`), option A (`Apply`) is **omitted from the menu**. Stage 5 step 6b already maps these to a `Defer` recommendation, so the `(recommended)` marker lands on a still-visible option. The menu shows three options: `Defer` / `Skip` / `Auto-resolve with best judgment on the rest` (and reduces to `Skip` / `Auto-resolve with best judgment on the rest` when combined with the no-sink adaptation). When this combines with the advisory variant, the same suppression is moot because option A is already replaced with `Acknowledge`. This rule mirrors the suppression applied during `SKILL.md` Step 2 Interactive option B's post-run `Walk through these one at a time` re-entry, so the same handling applies regardless of which entry path the user came in through.
|
||||
- **Advisory-only finding:** when the finding's `autofix_class` is `advisory` (no actionable fix), option A is replaced with `Acknowledge — mark as reviewed`. The other three options remain. The advisory variant is the only case where `Acknowledge` appears in the menu.
|
||||
- **N=1 (exactly one pending finding):** the terminal block's heading omits `Finding N of M` and renders as `## {severity} {plain-English title}`. The stem's first line drops the position counter, becoming `{severity} {short handle}.` Option D (`Auto-resolve with best judgment on the rest`) is suppressed because no subsequent findings exist — the menu shows three options: Apply / Defer / Skip (or Acknowledge, for advisory).
|
||||
- **No sink (Defer option unavailable):** when the tracker-detection tuple reports `any_sink_available: false` (every tier in the fallback chain — named tracker and GitHub Issues via `gh` — is unreachable), option B (`Defer`) is omitted. The stem appends one line explaining that no issue tracker is configured for this checkout (Linear, GitHub Issues, etc., were probed and unavailable). Phrase it for a developer audience — avoid `tracker sink` jargon, and avoid `platform` since the missing piece is per-project, not per-agent-platform. The menu shows three options: Apply / Skip / Auto-resolve with best judgment on the rest (and Acknowledge in place of Apply for advisory-only findings). **Before rendering the options, remap any per-finding `Defer` recommendation produced by Stage 5 step 7b to `Skip`** so the `(recommended)` marker always lands on an option that is actually in the menu. When the remap fires, surface it on the R15 conflict context line — name what was downgraded and why (so the reader sees the cross-reviewer Defer recommendation hasn't silently disappeared). This is a render-time runtime step; Stage 5 step 7b has no knowledge of sink availability and only orders conflicting reviewer recommendations.
|
||||
- **Combined N=1 + no sink:** the menu shows two options: Apply / Skip (or Acknowledge / Skip).
|
||||
|
||||
Only when `ToolSearch` explicitly returns no match or the tool call errors — or on a platform with no blocking question tool — fall back to presenting the options as a numbered list and waiting for the user's next reply.
|
||||
|
||||
---
|
||||
|
||||
## Per-finding routing
|
||||
|
||||
For each finding's answer:
|
||||
|
||||
- **Apply the proposed fix** — add the finding's id to an in-memory Apply set. Advance to the next finding. Do not dispatch the fixer inline — Apply accumulates for end-of-walk-through batch dispatch.
|
||||
- **Acknowledge — mark as reviewed** (advisory variant) — record Acknowledge in the in-memory decision list. Advance to the next finding. No side effects.
|
||||
- **Defer — file a [TRACKER] ticket** — invoke the tracker-defer flow from `tracker-defer.md`. The walk-through's position indicator stays on the current finding during any failure-path sub-question (Retry / Fall back / Convert to Skip). On success, record the tracker URL / reference in the in-memory decision list and advance. On conversion-to-Skip from the failure path, advance with the failure noted in the completion report.
|
||||
- **Skip — don't apply, don't track** — record Skip in the in-memory decision list. Advance. No side effects.
|
||||
- **Auto-resolve with best judgment on the rest** — exit the walk-through loop and dispatch the fixer subagent (`SKILL.md` Step 3) immediately on the remaining action set: the current finding plus everything not yet decided. No Stage 5b pre-pass. No bulk-preview approval gate. The fixer applies items with concrete `suggested_fix`, no-ops on advisory items, and routes items where the fix cannot be applied cleanly (or where evidence no longer matches the code) to a `failed` bucket with a one-line reason. Apply findings the user already picked during the walk-through are dispatched in the same fixer pass — the remaining set joins the in-memory Apply set so the fixer receives the union and applies all changes against a consistent tree. After the fixer returns, follow the post-run failure-handling logic in `SKILL.md` Step 2 Interactive option B — when the `failed` bucket is non-empty, fire one question with three options (file tickets / walk through / ignore). When the `failed` bucket is empty, emit the unified completion report directly.
|
||||
|
||||
---
|
||||
|
||||
## Override rule
|
||||
|
||||
"Override" means the user picks a different preset action (Defer or Skip in place of Apply, or Apply in place of the agent's recommendation). No inline freeform custom-fix authoring — the walk-through is a decision loop, not a pair-programming surface. A user who wants a variant of the proposed fix picks Skip and hand-edits outside the flow; if they also want the finding tracked, they file a ticket manually. This trade is explicit in v1's scope boundaries.
|
||||
|
||||
---
|
||||
|
||||
## State
|
||||
|
||||
Walk-through state is **in-memory only**. The orchestrator maintains:
|
||||
|
||||
- An Apply set (finding ids the user picked Apply on)
|
||||
- A decision list (every answered finding with its action and any metadata like `tracker_url` for Deferred or `reason` for Skipped)
|
||||
- The current position in the findings list
|
||||
|
||||
Nothing is written to disk per-decision. An interrupted walk-through (user cancels the prompt, session compacts, network dies) discards all in-memory state. Defer actions that already executed remain in the tracker — those are external side effects and cannot be rolled back. Apply decisions have not been dispatched yet (they batch at end-of-walk-through), so they are cleanly lost with no code changes.
|
||||
|
||||
Formal cross-session resumption is out of scope for v1.
|
||||
|
||||
---
|
||||
|
||||
## End-of-walk-through dispatch
|
||||
|
||||
This section covers the run-to-completion path only — every finding has been answered Apply / Defer / Skip / Acknowledge and the loop ended naturally. The `Auto-resolve with best judgment on the rest` path exits the walk-through earlier and dispatches its own fixer pass on the union of (accumulated Apply set ∪ remaining undecided findings); see that bullet under "Per-finding routing" above. There is no second dispatch in that branch.
|
||||
|
||||
When the loop runs to completion, the walk-through hands off to the dispatch phase:
|
||||
|
||||
1. **Apply set:** spawn one fixer subagent for the full accumulated Apply set. The fixer receives the set as its input queue and applies all changes in one pass against the current working tree. This preserves the existing "one fixer, consistent tree" mechanic and gives the fixer the full set at once to handle inter-fix dependencies (two Applies touching overlapping regions). The existing Step 3 fixer prompt needs a small update to acknowledge this queue may be heterogeneous (`gated_auto` and `manual` mix, not just `safe_auto`) — authored alongside this reference.
|
||||
2. **Defer set:** already executed inline during the walk-through. Nothing to dispatch here.
|
||||
3. **Skip / Acknowledge:** no-op.
|
||||
|
||||
After dispatch completes, emit the unified completion report described below.
|
||||
|
||||
---
|
||||
|
||||
## Unified completion report
|
||||
|
||||
Every terminal path of Interactive mode emits the same completion report structure. This covers:
|
||||
|
||||
- Walk-through completed (all findings answered)
|
||||
- Walk-through bailed via `Auto-resolve with best judgment on the rest`
|
||||
- Top-level best-judgment (routing option B) completed
|
||||
- Top-level File tickets (routing option C) completed
|
||||
- Zero findings after `safe_auto` (routing question was skipped — the completion summary is a one-line degenerate case of this structure)
|
||||
|
||||
### Minimum required fields (per R12)
|
||||
|
||||
- **Per-finding entries:** for every finding the flow touched, a line with — at minimum — title, severity, the action taken (Applied / Deferred / Skipped / Acknowledged), the tracker URL or in-session task reference for Deferred entries, and a one-line reason for Skipped entries (grounded in the finding's confidence or the one-line `why_it_matters` snippet).
|
||||
- **Summary counts by action:** totals per bucket (e.g., `4 applied, 2 deferred, 2 skipped`).
|
||||
- **Failures called out explicitly:** any fix application that failed, any ticket creation that failed (with the reason returned by the tracker). Failures are surfaced above the per-finding list so they are not missed.
|
||||
- **End-of-review verdict:** the existing Stage 6 verdict (Ready to merge / Ready with fixes / Not ready), computed from the residual state after all actions complete.
|
||||
|
||||
### Coverage section
|
||||
|
||||
Carry forward the existing Coverage data (suppressed-finding count, residual risks, testing gaps, failed reviewers) and add one new element:
|
||||
|
||||
- **Framing-enrichment gaps:** count of findings where artifact lookup returned no match (merge-synthesized findings, or failed persona artifact writes). Name the personas contributing those gaps so the data feeds any future persona-upgrade decision. A trail of gaps per run tells the team which persona agents still need attention.
|
||||
|
||||
### Report ordering
|
||||
|
||||
The report appears after all execution completes. Ordering inside the report: failures first (above the per-finding list), then per-finding entries grouped by action bucket in the order `Applied / Deferred / Skipped / Acknowledged`, then summary counts, then Coverage, then the verdict.
|
||||
|
||||
### Zero-findings degenerate case
|
||||
|
||||
When the routing question was skipped because no `gated_auto` / `manual` findings remained after `safe_auto`, the completion report collapses to its summary-counts + verdict form with one added line — the count of `safe_auto` fixes applied. The summary wording mirrors `SKILL.md` Step 2 Interactive mode's zero-remaining case: the unqualified `All findings resolved` form is only accurate when no advisory or pre-existing findings remain. When advisory and/or pre-existing findings remain in the report, use the qualified form that names what was cleared and names what still remains. Examples:
|
||||
|
||||
No remaining advisory or pre-existing findings:
|
||||
|
||||
```
|
||||
All findings resolved — 3 safe_auto fixes applied.
|
||||
|
||||
Verdict: Ready with fixes.
|
||||
```
|
||||
|
||||
Advisory and/or pre-existing findings remain in the report:
|
||||
|
||||
```
|
||||
All actionable findings resolved — 3 safe_auto fixes applied. (2 advisory, 1 pre-existing findings remain in the report.)
|
||||
|
||||
Verdict: Ready with fixes.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution posture
|
||||
|
||||
The walk-through is operationally read-only except for two permitted writes: the in-memory Apply set / decision list (managed by the orchestrator) and the tracker-defer dispatch (external ticket creation, described in `tracker-defer.md`). Persona agents remain strictly read-only. The end-of-walk-through fixer dispatch is the single point where file modifications happen — governed by the existing Step 3 fixer contract in `SKILL.md`.
|
||||
@@ -0,0 +1,628 @@
|
||||
---
|
||||
name: ce-compound
|
||||
description: Document a recently solved problem to compound your team's knowledge or CONCEPTS.md, the project's shared domain vocabulary.
|
||||
argument-hint: "[optional: brief context] [mode:headless] "
|
||||
---
|
||||
|
||||
# /ce-compound
|
||||
|
||||
Coordinate multiple subagents working in parallel to document a recently solved problem.
|
||||
|
||||
## Purpose
|
||||
|
||||
Captures problem solutions while context is fresh, creating structured documentation in `docs/solutions/` with YAML frontmatter for searchability and future reference. Uses parallel subagents for maximum efficiency.
|
||||
|
||||
**Why "compound"?** Each documented solution compounds your team's knowledge. The first time you solve a problem takes research. Document it, and the next occurrence takes minutes. Knowledge compounds.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/ce-compound # Document the most recent fix
|
||||
/ce-compound [brief context] # Provide additional context hint
|
||||
/ce-compound mode:headless # Non-interactive run for automations
|
||||
/ce-compound mode:headless [context] # Non-interactive run with context hint
|
||||
```
|
||||
|
||||
## CONCEPTS.md bootstrap requests
|
||||
|
||||
If invoked specifically to create or bootstrap `CONCEPTS.md` from scratch rather than to document a solved problem, do not run the normal phases — `ce-compound` populates `CONCEPTS.md` only as a side effect of documenting a real learning (it seeds the *learning's area*, not the whole repo; see Phase 2.4). Repo-wide concept-map creation is `ce-compound-refresh`'s job. Redirect a standalone bootstrap request to `ce-compound-refresh` (which asks whether to build the concept map or run a refresh cycle), then exit.
|
||||
|
||||
## Mode Detection
|
||||
|
||||
Check `$ARGUMENTS` for a `mode:headless` token. Tokens starting with `mode:` are flags, not context — strip `mode:headless` from arguments before treating the remainder as the brief context hint.
|
||||
|
||||
| Mode | When | Behavior |
|
||||
|------|------|----------|
|
||||
| **Interactive** (default) | No mode token present | Ask Full vs Lightweight, ask about session history (Full only), prompt for Discoverability Check consent, end with "What's next?" |
|
||||
| **Headless** | `mode:headless` in arguments | No blocking questions. Run **Full mode without session history**. Apply the Discoverability Check edit silently if a gap exists. Skip Phase 3 specialized reviews. End with a structured terminal report — no "What's next?" menu. |
|
||||
|
||||
Headless mode is intended for automations and skill-to-skill invocation where no human is present to answer questions. The doc itself is identical to what an interactive Full run would produce — classification work (track, category, overlap) follows the same rules and writes nothing extra into the artifact. Once detected, headless mode applies for the entire run.
|
||||
|
||||
## Pre-resolved context
|
||||
|
||||
**Git branch (pre-resolved):** !`git rev-parse --abbrev-ref HEAD 2>/dev/null || true`
|
||||
|
||||
If the line above resolved to a plain branch name (like `feat/my-branch`), include it in the `ce-sessions` invocation payload in Phase 1 so the orchestrator does not waste a turn deriving it. If it still contains a backtick command string or is empty, omit it and let `ce-sessions` derive it at runtime.
|
||||
|
||||
## Support Files
|
||||
|
||||
These files are the durable contract for the workflow. Read them on-demand at the step that needs them — do not bulk-load at skill start.
|
||||
|
||||
- `references/schema.yaml` — canonical frontmatter fields and enum values (read when validating YAML)
|
||||
- `references/yaml-schema.md` — category mapping from problem_type to directory (read when classifying)
|
||||
- `references/concepts-vocabulary.md` — CONCEPTS.md format and inclusion rules (read in Phase 2.4 when domain terms surface)
|
||||
- `assets/resolution-template.md` — section structure for new docs (read when assembling)
|
||||
|
||||
When spawning subagents, pass the relevant file contents into the task prompt so they have the contract without needing cross-skill paths.
|
||||
|
||||
## Execution Strategy
|
||||
|
||||
**In headless mode**, skip both questions below and go directly to **Full Mode** with session history disabled. Phase 1's session-history step (step 4) is omitted. Proceed straight to research.
|
||||
|
||||
**In interactive mode**, present the user with two options before proceeding, using the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to presenting options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
```
|
||||
1. Full (recommended) — the complete compound workflow. Researches,
|
||||
cross-references, and reviews your solution to produce documentation
|
||||
that compounds your team's knowledge.
|
||||
|
||||
2. Lightweight — same documentation, single pass. Faster and uses
|
||||
fewer tokens, but won't detect duplicates or cross-reference
|
||||
existing docs. Best for simple fixes or long sessions nearing
|
||||
context limits.
|
||||
```
|
||||
|
||||
In interactive mode, do NOT pre-select a mode, do NOT skip this prompt, and wait for the user's choice before proceeding. (Headless mode bypasses this prompt per the "**In headless mode**" rule above and runs Full directly — these "do not skip" directives do not apply to headless.)
|
||||
|
||||
**If the user chooses Full** (interactive mode only), ask one follow-up question before proceeding. Detect which harness is running (Claude Code, Codex, or Cursor) and ask:
|
||||
|
||||
```
|
||||
Would you also like to search your [harness name] session history
|
||||
for relevant knowledge to help the Compound process? This adds
|
||||
time and token usage.
|
||||
```
|
||||
|
||||
If the user says yes, invoke `ce-sessions` in Phase 1 (see step 4). If no, skip it. Do not ask this in lightweight mode or headless mode.
|
||||
|
||||
---
|
||||
|
||||
### Full Mode
|
||||
|
||||
<critical_requirement>
|
||||
**The primary deliverable is ONE file - the final documentation.**
|
||||
|
||||
Phase 1 subagents return TEXT DATA to the orchestrator. They must NOT use Write, Edit, or create any files. Only the orchestrator writes files. Beyond the Phase 2 solution doc, its other writes are maintenance side effects — not additional deliverables, and creating one when absent is expected, not a violation of this rule:
|
||||
- **`CONCEPTS.md`** — create or update in Phase 2.4 (Vocabulary Capture) when a qualifying domain term surfaces.
|
||||
- **A project instruction file** (AGENTS.md or CLAUDE.md) — a small edit when the Discoverability Check finds a gap.
|
||||
|
||||
Both ensure future agents can discover and ground in the knowledge store; neither makes the documentation any less the single deliverable.
|
||||
</critical_requirement>
|
||||
|
||||
### Phase 0.5: Auto Memory Scan
|
||||
|
||||
Before launching Phase 1 subagents, check the auto-memory block injected into your system prompt for notes relevant to the problem being documented.
|
||||
|
||||
1. Look for a block labeled "user's auto-memory" (Claude Code only) already present in your system prompt context — MEMORY.md's entries are inlined there
|
||||
2. If the block is absent, empty, or this is a non-Claude-Code platform, skip this step and proceed to Phase 1 unchanged
|
||||
3. Scan the entries for anything related to the problem being documented -- use semantic judgment, not keyword matching
|
||||
4. If relevant entries are found, prepare a labeled excerpt block:
|
||||
|
||||
```
|
||||
## Supplementary notes from auto memory
|
||||
Treat as additional context, not primary evidence. Conversation history
|
||||
and codebase findings take priority over these notes.
|
||||
|
||||
[relevant entries here]
|
||||
```
|
||||
|
||||
5. Pass this block as additional context to the Context Analyzer and Solution Extractor task prompts in Phase 1. If any memory notes end up in the final documentation (e.g., as part of the investigation steps or root cause analysis), tag them with "(auto memory [claude])" so their origin is clear to future readers.
|
||||
|
||||
If no relevant entries are found, proceed to Phase 1 without passing memory context.
|
||||
|
||||
### Phase 1: Research
|
||||
|
||||
Launch research subagents. Each returns text data to the orchestrator.
|
||||
|
||||
**Dispatch order:**
|
||||
- Launch `Context Analyzer`, `Solution Extractor`, and `Related Docs Finder` in parallel (background)
|
||||
- **Then** invoke the `ce-sessions` skill via the platform's skill-invocation primitive (see step 4 below) — only if the user opted in to session history. The skill call is synchronous from this orchestrator's main-context turn, but the already-dispatched background subagents continue running in parallel underneath, so the wall-clock benefit is preserved (`max(ce-sessions, slowest background subagent)`, not their sum). Issuing the skill call before the parallel block would serialize ce-sessions in front of the research subagents and regress wall-clock time.
|
||||
|
||||
<parallel_tasks>
|
||||
|
||||
#### 1. **Context Analyzer**
|
||||
- Extracts conversation history
|
||||
- Reads `references/schema.yaml` for enum validation and **track classification**
|
||||
- Determines the track (bug or knowledge) from the problem_type
|
||||
- Identifies problem type, component, and track-appropriate fields:
|
||||
- **Bug track**: symptoms, root_cause, resolution_type
|
||||
- **Knowledge track**: applies_when (symptoms/root_cause/resolution_type optional)
|
||||
- Incorporates auto memory excerpts (if provided by the orchestrator) as supplementary evidence
|
||||
- Reads `references/yaml-schema.md` for category mapping into `docs/solutions/`
|
||||
- Suggests a filename using the pattern `[sanitized-problem-slug].md` — no date suffix, even if existing files in the target directory have one; the `date:` frontmatter field is the canonical creation date
|
||||
- Returns: YAML frontmatter skeleton (must include `category:` field mapped from problem_type), category directory path, suggested filename, and which track applies
|
||||
- Does not invent enum values, categories, or frontmatter fields from memory; reads the schema and mapping files above
|
||||
- Does not force bug-track fields onto knowledge-track learnings or vice versa
|
||||
|
||||
#### 2. **Solution Extractor**
|
||||
- Reads `references/schema.yaml` for track classification (bug vs knowledge)
|
||||
- Adapts output structure based on the problem_type track
|
||||
- Incorporates auto memory excerpts (if provided by the orchestrator) as supplementary evidence -- conversation history and the verified fix take priority; if memory notes contradict the conversation, note the contradiction as cautionary context
|
||||
|
||||
**Bug track output sections:**
|
||||
|
||||
- **Problem**: 1-2 sentence description of the issue
|
||||
- **Symptoms**: Observable symptoms (error messages, behavior)
|
||||
- **What Didn't Work**: Failed investigation attempts and why they failed
|
||||
- **Solution**: The actual fix with code examples (before/after when applicable)
|
||||
- **Why This Works**: Root cause explanation and why the solution addresses it
|
||||
- **Prevention**: Strategies to avoid recurrence, best practices, and test cases. Include concrete code examples where applicable (e.g., gem configurations, test assertions, linting rules)
|
||||
|
||||
**Knowledge track output sections:**
|
||||
|
||||
- **Context**: What situation, gap, or friction prompted this guidance
|
||||
- **Guidance**: The practice, pattern, or recommendation with code examples when useful
|
||||
- **Why This Matters**: Rationale and impact of following or not following this guidance
|
||||
- **When to Apply**: Conditions or situations where this applies
|
||||
- **Examples**: Concrete before/after or usage examples showing the practice in action
|
||||
|
||||
#### 3. **Related Docs Finder**
|
||||
- Searches `docs/solutions/` for related documentation
|
||||
- Identifies cross-references and links
|
||||
- Finds related GitHub issues
|
||||
- Flags any related learning or pattern docs that may now be stale, contradicted, or overly broad
|
||||
- **Assesses overlap** with the new doc being created across five dimensions: problem statement, root cause, solution approach, referenced files, and prevention rules. Score as:
|
||||
- **High**: 4-5 dimensions match — essentially the same problem solved again
|
||||
- **Moderate**: 2-3 dimensions match — same area but different angle or solution
|
||||
- **Low**: 0-1 dimensions match — related but distinct
|
||||
- Returns: Links, relationships, refresh candidates, and overlap assessment (score + which dimensions matched)
|
||||
|
||||
**Search strategy (grep-first filtering for efficiency):**
|
||||
|
||||
1. Extract keywords from the problem context: module names, technical terms, error messages, component types
|
||||
2. If the problem category is clear, narrow search to the matching `docs/solutions/<category>/` directory
|
||||
3. Use the native content-search tool (e.g., Grep in Claude Code) to pre-filter candidate files BEFORE reading any content. Run multiple searches in parallel, case-insensitive, targeting frontmatter fields. These are template patterns -- substitute actual keywords:
|
||||
- `title:.*<keyword>`
|
||||
- `tags:.*(<keyword1>|<keyword2>)`
|
||||
- `module:.*<module name>`
|
||||
- `component:.*<component>`
|
||||
4. If search returns >25 candidates, re-run with more specific patterns. If <3, broaden to full content search
|
||||
5. Read only frontmatter (first 30 lines) of candidate files to score relevance
|
||||
6. Fully read only strong/moderate matches
|
||||
7. Return distilled links and relationships, not raw file contents
|
||||
|
||||
**GitHub issue search:**
|
||||
|
||||
Prefer the `gh` CLI for searching related issues: `gh issue list --search "<keywords>" --state all --limit 5`. If `gh` is not installed, fall back to the GitHub MCP tools (e.g., `unblocked` data_retrieval) if available. If neither is available, skip GitHub issue search and note it was skipped in the output.
|
||||
|
||||
</parallel_tasks>
|
||||
|
||||
#### 4. **Session History via `ce-sessions`** (synchronous skill call, after launching the parallel block — only if the user opted in)
|
||||
- **Skip entirely** if the user declined session history in the follow-up question, if running in lightweight mode, or if running in headless mode.
|
||||
- Invoke the `ce-sessions` skill via the platform's skill-invocation primitive (`Skill` in Claude Code, `Skill` in Codex, the equivalent on Gemini/Pi). Pass the dispatch payload below as the skill argument string. `ce-sessions` runs in main context — it owns discovery, branch/keyword filtering, scan-window selection, the deep-dive cap, per-session extraction to a `mktemp` scratch dir, and dispatch of the synthesis-only `ce-session-historian` subagent. The compound orchestrator only needs to pass the topic and time window and read back the findings text.
|
||||
|
||||
**Dispatch payload — keep tight.** A long, keyword-rich payload licenses ce-sessions to keep widening. Use this shape:
|
||||
|
||||
- **Pre-resolved context** (only if values resolved cleanly above; otherwise omit): repo name, current git branch.
|
||||
- **Time window**: explicit `7 days` unless the documented problem clearly spans a longer arc.
|
||||
- **Problem topic**: one sentence naming the concrete issue — error message, module name, what broke and how it was fixed. Not a paragraph; not a bullet list of related topics.
|
||||
- **Filter rule (one line)**: "Only surface findings directly relevant to this specific problem. Ignore unrelated work from the same sessions or branches."
|
||||
- **Output schema**:
|
||||
|
||||
```
|
||||
Structure your response with these sections (omit any with no findings):
|
||||
- What was tried before
|
||||
- What didn't work
|
||||
- Key decisions
|
||||
- Related context
|
||||
```
|
||||
|
||||
Do not append additional context blocks, exclusion lists, or topic-keyword bullets — verbose payloads give ce-sessions license to keep widening the search and rapidly compound wall time. If keyword search is needed, ce-sessions owns that decision internally based on the topic.
|
||||
- Returns: structured digest of findings from prior sessions, or "no relevant prior sessions" if none found.
|
||||
- **ce-sessions is the final Phase 1 input, not a workflow stop.** When it returns, proceed directly to Phase 2 with its output as the last input — do not emit a summary and do not pause for the user. A "no relevant prior sessions" return is still a valid input; the documentation gets written without session context.
|
||||
|
||||
### Phase 2: Assembly & Write
|
||||
|
||||
<sequential_tasks>
|
||||
|
||||
**WAIT for all Phase 1 inputs to complete before proceeding** — the three parallel subagents and, when the user opted in, the synchronous `ce-sessions` skill call. ce-sessions is a Phase 1 input even though it is a skill rather than a subagent.
|
||||
|
||||
The orchestrating agent (main conversation) performs these steps:
|
||||
|
||||
1. Collect all text results from Phase 1 subagents
|
||||
2. **Check the overlap assessment** from the Related Docs Finder before deciding what to write:
|
||||
|
||||
| Overlap | Action |
|
||||
|---------|--------|
|
||||
| **High** — existing doc covers the same problem, root cause, and solution | **Update the existing doc** with fresher context (new code examples, updated references, additional prevention tips) rather than creating a duplicate. The existing doc's path and structure stay the same. |
|
||||
| **Moderate** — same problem area but different angle, root cause, or solution | **Create the new doc** normally. Flag the overlap for Phase 2.5 to recommend consolidation review. |
|
||||
| **Low or none** | **Create the new doc** normally. |
|
||||
|
||||
The reason to update rather than create: two docs describing the same problem and solution will inevitably drift apart. The newer context is fresher and more trustworthy, so fold it into the existing doc rather than creating a second one that immediately needs consolidation.
|
||||
|
||||
When updating an existing doc, preserve its file path and frontmatter structure. Update the solution, code examples, prevention tips, and any stale references. Add a `last_updated: YYYY-MM-DD` field to the frontmatter. Do not change the title unless the problem framing has materially shifted.
|
||||
|
||||
3. **Incorporate session history findings** (if available). When `ce-sessions` returned relevant prior-session context:
|
||||
- Fold investigation dead ends and failed approaches into the **What Didn't Work** section (bug track) or **Context** section (knowledge track)
|
||||
- Use cross-session patterns to enrich the **Prevention** or **Why This Matters** sections
|
||||
- Tag session-sourced content with "(session history)" so its origin is clear to future readers
|
||||
- If findings are thin or "no relevant prior sessions," proceed without session context
|
||||
4. Assemble complete markdown file from the collected pieces, reading `assets/resolution-template.md` for the section structure of new docs
|
||||
5. Validate YAML frontmatter against `references/schema.yaml`, including the YAML-safety quoting rule for array items (see `references/yaml-schema.md` > YAML Safety Rules)
|
||||
6. Create directory if needed: `mkdir -p docs/solutions/[category]/`
|
||||
7. Write the file: either the updated existing doc or the new `docs/solutions/[category]/[filename].md`
|
||||
8. **Run `python3 scripts/validate-frontmatter.py <output-path>`** to catch silent-corruption parser-safety issues that the prose rules miss: malformed `---` delimiter lines, unquoted ` #` in scalar values (silent comment truncation), and unquoted `: ` in scalar values (silent mapping confusion). Exit 0 means the doc is parser-safe; exit 1 means the script's stderr names the offending field(s) and what to fix — quote the value(s), re-write the doc, and re-run until exit 0. Do not declare success while validation fails. The script does not enforce schema rules and does not flag YAML reserved-indicator characters (those produce loud parser errors downstream rather than silent corruption — out of scope). Uses Python 3 stdlib only (no PyYAML or other deps).
|
||||
|
||||
When creating a new doc, preserve the section order from `assets/resolution-template.md` unless the user explicitly asks for a different structure.
|
||||
|
||||
</sequential_tasks>
|
||||
|
||||
### Phase 2.4: Vocabulary Capture
|
||||
|
||||
**First, read `references/concepts-vocabulary.md`.** This is unconditional. Do not pre-judge from memory that nothing qualifies — the reference's criteria are non-obvious and qualifying terms often live in the surrounding conversation rather than the new doc itself. Reading the reference is what makes the rest of the phase possible.
|
||||
|
||||
Then, applying those criteria, scan the new doc **and** the surrounding conversation for qualifying domain terms. If `CONCEPTS.md` exists at repo root, add missing qualifying terms and refine existing entries when new precision surfaced. If it does not exist and at least one qualifying term surfaced, create it.
|
||||
|
||||
**Seed the learning's area at creation — don't write a lone term.** When `CONCEPTS.md` does not yet exist, alongside the surfaced term also seed the core domain nouns of the area this learning touched, following the **Seed goal** and **Scope of a seed** rules in `references/concepts-vocabulary.md`. The seed is scoped to the learning's area (the modules and domain the fix touched) and defines only terms investigated here — it does not reach for repo-wide nouns. This anchors the surfaced term so it does not dangle against undefined siblings. A repo-wide concept map is `ce-compound-refresh`'s bootstrap path, not this one.
|
||||
|
||||
**At creation, hold the qualifying bar conservatively for borderline terms.** A borderline term, or a class/table/file name dressed up as an entity, defers to a later run — clear core nouns are seeded, borderline ones wait. The conservatism is about quality, not count; updates to an existing file follow the normal criteria.
|
||||
|
||||
**When bootstrapping the file, start with this preamble under the `# Concepts` heading**, then add the qualifying entries below it:
|
||||
|
||||
> Shared domain vocabulary for this project — entities, named processes, and status concepts with project-specific meaning. Seeded with core domain vocabulary, then accretes as ce-compound and ce-compound-refresh process learnings; direct edits are fine. Glossary only, not a spec or catch-all.
|
||||
|
||||
**Refresh the coherence neighborhood of any entry you touch.** When adding or editing an entry, also inspect its *coherence neighborhood* — its cluster siblings and the terms it cross-references or that reference it. Within that neighborhood, do two things: fix glossary violations (implementation specifics — file paths, class names, function signatures, current-config values), and refresh entries the learning's own evidence shows have drifted. Bounds: neighborhood only, never a full-file audit; refresh only on evidence already in hand; if judging a neighbor would require investigation this learning did not do, flag it for `ce-compound-refresh` rather than editing on a guess. The test: after the edit, would a reader find the touched entry's siblings or referenced terms inconsistent with it? Broader audit is `ce-compound-refresh`'s job.
|
||||
|
||||
If no terms qualified after applying the reference's criteria, record that outcome explicitly in the success output (e.g., "Vocabulary capture: scanned, no qualifying terms"). Do not silently skip — the visible scan-and-no-result record is the audit signal that the reference was consulted.
|
||||
|
||||
**Apply edits silently in every mode — no user prompt in interactive, lightweight, or headless.** Vocabulary capture is a side effect of compounding, not a decision the user makes per run. Lightweight mode reaches this through its own single-pass step (see Lightweight Mode), and runs an **update-only** version — it refines an existing `CONCEPTS.md` but defers creation/seeding to a Full run.
|
||||
|
||||
### Phase 2.5: Selective Refresh Check
|
||||
|
||||
After writing the new learning, decide whether this new solution is evidence that older docs should be refreshed.
|
||||
|
||||
`ce-compound-refresh` is **not** a default follow-up. Use it selectively when the new learning suggests an older learning or pattern doc may now be inaccurate.
|
||||
|
||||
It makes sense to invoke `ce-compound-refresh` when one or more of these are true:
|
||||
|
||||
1. A related learning or pattern doc recommends an approach that the new fix now contradicts
|
||||
2. The new fix clearly supersedes an older documented solution
|
||||
3. The current work involved a refactor, migration, rename, or dependency upgrade that likely invalidated references in older docs
|
||||
4. A pattern doc now looks overly broad, outdated, or no longer supported by the refreshed reality
|
||||
5. The Related Docs Finder surfaced high-confidence refresh candidates in the same problem space
|
||||
6. The Related Docs Finder reported **moderate overlap** with an existing doc — there may be consolidation opportunities that benefit from a focused review
|
||||
|
||||
It does **not** make sense to invoke `ce-compound-refresh` when:
|
||||
|
||||
1. No related docs were found
|
||||
2. Related docs still appear consistent with the new learning
|
||||
3. The overlap is superficial and does not change prior guidance
|
||||
4. Refresh would require a broad historical review with weak evidence
|
||||
|
||||
Use these rules:
|
||||
|
||||
- If there is **one obvious stale candidate**, invoke `ce-compound-refresh` with a narrow scope hint after the new learning is written
|
||||
- If there are **multiple candidates in the same area**, ask the user whether to run a targeted refresh for that module, category, or pattern set
|
||||
- If context is already tight or you are in lightweight mode, do not expand into a broad refresh automatically; instead recommend `ce-compound-refresh` as the next step with a scope hint
|
||||
- **In headless mode**, never invoke `ce-compound-refresh` and never ask the user. Surface the recommended scope hint in the terminal report's "Refresh recommendation" line and let the caller decide
|
||||
|
||||
When invoking or recommending `ce-compound-refresh`, be explicit about the argument to pass. Prefer the narrowest useful scope:
|
||||
|
||||
- **Specific file** when one learning or pattern doc is the likely stale artifact
|
||||
- **Module or component name** when several related docs may need review
|
||||
- **Category name** when the drift is concentrated in one solutions area
|
||||
- **Pattern filename or pattern topic** when the stale guidance lives in `docs/solutions/patterns/`
|
||||
|
||||
Examples:
|
||||
|
||||
- `/ce-compound-refresh plugin-versioning-requirements`
|
||||
- `/ce-compound-refresh payments`
|
||||
- `/ce-compound-refresh performance-issues`
|
||||
- `/ce-compound-refresh critical-patterns`
|
||||
|
||||
A single scope hint may still expand to multiple related docs when the change is cross-cutting within one domain, category, or pattern area.
|
||||
|
||||
Do not invoke `ce-compound-refresh` without an argument unless the user explicitly wants a broad sweep.
|
||||
|
||||
Always capture the new learning first. Refresh is a targeted maintenance follow-up, not a prerequisite for documentation.
|
||||
|
||||
### Discoverability Check
|
||||
|
||||
After the learning is written and the refresh decision is made, check whether the project's instruction files would lead an agent to discover and search `docs/solutions/` before starting work in a documented area. This runs every time — the knowledge store only compounds value when agents can find it.
|
||||
|
||||
1. Identify which root-level instruction files exist (AGENTS.md, CLAUDE.md, or both). Read the file(s) and determine which holds the substantive content — one file may just be a shim that `@`-includes the other (e.g., `CLAUDE.md` containing only `@AGENTS.md`, or vice versa). The substantive file is the assessment and edit target; ignore shims. If neither file exists, skip this check entirely.
|
||||
2. Assess whether an agent reading the instruction files would learn three things:
|
||||
- That a searchable knowledge store of documented solutions exists
|
||||
- Enough about its structure to search effectively (category organization, YAML frontmatter fields like `module`, `tags`, `problem_type`)
|
||||
- When to search it (before implementing features, debugging issues, or making decisions in documented areas — learnings may cover bugs, best practices, workflow patterns, or other institutional knowledge)
|
||||
|
||||
This is a semantic assessment, not a string match. The information could be a line in an architecture section, a bullet in a gotchas section, spread across multiple places, or expressed without ever using the exact path `docs/solutions/`. Use judgment — if an agent would reasonably discover and use the knowledge store after reading the file, the check passes.
|
||||
|
||||
3. If the spirit is already met, no action needed — move on.
|
||||
4. If not:
|
||||
a. Based on the file's existing structure, tone, and density, identify where a mention fits naturally. Before creating a new section, check whether the information could be a single line in the closest related section — an architecture tree, a directory listing, a documentation section, or a conventions block. A line added to an existing section is almost always better than a new headed section. Only add a new section as a last resort when the file has clear sectioned structure and nothing is even remotely related.
|
||||
b. Draft the smallest addition that communicates the three things. Match the file's existing style and density. The addition should describe the knowledge store itself, not the plugin — an agent without the plugin should still find value in it.
|
||||
|
||||
Keep the tone informational, not imperative. Express timing as description, not instruction — "relevant when implementing or debugging in documented areas" rather than "check before implementing or debugging." Imperative directives like "always search before implementing" cause redundant reads when a workflow already includes a dedicated search step. The goal is awareness: agents learn the folder exists and what's in it, then use their own judgment about when to consult it.
|
||||
|
||||
Examples of calibration (not templates — adapt to the file):
|
||||
|
||||
When there's an existing directory listing or architecture section — add a line:
|
||||
```
|
||||
docs/solutions/ # documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (module, tags, problem_type)
|
||||
```
|
||||
|
||||
When nothing in the file is a natural fit — a small headed section is appropriate:
|
||||
```
|
||||
## Documented Solutions
|
||||
|
||||
`docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas.
|
||||
```
|
||||
c. In full interactive mode, explain to the user why this matters — agents working in this repo (including fresh sessions, other tools, or collaborators without the plugin) won't know to check `docs/solutions/` unless the instruction file surfaces it. Show the proposed change and where it would go, then use the platform's blocking question tool to get consent before making the edit: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to presenting the proposal in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question. In lightweight mode, output a one-liner note and move on. In headless mode, apply the edit directly without prompting and surface it in the terminal report under "Instruction-file edit"
|
||||
|
||||
5. **If `CONCEPTS.md` exists at repo root, run a parallel discoverability check for it.** Assess whether the instruction file would lead an agent to discover the project's shared domain vocabulary. Use the same workflow as the `docs/solutions/` check above: same target file, same edit-placement judgment, same consent-then-edit interaction shape per mode. A line in an existing section is almost always better than a new headed section. Example calibration when nothing else fits:
|
||||
|
||||
```
|
||||
CONCEPTS.md # shared domain vocabulary (entities, named processes, status concepts) — relevant when orienting to the codebase or discussing domain concepts
|
||||
```
|
||||
|
||||
**Skip this step entirely if `CONCEPTS.md` does not exist** — never nag for an artifact the project has not adopted. When skipped, this step produces no output and no edit.
|
||||
|
||||
### Phase 3: Optional Enhancement
|
||||
|
||||
**WAIT for Phase 2 to complete before proceeding.**
|
||||
|
||||
**Skip Phase 3 entirely in headless mode** to bound token usage — the caller does not have a human-in-the-loop to act on reviewer findings, and downstream automations can run specialized reviewers themselves if they want that pass.
|
||||
|
||||
<parallel_tasks>
|
||||
|
||||
Based on problem type, optionally invoke specialized agents to review the documentation:
|
||||
|
||||
- **performance_issue** → `ce-performance-oracle`
|
||||
- **security_issue** → `ce-security-sentinel`
|
||||
- **database_issue** → `ce-data-integrity-guardian`
|
||||
- Any code-heavy issue → always run `ce-code-simplicity-reviewer` for minimal, clear examples. Structural concerns in the diff are already covered when the same work goes through `/ce-code-review` (maintainability persona).
|
||||
|
||||
</parallel_tasks>
|
||||
|
||||
---
|
||||
|
||||
### Lightweight Mode
|
||||
|
||||
<critical_requirement>
|
||||
**Single-pass alternative — same documentation, fewer tokens.**
|
||||
|
||||
This mode skips parallel subagents entirely. The orchestrator performs all work in a single pass, producing the same solution document without cross-referencing or duplicate detection.
|
||||
|
||||
Headless mode forces Full and does not enter Lightweight — automations get the cross-reference and overlap detection benefits without the interactive overhead.
|
||||
</critical_requirement>
|
||||
|
||||
The orchestrator (main conversation) performs ALL of the following in one sequential pass:
|
||||
|
||||
1. **Extract from conversation**: Identify the problem and solution from conversation history. Also scan the "user's auto-memory" block injected into your system prompt, if present (Claude Code only) -- use any relevant notes as supplementary context alongside conversation history. Tag any memory-sourced content incorporated into the final doc with "(auto memory [claude])"
|
||||
2. **Classify**: Read `references/schema.yaml` and `references/yaml-schema.md`, then determine track (bug vs knowledge), category, and filename
|
||||
3. **Write minimal doc**: Create `docs/solutions/[category]/[filename].md` using the appropriate track template from `assets/resolution-template.md`, with:
|
||||
- YAML frontmatter with track-appropriate fields, applying the YAML-safety quoting rule for array items (see `references/yaml-schema.md` > YAML Safety Rules)
|
||||
- Bug track: Problem, root cause, solution with key code snippets, one prevention tip
|
||||
- Knowledge track: Context, guidance with key examples, one applicability note
|
||||
4. **Vocabulary capture (update-only)**: if `CONCEPTS.md` exists at repo root, read `references/concepts-vocabulary.md`, then scan the new doc and the conversation for qualifying terms and add/refine entries silently (same criteria as Phase 2.4). Do **not** bootstrap or seed in lightweight mode — if `CONCEPTS.md` does not exist, defer creation to a Full run, which owns seeding. Record the outcome in the output (e.g., "Vocabulary: 1 entry refined" or "scanned, no qualifying terms"). If you refined `CONCEPTS.md` and a quick read of `AGENTS.md`/`CLAUDE.md` shows it isn't surfaced there, add the discoverability tip to the output below — lightweight **tips**, it does not edit instruction files (a Full run owns that edit).
|
||||
5. **Skip specialized agent reviews** (Phase 3) to conserve context
|
||||
|
||||
**Lightweight output:**
|
||||
```
|
||||
✓ Documentation complete (lightweight mode)
|
||||
|
||||
File created:
|
||||
- docs/solutions/[category]/[filename].md
|
||||
|
||||
[If discoverability check found instruction files don't surface the knowledge store:]
|
||||
Tip: Your AGENTS.md/CLAUDE.md doesn't surface docs/solutions/ to agents —
|
||||
a brief mention helps all agents discover these learnings.
|
||||
|
||||
[If CONCEPTS.md was refined this run and isn't surfaced in the instruction files:]
|
||||
Tip: Your AGENTS.md/CLAUDE.md doesn't surface CONCEPTS.md —
|
||||
a one-line mention helps agents find the shared vocabulary.
|
||||
|
||||
Note: This was created in lightweight mode. For richer documentation
|
||||
(cross-references, detailed prevention strategies, specialized reviews),
|
||||
re-run /ce-compound in a fresh session.
|
||||
```
|
||||
|
||||
**No subagents are launched. No parallel tasks. The solution doc is the one deliverable** (Phase 2.4's update-only vocabulary capture may also refine an existing `CONCEPTS.md`).
|
||||
|
||||
In lightweight mode, the overlap check is skipped (no Related Docs Finder subagent). This means lightweight mode may create a doc that overlaps with an existing one. That is acceptable — `ce-compound-refresh` will catch it later. Only suggest `ce-compound-refresh` if there is an obvious narrow refresh target. Do not broaden into a large refresh sweep from a lightweight session.
|
||||
|
||||
---
|
||||
|
||||
## What It Captures
|
||||
|
||||
- **Problem symptom**: Exact error messages, observable behavior
|
||||
- **Investigation steps tried**: What didn't work and why
|
||||
- **Root cause analysis**: Technical explanation
|
||||
- **Working solution**: Step-by-step fix with code examples
|
||||
- **Prevention strategies**: How to avoid in future
|
||||
- **Cross-references**: Links to related issues and docs
|
||||
|
||||
## Preconditions
|
||||
|
||||
<preconditions enforcement="advisory">
|
||||
<check condition="problem_solved">
|
||||
Problem has been solved (not in-progress)
|
||||
</check>
|
||||
<check condition="solution_verified">
|
||||
Solution has been verified working
|
||||
</check>
|
||||
<check condition="non_trivial">
|
||||
Non-trivial problem (not simple typo or obvious error)
|
||||
</check>
|
||||
</preconditions>
|
||||
|
||||
## What It Creates
|
||||
|
||||
**Organized documentation:**
|
||||
|
||||
- File: `docs/solutions/[category]/[filename].md`
|
||||
|
||||
**Categories auto-detected from problem:**
|
||||
|
||||
Bug track:
|
||||
- build-errors/
|
||||
- test-failures/
|
||||
- runtime-errors/
|
||||
- performance-issues/
|
||||
- database-issues/
|
||||
- security-issues/
|
||||
- ui-bugs/
|
||||
- integration-issues/
|
||||
- logic-errors/
|
||||
|
||||
Knowledge track:
|
||||
- architecture-patterns/ — architectural or structural patterns (agent/skill/pipeline/workflow shape decisions)
|
||||
- design-patterns/ — reusable non-architectural design approaches (content generation, interaction patterns, prompt shapes)
|
||||
- tooling-decisions/ — language, library, or tool choices with durable rationale
|
||||
- conventions/ — team-agreed way of doing something, captured so it survives turnover
|
||||
- workflow-issues/
|
||||
- developer-experience/
|
||||
- documentation-gaps/
|
||||
- best-practices/ — fallback only, use when no narrower knowledge-track value applies
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
| ❌ Wrong | ✅ Correct |
|
||||
|----------|-----------|
|
||||
| Subagents write files like `context-analysis.md`, `solution-draft.md` | Subagents return text data; orchestrator writes one final file |
|
||||
| Research and assembly run in parallel | Research completes → then assembly runs |
|
||||
| Multiple files created during workflow | One solution doc written or updated: `docs/solutions/[category]/[filename].md` (plus optional maintenance writes: a `CONCEPTS.md` create/update from Phase 2.4 and a small instruction-file edit for discoverability) |
|
||||
| Creating a new doc when an existing doc covers the same problem | Check overlap assessment; update the existing doc when overlap is high |
|
||||
|
||||
## Success Output
|
||||
|
||||
### Headless mode
|
||||
|
||||
Emit a structured terminal report and end the turn. No "What's next?" question, no blocking prompt. End with `Documentation complete` as the terminal signal so callers can detect completion.
|
||||
|
||||
```
|
||||
✓ Documentation complete (headless mode)
|
||||
|
||||
File: docs/solutions/<category>/<filename>.md (created | updated)
|
||||
Track: <bug | knowledge>
|
||||
Category: <category>
|
||||
Overlap: <none | low | moderate — see <path> | high — existing doc updated>
|
||||
Instruction-file edit: <none needed | applied to <path> | gap noted, not applied>
|
||||
CONCEPTS.md: <scanned, no qualifying terms | created with N entries (M seeded from the learning's area) | updated — N added, N refined>
|
||||
Refresh recommendation: <none | scope hint for /ce-compound-refresh>
|
||||
|
||||
Documentation complete
|
||||
```
|
||||
|
||||
When no doc was written (e.g., headless invoked on a session where the problem is not yet solved), emit a structured failure instead and end with `Documentation skipped` so callers can distinguish success from no-op:
|
||||
|
||||
```
|
||||
✗ Documentation skipped (headless mode)
|
||||
|
||||
Reason: <one-sentence explanation — e.g., "no solved problem detected in
|
||||
conversation history" or "solution not yet verified">
|
||||
|
||||
Documentation skipped
|
||||
```
|
||||
|
||||
### Interactive mode
|
||||
|
||||
```
|
||||
✓ Documentation complete
|
||||
|
||||
Auto memory: 2 relevant entries used as supplementary evidence
|
||||
|
||||
Subagent Results:
|
||||
✓ Context Analyzer: Identified performance_issue in brief_system, category: performance-issues/
|
||||
✓ Solution Extractor: 3 code fixes, prevention strategies
|
||||
✓ Related Docs Finder: 2 related issues
|
||||
✓ Session History: 3 prior sessions on same branch, 2 failed approaches surfaced
|
||||
|
||||
Specialized Agent Reviews (Auto-Triggered):
|
||||
✓ ce-performance-oracle: Validated query optimization approach
|
||||
✓ ce-code-simplicity-reviewer: Solution is appropriately minimal
|
||||
|
||||
Files written:
|
||||
- docs/solutions/performance-issues/n-plus-one-brief-generation.md (created)
|
||||
- CONCEPTS.md (created with 3 entries: BriefSystem, EmailQueue, Brief Status)
|
||||
|
||||
This documentation will be searchable for future reference when similar
|
||||
issues occur in the Email Processing or Brief System modules.
|
||||
|
||||
What's next?
|
||||
1. Continue workflow (recommended)
|
||||
2. Link related documentation
|
||||
3. Update other references
|
||||
4. View documentation
|
||||
5. Other
|
||||
```
|
||||
|
||||
**After displaying the interactive success output above, present the "What's next?" options using the platform's blocking question tool:** `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question. Do not continue the workflow or end the turn without the user's selection. (Interactive mode only — headless skips this per the headless block above.)
|
||||
|
||||
**Alternate interactive output (when updating an existing doc due to high overlap):** in headless mode, this case is communicated via the `Overlap: high — existing doc updated` line of the headless terminal report above, not as a separate output block.
|
||||
|
||||
```
|
||||
✓ Documentation updated (existing doc refreshed with current context)
|
||||
|
||||
Overlap detected: docs/solutions/performance-issues/n-plus-one-queries.md
|
||||
Matched dimensions: problem statement, root cause, solution, referenced files
|
||||
Action: Updated existing doc with fresher code examples and prevention tips
|
||||
|
||||
File updated:
|
||||
- docs/solutions/performance-issues/n-plus-one-queries.md (added last_updated: 2026-03-24)
|
||||
```
|
||||
|
||||
## The Compounding Philosophy
|
||||
|
||||
This creates a compounding knowledge system:
|
||||
|
||||
1. First time you solve "N+1 query in brief generation" → Research (30 min)
|
||||
2. Document the solution → docs/solutions/performance-issues/n-plus-one-briefs.md (5 min)
|
||||
3. Next time similar issue occurs → Quick lookup (2 min)
|
||||
4. Knowledge compounds → Team gets smarter
|
||||
|
||||
The feedback loop:
|
||||
|
||||
```
|
||||
Build → Test → Find Issue → Research → Improve → Document → Validate → Deploy
|
||||
↑ ↓
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Each unit of engineering work should make subsequent units of work easier—not harder.**
|
||||
|
||||
## Auto-Invoke
|
||||
|
||||
<auto_invoke> <trigger_phrases> - "that worked" - "it's fixed" - "working now" - "problem solved" </trigger_phrases>
|
||||
|
||||
<manual_override> Use /ce-compound [context] to document immediately without waiting for auto-detection. </manual_override> </auto_invoke>
|
||||
|
||||
## Output
|
||||
|
||||
Writes the final learning directly into `docs/solutions/`.
|
||||
|
||||
## Applicable Specialized Agents
|
||||
|
||||
Based on problem type, these agents can enhance documentation:
|
||||
|
||||
### Code Quality & Review
|
||||
- **ce-code-simplicity-reviewer**: Ensures solution code is minimal and clear
|
||||
- **ce-pattern-recognition-specialist**: Identifies anti-patterns or repeating issues
|
||||
|
||||
### Specific Domain Experts
|
||||
- **ce-performance-oracle**: Analyzes performance_issue category solutions
|
||||
- **ce-security-sentinel**: Reviews security_issue solutions for vulnerabilities
|
||||
- **ce-data-integrity-guardian**: Reviews database_issue migrations and queries
|
||||
|
||||
### Enhancement & Research
|
||||
- **ce-best-practices-researcher**: Enriches solution with industry best practices
|
||||
- **ce-framework-docs-researcher**: Links to framework/library documentation references
|
||||
|
||||
### When to Invoke
|
||||
- **Auto-triggered** (optional): Agents can run post-documentation for enhancement
|
||||
- **Manual trigger**: User can invoke agents after /ce-compound completes for deeper review
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/research [topic]` - Deep investigation (searches docs/solutions/ for patterns)
|
||||
- `/ce-plan` - Planning workflow (references documented solutions)
|
||||
@@ -0,0 +1,94 @@
|
||||
# Resolution Templates
|
||||
|
||||
Choose the template matching the problem_type track (see `references/schema.yaml`).
|
||||
|
||||
---
|
||||
|
||||
## Bug Track Template
|
||||
|
||||
Use for: `build_error`, `test_failure`, `runtime_error`, `performance_issue`, `database_issue`, `security_issue`, `ui_bug`, `integration_issue`, `logic_error`
|
||||
|
||||
<!-- YAML safety: array items (symptoms, applies_when, tags, related_components) starting with ` [ * & ! | > % @ ? or containing ": " must be wrapped in double quotes. See references/yaml-schema.md > "YAML Safety Rules". -->
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: [Clear problem title]
|
||||
date: [YYYY-MM-DD]
|
||||
category: [docs/solutions subdirectory]
|
||||
module: [Module or area]
|
||||
problem_type: [schema enum]
|
||||
component: [schema enum]
|
||||
symptoms:
|
||||
- [Observable symptom 1]
|
||||
root_cause: [schema enum]
|
||||
resolution_type: [schema enum]
|
||||
severity: [schema enum]
|
||||
tags: [keyword-one, keyword-two]
|
||||
---
|
||||
|
||||
# [Clear problem title]
|
||||
|
||||
## Problem
|
||||
[1-2 sentence description of the issue and user-visible impact]
|
||||
|
||||
## Symptoms
|
||||
- [Observable symptom or error]
|
||||
|
||||
## What Didn't Work
|
||||
- [Attempted fix and why it failed]
|
||||
|
||||
## Solution
|
||||
[The fix that worked, including code snippets when useful]
|
||||
|
||||
## Why This Works
|
||||
[Root cause explanation and why the fix addresses it]
|
||||
|
||||
## Prevention
|
||||
- [Concrete practice, test, or guardrail]
|
||||
|
||||
## Related Issues
|
||||
- [Related docs or issues, if any]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Knowledge Track Template
|
||||
|
||||
Use for: `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`
|
||||
|
||||
<!-- YAML safety: array items (symptoms, applies_when, tags, related_components) starting with ` [ * & ! | > % @ ? or containing ": " must be wrapped in double quotes. See references/yaml-schema.md > "YAML Safety Rules". -->
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: [Clear, descriptive title]
|
||||
date: [YYYY-MM-DD]
|
||||
category: [docs/solutions subdirectory]
|
||||
module: [Module or area]
|
||||
problem_type: [schema enum]
|
||||
component: [schema enum]
|
||||
severity: [schema enum]
|
||||
applies_when:
|
||||
- [Condition where this applies]
|
||||
tags: [keyword-one, keyword-two]
|
||||
---
|
||||
|
||||
# [Clear, descriptive title]
|
||||
|
||||
## Context
|
||||
[What situation, gap, or friction prompted this guidance]
|
||||
|
||||
## Guidance
|
||||
[The practice, pattern, or recommendation with code examples when useful]
|
||||
|
||||
## Why This Matters
|
||||
[Rationale and impact of following or not following this guidance]
|
||||
|
||||
## When to Apply
|
||||
- [Conditions or situations where this applies]
|
||||
|
||||
## Examples
|
||||
[Concrete before/after or usage examples showing the practice in action]
|
||||
|
||||
## Related
|
||||
- [Related docs or issues, if any]
|
||||
```
|
||||
@@ -0,0 +1,78 @@
|
||||
# CONCEPTS.md vocabulary rules
|
||||
|
||||
`CONCEPTS.md` defines the words that mean something specific in this codebase — substrate that `docs/solutions/` and AGENTS.md can cite without redefinition. Lives at the repo root. Terms enter two ways — accretion and seeding (below) — and the file is created the first time either path produces a qualifying entry.
|
||||
|
||||
## How terms enter: accretion and seeding
|
||||
|
||||
Two paths populate the file, and they cover different gaps:
|
||||
|
||||
- **Accretion** — a learning surfaces a term whose meaning wasn't obvious, so it gets defined. This reliably catches *peripheral* terms, because friction is what surfaces them.
|
||||
- **Seeding** — a run proactively defines the **core domain nouns** of the area it is working in. This catches the *stable-central* terms accretion never reaches: the nouns a system is built around rarely break, so they rarely appear in a learning, yet they are exactly what a reader needs to orient. Without seeding, the file fills with peripheral mechanics and never names what the project is about.
|
||||
|
||||
### Seed goal
|
||||
|
||||
Define the core domain nouns the area's **declared domain model** exposes that meet the qualifying bar (see "What earns a slot"). The codebase sets the count: seed every term that genuinely qualifies, none added to reach a number and none pulled from beyond the declared model to inflate one. A small domain yields a few; a large one, more. The bound is the **source** (the declared domain model of the area in scope — schema, core types, primary models, top-level domain docs — not a full-codebase trawl) and the **bar** (the same "a new engineer would need this defined" test), never a fixed quantity.
|
||||
|
||||
### Scope of a seed
|
||||
|
||||
- A **scoped run** — a learning capture, or a refresh narrowed to an area — seeds only that area's core nouns, and defines only terms it actually investigated against code. It does not reach for repo-wide nouns it never touched.
|
||||
- A **repo-wide bootstrap** — an explicit "create CONCEPTS.md" request — seeds the whole project's declared domain model. This is the only path that produces a coherent "what is this project" glossary; a scoped run cannot, and should not pretend to.
|
||||
|
||||
## Be opinionated
|
||||
|
||||
When the team uses several words for the same concept, pick the best one and retire the rest. Record retired synonyms as aliases on the entry (see "Per entry"). Settled distinctions go to the Flagged ambiguities tail. The glossary is not a record of all words the team has ever used — it is the team's agreed-upon vocabulary.
|
||||
|
||||
## The file stands on its own
|
||||
|
||||
Each entry teaches its concept to a reader with no access to anything else — no codebase, no PR history, no architecture meetings, no Slack. This rules out:
|
||||
|
||||
- Implementation specifics (file paths, class names, function signatures, table names, library calls)
|
||||
- Status fields, dates, owners on the entries
|
||||
- Examples or current-config values drawn from the code — specific thresholds, counts, or enum values that will change. State the behavior, not the number: "each skill sets its own actionable threshold" rather than "surfaces at 50, fixes at 75."
|
||||
- Links to PRs, issues, channels, or roadmap milestones
|
||||
- Version-specific claims ("currently uses X; migrating to Y")
|
||||
|
||||
Cross-references between entries within `CONCEPTS.md` are fine — they resolve internally. General programming vocabulary (caches, queues, jobs, sessions) and everyday domain English need no redefinition either. But if an entry leans on another *project-specific* term to make sense, that term must be defined here too — an undefined project-specific sibling is itself a candidate to add.
|
||||
|
||||
## What earns a slot
|
||||
|
||||
A term qualifies when its meaning here is precise enough that a new engineer would need it defined to follow conversations, tickets, or code. General programming vocabulary does not belong, even when used heavily.
|
||||
|
||||
## Per entry
|
||||
|
||||
Definition is one sentence — what the term means in this domain, what makes it distinct from neighbors. A term with non-obvious behavioral rules (lifecycle, cancellation semantics, ownership invariants) earns a second paragraph for those rules — never for elaborating the definition itself.
|
||||
|
||||
When retired synonyms exist, list them as an aliases line directly under the definition: *Avoid: Booking, appointment*. Entities typically need more depth than value types; status concepts may need transition notes.
|
||||
|
||||
## Relationships (optional)
|
||||
|
||||
When relationships between entries carry load-bearing meaning (ownership, cardinality, lifecycle dependencies that span entries), capture them in a `## Relationships` section near the top of the file or its cluster. Skip when entries stand on their own without structural context — relationships are a lift for domains where structure is part of what makes terms meaningful, not a routine section.
|
||||
|
||||
## Organization
|
||||
|
||||
Cluster concepts by domain relationship — entities with their states, processes with their stages — so a reader sees structure without effort. A flat list works when the file is small. Reshape as the file grows.
|
||||
|
||||
## Flagged ambiguities (tail of file)
|
||||
|
||||
When two terms were used interchangeably and the team settled on a distinction, record the resolution as a one-line note: *"'account' had been used for both Customer and User — these are distinct."* This section is the audit trail for opinions the team has formed.
|
||||
|
||||
## One illustrative entry — the shape, not a template
|
||||
|
||||
```
|
||||
## Booking
|
||||
|
||||
### Reservation
|
||||
A future commitment to seat a Party at a specified date and time.
|
||||
*Avoid:* Booking, appointment
|
||||
|
||||
A Reservation owns its Party but does not own a Table — Tables are acquired only when the Party arrives, through a Seating. Lifecycle: Booked, Seated, Completed, No-Show. Cancellation before a Seating is non-destructive; cancellation after a Seating is recorded as a No-Show.
|
||||
|
||||
### Party
|
||||
The guests committed to a Reservation. Each Reservation has exactly one Party. Party size is the count promised at booking, not the count who arrive.
|
||||
|
||||
### Table
|
||||
A physical seating unit with fixed capacity. Tables are shared resources — they do not belong to Reservations and are allocated only on the day-of through Seatings.
|
||||
|
||||
### Seating
|
||||
The act of placing a Party at a Table once the Party arrives. A Reservation has at most one Seating; a Table accumulates many Seatings across its lifetime.
|
||||
```
|
||||
@@ -0,0 +1,231 @@
|
||||
# Documentation schema for learnings written by ce-compound
|
||||
# Treat this as the canonical frontmatter contract for docs/solutions/.
|
||||
#
|
||||
# The schema has two tracks based on problem_type:
|
||||
# Bug track — problem_type is a defect or failure (build_error, test_failure, etc.)
|
||||
# Knowledge track — problem_type is guidance or practice (best_practice, workflow_issue, etc.)
|
||||
#
|
||||
# Both tracks share the same required core fields. The tracks differ in which
|
||||
# additional fields are required vs optional (see track_rules below).
|
||||
|
||||
# --- Track classification ---------------------------------------------------
|
||||
tracks:
|
||||
bug:
|
||||
description: "Defects, failures, and errors that were diagnosed and fixed"
|
||||
problem_types:
|
||||
- build_error
|
||||
- test_failure
|
||||
- runtime_error
|
||||
- performance_issue
|
||||
- database_issue
|
||||
- security_issue
|
||||
- ui_bug
|
||||
- integration_issue
|
||||
- logic_error
|
||||
knowledge:
|
||||
description: "Practices, patterns, conventions, decisions, workflow improvements, and documentation"
|
||||
problem_types:
|
||||
- best_practice
|
||||
- documentation_gap
|
||||
- workflow_issue
|
||||
- developer_experience
|
||||
- architecture_pattern
|
||||
- design_pattern
|
||||
- tooling_decision
|
||||
- convention
|
||||
|
||||
# --- Fields required by BOTH tracks -----------------------------------------
|
||||
required_fields:
|
||||
module:
|
||||
type: string
|
||||
description: "Module or area affected"
|
||||
|
||||
date:
|
||||
type: string
|
||||
pattern: '^\d{4}-\d{2}-\d{2}$'
|
||||
description: "Date documented (YYYY-MM-DD)"
|
||||
|
||||
problem_type:
|
||||
type: enum
|
||||
values:
|
||||
- build_error
|
||||
- test_failure
|
||||
- runtime_error
|
||||
- performance_issue
|
||||
- database_issue
|
||||
- security_issue
|
||||
- ui_bug
|
||||
- integration_issue
|
||||
- logic_error
|
||||
- developer_experience
|
||||
- workflow_issue
|
||||
- best_practice
|
||||
- documentation_gap
|
||||
- architecture_pattern
|
||||
- design_pattern
|
||||
- tooling_decision
|
||||
- convention
|
||||
description: "Primary category — determines track (bug vs knowledge). Prefer the narrowest applicable value; best_practice is the fallback when no narrower knowledge-track value fits."
|
||||
|
||||
component:
|
||||
type: enum
|
||||
values:
|
||||
- rails_model
|
||||
- rails_controller
|
||||
- rails_view
|
||||
- service_object
|
||||
- background_job
|
||||
- database
|
||||
- frontend_stimulus
|
||||
- hotwire_turbo
|
||||
- email_processing
|
||||
- brief_system
|
||||
- assistant
|
||||
- authentication
|
||||
- payments
|
||||
- development_workflow
|
||||
- testing_framework
|
||||
- documentation
|
||||
- tooling
|
||||
description: "Component involved"
|
||||
|
||||
severity:
|
||||
type: enum
|
||||
values:
|
||||
- critical
|
||||
- high
|
||||
- medium
|
||||
- low
|
||||
description: "Impact severity"
|
||||
|
||||
# --- Track-specific rules ----------------------------------------------------
|
||||
track_rules:
|
||||
bug:
|
||||
required:
|
||||
symptoms:
|
||||
type: array[string]
|
||||
min_items: 1
|
||||
max_items: 5
|
||||
description: "Observable symptoms such as errors or broken behavior"
|
||||
root_cause:
|
||||
type: enum
|
||||
values:
|
||||
- missing_association
|
||||
- missing_include
|
||||
- missing_index
|
||||
- wrong_api
|
||||
- scope_issue
|
||||
- thread_violation
|
||||
- async_timing
|
||||
- memory_leak
|
||||
- config_error
|
||||
- logic_error
|
||||
- test_isolation
|
||||
- missing_validation
|
||||
- missing_permission
|
||||
- missing_workflow_step
|
||||
- inadequate_documentation
|
||||
- missing_tooling
|
||||
- incomplete_setup
|
||||
description: "Fundamental technical cause of the problem"
|
||||
resolution_type:
|
||||
type: enum
|
||||
values:
|
||||
- code_fix
|
||||
- migration
|
||||
- config_change
|
||||
- test_fix
|
||||
- dependency_update
|
||||
- environment_setup
|
||||
- workflow_improvement
|
||||
- documentation_update
|
||||
- tooling_addition
|
||||
- seed_data_update
|
||||
description: "Type of fix applied"
|
||||
|
||||
knowledge:
|
||||
optional:
|
||||
applies_when:
|
||||
type: array[string]
|
||||
max_items: 5
|
||||
description: "Conditions or situations where this guidance applies"
|
||||
symptoms:
|
||||
type: array[string]
|
||||
max_items: 5
|
||||
description: "Observable gaps or friction that prompted this guidance (optional for knowledge track)"
|
||||
root_cause:
|
||||
type: enum
|
||||
values:
|
||||
- missing_association
|
||||
- missing_include
|
||||
- missing_index
|
||||
- wrong_api
|
||||
- scope_issue
|
||||
- thread_violation
|
||||
- async_timing
|
||||
- memory_leak
|
||||
- config_error
|
||||
- logic_error
|
||||
- test_isolation
|
||||
- missing_validation
|
||||
- missing_permission
|
||||
- missing_workflow_step
|
||||
- inadequate_documentation
|
||||
- missing_tooling
|
||||
- incomplete_setup
|
||||
description: "Underlying cause, if there is a specific one (optional for knowledge track)"
|
||||
resolution_type:
|
||||
type: enum
|
||||
values:
|
||||
- code_fix
|
||||
- migration
|
||||
- config_change
|
||||
- test_fix
|
||||
- dependency_update
|
||||
- environment_setup
|
||||
- workflow_improvement
|
||||
- documentation_update
|
||||
- tooling_addition
|
||||
- seed_data_update
|
||||
description: "Type of change, if applicable (optional for knowledge track)"
|
||||
|
||||
# --- Fields optional for BOTH tracks ----------------------------------------
|
||||
optional_fields:
|
||||
related_components:
|
||||
type: array[string]
|
||||
description: "Other components involved"
|
||||
|
||||
tags:
|
||||
type: array[string]
|
||||
max_items: 8
|
||||
description: "Search keywords, lowercase and hyphen-separated"
|
||||
|
||||
# --- Fields optional for bug track only -------------------------------------
|
||||
bug_optional_fields:
|
||||
rails_version:
|
||||
type: string
|
||||
pattern: '^\d+\.\d+\.\d+$'
|
||||
description: "Rails version in X.Y.Z format. Only relevant for bug-track docs."
|
||||
|
||||
# --- Backward compatibility --------------------------------------------------
|
||||
# Docs created before the track system was introduced may have bug-track
|
||||
# fields (symptoms, root_cause, resolution_type) on knowledge-type
|
||||
# problem_types. These are valid legacy docs:
|
||||
# - Bug-track fields present on a knowledge-track doc are harmless. Do not
|
||||
# strip them during refresh unless the doc is being rewritten for other reasons.
|
||||
# - When creating NEW docs, follow the track rules above.
|
||||
|
||||
# --- Validation rules --------------------------------------------------------
|
||||
validation_rules:
|
||||
- "Determine track from problem_type using the tracks section above"
|
||||
- "All shared required_fields must be present"
|
||||
- "Bug-track required fields (symptoms, root_cause, resolution_type) must be present on bug-track docs"
|
||||
- "Knowledge-track docs have no additional required fields beyond the shared ones"
|
||||
- "Bug-track fields on existing knowledge-track docs are harmless (see backward compatibility note)"
|
||||
- "Track-specific optional fields may be included but are not required"
|
||||
- "Enum fields must match allowed values exactly"
|
||||
- "Array fields must respect min_items/max_items when specified"
|
||||
- "date must match YYYY-MM-DD format"
|
||||
- "rails_version, if provided, must match X.Y.Z format and only applies to bug-track docs"
|
||||
- "tags should be lowercase and hyphen-separated"
|
||||
- "Array-of-strings frontmatter items (symptoms, applies_when, tags, related_components, or any future array field) must be wrapped in double quotes when the value starts with a YAML reserved indicator (`, [, *, &, !, |, >, %, @, ?) or contains the substring `: ` — otherwise strict YAML parsers reject the file"
|
||||
@@ -0,0 +1,118 @@
|
||||
# YAML Frontmatter Schema
|
||||
|
||||
`schema.yaml` in this directory is the canonical contract for `docs/solutions/` frontmatter written by `ce-compound`.
|
||||
|
||||
Use this file as the quick reference for:
|
||||
- required fields
|
||||
- enum values
|
||||
- validation expectations
|
||||
- category mapping
|
||||
- track classification (bug vs knowledge)
|
||||
|
||||
## Tracks
|
||||
|
||||
The `problem_type` determines which **track** applies. Each track has different required and optional fields.
|
||||
|
||||
| Track | problem_types | Description |
|
||||
|-------|--------------|-------------|
|
||||
| **Bug** | `build_error`, `test_failure`, `runtime_error`, `performance_issue`, `database_issue`, `security_issue`, `ui_bug`, `integration_issue`, `logic_error` | Defects and failures that were diagnosed and fixed |
|
||||
| **Knowledge** | `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`, `architecture_pattern`, `design_pattern`, `tooling_decision`, `convention` | Practices, patterns, conventions, decisions, workflow improvements, and documentation. Prefer the narrowest applicable value; `best_practice` is the fallback. |
|
||||
|
||||
## Required Fields (both tracks)
|
||||
|
||||
- **module**: Module or area affected
|
||||
- **date**: ISO date in `YYYY-MM-DD`
|
||||
- **problem_type**: One of the values listed in the Tracks table above
|
||||
- **component**: One of `rails_model`, `rails_controller`, `rails_view`, `service_object`, `background_job`, `database`, `frontend_stimulus`, `hotwire_turbo`, `email_processing`, `brief_system`, `assistant`, `authentication`, `payments`, `development_workflow`, `testing_framework`, `documentation`, `tooling`
|
||||
- **severity**: One of `critical`, `high`, `medium`, `low`
|
||||
|
||||
## Bug Track Fields
|
||||
|
||||
Required:
|
||||
- **symptoms**: YAML array with 1-5 observable symptoms (errors, broken behavior)
|
||||
- **root_cause**: One of `missing_association`, `missing_include`, `missing_index`, `wrong_api`, `scope_issue`, `thread_violation`, `async_timing`, `memory_leak`, `config_error`, `logic_error`, `test_isolation`, `missing_validation`, `missing_permission`, `missing_workflow_step`, `inadequate_documentation`, `missing_tooling`, `incomplete_setup`
|
||||
- **resolution_type**: One of `code_fix`, `migration`, `config_change`, `test_fix`, `dependency_update`, `environment_setup`, `workflow_improvement`, `documentation_update`, `tooling_addition`, `seed_data_update`
|
||||
|
||||
## Knowledge Track Fields
|
||||
|
||||
No additional required fields beyond the shared ones. All fields below are optional:
|
||||
|
||||
- **applies_when**: Conditions or situations where this guidance applies
|
||||
- **symptoms**: Observable gaps or friction that prompted this guidance
|
||||
- **root_cause**: Underlying cause, if there is a specific one
|
||||
- **resolution_type**: Type of change, if applicable
|
||||
|
||||
## Optional Fields (both tracks)
|
||||
|
||||
- **related_components**: Other components involved
|
||||
- **tags**: Search keywords, lowercase and hyphen-separated
|
||||
|
||||
## Optional Fields (bug track only)
|
||||
|
||||
- **rails_version**: Rails version in `X.Y.Z` format
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
Docs created before the track system may have `symptoms`/`root_cause`/`resolution_type` on knowledge-type problem_types. These are valid legacy docs:
|
||||
|
||||
- Bug-track fields present on a knowledge-track doc are harmless. Do not strip them during refresh unless the doc is being rewritten for other reasons.
|
||||
- When creating **new** docs, follow the track rules above.
|
||||
|
||||
## Category Mapping
|
||||
|
||||
- `build_error` -> `docs/solutions/build-errors/`
|
||||
- `test_failure` -> `docs/solutions/test-failures/`
|
||||
- `runtime_error` -> `docs/solutions/runtime-errors/`
|
||||
- `performance_issue` -> `docs/solutions/performance-issues/`
|
||||
- `database_issue` -> `docs/solutions/database-issues/`
|
||||
- `security_issue` -> `docs/solutions/security-issues/`
|
||||
- `ui_bug` -> `docs/solutions/ui-bugs/`
|
||||
- `integration_issue` -> `docs/solutions/integration-issues/`
|
||||
- `logic_error` -> `docs/solutions/logic-errors/`
|
||||
- `developer_experience` -> `docs/solutions/developer-experience/`
|
||||
- `workflow_issue` -> `docs/solutions/workflow-issues/`
|
||||
- `best_practice` -> `docs/solutions/best-practices/`
|
||||
- `documentation_gap` -> `docs/solutions/documentation-gaps/`
|
||||
- `architecture_pattern` -> `docs/solutions/architecture-patterns/`
|
||||
- `design_pattern` -> `docs/solutions/design-patterns/`
|
||||
- `tooling_decision` -> `docs/solutions/tooling-decisions/`
|
||||
- `convention` -> `docs/solutions/conventions/`
|
||||
|
||||
## Validation Rules
|
||||
|
||||
1. Determine the track from `problem_type` using the Tracks table.
|
||||
2. All shared required fields must be present.
|
||||
3. Bug-track required fields (`symptoms`, `root_cause`, `resolution_type`) must be present on bug-track docs.
|
||||
4. Knowledge-track docs have no additional required fields beyond the shared ones.
|
||||
5. Bug-track fields on existing knowledge-track docs are harmless (see Backward Compatibility).
|
||||
6. Enum fields must match the allowed values exactly.
|
||||
7. Array fields must respect min/max item counts.
|
||||
8. `date` must match `YYYY-MM-DD`.
|
||||
9. `rails_version`, if present, must match `X.Y.Z` and only applies to bug-track docs.
|
||||
|
||||
## YAML Safety Rules
|
||||
|
||||
Strict YAML 1.2 parsers (`yq`, `js-yaml` strict, PyYAML) reject array items
|
||||
that start with a reserved indicator character as unquoted scalars. When
|
||||
writing items for any array-of-strings field (`symptoms`, `applies_when`,
|
||||
`tags`, `related_components`, or any future array field), wrap the value in
|
||||
double quotes if it starts with any of:
|
||||
|
||||
`` ` ``, `[`, `*`, `&`, `!`, `|`, `>`, `%`, `@`, `?`
|
||||
|
||||
Also quote if the value contains the substring `": "` — that punctuation
|
||||
confuses flow-style parsers.
|
||||
|
||||
Example — before (breaks strict YAML):
|
||||
|
||||
symptoms:
|
||||
- `sudo dscacheutil -flushcache` does not restore in-container mDNS
|
||||
|
||||
Example — after (parses cleanly):
|
||||
|
||||
symptoms:
|
||||
- "`sudo dscacheutil -flushcache` does not restore in-container mDNS"
|
||||
|
||||
This rule applies to all array-of-strings frontmatter fields. Scalar string
|
||||
fields like `description:` have their own quoting rules (see plugin
|
||||
`AGENTS.md` under "YAML Frontmatter").
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate ce-compound docs/solutions/ frontmatter for parser-safety issues.
|
||||
|
||||
Usage:
|
||||
python3 validate-frontmatter.py <doc-path>
|
||||
|
||||
Exit codes:
|
||||
0 — frontmatter passes all checks
|
||||
1 — validation failure (diagnostics on stderr)
|
||||
2 — usage error (bad arguments, missing file)
|
||||
|
||||
Scope: this script catches *parser-safety* issues — frontmatter that strict
|
||||
YAML parsers will silently misread. It does NOT validate against the
|
||||
schema's required-field or enum-value rules; that's a separate concern. The
|
||||
intent is to prevent the silent-data-loss bug class where YAML's quoting
|
||||
rules truncate or reframe scalar values without raising.
|
||||
|
||||
Checks (regex-based, no YAML parser dependency):
|
||||
1. File starts and ends frontmatter with `---` lines (matched as full
|
||||
lines, not substrings — `----` and `---extra` are rejected)
|
||||
2. No top-level scalar value contains ` #` unquoted (silent comment
|
||||
truncation — what Codex caught on PR #695)
|
||||
3. No top-level scalar value contains `: ` unquoted (mapping confusion —
|
||||
what surfaced in a 2026-04-16 plan doc's `title:` field)
|
||||
|
||||
The script does NOT flag values starting with YAML reserved indicators
|
||||
(`` ` ``, `*`, `&`, `!`, etc.) because those produce loud parser errors
|
||||
downstream rather than silent corruption — they're already caught by
|
||||
whatever consumes the doc. This validator's purpose is silent-corruption
|
||||
prevention, not lint.
|
||||
|
||||
Pure-stdlib (no PyYAML or other third-party deps). Runs in <50ms typical.
|
||||
Designed to produce concrete, actionable error messages so the calling
|
||||
agent can fix and retry without ambiguity.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def usage_fail(msg: str) -> "NoReturn":
|
||||
sys.stderr.write(f"validate-frontmatter: {msg}\n")
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if len(argv) != 2:
|
||||
usage_fail(f"usage: {os.path.basename(argv[0])} <doc-path>")
|
||||
|
||||
doc_path = argv[1]
|
||||
if not os.path.isfile(doc_path):
|
||||
usage_fail(f"file not found: {doc_path}")
|
||||
|
||||
with open(doc_path) as f:
|
||||
text = f.read()
|
||||
|
||||
issues: list[str] = []
|
||||
|
||||
# Check 1: frontmatter delimiters. Match the delimiter as a complete
|
||||
# line whose stripped content is exactly `---` — substring matching
|
||||
# (e.g. `text.find("\n---", 4)`) would falsely accept `----` or
|
||||
# `---extra` as a terminator and let malformed docs slip through to
|
||||
# downstream parsers that require a strict `---` line.
|
||||
lines = text.split("\n")
|
||||
if not lines or lines[0].rstrip() != "---":
|
||||
sys.stderr.write(
|
||||
f"FAIL: {doc_path}\n"
|
||||
f" file does not start with '---' frontmatter delimiter line\n"
|
||||
)
|
||||
return 1
|
||||
|
||||
end_idx: int | None = None
|
||||
for i in range(1, len(lines)):
|
||||
if lines[i].rstrip() == "---":
|
||||
end_idx = i
|
||||
break
|
||||
|
||||
if end_idx is None:
|
||||
sys.stderr.write(
|
||||
f"FAIL: {doc_path}\n"
|
||||
f" frontmatter not closed (no '---' line after the opening delimiter)\n"
|
||||
)
|
||||
return 1
|
||||
|
||||
fm_text = "\n".join(lines[1:end_idx])
|
||||
|
||||
# Checks 2 & 3: silent-corruption quoting risks on top-level scalar
|
||||
# fields. We scan line-by-line and only flag top-level mapping entries
|
||||
# (no leading whitespace) whose value isn't already quoted/structured.
|
||||
for lineno, line in enumerate(fm_text.split("\n"), start=2):
|
||||
stripped = line.lstrip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
if ":" not in line:
|
||||
continue
|
||||
# Top-level mapping keys only — skip nested values, array items
|
||||
if line.startswith((" ", "\t")):
|
||||
continue
|
||||
# Skip pure list-marker lines like "- item" (these can't be top-level
|
||||
# in our frontmatter convention, but be defensive)
|
||||
if stripped.startswith("- "):
|
||||
continue
|
||||
|
||||
key, _, val = line.partition(":")
|
||||
val_stripped = val.strip()
|
||||
if not val_stripped:
|
||||
# Key with no value on this line — likely a parent of a nested
|
||||
# block (`tags:` followed by `- foo`). Nothing to validate here.
|
||||
continue
|
||||
# Already quoted or structured (block scalar, flow collection)
|
||||
if val_stripped[0] in '"\'[{|>':
|
||||
continue
|
||||
|
||||
if re.search(r"\s#", val_stripped):
|
||||
issues.append(
|
||||
f"line {lineno}: '{key.strip()}' value contains ' #' — quote it. "
|
||||
"YAML treats space-then-# as a comment delimiter and silently "
|
||||
"drops the rest of the value."
|
||||
)
|
||||
if re.search(r":\s", val_stripped):
|
||||
issues.append(
|
||||
f"line {lineno}: '{key.strip()}' value contains ': ' — quote it. "
|
||||
"Strict YAML parsers may treat this as a nested mapping."
|
||||
)
|
||||
|
||||
if issues:
|
||||
sys.stderr.write(f"FAIL: {doc_path}\n")
|
||||
for issue in issues:
|
||||
sys.stderr.write(f" {issue}\n")
|
||||
return 1
|
||||
|
||||
print(f"OK: {doc_path}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -0,0 +1,400 @@
|
||||
---
|
||||
name: ce-ideate
|
||||
description: "Generate and critically evaluate grounded ideas about a topic. Use when asking what to improve, requesting idea generation, exploring surprising directions, or wanting the AI to proactively suggest strong options before brainstorming one in depth. Triggers on phrases like 'what should I improve', 'give me ideas', 'ideate on X', 'surprise me', 'what would you change', or any request for AI-generated suggestions rather than refining the user's own idea."
|
||||
argument-hint: "[feature, focus area, or constraint]"
|
||||
|
||||
---
|
||||
|
||||
# Generate Improvement Ideas
|
||||
|
||||
**Note: The current year is 2026.** Use this when dating ideation documents and checking recent ideation artifacts.
|
||||
|
||||
`ce-ideate` precedes `ce-brainstorm`.
|
||||
|
||||
- `ce-ideate` answers: "What are the strongest ideas worth exploring?"
|
||||
- `ce-brainstorm` answers: "What exactly should one chosen idea mean?"
|
||||
- `ce-plan` answers: "How should it be built?"
|
||||
|
||||
This workflow produces a ranked ideation artifact in `docs/ideation/`. It does **not** produce requirements, plans, or code.
|
||||
|
||||
## Interaction Method
|
||||
|
||||
Use the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
Ask one question at a time. Prefer concise single-select choices when natural options exist.
|
||||
|
||||
## Focus Hint
|
||||
|
||||
<focus_hint> #$ARGUMENTS </focus_hint>
|
||||
|
||||
Interpret any provided argument as optional context. It may be:
|
||||
|
||||
- a concept such as `DX improvements`
|
||||
- a path such as `plugins/compound-engineering/skills/`
|
||||
- a constraint such as `low-complexity quick wins`
|
||||
- a volume hint such as `top 3`, `100 ideas`, or `raise the bar`
|
||||
|
||||
If no argument is provided, proceed with open-ended ideation.
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Ground before ideating** - Scan the actual codebase first. Do not generate abstract product advice detached from the repository.
|
||||
2. **Generate many -> critique all -> explain survivors only** - The quality mechanism is explicit rejection with reasons, not optimistic ranking. Do not let extra process obscure this pattern.
|
||||
3. **Route action into brainstorming** - Ideation identifies promising directions; `ce-brainstorm` defines the selected one precisely enough for planning. Do not skip to planning from ideation output.
|
||||
|
||||
## Execution Flow
|
||||
|
||||
### Phase 0: Resume and Scope
|
||||
|
||||
#### 0.1 Check for Recent Ideation Work
|
||||
|
||||
Look in `docs/ideation/` for ideation documents created within the last 30 days.
|
||||
|
||||
Treat a prior ideation doc as relevant when:
|
||||
|
||||
- the topic matches the requested focus
|
||||
- the path or subsystem overlaps the requested focus
|
||||
- the request is open-ended and there is an obvious recent open ideation doc
|
||||
- the issue-grounded status matches: do not offer to resume a non-issue ideation when the current argument indicates issue-tracker intent, or vice versa — treat these as distinct topics
|
||||
|
||||
If a relevant doc exists, ask whether to:
|
||||
|
||||
1. continue from it
|
||||
2. start fresh
|
||||
|
||||
If continuing:
|
||||
|
||||
- read the document
|
||||
- summarize what has already been explored
|
||||
- preserve previous idea statuses
|
||||
- update the existing file instead of creating a duplicate
|
||||
|
||||
#### 0.2 Subject-Identification Gate
|
||||
|
||||
Before classifying mode or dispatching any grounding, check whether the subject of ideation is identifiable. Every downstream agent — grounding and ideation — needs to know what it's working on. If the subject is ambiguous enough that reasonable sub-agents would diverge on what the topic even is (bare words like `improvements`, `ideas`, `birthday cakes`, `vacation destinations`), the output will be scattered.
|
||||
|
||||
**Questioning principles (apply in this phase and in 0.4):**
|
||||
|
||||
- Questions exist only to supply what sub-agents need to operate: an identifiable subject (this phase) and enough context for the agent to say something specific about it (0.4, elsewhere modes only). Nothing else.
|
||||
- Never ask about solution direction, constraints, audience, tone, success criteria, or anything that characterizes the subject — those belong to `ce-brainstorm`.
|
||||
- Always keep "Surprise me" (letting the agent decide the focus) as a real option, not a fallback for when the user can't name a subject. Ideation is allowed to be greenfield by design.
|
||||
- Stop as soon as the subject is identifiable or the user has delegated to "Surprise me." More than 3 total questions across 0.2 and 0.4 is a smell that ideation is not the right workflow — consider suggesting `ce-brainstorm`.
|
||||
|
||||
**Detection — issue-tracker intent (repo mode only; subject-identifying).**
|
||||
|
||||
Issue-tracker intent requires an explicit reference to the tracker or to reports filed in it. Trigger only when the prompt uses phrases like `github issues`, `open issues`, `issue patterns`, `issue themes`, `what users are reporting`, or `bug reports` — the subject is "issues in the tracker." Proceed to 0.3 with issue-tracker intent flagged.
|
||||
|
||||
Do NOT trigger on arguments that merely mention bugs as a focus: `bug in auth`, `fix the login issue`, `the signup bug`, `top 3 bugs in authentication` — these are focus hints on regular ideation, not requests to analyze the issue tracker. A bare `bugs` with no tracker phrasing is handled by the vagueness check below, not here.
|
||||
|
||||
When combined (e.g., `top 3 issue themes in authentication`, `biggest bug reports about checkout`): detect issue-tracker intent first, volume override in 0.5, remainder is the focus hint. The focus narrows which issues matter; the volume override controls survivor count.
|
||||
|
||||
**Detection — subject identifiability.**
|
||||
|
||||
The test: would a reader, seeing only this prompt, know what subject the agent should ideate on? Apply judgment to what the words *refer to*, not to their length or surface form.
|
||||
|
||||
- **Vague — ask the scope question.** The prompt refers to a quality, category, or placeholder without naming a specific thing. Reasonable readers would pick different subjects. Illustrative cases: `improvements`, `ideas`, `things to fix`, `quick wins`, `what to build`, `bugs` (as the whole prompt, not as a topic like "bugs in auth"), an empty prompt. These are examples of the pattern, not a lookup table — recognize vagueness by what the words point to (a catch-all quality), not by matching specific words.
|
||||
|
||||
- **Identifiable — proceed to 0.3.** The prompt names or plausibly names a specific subject: a feature, concept, document, subsystem, page, flow, or concrete topic. A reader would know where to direct thought even without knowing the domain. Illustrative cases: `authentication system`, `our sign-up page`, `browser sniff`, `dark mode`, `cache invalidation`, `a unicorn cake for my 7-year-old`, `plot ideas for a short story`.
|
||||
|
||||
**Key distinction:** vagueness is about what the words *refer to*, not phrase length. `browser sniff` is two words but plausibly names a feature, so it is identifiable. `quick wins` is two words but refers only to a quality, so it is vague. Do not treat short phrases as vague by default.
|
||||
|
||||
**Being inside a repo does not settle vagueness.** `improvements` in any repo is still scattered across DX, reliability, features, docs, tests, architecture. The repo provides material for grounding *after* a subject is settled, not the subject itself. Do not silently interpret a vague prompt as "about this repo" and proceed.
|
||||
|
||||
**Genuine ambiguity (repo mode).** When judgment leaves real doubt on a short phrase — it could be a named feature or a vague concept — a single cheap check settles it: Glob for the phrase in filenames, or Grep for it in README/docs. If it appears anywhere, treat as identifiable and proceed. If it has no repo footprint and still reads vaguely, ask the scope question.
|
||||
|
||||
When in doubt otherwise, err toward asking — one question is trivial compared to dispatching ~9 agents on a scattered interpretation.
|
||||
|
||||
**The scope question.**
|
||||
|
||||
Use the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists or the call errors — not because a schema load is required. Never silently skip.
|
||||
|
||||
- **Stem:** "What should the agent ideate about?"
|
||||
- **Options:**
|
||||
- "Specify a subject the agent should ideate on"
|
||||
- "Surprise me — let the agent decide what to focus on"
|
||||
- "Cancel — let me rephrase"
|
||||
|
||||
Routing:
|
||||
|
||||
- **Specify** → accept the user's follow-up as the subject. Re-apply the identifiability check once. If still ambiguous, ask once more with "Surprise me" still on the menu. Do not cascade toward specificity about *how* to solve — only about *what* the subject is.
|
||||
- **Surprise me** → mark the run as **surprise-me mode**. The agent will discover subjects from Phase 1 material rather than carry a user-specified subject. This is a first-class mode — it changes how Phase 1 scans and how Phase 2 sub-agents operate (see those phases). **Dispatch routing for surprise-me is deterministic:** if CWD is inside a git repo, route to repo-grounded (the codebase supplies substance); otherwise route to elsewhere-software and require Phase 0.4 to collect at least one piece of substance (URL, description, draft, or paste) before dispatching — "surprise me" outside a repo is only viable once the user has supplied something to surprise them about. Skip Decision 1/2 in Phase 0.3: with no user subject there is no prompt content to weigh, and surprise-me never routes to elsewhere-non-software (no way to infer naming/narrative/personal intent without a subject). The user can correct by interrupting and re-invoking with a named subject.
|
||||
- **Cancel** → exit cleanly. Narrate that the user can rephrase and re-invoke.
|
||||
|
||||
#### 0.3 Mode Classification
|
||||
|
||||
Classify the **subject of ideation** (settled in 0.2) into one of three modes for dispatch routing. A user inside any repo can ideate about something unrelated to that repo; a user in `/tmp` can ideate about code they hold in their head.
|
||||
|
||||
**Surprise-me short-circuit.** When Phase 0.2 routed to surprise-me mode, skip the two-decision classification below and use the deterministic rule stated in 0.2: repo-grounded when CWD is inside a git repo, elsewhere-software otherwise. The ambiguity-confirmation step at the end of this section also does not fire for surprise-me — there is no user subject to be ambiguous about. State the chosen mode in one sentence and proceed to 0.4.
|
||||
|
||||
For specified subjects, make two sequential binary decisions, enumerating negative signals at each:
|
||||
|
||||
**Decision 1 — repo-grounded vs elsewhere.** Weigh prompt content first, topic-repo coherence second, and CWD repo presence as supporting evidence only.
|
||||
|
||||
- Positive signals for **repo-grounded**: prompt references repo files, code, architecture, modules, tests, or workflows; topic is clearly bounded by the current codebase. Issue-tracker intent from 0.2 is always repo-grounded.
|
||||
- Negative signals (push toward **elsewhere**): prompt names things absent from the repo (pricing, naming, narrative, business model, personal decisions, brand, content, market positioning); topic is creative, business, or personal with no code surface.
|
||||
|
||||
**Decision 2 (only fires if Decision 1 = elsewhere) — software vs non-software.** Classify by whether the *subject* of ideation is a software artifact or system, not by where the individual ideas will eventually land. If the topic concerns a product, app, SaaS, web/mobile UI, feature, page, or service, it is **elsewhere-software** — even when the ideas themselves are about copy, UX, CRO, pricing, onboarding, visual design, or positioning *for that software product*. **Elsewhere-non-software** is reserved for topics with no software surface at all: company or brand naming (independent of product), narrative and creative writing, personal decisions, non-digital business strategy, physical-product design.
|
||||
|
||||
Sample classifications:
|
||||
|
||||
- "Improve conversion on our sign-up page" → elsewhere-software (the subject is a page)
|
||||
- "Redesign the onboarding flow" → elsewhere-software (the subject is a flow)
|
||||
- "Pricing page A/B test ideas" → elsewhere-software (the subject is a page)
|
||||
- "Features to add to our note-taking app" → elsewhere-software
|
||||
- "Name my new coffee shop" → elsewhere-non-software (the subject is a brand)
|
||||
- "Plot ideas for a short story" → elsewhere-non-software (the subject is a narrative)
|
||||
- "Options for my next career move" → elsewhere-non-software (the subject is a personal decision)
|
||||
|
||||
State the inferred approach in one sentence at the top, using plain language the user will recognize. Never print the internal taxonomy label (`repo-grounded`, `elsewhere-software`, `elsewhere-non-software`) to the user — those names are for routing only. Adapt the template below to the actual topic; pick a domain word from the topic itself (e.g., "landing page", "onboarding flow", "naming", "career decision") instead of a mode label.
|
||||
|
||||
- **Repo-grounded:** "Treating this as a topic in this codebase — about X."
|
||||
- **Elsewhere-software:** "Treating this as a product/software topic outside this repo — about X."
|
||||
- **Elsewhere-non-software:** "Treating this as a [naming | narrative | business | personal] topic — about X."
|
||||
|
||||
Do not prescribe correction phrases ("say X to switch"). State the inferred mode plainly and proceed. If the user disagrees, they will correct in their own words or interrupt to re-invoke — reclassify and re-run any affected routing when that happens.
|
||||
|
||||
**Active confirmation on mode ambiguity.** Only fire when mode classification is genuinely ambiguous *after* 0.2 settled the subject — e.g., "our docs" could mean repo docs (repo-grounded) or public marketing docs (elsewhere-software). Most subjects settled in 0.2 classify cleanly here. When ambiguous, ask one confirmation question via the blocking tool with two self-contained labels naming the two candidate interpretations in plain language (e.g., "Treat as repo docs in this codebase" vs "Treat as public marketing docs") — never leak internal mode names. Otherwise the one-sentence inferred-mode statement is sufficient; do not ask.
|
||||
|
||||
**Routing rule (non-software mode).** When Decision 2 = non-software, still run Phase 1 Elsewhere-mode grounding (user-context synthesis + web-research by default; skip phrases honored). Learnings-researcher is skipped by default in this mode — the CWD's `docs/solutions/` rarely transfers to naming, narrative, personal, or non-digital business topics; see Phase 1 for the full rationale. Then load `references/universal-ideation.md` and follow it in place of Phase 2's software frame dispatch and the Phase 6 menu narrative. This load is non-optional — the file contains the domain-agnostic generation frames, critique rubric, and wrap-up menu that replace Phase 2 and the post-ideation menu for this mode, and none of those details live in this main body. Improvising from memory produces the wrong facilitation for non-software topics. Do not run the repo-specific codebase scan at any point. The §6.5 Proof Failure Ladder in `references/post-ideation-workflow.md` still applies — load and follow it whenever a Proof save (the elsewhere-mode default for Save and end) fails, so the local-save fallback path stays reachable in non-software elsewhere runs.
|
||||
|
||||
#### 0.4 Context-Substance Gate (Elsewhere Modes Only)
|
||||
|
||||
Skip in repo mode — the repo provides the substance Phase 1 agents work from. In elsewhere modes (both software and non-software), Phase 1 agents depend on user-supplied context for substance. A bare prompt with no description, URL, or artifact leaves the user-context-synthesis agent with nothing to synthesize and weakens web research's relevance.
|
||||
|
||||
Apply the discrimination test: would swapping one piece of the user's stated context for a contrasting alternative materially change which ideas survive? If yes, context is load-bearing — proceed. If no, ask 1-3 narrowly chosen questions focused on **supplying substance, not characterizing the subject**:
|
||||
|
||||
- A URL or file to read
|
||||
- A brief description of the current state
|
||||
- A paste of an existing draft or brief
|
||||
|
||||
Build on what the user already provided rather than starting from a template. Default to free-form questions; use single-select only when the answer space is small and discrete. After each answer, re-apply the test before asking another. Stop on dismissive responses ("idk just go") — treat genuine "no context" answers as real answers and note context is thin in the summary so Phase 2 can compensate with broader generation.
|
||||
|
||||
**Surprise-me exception.** When the run is in surprise-me mode and routed to elsewhere-software (per 0.2's deterministic routing for no-repo CWDs), at least one piece of substance is required — there is no subject AND no repo, so Phase 1 and 2 agents would have nothing to discover subjects from. Dismissive responses are not acceptable here; if the user still has no context after one ask, tell them the run needs a URL, description, or paste to proceed and end cleanly so they can re-invoke with material.
|
||||
|
||||
When the user provides rich context up front (a paste, a brief, an existing draft, a URL), confirm understanding in one line and skip this step entirely.
|
||||
|
||||
If this step materially changes the topic (not just adds context but shifts the subject), re-run 0.2 and 0.3 against the refined scope before dispatching Phase 1 — classify on what's actually being ideated on, not the scope at first read.
|
||||
|
||||
#### 0.5 Interpret Focus and Volume
|
||||
|
||||
Infer two things from the argument and any intake so far:
|
||||
|
||||
- **Focus context** — concept, path, constraint, or open-ended
|
||||
- **Volume override** — any hint that changes candidate or survivor counts
|
||||
|
||||
Default volume:
|
||||
|
||||
- each ideation sub-agent generates about 6-8 ideas (yielding ~36-48 raw ideas across 6 frames in the default path, or ~24-32 across 4 frames in issue-tracker mode; roughly 25-30 survivors after dedupe in the 6-frame path and fewer in the 4-frame path)
|
||||
- keep the top 5-7 survivors
|
||||
|
||||
Honor clear overrides such as:
|
||||
|
||||
- `top 3`
|
||||
- `100 ideas`
|
||||
- `go deep`
|
||||
- `raise the bar`
|
||||
|
||||
**Tactical scope detection.** Parse the focus hint (and any intake answers from 0.2 specify path) for tactical signals: `polish`, `typo`, `typos`, `quick wins`, `small improvements`, `cleanup`, `small fixes`. When present, lower the Phase 2 ambition floor — the user has explicitly opted into tactical scope. Default otherwise is step-function (see Phase 2 meeting-test floor).
|
||||
|
||||
Use reasonable interpretation rather than formal parsing.
|
||||
|
||||
#### 0.6 Cost Transparency Notice
|
||||
|
||||
Before dispatching Phase 1, surface the agent count for the inferred mode in one short line so multi-agent cost is not invisible. Compute the count from the actual dispatch decision: 1 grounding-context agent (codebase scan in repo mode; user-context synthesis in elsewhere) + 1 learnings (skip in elsewhere-non-software) + 1 web researcher + 6 ideation = baseline 9 in repo mode and elsewhere-software, 8 in elsewhere-non-software. When issue-tracker intent triggers (repo mode only): add 1 for the issue-intelligence agent and drop ideation from 6 to 4, for a net -1 (baseline 8). Add 1 if the user opted into Slack research. Subtract 1 if the user issued a web-research skip phrase or V15 reuse will fire. In **surprise-me mode**, agent count is the same but per-agent exploration is deeper — note "(surprise-me mode: deeper exploration per agent)" when active. Phase 2's axis-coverage check may dispatch up to 2 additional recovery sub-agents when generation leaves any topic axis empty (skipped in surprise-me mode); when not in surprise-me, append "(+up to 2 if axis-coverage requires recovery)" to the count line.
|
||||
|
||||
Examples (defaults, no skips, no opt-ins):
|
||||
|
||||
- **Repo mode, specified subject:** "Will dispatch ~9 agents: codebase scan + learnings + web research + 6 ideation sub-agents. Skip phrases: 'no external research', 'no slack'."
|
||||
- **Repo mode, surprise-me:** "Will dispatch ~9 agents (surprise-me mode: deeper exploration per agent): codebase scan + learnings + web research + 6 ideation sub-agents. Skip phrases: 'no external research', 'no slack'."
|
||||
- **Repo mode, issue-tracker intent:** "Will dispatch ~8 agents: codebase scan + learnings + web research + issue intelligence + 4 ideation sub-agents. Skip phrases: 'no external research', 'no slack'." Reflects the successful-theme path; if issue intelligence returns insufficient signal (see Phase 1), ideation falls back to 6 sub-agents and the total becomes ~9.
|
||||
- **Elsewhere-software:** "Will dispatch ~9 agents: context synthesis + learnings + web research + 6 ideation sub-agents. Skip phrases: 'no external research'."
|
||||
- **Elsewhere-non-software:** "Will dispatch ~8 agents: context synthesis + web research + 6 ideation sub-agents. Skip phrases: 'no external research'."
|
||||
|
||||
The line is informational; users do not need to acknowledge it.
|
||||
|
||||
### Phase 1: Mode-Aware Grounding
|
||||
|
||||
Before generating ideas, gather grounding. The dispatch set depends on the mode chosen in Phase 0.3. Web research runs in all modes (skip phrases honored). Learnings runs in repo mode and elsewhere-software, and is **skipped by default in elsewhere-non-software** — the CWD repo's `docs/solutions/` almost always contains engineering patterns that do not transfer to naming, narrative, personal, or non-digital business topics.
|
||||
|
||||
**Surprise-me grounding depth.** When Phase 0.2 routed to surprise-me mode, Phase 1 must produce richer material than specified mode — Phase 2 sub-agents will discover their own subjects from what Phase 1 returns, so texture matters:
|
||||
|
||||
- **Repo mode surprise-me:** the codebase-scan sub-agent samples a few representative files per top-level area (not just reads the top-level layout + AGENTS.md), surfaces recent PR/commit activity as signal about what's actively being worked on, and — when issue intelligence runs — passes issue themes as first-class input rather than footnote. Keep the scan bounded: representative, not exhaustive.
|
||||
- **Elsewhere mode surprise-me:** user-context synthesis extracts themes, recurring language, tensions, and omissions from whatever the user supplied, rather than just restating it. Web research broadens beyond narrow prior-art for a single subject toward the domain's landscape.
|
||||
- Specified mode keeps the current shallower scan — the user's named subject anchors what's relevant, so broader exploration is unnecessary.
|
||||
|
||||
Generate a `<run-id>` once at the start of Phase 1 (8 hex chars). Reuse it for the V15 cache file (this phase) and the V17 checkpoints (Phases 2 and 4) so they share one per-run scratch directory.
|
||||
|
||||
**Pre-resolve the scratch directory path.** Scratch lives directly under `/tmp` (not under `$TMPDIR` and not under `.context/`). `$TMPDIR` on macOS resolves to an obscure per-user path like `/var/folders/64/.../T/` that is hostile for users who want to inspect checkpoints, copy them elsewhere, or reference them later — `/tmp` is universally accessible on macOS, Linux, and WSL, and the per-user isolation `$TMPDIR` provides is not valuable for ephemeral ideation scratch. Run one bash command to create the directory and capture its absolute path for downstream use.
|
||||
|
||||
```bash
|
||||
SCRATCH_DIR="/tmp/compound-engineering/ce-ideate/<run-id>"
|
||||
mkdir -p "$SCRATCH_DIR"
|
||||
echo "$SCRATCH_DIR"
|
||||
```
|
||||
|
||||
Use the echoed absolute path (`/tmp/compound-engineering/ce-ideate/<run-id>`) as `<scratch-dir>` for every subsequent checkpoint write and cache read in this run. The run directory is not deleted on Phase 6 completion — the V15 cache is session-scoped and reused across run-ids, and the checkpoints follow the cross-invocation-reusable convention of leaving session-scoped artifacts for later invocations to find.
|
||||
|
||||
Run grounding agents in parallel in the **foreground** (do not background — results are needed before Phase 2):
|
||||
|
||||
**Repo mode dispatch:**
|
||||
|
||||
1. **Quick context scan** — dispatch a general-purpose sub-agent using the platform's cheapest capable model (e.g., `model: "haiku"` in Claude Code) with this prompt:
|
||||
|
||||
> Read the project's AGENTS.md (or CLAUDE.md only as compatibility fallback, then README.md if neither exists), then discover the top-level directory layout using the native file-search/glob tool (e.g., `Glob` with pattern `*` or `*/*` in Claude Code). Also read `STRATEGY.md` if it exists — it captures the product's target problem, approach, persona, metrics, and tracks.
|
||||
>
|
||||
> **Two paths for other root-level `*.md` files**, depending on whether the focus hint names them:
|
||||
>
|
||||
> - **User-named references** — if the focus hint names a specific root-level `*.md` file (e.g., focus is "ideate based on FEEDBACK.md", "use NOTES.md as input", "review the gaps in TODO.md"), fully read that file and include its content under a heading `User-named references`. Phase 2 treats these as *constraint*, so sub-agents need actual content, not a gist. Quote or summarize substantive sections; keep one-line gists for files that are mentioned but not the actual subject.
|
||||
> - **Additional context** — for any other root-level `*.md` files (not named in the focus), read briefly and include a one-line gist under a heading `Additional context`. Phase 2 treats these as *background*, so a gist is sufficient.
|
||||
>
|
||||
> Return a concise summary (under 40 lines, longer if user-named references include substantive content) covering:
|
||||
>
|
||||
> - project shape (language, framework, top-level directory layout)
|
||||
> - notable patterns or conventions
|
||||
> - obvious pain points or gaps
|
||||
> - likely leverage points for improvement
|
||||
> - product strategy summary, if `STRATEGY.md` was present — include the approach and active tracks verbatim so ideation can weight toward strategy-aligned directions
|
||||
> - `User-named references` section (when the focus hint named root-level `*.md` files)
|
||||
> - `Additional context` section (when other root-level `*.md` files exist that the focus did not name)
|
||||
>
|
||||
> Keep the scan shallow otherwise — read only top-level documentation and directory structure. Do not analyze GitHub issues, templates, or contribution guidelines. Do not do deep code search.
|
||||
>
|
||||
> Focus hint: {focus_hint}
|
||||
|
||||
2. **Learnings search** — dispatch `ce-learnings-researcher` with a brief summary of the ideation focus.
|
||||
|
||||
3. **Web research** (always-on; see "Web research" subsection below for skip-phrase and V15 cache handling).
|
||||
|
||||
4. **Issue intelligence** (conditional) — if issue-tracker intent was detected in Phase 0.3, dispatch `ce-issue-intelligence-analyst` with the focus hint. Run in parallel with the other agents.
|
||||
|
||||
If the agent returns an error (gh not installed, no remote, auth failure), log a warning to the user ("Issue analysis unavailable: {reason}. Proceeding with standard ideation.") and continue with the remaining grounding.
|
||||
|
||||
If the agent reports fewer than 5 total issues, note "Insufficient issue signal for theme analysis" and proceed with default ideation frames in Phase 2.
|
||||
|
||||
**Elsewhere mode dispatch (skip the codebase scan; user-supplied context is the primary grounding):**
|
||||
|
||||
1. **User-context synthesis** — dispatch a general-purpose sub-agent (cheapest capable model) to read the user-supplied context from Phase 0.4 intake plus any rich-prompt material, and return a structured grounding summary that mirrors the codebase-context shape (project shape → topic shape; notable patterns → stated constraints; pain points → user-named pain points; leverage points → opportunity hooks the context implies). This keeps Phase 2 sub-agents agnostic to grounding source.
|
||||
|
||||
2. **Learnings search** *(elsewhere-software only; skipped by default in elsewhere-non-software)* — dispatch `ce-learnings-researcher` with the topic summary in case relevant institutional knowledge exists (skill-design patterns, prior solutions in similar shape). Skip for elsewhere-non-software: the CWD's `docs/solutions/` is unlikely to be topically relevant for non-digital topics, and running it risks polluting generation with unrelated engineering patterns.
|
||||
|
||||
3. **Web research** — same as repo mode (see subsection below).
|
||||
|
||||
Issue intelligence does not apply in elsewhere mode. Slack research is opt-in for both modes (see "Slack context" below).
|
||||
|
||||
#### Web Research (V5, V15)
|
||||
|
||||
Always-on for both modes. Skip when the user said "no external research", "skip web research", or equivalent in their prompt or earlier answers; in that case, omit `ce-web-researcher` from dispatch and note the skip in the consolidated grounding summary.
|
||||
|
||||
Reuse prior web research within a session via a sidecar cache — see `references/web-research-cache.md` for the cache file shape, reuse check, append behavior, and platform-degradation rules. Read it the first time `ce-web-researcher` would be dispatched in this run (and on every subsequent dispatch where the cache might apply).
|
||||
|
||||
When dispatching `ce-web-researcher`, pass: the focus hint, a brief planning context summary (one or two sentences), and the mode. Do not pass codebase content — the agent operates externally.
|
||||
|
||||
#### Consolidated Grounding Summary
|
||||
|
||||
Consolidate all dispatched results into a short grounding summary using these sections (omit any section that produced nothing). Phase 1.5 will append a `Topic axes` section to this same summary after consolidation completes:
|
||||
|
||||
- **Codebase context** *(repo mode)* — project shape, notable patterns, pain points, leverage points (project-defining files: AGENTS.md/CLAUDE.md/README.md/STRATEGY.md) OR **Topic context** *(elsewhere mode)* — topic shape, stated constraints, user-named pain points, opportunity hooks
|
||||
- **User-named references** *(repo mode, when the focus hint named root-level `*.md` files)* — full content from files the user explicitly named in their prompt or focus. Phase 2 treats these as constraint
|
||||
- **Additional context** *(repo mode, when other root-level markdown was discovered but not named)* — one-line gists per file. Phase 2 treats these as background, not direction
|
||||
- **Past learnings** — relevant institutional knowledge from `docs/solutions/`
|
||||
- **Issue intelligence** *(when present, repo mode only)* — theme summaries with titles, descriptions, issue counts, and trend directions
|
||||
- **External context** *(when web research ran)* — prior art, adjacent solutions, market signals, cross-domain analogies. Note "(reused from earlier dispatch)" when V15 reuse fired
|
||||
- **Slack context** *(when present)* — organizational context
|
||||
|
||||
**Failure handling.** Grounding agent failures follow "warn and proceed" — never block on grounding failure. If `ce-web-researcher` fails (network, tool unavailable), log a warning ("External research unavailable: {reason}. Proceeding with internal grounding only.") and continue. If elsewhere-mode intake produced no usable context, note in the grounding summary that context is thin so Phase 2 sub-agents can compensate with broader generation.
|
||||
|
||||
**Slack context** (opt-in, both modes) — never auto-dispatch. When the user asks for Slack context and Slack tools are available (look for any `slack-researcher` agent or `slack` MCP tools in the current environment), dispatch `ce-slack-researcher` with the focus hint in parallel with other Phase 1 agents. When tools are present but the user did not ask, mention availability in the grounding summary so they can opt in. When the user asked but no Slack tools are reachable, surface the install hint instead.
|
||||
|
||||
### Phase 1.5: Topic-Surface Decomposition
|
||||
|
||||
Before dispatching frame agents in Phase 2, decompose the topic into 3-5 orthogonal **axes** that name *what aspects of the subject to think about*. Phase 2 frames determine *how to think* (the lens); axes determine *what to think on* (the surface). Without an explicit axis list, parallel frames tend to converge on whichever interpretation of the subject is most salient at first read — other parts of the surface go unexamined regardless of how many frames run. Lens diversity alone does not produce surface coverage.
|
||||
|
||||
This step is a single orchestrator-side analysis against the grounding summary already in context. No sub-agent dispatch, no additional grounding read, no user-facing question.
|
||||
|
||||
**Axis criteria:**
|
||||
|
||||
- **3-5 axes.** Fewer than 3 means the topic is atomic — skip per the rule below. More than 5 fragments dispatch and produces thin coverage on each.
|
||||
- **Orthogonal.** A single idea should naturally fall on one axis, not span multiple. Merge axes that overlap heavily.
|
||||
- **Derived from grounding.** The grounding summary contains the substance the axes name; do not pick axes from a generic template (e.g., "discovery / engagement / retention" applied to every topic).
|
||||
- **At the same level.** Don't mix "the entire pricing page" with "the $9.99 tier copy" in the same list.
|
||||
- **Named in the topic's language.** "Send mechanics" beats "outbound flow optimization." Use words a reader of the topic would recognize, not meta-language about ideation.
|
||||
|
||||
**Worked examples (illustrative, not a template — derive from actual grounding):**
|
||||
|
||||
| Topic | Axes |
|
||||
|---|---|
|
||||
| Social sharing of crossfire and convergence pages | Send mechanics; discovery (receive side); arrival/dwell experience; compounding over time; actor types (first-party, expert, reader) |
|
||||
| Improve our authentication system | Sign-in flow; session management; account recovery; permissions; identity providers |
|
||||
| Dark mode for our app | Visual surfaces; toggle UX; system-preference detection; asset variants; edge cases (third-party content) |
|
||||
| Cache invalidation in the data layer | Trigger surfaces; coordination across replicas; staleness tolerance per data class; observability of invalidation events |
|
||||
|
||||
**Skip condition.** Some subjects are atomic and resist meaningful decomposition — a single string output (a name, a tagline), a narrowly-scoped tactical fix ("the typo on line 47 of README"), or a topic where the candidate axes *are* the deliverable (e.g., "what surface should the API expose?"). When 3+ orthogonal axes that pass the criteria above cannot be generated, skip decomposition. Note `Decomposition skipped — atomic subject` in the grounding summary so the artifact records the choice.
|
||||
|
||||
**Surprise-me skip.** In surprise-me mode there is no settled subject to decompose — different frames will surface different subjects in Phase 2, and the cross-cutting synthesis step there serves the analogous coverage role. Skip Phase 1.5 in surprise-me mode and note `Decomposition skipped — surprise-me mode` in the grounding summary.
|
||||
|
||||
Append the axis list (or skip-reason) to the consolidated grounding summary under a section labeled `Topic axes`. Phase 2 reads this section to thread axes into sub-agent prompts; Phase 3 uses it for axis-spread scoring; Phase 5's artifact template includes it under Grounding Context.
|
||||
|
||||
### Phase 2: Divergent Ideation
|
||||
|
||||
Generate the full candidate list before critiquing any idea.
|
||||
|
||||
Dispatch parallel ideation sub-agents on the inherited model (do not tier down -- creative ideation needs the orchestrator's reasoning level). Omit the `mode` parameter so the user's configured permission settings apply. Dispatch count is mode-conditional: **4 sub-agents only when issue-tracker intent was detected in Phase 0.2 AND the issue intelligence agent returned usable themes** (see override below — cluster-derived frames capped at 4); **6 sub-agents otherwise**, including the insufficient-issue-signal fallback from Phase 1 where intent triggered but themes were not returned. Each targets ~6-8 ideas (yielding ~36-48 raw ideas across 6 frames or ~24-32 across 4 frames, roughly 25-30 survivors after dedupe in the 6-frame path and fewer in the 4-frame path). Adjust per-agent targets when volume overrides apply (e.g., "100 ideas" raises it, "top 3" may lower the survivor count instead).
|
||||
|
||||
Give each sub-agent: the grounding summary, the focus hint, the per-agent volume target, the **topic axis list from Phase 1.5** (when decomposition produced one), and an instruction to generate raw candidates only (not critique). Each agent's first few ideas tend to be obvious -- push past them. Ground every idea in the Phase 1 grounding summary.
|
||||
|
||||
**Axis spread instruction.** When an axis list is present, instruct each sub-agent to distribute its ideas across multiple axes — the frame's lens applies to every axis, but ideas should not all cluster on one. Each idea must be tagged with the axis it targets. The frame is a lens; the axis list is the surface map. A frame that plausibly reaches an axis should produce at least one idea there before doubling up on a different axis. When decomposition was skipped (atomic subject or surprise-me), omit the axis instruction entirely — do not invent axes at dispatch time.
|
||||
|
||||
**Constraint vs background.** In the dispatch prompt, mark the user's prompt, focus hint, and any *User-named references* (root-level files the user named in their focus and the codebase-scan fully read) as *constraints* — ideas that violate them are out regardless of basis. Mark the rest of the grounding summary (codebase context, additional context, learnings, external context) as *background* — informative, not directive. Background can support an idea's basis and inform direction; it must not pull ideation toward whatever was loudest in the corpus when the user named a different focus. This is the primary defense against grounding noise (an unrelated `FEEDBACK.md` the user did not name, a tangentially-cited prior-art result) shaping survivors against user intent.
|
||||
|
||||
Assign each sub-agent a different ideation frame as a **starting bias, not a constraint**. Prompt each to begin from its assigned perspective but follow any promising thread -- cross-cutting ideas that span multiple frames are valuable.
|
||||
|
||||
**Frame selection (mode-symmetric — same six frames in repo and elsewhere modes):**
|
||||
|
||||
1. **Pain and friction** — user, operator, or topic-level pain points; what is consistently slow, broken, or annoying.
|
||||
2. **Inversion, removal, or automation** — invert a painful step, remove it entirely, or automate it away.
|
||||
3. **Assumption-breaking and reframing** — what is being treated as fixed that is actually a choice; reframe one level up or sideways.
|
||||
4. **Leverage and compounding** — choices that, once made, make many future moves cheaper or stronger; second-order effects.
|
||||
5. **Cross-domain analogy** — generate ideas by asking how completely different fields solve a structurally analogous problem. The grounding domain is the user's topic; the analogy domain is anywhere else (other industries, biology, games, infrastructure, history). Push past the obvious analogy to non-obvious ones.
|
||||
6. **Constraint-flipping** — invert the obvious constraint to its opposite or extreme. What if the budget were 10x or 0? What if the team were 100 people or 1? What if there were no users, or 1M? Use the resulting design as a candidate even if the constraint flip itself is not realistic.
|
||||
|
||||
**Issue-tracker mode override (repo mode only).** When issue-tracker intent is active and themes were returned by the issue intelligence agent: each high/medium-confidence theme becomes a frame. Pad with frames from the 6-frame default pool (in the order listed above) if fewer than 3 cluster-derived frames. Cap at 4 total — issue-tracker mode keeps its tighter dispatch by design.
|
||||
|
||||
**Per-idea output contract (uniform across all frames, all modes):**
|
||||
|
||||
Each sub-agent returns this structure per idea:
|
||||
|
||||
- **title**
|
||||
- **summary** (2-4 sentences)
|
||||
- **axis** — required when Phase 1.5 produced an axis list. Pick the one axis this idea most centrally targets; do not span. Omit entirely when decomposition was skipped.
|
||||
- **basis** (required, tagged) — one of:
|
||||
- `direct:` quoted line / specific file / named issue / explicit user-supplied context
|
||||
- `external:` named prior art, domain research, adjacent pattern, with source
|
||||
- `reasoned:` explicit first-principles argument for why this move likely applies — not a gesture; the argument is written out
|
||||
- **why_it_matters** — connects the basis to the move's significance
|
||||
- **meeting_test** — one line confirming this would warrant team discussion (waived when Phase 0.5 detected tactical focus signals)
|
||||
|
||||
Basis is required, not optional. If a sub-agent cannot articulate a basis of at least one type, the idea does not surface. The failure mode to prevent is generic "AI-slop" ideas that sound plausible but lack a basis the user can verify.
|
||||
|
||||
**Generation rules (uniform across frames, all modes):**
|
||||
|
||||
- Every idea carries an articulated basis. Unjustified speculation does not surface, regardless of how plausible it sounds.
|
||||
- Bias toward the basis type your frame naturally produces — pain/inversion/leverage tend toward `direct:`; analogy and constraint-flipping tend toward `reasoned:`; assumption-breaking is mixed — but don't exclude other basis types.
|
||||
- Apply the meeting-test as a default floor: would this idea warrant team discussion? If not, it's below the floor and does not surface. The floor is relaxed only when Phase 0.5 detected tactical focus signals.
|
||||
- Stay within the subject's identity. Product expansions, new surfaces, new markets, retirements, and architectural pivots are fair game when the basis supports them. Subject-replacement moves (abandoning the project, pivoting to unrelated domains, becoming a different organization) are out regardless of basis.
|
||||
- **Honor the asked scope.** When the focus hint names a part of the subject (a flow, a stage, a section, a feature within a larger product — e.g., "account settings", "onboarding flow", "pricing page copy", "gameplay rules"), ideate at full ambition *within that scope*. Expanding the surface to the whole subject — proposing fundamental changes to the broader product when the user named one slice — is a scope mismatch even when no subject-replacement occurred. Big-picture thinking still applies; it just operates inside the bounded surface the user named, not by widening the surface.
|
||||
|
||||
**Surprise-me mode addendum.** When Phase 0.2 routed to surprise-me, include this additional instruction in each sub-agent's dispatch prompt:
|
||||
|
||||
> No user-specified subject. Through your frame's lens, explore the Phase 1 material and identify the subject(s) you find most interesting for this frame. Different frames finding different subjects is the feature — cross-subject divergence is what makes surprise-me valuable. Each idea still carries a basis; the basis may include identification of the subject itself (why *this* subject is worth ideating on through your lens, citing what in the Phase 1 material signals it).
|
||||
|
||||
After all sub-agents return:
|
||||
|
||||
1. Merge and dedupe into one master candidate list.
|
||||
2. Synthesize cross-cutting combinations -- scan for ideas from different frames that combine into something stronger. In specified mode, expect 3-5 additions at most. **In surprise-me mode, cross-cutting is the magic layer** — frames often converge on overlapping subjects or find complementary angles; expect 5-8 additions and give this step more attention. Surface combinations that span multiple frame-chosen subjects as a distinctive surprise-me output pattern.
|
||||
3. **Axis-coverage check (when Phase 1.5 produced an axis list; skipped otherwise).** Count ideas per axis after dedupe. For any axis with zero ideas, dispatch one recovery sub-agent (any unused frame, or the frame whose lens fits the missing axis best — e.g., Pain & friction for usability axes, Cross-domain analogy for distribution or compounding axes) targeting that axis specifically. The recovery dispatch carries the same per-idea output contract and ~3-5 ideas as its target. **Cap recovery at 2 axes total** — if more than 2 axes are empty after the first round, accept thin coverage rather than fanning out further. After recovery returns, merge into the master list and dedupe again. Note empty axes that were not recovered in the rejection summary as "axis: <name> — recovery skipped (cap reached)" so the gap is visible to the user.
|
||||
4. If a focus was provided, weight the merged list toward it without excluding stronger adjacent ideas.
|
||||
5. Spread ideas across multiple dimensions when justified: workflow/DX, reliability, extensibility, missing capabilities, docs/knowledge compounding, quality/maintenance, leverage on future work.
|
||||
|
||||
**Checkpoint A (V17).** Immediately after the cross-cutting synthesis step completes and the raw candidate list is consolidated, write `<scratch-dir>/raw-candidates.md` (using the absolute path captured in Phase 1) containing the full candidate list with sub-agent attribution. This protects the most expensive output (6 parallel sub-agent dispatches + dedupe) before Phase 3 critique potentially compacts context. Best-effort: if the write fails (disk full, permissions), log a warning and proceed; the checkpoint is not load-bearing. Not cleaned up at the end of the run (the run directory is preserved so the V15 cache remains reusable across run-ids in the same session — see Phase 6).
|
||||
|
||||
After merging and synthesis — and before presenting survivors — load `references/post-ideation-workflow.md`. This load is non-optional. The file contains the adversarial filtering rubric, artifact template, quality bar, and the canonical Phase 6 handoff menu (Refine, Open and iterate in Proof, Brainstorm, Save and end) — these options do not appear anywhere in this main body. Skipping the load silently degrades every subsequent step; the agent improvises the menu from memory instead of presenting the documented options. "Quickly" means fewer Phase 2 sub-agents, not skipping references. Do not load this file before Phase 2 agent dispatch completes.
|
||||
@@ -0,0 +1,252 @@
|
||||
# Post-Ideation Workflow
|
||||
|
||||
Read this file after Phase 2 ideation agents return and the orchestrator has merged and deduped their outputs into a master candidate list. Do not load before Phase 2 completes.
|
||||
|
||||
## Phase 3: Adversarial Filtering
|
||||
|
||||
Review every candidate idea critically. The orchestrator performs this filtering directly -- do not dispatch sub-agents for critique.
|
||||
|
||||
Do not generate replacement ideas in this phase unless explicitly refining.
|
||||
|
||||
For each rejected idea, write a one-line reason.
|
||||
|
||||
Rejection criteria:
|
||||
- too vague
|
||||
- not actionable
|
||||
- duplicates a stronger idea
|
||||
- not grounded in the stated context
|
||||
- too expensive relative to likely value
|
||||
- already covered by existing workflows or docs
|
||||
- interesting but better handled as a brainstorm variant, not a product improvement
|
||||
- **unjustified — no articulated basis** (sub-agent failed to provide `direct:`, `external:`, or `reasoned:` justification, or the stated basis does not actually support the claimed move)
|
||||
- **below ambition floor** (fails the meeting-test: would not warrant team discussion — except when Phase 0.5 detected tactical focus signals, in which case this criterion is waived)
|
||||
- **subject-replacement** (abandons or replaces the subject of ideation rather than operating on it — e.g., "pivot to an unrelated domain," "become a different organization")
|
||||
- **scope overrun** (expands beyond the asked scope rather than ideating within it — e.g., proposes changes to the whole product when the user asked about one flow, stage, or section). Allowed only when the basis explicitly justifies the expansion; default is reject or downgrade.
|
||||
|
||||
Score survivors using a consistent rubric weighing: groundedness in stated context, **basis strength** (`direct:` > `external:` > `reasoned:`; none excluded, but direct-evidence ideas score higher all else equal), expected value, novelty, pragmatism, leverage on future work, implementation burden, overlap with stronger ideas, and **axis spread** (when Phase 1.5 produced an axis list) — survivor sets that cover the topic's surface outscore sets that cluster on one axis, all else equal.
|
||||
|
||||
**Axis coverage as a list-level concern.** When axes were defined, axis spread is evaluated across the survivor set, not per-idea. After per-idea filtering, check the survivor set: if axis coverage is uneven and stronger candidates exist on under-represented axes, prefer the spread when promoting borderline candidates. Phase 2's recovery dispatch should already have surfaced candidates for empty axes; this is a polish step on the survivor selection. If an axis ends up with zero survivors despite recovery (or because recovery hit the 2-axis cap), note it in the rejection summary as a deliberate gap rather than an oversight.
|
||||
|
||||
Target output:
|
||||
- keep 5-7 survivors by default
|
||||
- if too many survive, run a second stricter pass
|
||||
- if fewer than 5 survive, report that honestly rather than lowering the bar
|
||||
|
||||
## Phase 4: Present the Survivors
|
||||
|
||||
**Checkpoint B (V17).** Before presenting, write `<scratch-dir>/survivors.md` (using the absolute path captured in Phase 1) containing the survivor list plus key context (focus hint, grounding summary, rejection summary). This protects the post-critique state before the user reaches the persistence menu. Best-effort: if the write fails (disk full, permissions), log a warning and proceed; the checkpoint is not load-bearing. Reuses the same `<run-id>` and `<scratch-dir>` generated in Phase 1; not cleaned up at the end of the run (the run directory is preserved so the V15 cache remains reusable across run-ids in the same session — see Phase 6).
|
||||
|
||||
Present the surviving ideas to the user. The terminal review loop is a complete ideation cycle in itself — persistence is opt-in (Phase 5), and refinement happens in conversation with no file or network cost (Phase 6).
|
||||
|
||||
Present only the surviving ideas in structured form:
|
||||
|
||||
- title
|
||||
- description
|
||||
- **axis** (when Phase 1.5 produced an axis list)
|
||||
- **basis** (tagged `direct:` / `external:` / `reasoned:`, with the quoted evidence, cited source, or written-out argument)
|
||||
- rationale (how the basis connects to the move's significance)
|
||||
- downsides
|
||||
- confidence score
|
||||
- estimated complexity
|
||||
|
||||
Then include a brief rejection summary so the user can see what was considered and cut.
|
||||
|
||||
Keep the presentation concise. Allow brief follow-up questions and lightweight clarification.
|
||||
|
||||
## Phase 5: Persistence (Opt-In, Mode-Aware)
|
||||
|
||||
Persistence is opt-in. The terminal review loop is a complete ideation cycle. Refinement loops happen in conversation with no file or network cost. Persistence triggers only when the user explicitly chooses to save, share, or hand off (selected in Phase 6).
|
||||
|
||||
When the user picks an option in Phase 6 that requires a durable record (Open and iterate in Proof, Brainstorm, Save and end), ensure a record exists first. When the user chooses to keep refining, no record is needed unless the user asks.
|
||||
|
||||
**Mode-determined defaults:**
|
||||
|
||||
| Action | Repo mode default | Elsewhere mode default |
|
||||
|---|---|---|
|
||||
| Save | `docs/ideation/YYYY-MM-DD-<topic>-ideation.md` | Proof |
|
||||
| Share | Proof (additional) | Proof (primary) |
|
||||
| Brainstorm handoff | `ce-brainstorm` | `ce-brainstorm` (universal-brainstorming) |
|
||||
| End | Conversation only is fine | Conversation only is fine |
|
||||
|
||||
Either mode can also use the other destination on explicit request ("save to Proof even though this is repo mode", "save to a local file even though this is elsewhere"). Honor such overrides directly.
|
||||
|
||||
### 5.1 File Save (default for repo mode; on request for elsewhere mode)
|
||||
|
||||
1. Ensure `docs/ideation/` exists
|
||||
2. Choose the file path:
|
||||
- `docs/ideation/YYYY-MM-DD-<topic>-ideation.md`
|
||||
- `docs/ideation/YYYY-MM-DD-open-ideation.md` when no focus exists
|
||||
3. Write or update the ideation document
|
||||
|
||||
Use this structure and omit clearly irrelevant fields only when necessary:
|
||||
|
||||
```markdown
|
||||
---
|
||||
date: YYYY-MM-DD
|
||||
topic: <kebab-case-topic>
|
||||
focus: <optional focus hint>
|
||||
mode: <repo-grounded | elsewhere-software | elsewhere-non-software>
|
||||
---
|
||||
|
||||
# Ideation: <Title>
|
||||
|
||||
## Grounding Context
|
||||
[Grounding summary from Phase 1 — labeled "Codebase Context" in repo mode, "Topic Context" in elsewhere mode]
|
||||
|
||||
## Topic Axes
|
||||
[3-5 axes from Phase 1.5, one per line, OR a single line `Decomposition skipped — atomic subject` / `Decomposition skipped — surprise-me mode` when Phase 1.5 was skipped. Omit this section entirely if not applicable.]
|
||||
|
||||
## Ranked Ideas
|
||||
|
||||
### 1. <Idea Title>
|
||||
**Description:** [Concrete explanation]
|
||||
**Axis:** [Topic axis this idea targets — omit when decomposition was skipped]
|
||||
**Basis:** [`direct:` / `external:` / `reasoned:` — quoted, cited, or written-out argument]
|
||||
**Rationale:** [How the basis connects to the move's significance]
|
||||
**Downsides:** [Tradeoffs or costs]
|
||||
**Confidence:** [0-100%]
|
||||
**Complexity:** [Low / Medium / High]
|
||||
**Status:** [Unexplored / Explored]
|
||||
|
||||
## Rejection Summary
|
||||
|
||||
| # | Idea | Reason Rejected |
|
||||
|---|------|-----------------|
|
||||
| 1 | <Idea> | <Reason rejected> |
|
||||
|
||||
[When applicable, append axis-coverage gaps as their own rows so the gap is visible:]
|
||||
| - | axis: <name> | recovery skipped (cap reached) — no survivors on this axis |
|
||||
```
|
||||
|
||||
If resuming:
|
||||
- update the existing file in place
|
||||
- preserve explored markers
|
||||
|
||||
### 5.2 Proof Save (default for elsewhere mode; on request for repo mode)
|
||||
|
||||
Hand off the ideation content to the `ce-proof` skill in HITL review mode. This uploads the doc, runs an iterative review loop (user annotates in Proof, agent ingests feedback, applies agreed edits, and replies/resolves in-thread), and (in repo mode) syncs the reviewed markdown back to `docs/ideation/`.
|
||||
|
||||
Load the `ce-proof` skill in HITL-review mode with:
|
||||
|
||||
- **source content:** the survivors and rejection summary from Phase 4 (in repo mode, this is the file written in 5.1; in elsewhere mode, render to a temp file as the source for upload)
|
||||
- **doc title:** `Ideation: <topic>` or the H1 of the ideation doc
|
||||
- **identity:** `ai:compound-engineering` / `Compound Engineering`
|
||||
- **recommended next step:** `/ce-brainstorm` (shown in the proof skill's final terminal output)
|
||||
|
||||
The Proof failure ladder in Phase 6.5 governs what happens when this hand-off fails.
|
||||
|
||||
**Caller-aware return.** The return-rule bullets below describe the default control flow, but the next step depends on which Phase 6 option invoked the Proof save. Apply the right branch for the caller:
|
||||
|
||||
- **§6.2 Open and iterate in Proof.** Behavior is mode-aware:
|
||||
- *Repo mode:* return to the Phase 6 menu on every status. The Proof-reviewed content is now synced locally, and the user typically has a follow-up action in the repo (brainstorm toward a plan, save and end, or keep refining).
|
||||
- *Elsewhere mode:* on a successful Proof return (`proceeded` or `done_for_now`), exit cleanly — narrate that the artifact lives at `docUrl` (including any stale-local note if applicable) and stop. Proof iteration is often the terminal act in elsewhere mode; forcing another menu choice after the user already got what they came for produces decision fatigue. Only the `aborted` branch returns to the Phase 6 menu so the user can retry or pick another path.
|
||||
- **§6.3 Brainstorm a selected idea.** On a successful Proof return (`proceeded` or `done_for_now`), do **not** stop at the Phase 6 menu — after applying the per-status handling below (including any stale-local pull offer), continue into §6.3's remaining bullets (mark the chosen idea as `Explored`, then load `ce-brainstorm`). Only the `aborted` branch returns to the Phase 6 menu, since no durable record was written.
|
||||
- **§6.4 Save and end.** On a successful Proof return (`proceeded` or `done_for_now`), exit cleanly: narrate that the ideation was saved, surface the `docUrl` (and the local-path note if applicable), and stop. Do **not** re-ask the Phase 6 question — the user already chose to end. Only the `aborted` branch returns to the Phase 6 menu so the user can retry or pick a different path.
|
||||
|
||||
When the proof skill returns control:
|
||||
|
||||
- `status: proceeded` with `localSynced: true` → the ideation doc on disk now reflects the review. Apply the caller-aware return rule above for the invoking branch.
|
||||
- `status: proceeded` with `localSynced: false` → the reviewed version lives in Proof at `docUrl` but the local copy is stale. Offer to pull the Proof doc to `localPath` using the proof skill's Pull workflow. Apply the caller-aware return rule above; if the pull was declined, include a one-line note that `<localPath>` is stale vs. Proof so the next handoff (or final exit narration) doesn't read the old content silently. Placement: above the Phase 6 menu when the caller-aware rule returns to it, in the handoff preamble to `ce-brainstorm` for §6.3, or alongside the final save/exit narration for §6.2 elsewhere / §6.4.
|
||||
- `status: done_for_now` → the doc on disk may be stale if the user edited in Proof before leaving. Offer to pull the Proof doc to `localPath` so the local ideation artifact stays in sync, then apply the caller-aware return rule above. `done_for_now` means the user stopped the HITL loop — it does not mean they ended the whole ideation session unless the caller-aware rule exits (§6.2 elsewhere mode or §6.4). If the pull was declined, include the stale-local note at the placement described in the previous bullet.
|
||||
- `status: aborted` → fall back to the Phase 6 menu without changes, regardless of caller. No durable record was written, so §6.3 must not proceed with the brainstorm handoff and §6.4 must not end — the menu lets the user retry or pick another path.
|
||||
|
||||
## Phase 6: Refine or Hand Off
|
||||
|
||||
Ask what should happen next using the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
**Question:** "What should the agent do next?"
|
||||
|
||||
Offer these four options (labels are self-contained with the distinguishing word front-loaded so options stay distinct when truncated):
|
||||
|
||||
1. **Refine the ideation in conversation (or stop here — no save)** — add ideas, re-evaluate, or deepen analysis. No file or network side effects; ending the conversation at any point after this pick is a valid no-save exit.
|
||||
2. **Open and iterate in Proof** — save the ideation to Proof and enter the proof skill's HITL review loop: iterate via comments in the Proof editor; reviewed edits sync back to `docs/ideation/` in repo mode.
|
||||
3. **Brainstorm a selected idea** — load `ce-brainstorm` with the chosen idea as the seed. The orchestrator first writes a durable record using the mode default in Phase 5.
|
||||
4. **Save and end** — persist the ideation using the mode default (file in repo mode, Proof in elsewhere mode), then end.
|
||||
|
||||
No-save exit is supported without a dedicated menu option. Pick option 1 and stop the conversation, or use the question tool's free-text escape to say so directly — persistence is opt-in and the terminal review loop is already a complete ideation cycle.
|
||||
|
||||
Do not delete the run's scratch directory (`<scratch-dir>` resolved in Phase 1) on completion. The V15 web-research cache is session-scoped and reused across run-ids by later ideation invocations in the same session (see `references/web-research-cache.md`); per-run cleanup would defeat that reuse. Checkpoint A (`raw-candidates.md`) and Checkpoint B (`survivors.md`) are cheap to leave behind and follow the repo's Scratch Space cross-invocation-reusable convention — OS handles eventual cleanup.
|
||||
|
||||
### 6.1 Refine the Ideation in Conversation
|
||||
|
||||
Route refinement by intent:
|
||||
|
||||
- `add more ideas` or `explore new angles` -> return to Phase 2
|
||||
- `re-evaluate` or `raise the bar` -> return to Phase 3
|
||||
- `dig deeper on idea #N` -> expand only that idea's analysis
|
||||
|
||||
No persistence triggers during refinement. The user can choose Save and end (or Brainstorm, or Open and iterate in Proof) when they are ready to persist.
|
||||
|
||||
Ending after refinement — or without any refinement at all — is a valid no-save exit. There is no required next step; stopping the conversation here leaves no durable artifact, which matches the opt-in persistence contract.
|
||||
|
||||
### 6.2 Open and Iterate in Proof
|
||||
|
||||
Invoke the Proof HITL review path via §5.2 with §6.2 as the caller. In repo mode, ensure the local file exists first (run §5.1) so the HITL sync-back has a target; in elsewhere mode, §5.2 renders to a temp file as usual. Honor Phase 5's "ensure a record exists first" contract either way.
|
||||
|
||||
Apply §5.2's caller-aware return rule for the §6.2 branch — behavior is mode-aware. In repo mode, return to the Phase 6 menu on every status so the user can pick a follow-up (brainstorm toward a plan, save-and-end, or keep refining) now that the Proof review is reflected in the local file. In elsewhere mode, exit cleanly on a successful Proof return since Proof iteration is often the terminal act — the artifact lives at `docUrl` and is the canonical record; only the `aborted` status returns to the menu.
|
||||
|
||||
If the Proof handoff fails, the §6.5 Proof Failure Ladder governs recovery.
|
||||
|
||||
### 6.3 Brainstorm a Selected Idea
|
||||
|
||||
- Write or update the durable record per the mode default in Phase 5 (file in repo mode, Proof in elsewhere mode). When this routes through §5.2 Proof Save, apply §5.2's caller-aware return rule: continue into the next bullet on a successful Proof return instead of bouncing back to the Phase 6 menu. If Proof returned `aborted` (no durable record written), go back to the Phase 6 menu and do **not** proceed with the brainstorm handoff.
|
||||
- Mark the chosen idea as `Explored` in the saved record
|
||||
- Load the `ce-brainstorm` skill with the chosen idea as the seed
|
||||
|
||||
**Repo mode only:** do **not** skip brainstorming and go straight to `ce-plan` from ideation output — `ce-plan` wants brainstorm-grounded requirements. In elsewhere modes, ideation (or ideation + Proof iteration) is a legitimate terminal state; brainstorming is optional deeper development of one idea, not a required next rung on an implementation ladder that does not exist in these modes.
|
||||
|
||||
### 6.4 Save and End
|
||||
|
||||
Persist via the mode default (5.1 in repo mode, 5.2 in elsewhere mode), then end. If the user instead asked to use the non-default destination, honor that explicit request.
|
||||
|
||||
When the path lands in a Proof save (5.2), apply §5.2's caller-aware return rule for the §6.4 branch: on a successful Proof return, exit cleanly — narrate the save, surface the `docUrl` (and any stale-local note if the pull was declined), and stop. Do **not** loop back to the Phase 6 menu; the user already chose to end. Only a `status: aborted` from Proof returns to the menu so the user can retry or pick another path (file save, custom path, or keep refining). The §6.5 Proof Failure Ladder still governs persistent Proof failures and ends at the Phase 6 menu — that failure-recovery path is distinct from the successful-save exit described here.
|
||||
|
||||
When the path lands in a file save (5.1):
|
||||
|
||||
- offer to commit only the ideation doc
|
||||
- do not create a branch
|
||||
- do not push
|
||||
- if the user declines, leave the file uncommitted
|
||||
|
||||
After the file save (and optional commit), end the session — do not return to the Phase 6 menu.
|
||||
|
||||
### 6.5 Proof Failure Ladder
|
||||
|
||||
The `ce-proof` skill performs single-retry-once internally on transient failures (`STALE_BASE`, `BASE_TOKEN_REQUIRED`) before surfacing failure. The proof skill's return contract does not expose typed error classes to callers — the orchestrator cannot distinguish retryable vs terminal failures from outside.
|
||||
|
||||
**Orchestrator-side retry harness (intentionally minimal):** wrap the proof skill invocation in **one** additional best-effort retry with a short pause (~2 seconds). The proof skill already retried internally, so this catches transient races at the orchestrator boundary without compounding latency. Do not classify error types from outside the skill — no detection mechanism exists.
|
||||
|
||||
Distinguish create-failure from ops-failure by inspecting whether the proof skill returned a `docUrl` before failing:
|
||||
|
||||
- **Create-failure** (no `docUrl` returned): retry the create.
|
||||
- **Ops-failure** (a `docUrl` was returned, but a later operation failed): retry only the failing operation. **Do not recreate** the document.
|
||||
|
||||
**Failure narration.** Narrate the single retry to the terminal so the pause does not look like a hang ("Retrying Proof... attempt 2/2"). On persistent failure, narrate that retry exhausted before showing the fallback menu.
|
||||
|
||||
**Fallback menu after persistent failure.** Use the platform's blocking question tool. Present these options (omit option (a) if no repo exists at CWD):
|
||||
|
||||
- "Save to `docs/ideation/` instead" (repo-mode default destination, available when CWD is inside a git repo)
|
||||
- "Save to a custom path the user provides" (validate writable; create parent dirs)
|
||||
- "Skip save and keep the ideation in conversation" (no persistence)
|
||||
|
||||
If proof returned a partial `docUrl` before failing, surface that URL alongside the fallback options so the user can recover or share the partial record.
|
||||
|
||||
After the fallback completes (any path), continue back to the Phase 6 menu so the user can still refine, iterate in Proof, brainstorm, or save and end.
|
||||
|
||||
## Quality Bar
|
||||
|
||||
Before finishing, check:
|
||||
|
||||
- the idea set is grounded in the stated context (codebase in repo mode; user-supplied context in elsewhere mode)
|
||||
- **every surviving idea has an articulated basis** (`direct:`, `external:`, or `reasoned:`) that actually supports the claimed move — speculation dressed as ambition was rejected, with reasons
|
||||
- **every surviving idea passes the meeting-test** unless Phase 0.5 detected tactical focus signals that waived the floor
|
||||
- **no surviving idea replaces the subject** rather than operating on it
|
||||
- when Phase 1.5 produced an axis list, the survivor set spreads across axes rather than clustering on one — and any axis with zero survivors is noted as a deliberate gap in the rejection summary, not silently absent
|
||||
- the candidate list was generated before filtering
|
||||
- the original many-ideas -> critique -> survivors mechanism was preserved
|
||||
- if sub-agents were used, they improved diversity without replacing the core workflow
|
||||
- every rejected idea has a reason
|
||||
- survivors are materially better than a naive "give me ideas" list
|
||||
- persistence followed user choice — terminal-only sessions did not write a file or call Proof
|
||||
- when persistence did trigger, the mode default was respected unless the user explicitly overrode it
|
||||
- acting on an idea routes to `ce-brainstorm`, not directly to implementation
|
||||
@@ -0,0 +1,103 @@
|
||||
# Universal Ideation Facilitator
|
||||
|
||||
This file is loaded when ce-ideate detects an elsewhere-mode topic with no software surface at all — naming (independent of product), narrative writing, personal decisions, non-digital business strategy, physical-product design. Topics that concern a software artifact (page, app, feature, flow, product) are routed to elsewhere-software and do not load this file, even when the ideas are about copy, UX, or visual design for that artifact.
|
||||
|
||||
Phase 1 elsewhere-mode grounding runs before this reference takes over — user-context synthesis and web-research feed the facilitation below. Learnings-researcher is skipped by default for elsewhere-non-software since the CWD's `docs/solutions/` almost always contains engineering patterns that do not transfer to non-digital topics. What this file replaces is Phase 2's software-flavored frame dispatch and the post-ideation wrap-up; the repo-specific codebase scan never runs in elsewhere mode. Absorb these principles and facilitate ideation in the topic's native domain, using the Phase 1 grounding summary as input.
|
||||
|
||||
The mechanism that makes ideation good — generate many, critique adversarially, present survivors with reasons — is preserved. Only the framing of the work changes.
|
||||
|
||||
---
|
||||
|
||||
## Your role
|
||||
|
||||
Be a divergent thinking partner, not a delivery service. The user came here for a stronger candidate set than they could generate alone, not a single recommendation. Resist the urge to converge early. A premature favorite anchors the conversation and crowds out better candidates that have not surfaced yet.
|
||||
|
||||
Match the tone to the stakes. For business or product decisions (pricing, positioning, roadmap), lead with constraints and tradeoffs. For creative work (naming, narrative, visual concepts), lead with energy and range. For personal decisions, lead with values before mechanics.
|
||||
|
||||
## How to start
|
||||
|
||||
Match depth to scope:
|
||||
|
||||
- **Quick** — the user wants a starter set right now. Generate one round, critique briefly, present 3-5 survivors, done.
|
||||
- **Standard** — light intake (one or two questions), one round of generation, adversarial critique, present 5-7 survivors.
|
||||
- **Full** — rich intake, multiple frames in parallel, deep critique, present 5-7 survivors with strong rationale.
|
||||
|
||||
Apply the discrimination test before asking anything. Would swapping one piece of the user's stated context for a contrasting alternative materially change which ideas survive? If yes, the context is load-bearing — proceed. If no, ask 1-3 narrowly chosen questions. Follow the questioning principles from SKILL.md Phase 0.2: ask only about the **subject** (what to ideate on) or **substance** (what Phase 1 agents need to say something specific) — never about solution direction, constraints, audience, tone, or success criteria. Those belong to `ce-brainstorm`. Build on what the user already provided rather than starting from a template. After each answer, re-apply the test before asking another. Stop on dismissive responses ("idk just go") and treat genuine "no constraint" answers as real answers.
|
||||
|
||||
**Grounding freshness.** Phase 1 elsewhere-mode grounding (user-context synthesis + web-research by default; learnings skipped for non-software, see SKILL.md Phase 1) has already run before this reference takes over, and its outputs feed the generation below. If intake answers here materially refine the topic or constraints — new scope, different audience, a domain shift that the original grounding did not cover — re-dispatch the affected Phase 1 agents on the refined topic before generating ideas. The guardrail mirrors SKILL.md Phase 0.4's rule that mode and grounding re-evaluate when intake changes the scope to be acted on; ranking against stale grounding risks surfacing ideas fit to the wrong topic.
|
||||
|
||||
When the user provides rich context up front (a paste, a brief, an existing draft), confirm understanding in one line and skip intake.
|
||||
|
||||
## How to decompose
|
||||
|
||||
Before generating, decompose the topic into 3-5 orthogonal **axes** that name *what aspects of the subject to think about*. Frames in "How to generate" determine *how to think* (the lens); axes determine *what to think on* (the surface). Without explicit axes, the same topic interpreted six ways through six lenses still leaves most of the surface unexamined — lens diversity does not produce surface coverage on its own.
|
||||
|
||||
This step is the facilitator's own analysis — no sub-agent, no additional research. The Phase 1 grounding supplies the substance.
|
||||
|
||||
Axes should be:
|
||||
|
||||
- **3-5 in number.** Fewer means atomic — skip decomposition. More fragments coverage.
|
||||
- **Orthogonal.** A single idea should fall on one axis, not span multiple.
|
||||
- **Derived from grounding**, not from a generic template.
|
||||
- **At the same level** of granularity.
|
||||
- **Named in the topic's language**, not meta-language about ideation.
|
||||
|
||||
**Worked examples (illustrative, not a template):**
|
||||
|
||||
- "Name my new coffee shop" → atomic; skip decomposition (the candidate *is* a name)
|
||||
- "Plot ideas for a short story" → atomic; skip decomposition (the candidate *is* a plot)
|
||||
- "Brand strategy for a launch" → axes might be: positioning; visual identity; voice; launch channels; pricing/packaging
|
||||
- "Career options for the next 5 years" → axes might be: domain (industry/role); structure (employee/founder/freelance); geography; growth ambition; financial floor
|
||||
|
||||
**Skip condition.** Many elsewhere-non-software topics are atomic by nature — a single name, tagline, or one-shot creative output. When 3+ orthogonal axes do not emerge, skip decomposition and note `Decomposition skipped — atomic subject` in the grounding summary.
|
||||
|
||||
**Surprise-me skip.** No settled subject in surprise-me mode; skip decomposition and note `Decomposition skipped — surprise-me mode`.
|
||||
|
||||
Record the axes (or skip-reason) at the head of generation. Generation will distribute ideas across axes; convergence will weight axis spread alongside other rubric criteria.
|
||||
|
||||
## How to generate
|
||||
|
||||
Generate the full candidate list before critiquing any idea. Use the same six frames as software ideation, described in domain-agnostic language. Each frame is a **starting bias, not a constraint** — follow promising threads across frames.
|
||||
|
||||
- **Pain and friction** — what is consistently annoying, slow, or broken in the current state of the topic? Generate ideas that remove or reduce that friction.
|
||||
- **Inversion, removal, automation** — what would happen if a step were inverted, removed entirely, or automated away? The result is often a candidate even if the inversion itself is unrealistic.
|
||||
- **Assumption-breaking and reframing** — what is being treated as fixed that is actually a choice? Reframe the problem one level up or sideways.
|
||||
- **Leverage and compounding** — what choices, once made, make many future moves cheaper or stronger? Look for second-order effects.
|
||||
- **Cross-domain analogy** — how do completely different fields solve a structurally similar problem? The grounding domain is the user's topic; the analogy domain is anywhere else (other industries, biology, games, infrastructure, history). Push past the obvious analogy to non-obvious ones.
|
||||
- **Constraint-flipping** — invert the obvious constraint to its opposite or extreme. What if the budget were 10x or 0? What if there were one constraint instead of ten, or ten instead of one? Use the resulting design as a candidate even if the flip itself is not realistic.
|
||||
|
||||
Aim for 5-8 ideas per frame. **When axes are present, distribute ideas across axes** — each frame's lens applies to every axis, but ideas should not all cluster on one. Tag each idea with the axis it targets. After generating, merge and dedupe; scan for cross-cutting combinations (3-5 additions at most; more in surprise-me mode, where different frames often discover different subjects and combinations are the magic layer).
|
||||
|
||||
**Axis-coverage check (when axes are present).** After merging, count ideas per axis. If any axis has zero ideas, generate one additional small batch (3-5 ideas) targeting the empty axis with the frame whose lens best fits — Pain & friction for usability gaps, Cross-domain analogy for distribution or compounding gaps, etc. Cap recovery at 2 axes; beyond that, accept thin coverage rather than fan out. Note any axis that was not recovered in the rejection summary so the gap is visible.
|
||||
|
||||
**Per-idea output contract (mirrors SKILL.md Phase 2):** each idea carries title, summary, **axis** (when decomposition produced an axis list — pick the one this idea most centrally targets; omit when skipped), **basis** (required, tagged `direct:` quoted evidence / `external:` named prior art or domain research / `reasoned:` written-out first-principles argument), why-it-matters connecting the basis to the move's significance, and a one-line meeting-test self-check (waived when tactical focus signals were detected in Phase 0.5). Basis is required, not optional — unjustified speculation does not surface.
|
||||
|
||||
**Generation rules:**
|
||||
|
||||
- Every idea carries an articulated basis. The failure mode to prevent is plausible-sounding speculation that lacks any basis the user can verify.
|
||||
- Bias toward the basis type your frame naturally produces — pain/inversion/leverage tend toward `direct:`; analogy and constraint-flipping tend toward `reasoned:` — but don't exclude other types. When a frame produces a reasoned basis, write the argument out, don't gesture at it.
|
||||
- Apply the meeting-test as a default floor: would this idea warrant the equivalent of team discussion (or whatever maps to "worth talking through" in this topic's native domain)? If not, it's below the floor and does not surface. The floor is relaxed only when Phase 0.5 detected tactical focus signals.
|
||||
- Stay within the subject's identity. Expansions, new surfaces, new directions, retirements are fair game when the basis supports them. Subject-replacement moves (abandoning the subject, pivoting to an unrelated domain) are out regardless of basis.
|
||||
|
||||
**Surprise-me mode in this reference.** When Phase 0.2 routed to surprise-me, there is no user-specified subject. Through each frame's lens, explore the Phase 1 grounding (user-context synthesis + web research) and identify the subject(s) you find most interesting for that lens. Different frames finding different subjects is the feature. The basis may include identification of the subject itself — why this subject is worth ideating on through this lens, citing what in the Phase 1 material signals it.
|
||||
|
||||
## How to converge
|
||||
|
||||
Apply adversarial critique. For each candidate, write a one-line reason if rejected. **Basis-integrity check:** reject any idea lacking an articulated basis, any idea whose stated basis does not actually support the claimed move (speculation dressed as ambition), and any idea that replaces the subject rather than operating on it. Score survivors using a consistent rubric weighing: groundedness in stated context, **basis strength** (`direct:` > `external:` > `reasoned:`; none excluded, but direct-evidence ideas score higher all else equal), expected value, novelty, pragmatism, leverage, implementation burden, overlap with stronger candidates, and **axis spread** (when axes were defined) — survivor sets that cover the topic's surface outscore sets that cluster on one axis, all else equal. Axis spread is a list-level concern, not a per-idea reject reason; apply it after per-idea filtering when choosing among comparable candidates.
|
||||
|
||||
Target 5-7 survivors by default. If too many survive, run a second stricter pass. If fewer than five survive, report that honestly rather than lowering the bar.
|
||||
|
||||
## When to wrap up
|
||||
|
||||
Present survivors before any persistence. For each: title, description, **axis** (when decomposition produced an axis list), **basis** (tagged `direct:` / `external:` / `reasoned:`, with the quoted evidence, cited source, or written-out argument), rationale (how the basis connects to the move's significance), downsides, confidence, complexity. Then a brief rejection summary so the user can see what was considered and cut — including any axis that ended up with zero survivors despite recovery, so the coverage gap is visible.
|
||||
|
||||
Persistence is opt-in. The terminal review loop is a complete ideation cycle. Refinement happens in conversation with no file or network cost. Persistence triggers only when the user explicitly chooses to save, share, or hand off.
|
||||
|
||||
Use the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question. Offer four choices:
|
||||
|
||||
- **Refine the ideation in conversation (or stop here — no save)** — add ideas, re-evaluate, or deepen analysis without writing anything. Ending the conversation at any point after this pick is a valid no-save exit.
|
||||
- **Open and iterate in Proof** — invoke the Proof HITL review path per the §6.2 contract in `references/post-ideation-workflow.md`: upload the survivors to Proof (rendered to a temp file since no local file is written in non-software elsewhere mode), iterate via comments, and exit cleanly with the Proof URL as the canonical record on successful return. Proof iteration is typically the terminal act in this mode, so the flow does not force another menu choice afterward. Only an `aborted` status returns to this menu. On persistent Proof failure, apply the §6.5 Proof Failure Ladder from `references/post-ideation-workflow.md` so the iteration attempt is not stranded without recovery.
|
||||
- **Brainstorm a selected idea** — go deeper on one idea through dialogue. Unlike repo mode, this is not the first step of an implementation chain — there is no `ce-plan` → `ce-work` after; `ce-brainstorm` in universal mode develops the idea further (e.g., expands a name into a brand brief, a plot into an outline, a decision into a weighed framework) and ends there. Persist first per the §6.3 contract in `references/post-ideation-workflow.md`: save the survivors to Proof (the elsewhere-mode default) or to `docs/ideation/` when the user explicitly asked for a local file, mark the chosen idea as `Explored`, then load `ce-brainstorm` with that idea as the seed. On a successful Proof return (`proceeded` or `done_for_now`), continue into the brainstorm handoff per §5.2's caller-aware return rule; on `aborted`, return to this menu without handing off. On persistent Proof failure, apply the §6.5 Proof Failure Ladder before ending so the brainstorm seed is preserved through a local-save fallback.
|
||||
- **Save and end** — share the survivors to Proof (the elsewhere-mode default) and end. Use `docs/ideation/` instead only when the user explicitly asks for a local file. On Proof failure (including after the single orchestrator-side retry), apply the §6.5 Proof Failure Ladder from `references/post-ideation-workflow.md` — surface the local-save fallback menu (custom path or skip) before ending so the user is not stranded without a recovery path.
|
||||
|
||||
No-save exit is supported without a dedicated menu option. Pick Refine and stop the conversation, or use the question tool's free-text escape to say so directly — persistence is opt-in and the terminal review loop is already a complete ideation cycle.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Web Research Cache (V15)
|
||||
|
||||
Read this when checking the V15 cache before dispatching `web-researcher`, or when appending fresh research to the cache after dispatch. The behavior here is conditional — most invocations either hit the cache or write to it once and move on.
|
||||
|
||||
## Cache file shape
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"key": {
|
||||
"mode": "repo|elsewhere-software|elsewhere-non-software",
|
||||
"focus_hint_normalized": "<lowercase, whitespace-collapsed focus hint or empty string>",
|
||||
"topic_surface_hash": "<short hash of the user-supplied topic surface>"
|
||||
},
|
||||
"result": "<web-researcher output as plain text>",
|
||||
"ts": "<iso8601>"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Files live under `<scratch-dir>/web-research-cache.json`, where `<scratch-dir>` is `/tmp/compound-engineering/ce-ideate/<run-id>`, resolved once in SKILL.md Phase 1.
|
||||
|
||||
## Reuse check
|
||||
|
||||
Before dispatching `web-researcher`, resolve the scratch root (the parent of `<scratch-dir>`) in bash and list sibling run-id directories — refinement loops within a session may legitimately reuse another run's cache by topic, not run-id:
|
||||
|
||||
```bash
|
||||
SCRATCH_ROOT="/tmp/compound-engineering/ce-ideate"
|
||||
find "$SCRATCH_ROOT" -maxdepth 2 -name 'web-research-cache.json' -type f 2>/dev/null
|
||||
```
|
||||
|
||||
`find` exits 0 with empty output when no cache files exist, so the first-run case does not abort the reuse-check step.
|
||||
|
||||
Read each matching file. If any entry's `key` matches the current dispatch (same full mode variant — `repo`, `elsewhere-software`, or `elsewhere-non-software` — plus same case-insensitive normalized focus hint plus same topic surface hash), skip the dispatch and pass the cached `result` to the consolidated grounding summary. Mode variants must match exactly: `elsewhere-software` and `elsewhere-non-software` are distinct domains and must not cross-reuse. Note in the summary: "Reusing prior web research from this session — say 're-research' to refresh."
|
||||
|
||||
On `re-research` override, delete the matching entry and dispatch fresh.
|
||||
|
||||
## Append after fresh dispatch
|
||||
|
||||
After a fresh dispatch, append the new result to the current run's cache file at `<scratch-dir>/web-research-cache.json` using the absolute path from Phase 1 (create directory and file if needed). The next invocation in the session can reuse it via the `find` listing above.
|
||||
|
||||
## Topic surface hash
|
||||
|
||||
The topic surface is the user-supplied content the web research is grounded on:
|
||||
- **Elsewhere modes (`elsewhere-software`, `elsewhere-non-software`):** the user's topic prompt plus any Phase 0.4 intake answers (the actual subject the agent is researching). The two sub-modes are keyed separately — a reclassification between software and non-software for the same topic hash must force a fresh dispatch, since the research domain differs.
|
||||
- **Repo mode:** the focus hint plus a stable repo discriminator. This keeps the cache key meaningful when focus is empty — two bare-prompt invocations in the same repo legitimately share research, but the key still differentiates repos. Since cache files from every repo's runs now live under the shared OS-temp root, a bare basename like `app` or `frontend` would collide across unrelated repos. Resolve the discriminator with this fallback chain and hash the result (first 8 hex chars of sha256 is sufficient):
|
||||
1. `git remote get-url origin` — stable across machines, correct for collaborators on the same remote.
|
||||
2. `git rev-parse --show-toplevel` — absolute repo path; machine-local but always available in a git checkout.
|
||||
3. The current working directory's absolute path — last resort when not in a git repo.
|
||||
|
||||
Normalize before hashing: lowercase, collapse whitespace. (The repo discriminator hash is computed from the raw command output; only the focus hint and topic text are normalized.)
|
||||
|
||||
## Degradation
|
||||
|
||||
If the cache file is unreachable across invocations on the current platform (filesystem isolation, sandboxing, ephemeral working directory), degrade to "no reuse, dispatch every time." Surface the limitation in the consolidated grounding summary and proceed without reuse rather than inventing a capability the platform may not have.
|
||||
@@ -0,0 +1,770 @@
|
||||
---
|
||||
name: ce-plan
|
||||
description: "Create structured plans for multi-step tasks -- software features, research workflows, events, study plans, or any goal that benefits from breakdown. Also deepens existing plans with interactive sub-agent review. Use when the user says 'plan this', 'create a plan', 'how should we build', 'break this down', or when a brainstorm doc is ready for planning. Use 'deepen the plan' or 'deepening pass' for the deepening flow. For exploratory requests, prefer ce-brainstorm first."
|
||||
argument-hint: "[optional: feature description, requirements doc path, plan path to deepen, or any task to plan] [output:html]"
|
||||
---
|
||||
|
||||
# Create Technical Plan
|
||||
|
||||
**Note: The current year is 2026.** Use this when dating plans and searching for recent documentation.
|
||||
|
||||
`ce-brainstorm` defines **WHAT** to build. `ce-plan` defines **HOW** to build it. `ce-work` executes the plan. A prior brainstorm is useful context but never required — `ce-plan` works from any input: a requirements doc, a bug report, a feature idea, or a rough description.
|
||||
|
||||
**When directly invoked, always plan.** Never classify a direct invocation as "not a planning task" and abandon the workflow. If the input is unclear, ask clarifying questions or use the planning bootstrap (Phase 0.4) to establish enough context — but always stay in the planning workflow.
|
||||
|
||||
This workflow produces a durable implementation plan. It does **not** implement code, run tests, or learn from execution-time results. If the answer depends on changing code and seeing what happens, that belongs in `ce-work`, not here.
|
||||
|
||||
## Interaction Method
|
||||
|
||||
When asking the user a question, use the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
Ask one question at a time. Prefer a concise single-select choice when natural options exist.
|
||||
|
||||
## Feature Description
|
||||
|
||||
<feature_description> #$ARGUMENTS </feature_description>
|
||||
|
||||
**If the feature description above is empty, ask the user:** "What would you like to plan? Describe the task, goal, or project you have in mind." Then wait for their response before continuing.
|
||||
|
||||
If the input is present but unclear or underspecified, do not abandon — ask one or two clarifying questions, or proceed to Phase 0.4's planning bootstrap to establish enough context. The goal is always to help the user plan, never to exit the workflow.
|
||||
|
||||
**IMPORTANT: All file references in the plan document must use repo-relative paths (e.g., `src/models/user.rb`), never absolute paths (e.g., `/Users/name/Code/project/src/models/user.rb`). This applies everywhere — implementation unit file lists, pattern references, origin document links, and prose mentions. Absolute paths break portability across machines, worktrees, and teammates.**
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Use requirements as the source of truth** - If `ce-brainstorm` produced a requirements document, planning should build from it rather than re-inventing behavior.
|
||||
2. **Decisions, not code** - Capture approach, boundaries, files, dependencies, risks, and test scenarios. Do not pre-write implementation code or shell command choreography. Pseudo-code sketches or DSL grammars that communicate high-level technical design are welcome when they help a reviewer validate direction — but they must be explicitly framed as directional guidance, not implementation specification.
|
||||
3. **Research before structuring** - Explore the codebase, institutional learnings, and external guidance when warranted before finalizing the plan.
|
||||
4. **Right-size the artifact** - Small work gets a compact plan. Large work gets more structure. The philosophy stays the same at every depth.
|
||||
5. **Separate planning from execution discovery** - Resolve planning-time questions here. Explicitly defer execution-time unknowns to implementation.
|
||||
6. **Keep the plan portable** - The plan should work as a living document, review artifact, or issue body without embedding tool-specific executor instructions.
|
||||
7. **Carry execution posture lightly when it matters** - If the request, origin document, or repo context clearly implies test-first, characterization-first, or another non-default execution posture, reflect that in the plan as a lightweight signal. Do not turn the plan into step-by-step execution choreography.
|
||||
8. **Honor user-named resources** - When the user names a specific resource — a CLI, MCP server, URL, file, doc link, or prior artifact — treat it as authoritative input, not a suggestion. Discover it if unknown (`command -v`, fetch, read) before assuming it's unavailable. Use it in place of generic alternatives. If it fails or doesn't exist, say so explicitly rather than silently substituting.
|
||||
|
||||
## Plan Quality Bar
|
||||
|
||||
Every plan should contain:
|
||||
- A clear problem frame and scope boundary
|
||||
- Concrete requirements traceability back to the request or origin document
|
||||
- Repo-relative file paths for the work being proposed (never absolute paths — see Planning Rules)
|
||||
- Explicit test file paths for feature-bearing implementation units
|
||||
- Decisions with rationale, not just tasks
|
||||
- Existing patterns or code references to follow
|
||||
- Enumerated test scenarios for each feature-bearing unit, specific enough that an implementer knows exactly what to test without inventing coverage themselves
|
||||
- Clear dependencies and sequencing
|
||||
|
||||
A plan is ready when an implementer can start confidently without needing the plan to write the code for them.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Phase 0: Resume, Source, and Scope
|
||||
|
||||
#### 0.0 Resolve Output Mode
|
||||
|
||||
Determine `OUTPUT_FORMAT` before any other phase fires. Output mode is **exclusive** — the plan is written as either markdown (`.md`) OR HTML (`.html`), never both. Precedence: CLI arg > config > default (`md`), with a hard pipeline-mode override.
|
||||
|
||||
**Read config (pre-resolved at skill load):**
|
||||
!`cat "$(git rev-parse --show-toplevel 2>/dev/null)/.compound-engineering/config.local.yaml" 2>/dev/null || echo '__NO_CONFIG__'`
|
||||
|
||||
Resolution steps:
|
||||
|
||||
1. **CLI arg.** Scan `$ARGUMENTS` for a token starting with the literal prefix `output:`. If found, strip it from arguments before treating the remainder as the feature description, and match its value case-insensitively against `md` and `html`.
|
||||
- `output:` alone (no value) → no-op, fall through to step 2.
|
||||
- `output:<unknown>` (e.g., `output:pdf`) → drop the token, fall through to step 2, and remember to emit a one-line note above the post-generation menu after final resolution: `Ignored unknown output: value '<value>' — using <resolved_format> instead.` where `<resolved_format>` is the value `OUTPUT_FORMAT` actually resolved to after steps 2-4. Do not hardcode `md` in the note — that misleads users when config has set HTML.
|
||||
2. **Config.** If step 1 did not resolve and the pre-resolved YAML above has an **active (non-commented)** `plan_output:` key whose value matches `md` or `html` (case-insensitive), use it. Missing, invalid, or commented values fall through silently. Critical: lines starting with `#` are YAML comments and must be ignored — the shipped config template includes commented examples like `# plan_output: html` to document the option, and matching those as active settings would silently force HTML mode on every run without the user having opted in.
|
||||
3. **Default.** Otherwise `OUTPUT_FORMAT=md`.
|
||||
4. **Pipeline override.** When invoked from LFG or any `disable-model-invocation` context, force `OUTPUT_FORMAT=md` regardless of steps 1-3. `ce-work` and other automated downstream consumers parse markdown reliably; HTML in pipeline runs is unnecessary friction.
|
||||
|
||||
**Token-parsing convention:** only literal-prefix flag tokens (`output:`, `mode:`, `delegate:` where applicable) are consumed and stripped. Other `<word>:<word>` tokens — including conventional commit prefixes like `feat:`, `fix:`, `chore:` that may appear inside a feature description — pass through verbatim.
|
||||
|
||||
**Load the format-rendering reference based on the resolved value.** Section content is the same in either format; presentation differs. Both references are paired with `references/plan-sections.md`, which describes what the plan contains regardless of format.
|
||||
|
||||
- When `OUTPUT_FORMAT=md`, read `references/markdown-rendering.md` for format principles.
|
||||
- When `OUTPUT_FORMAT=html`, read `references/html-rendering.md` for format principles.
|
||||
|
||||
#### 0.1 Resume Existing Plan Work When Appropriate
|
||||
|
||||
If the user references an existing plan file or there is an obvious recent matching plan in `docs/plans/`:
|
||||
- Read it
|
||||
- Confirm whether to update it in place or create a new plan
|
||||
- If updating, revise only the still-relevant sections. Plans do not carry per-unit progress state — progress is derived from git by `ce-work`, so there is no progress to preserve across edits
|
||||
|
||||
**Deepen intent:** The word "deepen" (or "deepening") in reference to a plan is the primary trigger for the deepening fast path. When the user says "deepen the plan", "deepen my plan", "run a deepening pass", or similar, the target document is a **plan** in `docs/plans/`, not a requirements document. Use any path, keyword, or context the user provides to identify the right plan. If a path is provided, verify it is actually a plan document. If the match is not obvious, confirm with the user before proceeding.
|
||||
|
||||
Words like "strengthen", "confidence", "gaps", and "rigor" are NOT sufficient on their own to trigger deepening. These words appear in normal editing requests ("strengthen that section about the diagram", "there are gaps in the test scenarios") and should not cause a holistic deepening pass. Only treat them as deepening intent when the request clearly targets the plan as a whole and does not name a specific section or content area to change — and even then, prefer to confirm with the user before entering the deepening flow.
|
||||
|
||||
Once the plan is identified and appears complete (all major sections present, implementation units defined, `status: active`):
|
||||
- **Routing is keyed on file extension first, then frontmatter.** HTML plans (`.html`) are always software plans — the html-rendering invariant forbids YAML frontmatter, so frontmatter absence is not a non-software signal for HTML. Treat the visible-header metadata (title, status, date) as the frontmatter equivalent.
|
||||
- **`.html` plan:** short-circuit to Phase 5.3 (Confidence Check and Deepening) in **interactive mode**. Never route to `references/universal-planning.md` based on missing YAML.
|
||||
- **`.md` plan WITH YAML frontmatter:** short-circuit to Phase 5.3 in **interactive mode**.
|
||||
- **`.md` plan WITHOUT YAML frontmatter** (non-software plans use a simple `# Title` heading with `Created:` date instead): route to `references/universal-planning.md` for editing or deepening instead of Phase 5.3. Non-software plans do not use the software confidence check.
|
||||
|
||||
The Phase 5.3 short-circuit avoids re-running the full planning workflow and gives the user control over which findings are integrated.
|
||||
|
||||
Normal editing requests (e.g., "update the test scenarios", "add a new implementation unit", "strengthen the risk section") should NOT trigger the fast path — they follow the standard resume flow.
|
||||
|
||||
If the plan already has a `deepened: YYYY-MM-DD` frontmatter field and there is no explicit user request to re-deepen, the fast path still applies the same confidence-gap evaluation — it does not force deepening.
|
||||
|
||||
**Resume preserves the existing artifact's format, except pipeline mode.** When resuming an existing plan, the resume run writes back in whatever format the existing artifact uses — markdown if the existing file is `.md`, HTML if it is `.html` — so a resume doesn't silently change the artifact shape. Explicit `output:` arguments on this run override (e.g., resuming an `.html` plan with `output:md` switches the artifact to markdown). Pipeline mode (LFG, any `disable-model-invocation` context) always wins per Phase 0.0: even when resuming an existing `.html` plan, pipeline runs force `OUTPUT_FORMAT=md` so downstream automation receives the markdown shape it expects. The resume rewrites the markdown file at the parallel path (`<plan-basename>.md`) and the original `.html` is left in place untouched.
|
||||
|
||||
#### 0.1b Classify Task Domain
|
||||
|
||||
If the task asks to build, modify, refactor, deploy, or architect software (code, schemas, infrastructure), continue to Phase 0.2.
|
||||
|
||||
Classify by task-type, not topic. A request that merely *references* code, a repo, an API, or a database is not automatically software work: building or modifying code is software; investigating or analyzing it is an answer-seeking question. "How often does X star repos — is it a big deal?" or "how does our approach compare to Y?" route to `references/universal-planning.md` (answer-seeking), not the implementation-plan path.
|
||||
|
||||
If the domain is genuinely ambiguous (e.g., "plan a migration" with no other context), ask the user before routing.
|
||||
|
||||
Otherwise, read `references/universal-planning.md` and follow that workflow instead. Skip all subsequent phases. Named tools or source links don't change this routing — they're inputs, handled per Core Principle 8.
|
||||
|
||||
#### 0.2 Find Upstream Requirements Document
|
||||
|
||||
Before asking planning questions, search `docs/brainstorms/` for files matching `*-requirements.md` or `*-requirements.html` (ce-brainstorm emits whichever extension matches its resolved output format; both are valid upstream requirements docs and either may be carried as the plan's `origin:`).
|
||||
|
||||
**Relevance criteria:** A requirements document is relevant if:
|
||||
- The topic semantically matches the feature description
|
||||
- It was created within the last 30 days (use judgment to override if the document is clearly still relevant or clearly stale)
|
||||
- It appears to cover the same user problem or scope
|
||||
|
||||
If multiple source documents match, ask which one to use using the platform's blocking question tool when available (see Interaction Method). Otherwise, present numbered options in chat and wait for the user's reply before proceeding.
|
||||
|
||||
#### 0.3 Use the Source Document as Primary Input
|
||||
|
||||
If a relevant requirements document exists:
|
||||
1. Read it thoroughly
|
||||
2. Announce that it will serve as the origin document for planning
|
||||
3. Carry forward all of the following:
|
||||
- Problem frame
|
||||
- Actors (A-IDs), Key Flows (F-IDs), and Acceptance Examples (AE-IDs) when present — preserve these as constraints that implementation units must honor
|
||||
- Requirements and success criteria
|
||||
- Scope boundaries (including "Deferred for later" and "Outside this product's identity" subsections when present)
|
||||
- Key decisions and rationale
|
||||
- Dependencies or assumptions
|
||||
- Outstanding questions, preserving whether they are blocking or deferred
|
||||
4. Use the source document as the primary input to planning and research
|
||||
5. Reference important carried-forward decisions in the plan with `(see origin: <source-path>)`
|
||||
6. Do not silently omit source content — if the origin document discussed it, the plan must address it even if briefly. Before finalizing, scan each section of the origin document to verify nothing was dropped.
|
||||
|
||||
If no relevant requirements document exists, planning may proceed from the user's request directly.
|
||||
|
||||
#### 0.4 Planning Bootstrap (No Requirements Doc or Unclear Input)
|
||||
|
||||
If no relevant requirements document exists, or the input needs more structure:
|
||||
- Assess whether the request is already clear enough for direct technical planning — if so, continue to Phase 0.5
|
||||
- If the ambiguity is mainly product framing, user behavior, or scope definition, recommend `ce-brainstorm` as a suggestion — but always offer to continue planning here as well
|
||||
- If the user wants to continue here (or was already explicit about wanting a plan), run the planning bootstrap below
|
||||
|
||||
The planning bootstrap should establish:
|
||||
- Problem frame
|
||||
- Intended behavior
|
||||
- Scope boundaries and obvious non-goals
|
||||
- Success criteria
|
||||
- Blocking questions or assumptions
|
||||
|
||||
Keep this bootstrap brief. It exists to preserve direct-entry convenience, not to replace a full brainstorm.
|
||||
|
||||
If the bootstrap uncovers major unresolved product questions:
|
||||
- Recommend `ce-brainstorm` again
|
||||
- If the user still wants to continue, require explicit assumptions before proceeding
|
||||
|
||||
If the bootstrap reveals that a different workflow would serve the user better:
|
||||
|
||||
- **Bug-shaped prompt** (user describes broken behavior — "fix the bug where X", error message, regression, "doesn't work"). Surface `ce-debug` as a route-out option alongside continuing with `ce-plan` whenever the bug surface is reachable (in cwd OR named repo found at another local path). Stay in `ce-plan` silently when the named code can't be found anywhere local — paper-planning is the only useful output for unreachable surfaces.
|
||||
|
||||
**When the bug is at another local path (not cwd):**
|
||||
- Announce the target explicitly **before** any cross-repo investigation: which path will be read AND where plan outputs will land (default: target repo's `docs/plans/`, not cwd's).
|
||||
- Default: proceed from the target repo for both investigation and plan-write. The user can interrupt to redirect (switch context, paper-plan, abandon, etc.). No location menu — the announcement makes the cross-repo nature visible, and the user can speak up if they want something unusual.
|
||||
- **After** announcing and proceeding, fire the standard ce-debug routing menu (continue with `ce-plan` vs switch to `ce-debug`) — same shape as the in-cwd case. Cross-repo location and ce-debug skill routing are orthogonal decisions; do not merge them into a single question.
|
||||
|
||||
Reading code at another path is fine in principle — that's just file access. The harm to avoid is silent operation on the wrong repo, especially writing the plan doc somewhere it won't be discovered (a busyblock plan landing in `cli-printing-press/docs/plans/` is a discoverability disaster). The announcement requirement makes the target visible; defaulting to the target repo for both investigation and outputs respects the user's stated intent (they named that repo); the orthogonal ce-debug menu keeps the skill-choice question clean.
|
||||
|
||||
The accessibility classification is conservative and may under-suggest in monorepos, dependency bugs, or after renames. Users can always invoke `/ce-debug` manually.
|
||||
|
||||
**Headless mode**: skip the ce-debug suggestion menu entirely; default to continuing with `/ce-plan` (the user's explicit invocation). There is no synchronous user to resolve a route-out choice, and auto-routing to ce-debug would change the skill mid-flight without authorization.
|
||||
|
||||
- **Clear task ready to execute** (known root cause, obvious fix, no architectural decisions) — suggest `ce-work` as a faster alternative alongside continuing with planning. The user decides.
|
||||
|
||||
#### 0.5 Classify Outstanding Questions Before Planning
|
||||
|
||||
If the origin document contains `Resolve Before Planning` or similar blocking questions:
|
||||
- Review each one before proceeding
|
||||
- Reclassify it into planning-owned work **only if** it is actually a technical, architectural, or research question
|
||||
- Keep it as a blocker if it would change product behavior, scope, or success criteria
|
||||
|
||||
If true product blockers remain:
|
||||
- Surface them clearly
|
||||
- Ask the user, using the platform's blocking question tool when available (see Interaction Method), whether to:
|
||||
1. Resume `ce-brainstorm` to resolve them
|
||||
2. Convert them into explicit assumptions or decisions and continue
|
||||
- Do not continue planning while true blockers remain unresolved
|
||||
|
||||
#### 0.6 Assess Plan Depth
|
||||
|
||||
Classify the work into one of these plan depths:
|
||||
|
||||
- **Lightweight** - small, well-bounded, low ambiguity
|
||||
- **Standard** - normal feature or bounded refactor with some technical decisions to document
|
||||
- **Deep** - cross-cutting, strategic, high-risk, or highly ambiguous implementation work
|
||||
|
||||
If depth is unclear, ask one targeted question and then continue.
|
||||
|
||||
#### 0.7 Solo-Mode Scoping Synthesis
|
||||
|
||||
Surface call-outs to the user — the specific forks in scope or approach where user input materially changes the plan — so scope can be corrected **before Phase 1 research is spent**. Sub-agent dispatch (repo-research-analyst, learnings-researcher, etc.) is the expensive next step this phase guards against wasted effort on.
|
||||
|
||||
Fires **only in solo invocation** — when Phase 0.2 found no upstream brainstorm doc AND Phase 0.4 stayed in ce-plan (did not route to ce-debug, ce-work, or universal-planning) AND Phase 0.5 cleared (no unresolved blockers) AND not on Phase 0.1 fast paths (resume normal, deepen-intent). Each guard is an explicit conditional. Skip Phase 0.7 entirely when any guard fails — brainstorm-sourced invocations defer to Phase 5.1.5 instead.
|
||||
|
||||
**Read `references/synthesis-summary.md` before composing the scoping synthesis.** It carries the affirmability test, keep-test criteria, detail test, summary shape budgets, granularity rules, anti-patterns, revision-vs-confirmation discipline, doc-shape routing, soft-cut behavior, self-redirect support, the worked PII compression example, and full headless-mode routing — all required for a well-shaped synthesis.
|
||||
|
||||
**Required gate output — do not skip; silent proceeding is not allowed.** Compose an internal three-bucket scope draft (Stated / Inferred / Out of scope — internal thinking that feeds plan-body routing at Phase 5.2, not the chat output below). Derive call-outs (specific forks where user input materially changes the plan), then emit one of the two literal templates below in chat before continuing to Phase 1.
|
||||
|
||||
**Synthesis is pre-plan-write.** The agent does NOT yet know how plan-write will sequence the work. Do not claim PR count ("one PR"), commit/branch shape, effort or time estimates, Implementation Unit boundaries, or exact file paths in the synthesis. The synthesis surfaces decisions knowable at THIS point — for the solo variant, that's the user's request plus the Phase 0.4 bootstrap dialogue plus the agent's own internal three-bucket draft. Phase 1 research has not happened yet and there is no upstream brainstorm; do not claim grounding from either. Plan-write produces the rest. This rule holds even when the agent has formed plan-write opinions earlier in the session — those stay internal until plan-write.
|
||||
|
||||
**Summary shape:** the summary is a **scope claim** — what the plan will target, what it will not — at affirm-or-redirect level. NOT an enumeration of Implementation Units. Form is prose, bullets, or mix; tier budgets are **ceilings, not targets** (Lightweight 1-3 lines; Standard up to 3-5 lines or 2-4 bullets; Deep up to 4-6 lines or 3-6 bullets). 1-2 lines per bullet, conversational not documentary. Less is correct when there isn't more to say. See reference for keep test, detail test, and source-vocabulary discipline.
|
||||
|
||||
**Do NOT enumerate the touch surface.** Sentences like "The touch surface is...", "This plan touches...", "The implementation reaches into..." are plan-pitch leaks. File paths, module names, directory introductions, and per-file change descriptions belong in the plan body (Implementation Units at Phase 5.2), not the synthesis. The synthesis names *what* the plan targets, not *where* the code lives.
|
||||
|
||||
**Pre-emit scans.** Before emitting the synthesis, scan the output:
|
||||
- Bare ID references (`AE\d+`, `R\d+`, `F\d+`, `A\d+`, `U\d+`) → replace with plain names.
|
||||
- File paths (`path/like.md`, `path/like.py`, etc.) → cut unless the path IS the topic of an explicit fork in the call-outs.
|
||||
|
||||
**Tier guard on auto-proceed:** the auto-proceed path (announce without waiting for confirmation) fires only when plan depth is **Lightweight AND zero call-outs survive**. Standard and Deep plans always fire the confirmation gate, even with zero call-outs — substance earns the checkpoint, not interaction history.
|
||||
|
||||
**Confirmation template (Standard/Deep regardless of call-out count, or any tier with one or more call-outs surviving):**
|
||||
|
||||
````text
|
||||
Based on your request and our brief discussion, here's the scope I'm proposing to plan against:
|
||||
|
||||
[scope claim — what the plan will target, what it will not; affirm-or-redirect level; NOT an enumeration of Implementation Units]
|
||||
|
||||
**Call outs:** (omit this header when zero forks survived the keep test)
|
||||
- [decision-level fork in 1-2 lines: name the choice and optional one-clause trade-off in parens. NO multi-sentence rationale, NO "my default is X" pitch]
|
||||
|
||||
Confirm and I'll proceed to research, drawing on this scope. (You can also redirect to /ce-brainstorm if this is bigger than you initially thought — I'll stop here and load it for you.)
|
||||
````
|
||||
|
||||
Wait for user confirmation before continuing to Phase 1.
|
||||
|
||||
**Auto-proceed template (Lightweight with zero call-outs only):**
|
||||
|
||||
````text
|
||||
Planning: [1-3 line scope claim]
|
||||
|
||||
No open decisions to weigh in on — proceeding to research. Interrupt if I have the scope wrong.
|
||||
````
|
||||
|
||||
Then continue to Phase 1 without a blocking question.
|
||||
|
||||
**Headless mode**: internal draft is composed but stage 2 (chat-time call-outs) is skipped — no synchronous user to confirm to. Continue to Phase 1 research as normal. At plan-write time (Phase 5.2), Inferred bets from the internal draft route to a `## Assumptions` section in the plan instead of Key Technical Decisions. See `references/synthesis-summary.md` Headless mode for the full routing.
|
||||
|
||||
### Phase 1: Gather Context
|
||||
|
||||
#### 1.1 Local Research (Always Runs)
|
||||
|
||||
Prepare a concise planning context summary (a paragraph or two) to pass as input to the research agents:
|
||||
- If an origin document exists, summarize the problem frame, requirements, and key decisions from that document
|
||||
- Otherwise use the feature description directly
|
||||
- If `STRATEGY.md` exists, read it and include the relevant pieces (target problem, approach, active tracks) in the summary so downstream research and planning decisions are anchored to product strategy
|
||||
- If `CONCEPTS.md` exists at repo root, read it — its definitions are the canonical names for domain entities, named processes, and status concepts. Plan with those terms rather than synonyms.
|
||||
|
||||
Run these agents in parallel:
|
||||
|
||||
- Task ce-repo-research-analyst(Scope: technology, architecture, patterns. {planning context summary})
|
||||
- Task ce-learnings-researcher(planning context summary)
|
||||
Collect:
|
||||
- Technology stack and versions (used in section 1.2 to make sharper external research decisions)
|
||||
- Architectural patterns and conventions to follow
|
||||
- Implementation patterns, relevant files, modules, and tests
|
||||
- AGENTS.md guidance that materially affects the plan, with CLAUDE.md used only as compatibility fallback when present
|
||||
- Institutional learnings from `docs/solutions/`
|
||||
- Product strategy context when `STRATEGY.md` is present — flag any plan decisions that pull away from the active tracks or the stated approach
|
||||
|
||||
**Slack context** (opt-in) — never auto-dispatch. Route by condition:
|
||||
|
||||
- **Tools available + user asked**: Dispatch `ce-slack-researcher` with the planning context summary in parallel with other Phase 1.1 agents. If the origin document has a Slack context section, pass it verbatim so the researcher focuses on gaps. Include findings in consolidation.
|
||||
- **Tools available + user didn't ask**: Note in output: "Slack tools detected. Ask me to search Slack for organizational context at any point, or include it in your next prompt."
|
||||
- **No tools + user asked**: Note in output: "Slack context was requested but no Slack tools are available. Install and authenticate the Slack plugin to enable organizational context search."
|
||||
|
||||
#### 1.1b Detect Execution Posture Signals
|
||||
|
||||
Decide whether the plan should carry a lightweight execution posture signal.
|
||||
|
||||
Look for signals such as:
|
||||
- The user explicitly asks for TDD, test-first, or characterization-first work
|
||||
- The origin document calls for test-first implementation or exploratory hardening of legacy code
|
||||
- Local research shows the target area is legacy, weakly tested, or historically fragile, suggesting characterization coverage before changing behavior
|
||||
|
||||
When the signal is clear, carry it forward silently in the relevant implementation units.
|
||||
|
||||
Ask the user only if the posture would materially change sequencing or risk and cannot be responsibly inferred.
|
||||
|
||||
#### 1.2 Decide on External Research
|
||||
|
||||
Based on the origin document, user signals, and local findings, decide **whether** external research adds value and, if so, **what kind**. Resolve this in three stages: explicit-request priority, intent classification, then the implicit signals below.
|
||||
|
||||
**Stage 1 — An explicit request takes precedence.** If the user prompt **or** the origin requirements document explicitly asks for external input — a signal that the answer lives outside the repo, such as competitor/prior-art comparison, "what should we borrow", "from the web", "best practices", "official docs", "alternatives to", a market scan, or naming a specific external technology to consult — external research is **required**, regardless of how strong local patterns look. The list is illustrative; key on the signal, not the exact phrase — any wording that clearly points outside the repo qualifies. The skip conditions below do **not** apply to an explicit request. The only thing that overrides it is an explicit opt-out ("no web research", "skip external research"): honor that, skip, and note it. Improvement or quality verbs ("improve", "make better") carry no external signal on their own and never trigger research by themselves.
|
||||
|
||||
**Stage 2 — Classify the research intent** (whenever external research will run, from Stage 1 or the implicit signals below) so Phase 1.3 routes correctly. Use this mechanical test, not a fixed phrase list:
|
||||
- **Implementation-guidance** — the approach or technology is already settled; the question is *how to build it well* (best practices, version-specific docs, API constraints, known pitfalls, deprecations).
|
||||
- **Landscape / option-discovery** — the question is *what options or prior art exist* (competitor scans, build-vs-buy, library/provider selection, prior art, market signals, cross-domain analogies).
|
||||
- **Mixed** — both: discover an unsettled external option set first, then research the shortlisted choice for implementation guidance.
|
||||
|
||||
**Stage 3 — Implicit signals** decide the call when no explicit request fired.
|
||||
|
||||
**Read between the lines.** Pay attention to signals from the conversation so far:
|
||||
- **User familiarity** — Are they pointing to specific files or patterns? They likely know the codebase well.
|
||||
- **User intent** — Do they want speed or thoroughness? Exploration or execution?
|
||||
- **Topic risk** — Security, payments, external APIs warrant more caution regardless of user signals.
|
||||
- **Uncertainty level** — Is the approach clear or still open-ended?
|
||||
|
||||
**Leverage ce-repo-research-analyst's technology context:**
|
||||
|
||||
The ce-repo-research-analyst output includes a structured Technology & Infrastructure summary. Use it to make sharper external research decisions:
|
||||
|
||||
- If specific frameworks and versions were detected (e.g., Rails 7.2, Next.js 14, Go 1.22), pass those exact identifiers to ce-framework-docs-researcher so it fetches version-specific documentation
|
||||
- If the feature touches a technology layer the scan found well-established in the repo (e.g., existing Sidekiq jobs when planning a new background job), lean toward skipping external research -- local patterns are likely sufficient
|
||||
- If the feature touches a technology layer the scan found absent or thin (e.g., no existing proto files when planning a new gRPC service), lean toward external research -- there are no local patterns to follow
|
||||
- If the scan detected deployment infrastructure (Docker, K8s, serverless), note it in the planning context passed to downstream agents so they can account for deployment constraints
|
||||
- If the scan detected a monorepo and scoped to a specific service, pass that service's tech context to downstream research agents -- not the aggregate of all services. If the scan surfaced the workspace map without scoping, use the feature description to identify the relevant service before proceeding with research
|
||||
|
||||
**Always lean toward external research when:**
|
||||
- The topic is high-risk: security, payments, privacy, external APIs, migrations, compliance
|
||||
- The codebase lacks relevant local patterns -- fewer than 3 direct examples of the pattern this plan needs
|
||||
- Local patterns exist for an adjacent domain but not the exact one -- e.g., the codebase has HTTP clients but not webhook receivers, or has background jobs but not event-driven pub/sub. Adjacent patterns suggest the team is comfortable with the technology layer but may not know domain-specific pitfalls. When this signal is present, frame the external research query around the domain gap specifically, not the general technology
|
||||
- The user is exploring unfamiliar territory
|
||||
- The technology scan found the relevant layer absent or thin in the codebase
|
||||
- The plan's recommendations depend on a genuinely external, **unsettled** option set — which library, provider, or approach to adopt, or what competitors and prior art do — **even when local implementation patterns are strong** (intent: landscape). Bound this implicit landscape trigger by three gates: (a) the option set genuinely lives outside the repo, (b) the decision materially shapes the plan (a KTD, dependency, or architecture choice — not an incidental detail), and (c) no settled local or team choice already exists. Improvement verbs alone never satisfy this.
|
||||
|
||||
**Skip external research when** (only when Stage 1 found no explicit request — an explicit request is never skipped):
|
||||
- The codebase already shows a strong local pattern -- multiple direct examples (not adjacent-domain), recently touched, following current conventions
|
||||
- The user already knows the intended shape
|
||||
- Additional external context would add little practical value
|
||||
- The technology scan found the relevant layer well-established with existing examples to follow
|
||||
|
||||
When an explicit request *did* fire but a settled local or team choice already exists, **narrow the research rather than skipping it** — research the current pitfalls, docs, and practices for the chosen library/pattern instead of re-surveying the whole option set.
|
||||
|
||||
Announce the decision and the intent briefly before continuing. Examples:
|
||||
- "Your codebase has solid patterns for this. Proceeding without external research."
|
||||
- "This involves payment processing, so I'll research current best practices first (implementation-guidance)."
|
||||
- "You asked what to borrow from competitors, so I'll run a landscape scan first (landscape/option-discovery)."
|
||||
|
||||
#### 1.3 External Research (Conditional)
|
||||
|
||||
If Step 1.2 indicates external research is useful, dispatch by the **intent** classified in Stage 2, using the platform's subagent primitive (`Agent`/`Task` in Claude Code, `spawn_agent` in Codex, `subagent` in Pi). For `ce-web-researcher`, pass a focus hint plus the planning context summary and do **not** pass codebase content — it operates externally.
|
||||
|
||||
- **Implementation-guidance** — run in parallel:
|
||||
- Task ce-best-practices-researcher(planning context summary)
|
||||
- Task ce-framework-docs-researcher(planning context summary, with exact frameworks/versions from Phase 1.1 where available)
|
||||
- **Landscape / option-discovery** — Task ce-web-researcher(focus hint, planning context summary). When the request targets projects on a code host (e.g., "competitors on GitHub"), name the discovery dimensions in the focus hint: project names and URLs, release recency and activity, CLI/UX shape, install path, docs and examples, plugin/extension surfaces, recurring issue themes, and license — treating star counts as a weak signal only.
|
||||
- **Mixed** — **sequential, not parallel**: run `ce-web-researcher` first to map the landscape and produce a shortlist; then run `ce-framework-docs-researcher` and/or `ce-best-practices-researcher` against the shortlisted technologies only when their details materially shape the plan.
|
||||
|
||||
**Tool-unavailable handling.** `ce-web-researcher` self-checks for web tools and stops if they are missing. Never block on this: if it reports research unavailable, or any researcher fails, warn and proceed, and carry the gap into Phase 1.4 so the plan records it honestly — especially when the user explicitly requested external research, where a silent skip would leave the plan looking evidence-based when it is not.
|
||||
|
||||
#### 1.4 Consolidate Research
|
||||
|
||||
Summarize:
|
||||
- Relevant codebase patterns and file paths
|
||||
- Relevant institutional learnings
|
||||
- Organizational context from Slack conversations, if gathered (prior discussions, decisions, or domain knowledge relevant to the feature)
|
||||
- External references, prior art, competitor/landscape findings, and best practices, if gathered
|
||||
- Related issues, PRs, or prior art
|
||||
- Any constraints that should materially shape the plan
|
||||
|
||||
**Land external findings in decisions, not an appendix.** Any external research that ran must surface where it changes a choice — Key Technical Decisions rationale, Alternatives, Risks, or Sources & Research — not as a detached list with no bearing on the plan. If a finding shaped nothing, it was not load-bearing; do not pad the plan with it.
|
||||
|
||||
**Mark whether external research was load-bearing.** Record a single internal flag: did external findings materially shape a KTD, Alternative, Scope boundary, or Risk? This flag answers only that question — it does **not** gate whether research runs (Phase 1.2 owns that decision). Phase 5.3.2 reads it to decide whether to enter a confidence-scoring pass.
|
||||
|
||||
**Record requested-but-unavailable.** If the user explicitly requested external research but it could not run (web tools unavailable, researcher failed), state that in the plan as an assumption or open question rather than presenting the plan as externally grounded.
|
||||
|
||||
#### 1.4b Reclassify Depth When Research Reveals External Contract Surfaces
|
||||
|
||||
If the current classification is **Lightweight** and Phase 1 research found that the work touches any of these external contract surfaces, reclassify to **Standard**:
|
||||
|
||||
- Environment variables consumed by external systems, CI, or other repositories
|
||||
- Exported public APIs, CLI flags, or command-line interface contracts
|
||||
- CI/CD configuration files (`.github/workflows/`, `Dockerfile`, deployment scripts)
|
||||
- Shared types or interfaces imported by downstream consumers
|
||||
- Documentation referenced by external URLs or linked from other systems
|
||||
|
||||
This ensures flow analysis (Phase 1.5) runs and the confidence check (Phase 5.3) applies critical-section bonuses. Announce the reclassification briefly: "Reclassifying to Standard — this change touches [environment variables / exported APIs / CI config] with external consumers."
|
||||
|
||||
#### 1.5 Flow and Edge-Case Analysis (Conditional)
|
||||
|
||||
For **Standard** or **Deep** plans, or when user flow completeness is still unclear, run:
|
||||
|
||||
- Task ce-spec-flow-analyzer(planning context summary, research findings)
|
||||
|
||||
Use the output to:
|
||||
- Identify missing edge cases, state transitions, or handoff gaps
|
||||
- Tighten requirements trace or verification strategy
|
||||
- Add only the flow details that materially improve the plan
|
||||
|
||||
### Phase 2: Resolve Planning Questions
|
||||
|
||||
Build a planning question list from:
|
||||
- Deferred questions in the origin document
|
||||
- Gaps discovered in repo or external research
|
||||
- Technical decisions required to produce a useful plan
|
||||
|
||||
For each question, decide whether it should be:
|
||||
- **Resolved during planning** - the answer is knowable from repo context, documentation, or user choice
|
||||
- **Deferred to implementation** - the answer depends on code changes, runtime behavior, or execution-time discovery
|
||||
|
||||
Ask the user only when the answer materially affects architecture, scope, sequencing, or risk and cannot be responsibly inferred. Use the platform's blocking question tool when available (see Interaction Method).
|
||||
|
||||
**Do not** run tests, build the app, or probe runtime behavior in this phase. The goal is a strong plan, not partial execution.
|
||||
|
||||
### Phase 3: Structure the Plan
|
||||
|
||||
#### 3.1 Title and File Naming
|
||||
|
||||
- Draft a clear, searchable title using conventional format such as `feat: Add user authentication` or `fix: Prevent checkout double-submit`
|
||||
- Determine the plan type: `feat`, `fix`, or `refactor`
|
||||
- Build the filename following the repository convention: `docs/plans/YYYY-MM-DD-NNN-<type>-<descriptive-name>-plan.md`
|
||||
- Create `docs/plans/` if it does not exist
|
||||
- Check existing files for today's date to determine the next sequence number (zero-padded to 3 digits, starting at 001)
|
||||
- Keep the descriptive name concise (3-5 words) and kebab-cased
|
||||
- Examples: `2026-01-15-001-feat-user-authentication-flow-plan.md`, `2026-02-03-002-fix-checkout-race-condition-plan.md`
|
||||
- Avoid: missing sequence numbers, vague names like "new-feature", invalid characters (colons, spaces)
|
||||
|
||||
#### 3.2 Stakeholder and Impact Awareness
|
||||
|
||||
For **Standard** or **Deep** plans, briefly consider who is affected by this change — end users, developers, operations, other teams — and how that should shape the plan. For cross-cutting work, note affected parties in the System-Wide Impact section.
|
||||
|
||||
#### 3.3 Break Work into Implementation Units
|
||||
|
||||
Break the work into logical implementation units. Each unit should represent one meaningful change that an implementer could typically land as an atomic commit.
|
||||
|
||||
Good units are:
|
||||
- Focused on one component, behavior, or integration seam
|
||||
- Usually touching a small cluster of related files
|
||||
- Ordered by dependency
|
||||
- Concrete enough for execution without pre-writing code
|
||||
|
||||
Avoid:
|
||||
- 2-5 minute micro-steps
|
||||
- Units that span multiple unrelated concerns
|
||||
- Units that are so vague an implementer still has to invent the plan
|
||||
|
||||
Each unit carries a stable plan-local **U-ID** assigned in Phase 3.5 (`U1`, `U2`, …). U-IDs survive reordering, splitting, and deletion: new units take the next unused number, gaps are fine, and existing IDs are never renumbered. This lets `ce-work` reference units unambiguously across plan edits.
|
||||
|
||||
#### 3.4 High-Level Technical Design
|
||||
|
||||
When the plan's technical approach has shape that prose alone doesn't carry well — architecture across components, sequencing across processes, state machines, branching gates, lifecycles, quantitative comparisons — include a High-Level Technical Design section that conveys the shape. The exact form (component diagram, sequence, swim lane, flowchart, state machine, decision matrix, pseudo-code grammar, bar chart for sizing concerns) is the agent's call per artifact — pick what makes the content land fastest for the reader.
|
||||
|
||||
See `references/plan-sections.md` for the section catalog including HTD's "include when material" criterion. See the format-rendering reference loaded at Phase 0.0 for how visualizations render in the target format (mermaid in markdown, inline SVG in HTML — with the layout-legibility principles around halo, contrast, and label placement when in HTML).
|
||||
|
||||
When the plan's approach is a one-paragraph pattern application that prose conveys directly, skip the section. The presence of HTD should earn its keep with content that genuinely benefits from visualization.
|
||||
|
||||
Plan diagrams render authoritative content alongside the prose — they are not "directional sketches." Do not add hedging captions like *"directional guidance for review, not implementation specification"* to plan diagrams; the prose-is-authoritative rule already governs disagreement, and the hedging weakens the diagram unnecessarily.
|
||||
|
||||
#### 3.4b Output Structure (Optional)
|
||||
|
||||
For greenfield plans that create a new directory structure (new plugin, service, package, or module), include an `## Output Structure` section with a file tree showing the expected layout. This gives reviewers the overall shape before diving into per-unit details.
|
||||
|
||||
**When to include it:**
|
||||
- The plan creates 3+ new files in a new directory hierarchy
|
||||
- The directory layout itself is a meaningful design decision
|
||||
|
||||
**When to skip it:**
|
||||
- The plan only modifies existing files
|
||||
- The plan creates 1-2 files in an existing directory — the per-unit file lists are sufficient
|
||||
|
||||
The tree is a scope declaration showing the expected output shape. It is not a constraint — the implementer may adjust the structure if implementation reveals a better layout. The per-unit `**Files:**` sections remain authoritative for what each unit creates or modifies.
|
||||
|
||||
#### 3.5 Define Each Implementation Unit
|
||||
|
||||
Each unit is a level-3 heading carrying a stable U-ID prefix matching the format used for R/A/F/AE in requirements docs: `### U1. [Name]`. Number sequentially within the plan starting at U1. Do not render units as bulleted list items or prefix them with `- [ ]` / `- [x]` checkbox markers. List-based unit titles fragment in every standard renderer because the per-unit fields (`**Goal:**`, `**Files:**`, `**Approach:**`, etc.) are written flush-left, which terminates CommonMark list continuation and detaches the fields from the unit they describe. Headings render correctly everywhere, are the right semantic match for sections containing multi-block content, and give each unit an anchor link. The plan is a decision artifact; execution progress is derived from git by `ce-work` rather than stored in the plan body.
|
||||
|
||||
**Stability rule.** Once assigned, a U-ID is never renumbered. Reordering units leaves their IDs in place (e.g., U1, U3, U5 in their new order is correct; renumbering to U1, U2, U3 is not). Splitting a unit keeps the original U-ID on the original concept and assigns the next unused number to the new unit. Deletion leaves a gap; gaps are fine. This rule matters most during deepening (Phase 5.3), which is the most likely accidental-renumber vector.
|
||||
|
||||
For each unit, include:
|
||||
- **Goal** - what this unit accomplishes
|
||||
- **Requirements** - which requirements or success criteria it advances (cite R-IDs, and A/F/AE IDs when origin supplies them)
|
||||
- **Dependencies** - what must exist first (cite by U-ID, e.g., "U1, U3")
|
||||
- **Files** - repo-relative file paths to create, modify, or test (never absolute paths)
|
||||
- **Approach** - key decisions, data flow, component boundaries, or integration notes
|
||||
- **Execution note** - optional, only when the unit benefits from a non-default execution posture such as test-first or characterization-first
|
||||
- **Technical design** - optional pseudo-code or diagram when the unit's approach is non-obvious and prose alone would leave it ambiguous. Frame explicitly as directional guidance, not implementation specification
|
||||
- **Patterns to follow** - existing code or conventions to mirror
|
||||
- **Test scenarios** - enumerate the specific test cases the implementer should write, right-sized to the unit's complexity and risk. Consider each category below and include scenarios from every category that applies to this unit. A simple config change may need one scenario; a payment flow may need a dozen. The quality signal is specificity — each scenario should name the input, action, and expected outcome so the implementer doesn't have to invent coverage. For units with no behavioral change (pure config, scaffolding, styling), use `Test expectation: none -- [reason]` instead of leaving the field blank. **AE-link convention:** when a test scenario directly enforces an origin Acceptance Example, prefix it with `Covers AE<N>.` (or `Covers F<N> / AE<N>.`). This is sparse-by-design — most test scenarios are finer-grained than AEs and do not link. Do not force AE links onto tests that only cover lower-level implementation details.
|
||||
- **Happy path behaviors** - core functionality with expected inputs and outputs
|
||||
- **Edge cases** (when the unit has meaningful boundaries) - boundary values, empty inputs, nil/null states, concurrent access
|
||||
- **Error and failure paths** (when the unit has failure modes) - invalid input, downstream service failures, timeout behavior, permission denials
|
||||
- **Integration scenarios** (when the unit crosses layers) - behaviors that mocks alone will not prove, e.g., "creating X triggers callback Y which persists Z". Include these for any unit touching callbacks, middleware, or multi-layer interactions
|
||||
- **Verification** - how an implementer should know the unit is complete, expressed as outcomes rather than shell command scripts
|
||||
|
||||
Every feature-bearing unit should include the test file path in `**Files:**`.
|
||||
|
||||
Use `Execution note` sparingly. Good uses include:
|
||||
- `Execution note: Start with a failing integration test for the request/response contract.`
|
||||
- `Execution note: Add characterization coverage before modifying this legacy parser.`
|
||||
- `Execution note: Implement new domain behavior test-first.`
|
||||
|
||||
Do not expand units into literal `RED/GREEN/REFACTOR` substeps.
|
||||
|
||||
#### 3.6 Keep Planning-Time and Implementation-Time Unknowns Separate
|
||||
|
||||
If something is important but not knowable yet, record it explicitly under deferred implementation notes rather than pretending to resolve it in the plan.
|
||||
|
||||
Examples:
|
||||
- Exact method or helper names
|
||||
- Final SQL or query details after touching real code
|
||||
- Runtime behavior that depends on seeing actual test failures
|
||||
- Refactors that may become unnecessary once implementation starts
|
||||
|
||||
#### 3.7 Anti-Expansion: Tangential Cleanup and Scope Creep Go to Deferred
|
||||
|
||||
Distinct from 3.6 (which is about *unknowns* at plan time): 3.7 is about *known but tangential* work that the agent notices while planning but that falls outside the user's confirmed scope. When research surfaces an adjacent refactor, a "while we're here" cleanup, or a scope-adjacent nice-to-have ("we could also add rate limiting"), route it to the existing `### Deferred to Follow-Up Work` subsection in Scope Boundaries (Phase 4.2 Core Plan Template), not into active Implementation Units.
|
||||
|
||||
This reinforces the synthesis discipline established at Phase 0.7 / Phase 5.1.5 — the user's confirmed scope is what the active plan executes; everything else is deferred. Does NOT impose architectural bias on extend-vs-invent decisions within confirmed scope — that judgment stays with the agent (and is surfaced via the Phase 5.1.5 synthesis when material). The user's explicit ask overrides this default — if the user explicitly requested a refactor, it's in-scope, not deferred.
|
||||
|
||||
### Phase 4: Write the Plan
|
||||
|
||||
**NEVER CODE during this skill.** Research, decide, and write the plan — do not start implementation.
|
||||
|
||||
Use one planning philosophy across all depths. Change the amount of detail, not the boundary between planning and execution.
|
||||
|
||||
#### 4.1 Plan Depth Guidance
|
||||
|
||||
**Lightweight**
|
||||
- Keep the plan compact
|
||||
- Usually 2-4 implementation units
|
||||
- Omit optional sections that add little value
|
||||
|
||||
**Standard**
|
||||
- Use the full core template, omitting optional sections (including High-Level Technical Design) that add no value for this particular work
|
||||
- Usually 3-6 implementation units
|
||||
- Include risks, deferred questions, and system-wide impact when relevant
|
||||
|
||||
**Deep**
|
||||
- Use the full core template plus optional analysis sections where warranted
|
||||
- Usually 4-8 implementation units
|
||||
- Group units into phases when that improves clarity
|
||||
- Include alternatives considered, documentation impacts, and deeper risk treatment when warranted
|
||||
|
||||
#### 4.1b Optional Deep Plan Extensions
|
||||
|
||||
For sufficiently large, risky, or cross-cutting work, add the sections that genuinely help:
|
||||
- **Alternative Approaches Considered**
|
||||
- **Success Metrics**
|
||||
- **Dependencies / Prerequisites**
|
||||
- **Risk Analysis & Mitigation**
|
||||
- **Phased Delivery**
|
||||
- **Documentation Plan**
|
||||
- **Operational / Rollout Notes**
|
||||
- **Future Considerations** only when they materially affect current design
|
||||
|
||||
Do not add these as boilerplate. Include them only when they improve execution quality or stakeholder alignment.
|
||||
|
||||
**Alternatives Considered — what to vary.** When this section is included, alternatives must differ on *how* the work is built: architecture, sequencing, boundaries, integration pattern, rollout strategy. Tiny implementation variants (which hash function, which serialization format) belong in Key Technical Decisions, not Alternatives. Product-shape alternatives (different actors, different core outcome, different positioning) belong in `ce-brainstorm`, not here — surface them back upstream rather than re-litigating product questions during planning.
|
||||
|
||||
#### 4.2 Section Contract and Rendering
|
||||
|
||||
Compose the plan using two paired references:
|
||||
|
||||
- `references/plan-sections.md` — the section contract. Describes what the plan contains: the outcome the plan must enable for downstream consumers, the hard floor (Summary, Problem Frame, Requirements, KTDs, Implementation Units), the include-when-material catalog (HTD, Scope Boundaries, Open Questions, System-Wide Impact, Risks & Dependencies, Acceptance Examples, Documentation/Operational Notes, Sources & Research), the agency-driven escape hatch (introduce new sections when content warrants), and the ID/content rules.
|
||||
- The format-rendering reference loaded at Phase 0.0 (`markdown-rendering.md` OR `html-rendering.md`) — how to present the sections in the resolved output format.
|
||||
|
||||
The section catalog is the same regardless of format. Format-specific principles (table-vs-prose by content shape, ID prefix format, diagram rendering, etc.) live in the rendering reference.
|
||||
|
||||
Omit "include when material" sections that don't carry information for this specific plan. Filling a section with placeholder prose is worse than omitting it.
|
||||
|
||||
#### 4.3 Planning Rules
|
||||
|
||||
- **Horizontal rules (`---`) between top-level sections** in Standard and Deep plans, mirroring the `ce-brainstorm` requirements doc convention. Improves scannability of dense plans where many H2 sections sit close together. Omit for Lightweight plans where the whole doc fits on a single screen.
|
||||
- **All file paths must be repo-relative** — never use absolute paths like `/Users/name/Code/project/src/file.ts`. Use `src/file.ts` instead. Absolute paths make plans non-portable across machines, worktrees, and teammates. When a plan targets a different repo than the document's home, state the target repo once at the top of the plan (e.g., `**Target repo:** my-other-project`) and use repo-relative paths throughout
|
||||
- Prefer path plus class/component/pattern references over brittle line numbers
|
||||
- Do not include implementation code — no imports, exact method signatures, or framework-specific syntax
|
||||
- Pseudo-code sketches and DSL grammars are allowed in the High-Level Technical Design section and per-unit technical design fields when they communicate design direction. Frame them explicitly as directional guidance, not implementation specification
|
||||
- Mermaid diagrams are encouraged when they clarify relationships or flows that prose alone would make hard to follow — ERDs for data model changes, sequence diagrams for multi-service interactions, state diagrams for lifecycle transitions, flowcharts for complex branching logic
|
||||
- Do not include git commands, commit messages, or exact test command recipes
|
||||
- Do not expand implementation units into micro-step `RED/GREEN/REFACTOR` instructions
|
||||
- Do not pretend an execution-time question is settled just to make the plan look complete
|
||||
|
||||
### Phase 5: Final Review, Write File, and Handoff
|
||||
|
||||
#### 5.1 Review Before Writing
|
||||
|
||||
Before finalizing, check:
|
||||
- The plan does not invent product behavior that should have been defined in `ce-brainstorm`
|
||||
- If there was no origin document, the bounded planning bootstrap established enough product clarity to plan responsibly
|
||||
- Every major decision is grounded in the origin document or research
|
||||
- Each implementation unit is concrete, dependency-ordered, and implementation-ready
|
||||
- If test-first or characterization-first posture was explicit or strongly implied, the relevant units carry it forward with a lightweight `Execution note`
|
||||
- Each feature-bearing unit has test scenarios from every applicable category (happy path, edge cases, error paths, integration) — right-sized to the unit's complexity, not padded or skimped
|
||||
- Test scenarios name specific inputs, actions, and expected outcomes without becoming test code
|
||||
- Feature-bearing units with blank or missing test scenarios are flagged as incomplete — feature-bearing units must have actual test scenarios, not just an annotation. The `Test expectation: none -- [reason]` annotation is only valid for non-feature-bearing units (pure config, scaffolding, styling)
|
||||
- Deferred items are explicit and not hidden as fake certainty
|
||||
- **High-Level Technical Design presence audit (load-bearing).** For each architecture trigger in Phase 3.4 that the plan content satisfies (3+ components with directed relationships, 3+ protocol steps, 3+ state machine states, lifecycle, 3+ decision points, 3+ data-flow stages, mode/flag combinations, DSL/API surface design, non-obvious single-component shape), verify a corresponding sketch/diagram is present in the High-Level Technical Design section. Count the firing triggers; count the sketches; the sketch count must be at least the count of distinct trigger categories that fired. Missing the section when a trigger fired, OR including the section but skipping a triggered sketch within it, is incomplete — return to Phase 3.4 and add the missing sketch. Token cost is not a valid reason to fail this check.
|
||||
- If a High-Level Technical Design section is included, it uses the right medium for the work, carries the non-prescriptive framing, and does not contain implementation code (no imports, exact signatures, or framework-specific syntax)
|
||||
- Per-unit technical design fields, if present, are concise and directional rather than copy-paste-ready
|
||||
- If the plan creates a new directory structure, would an Output Structure tree help reviewers see the overall shape?
|
||||
- If Scope Boundaries lists items that are planned work for a separate PR, issue, or repo, are they under `### Deferred to Follow-Up Work` rather than mixed with true non-goals?
|
||||
- U-IDs are unique within the plan and follow the stability rule — no two units share an ID; reordering or splitting did not renumber existing units; gaps from deletions are preserved
|
||||
- Would a visual aid (dependency graph, interaction diagram, comparison table) help a reader grasp the plan structure faster than scanning prose alone?
|
||||
|
||||
If the plan originated from a requirements document, re-read that document and verify:
|
||||
- The chosen approach still matches the product intent
|
||||
- Scope boundaries and success criteria are preserved
|
||||
- Blocking questions were either resolved, explicitly assumed, or sent back to `ce-brainstorm`
|
||||
- Every section of the origin document is addressed in the plan — scan each section to confirm nothing was silently dropped
|
||||
- If origin supplies A/F/AE IDs: every origin R/F/AE that *affects implementation* is referenced in Requirements, a U-ID unit, test scenarios, verification, scope boundaries, or explicitly deferred. Actors are carried forward when they affect behavior, permissions, UX, orchestration, handoff, or verification. The standard is preservation of product intent, not mandatory ID spam — irrelevant origin IDs may be omitted
|
||||
- If origin was Deep-product (origin contains an `Outside this product's identity` subsection): the plan's Scope Boundaries preserves the three-way split — `Deferred for later` and `Outside this product's identity` carried verbatim from origin, `Deferred to Follow-Up Work` reserved for plan-local implementation sequencing
|
||||
|
||||
#### 5.1.5 Brainstorm-Sourced Scoping Synthesis
|
||||
|
||||
Surface plan-time call-outs to the user before Phase 5.2 commits the plan to disk — the latest cheap moment to catch plan-time scope errors. The brainstorm already validated WHAT to build; this phase surfaces HOW the plan will execute on the forks that matter.
|
||||
|
||||
Fires **only when the plan was sourced from an upstream brainstorm doc** (Phase 0.2 found a `*-requirements.md` or `*-requirements.html` match) AND not on Phase 0.1 fast paths (resume normal, deepen-intent). Skip Phase 5.1.5 in solo invocation — solo plans handled their synthesis in Phase 0.7.
|
||||
|
||||
**Read `references/synthesis-summary.md` before composing the scoping synthesis.** It carries the affirmability test, keep-test criteria, detail test, summary shape budgets, granularity rules, anti-patterns, revision-vs-confirmation discipline, doc-body reading rules, doc-shape routing, soft-cut behavior, self-redirect support, the worked PII compression example, and full headless-mode routing — all required for a well-shaped synthesis.
|
||||
|
||||
**Required gate output — do not skip; silent proceeding is not allowed.** Compose an internal three-bucket scope draft (Stated / Inferred / Out of scope — internal thinking that feeds plan-body routing at Phase 5.2, not the chat output below). Derive call-outs (specific forks where user input materially changes the plan), then emit one of the two literal templates below in chat before continuing to Phase 5.2.
|
||||
|
||||
**Synthesis is pre-plan-write.** The agent does NOT yet know how plan-write will sequence the work. Do not claim PR count ("one PR"), commit/branch shape, effort or time estimates, Implementation Unit boundaries, or exact file paths in the synthesis. The synthesis surfaces decisions knowable at THIS point (brainstorm + research + agent posture); plan-write produces the rest. This rule holds even when the agent has formed plan-write opinions earlier in the session — those stay internal until plan-write.
|
||||
|
||||
**Summary shape: two paragraphs.**
|
||||
|
||||
1. **Brainstorm-scope restatement** (1-2 sentences, prose). Restates the brainstorm's scope as orientation, in the brainstorm's own vocabulary. NOT an enumeration of Implementation Units, restated constraints, or listed acceptance examples — the user wrote those.
|
||||
2. **Plan-specific scoping decisions** (prose, or bullets when multi-faceted). Scope-level commitments the agent made that the brainstorm did not: full brainstorm coverage vs. narrowed subset; adjacent refactors pulled in vs. held out; test scope at scenario level. Each item must be affirmable by the user without reading code. Form follows substance; tier budgets are **ceilings, not targets** (Lightweight 1-3 lines; Standard up to 3-5 lines or 2-4 bullets; Deep up to 4-6 lines or 3-6 bullets). 1-2 lines per bullet. Less is correct when there isn't more to say. See reference for keep test, detail test, and source-vocabulary discipline.
|
||||
|
||||
**Do NOT enumerate the touch surface.** Sentences like "The touch surface is...", "This plan touches...", "The implementation reaches into...", "Files modified include..." are plan-pitch leaks. File paths, module names, directory introductions, and per-file change descriptions belong in the plan body (Implementation Units at Phase 5.2), not the synthesis. The synthesis names *what* the plan targets, not *where* the code lives.
|
||||
|
||||
**Pre-emit scans.** Before emitting the synthesis, scan the output:
|
||||
- Bare ID references (`AE\d+`, `R\d+`, `F\d+`, `A\d+`, `U\d+`) → replace with plain names.
|
||||
- File paths (`path/like.md`, `path/like.py`, etc.) → cut unless the path IS the topic of an explicit fork in the call-outs.
|
||||
|
||||
**Tier guard on auto-proceed:** the auto-proceed path (announce without waiting for confirmation) fires only when plan depth is **Lightweight AND zero call-outs survive**. Standard and Deep plans always fire the confirmation gate, even with zero call-outs — substance earns the checkpoint, not interaction history.
|
||||
|
||||
**Confirmation template (Standard/Deep regardless of call-out count, or any tier with one or more call-outs surviving):**
|
||||
|
||||
````text
|
||||
The brainstorm scopes [1-2 sentence restatement in the brainstorm's vocabulary as orientation; NOT an enumeration of Implementation Units, constraints, or acceptance examples].
|
||||
|
||||
This plan [plan-specific scoping decisions: full-brainstorm coverage vs. narrowed subset; adjacent refactors in or out; test scope at scenario level. NOT PR count, sequencing, IU lists, or file paths].
|
||||
|
||||
**Call outs:** (omit this header when zero forks survived the keep test)
|
||||
- [plan-time fork in 1-2 lines: name the choice and optional one-clause trade-off in parens. NO multi-sentence rationale, NO "my default is X" pitch]
|
||||
|
||||
Confirm and I'll write the plan next, drawing on the brainstorm, research, and this synthesis.
|
||||
````
|
||||
|
||||
Wait for user confirmation before continuing to Phase 5.2.
|
||||
|
||||
**Auto-proceed template (Lightweight with zero call-outs only):**
|
||||
|
||||
````text
|
||||
Planning [brief brainstorm-scope restatement] — [plan-specific shape in one clause].
|
||||
|
||||
No open decisions to weigh in on — proceeding to plan-write. Interrupt if I have the scope wrong.
|
||||
````
|
||||
|
||||
Then continue to Phase 5.2 without a blocking question.
|
||||
|
||||
**Headless mode**: internal draft is composed but stage 2 (chat-time call-outs) is skipped — no synchronous user to confirm to. Proceed to Phase 5.2 plan-write. Inferred bets from the internal draft route to a `## Assumptions` section in the plan instead of Key Technical Decisions. See `references/synthesis-summary.md` Headless mode for the full routing.
|
||||
|
||||
#### 5.2 Write Plan File
|
||||
|
||||
**REQUIRED: Write the plan file to disk before presenting any options.**
|
||||
|
||||
Use the Write tool to save the complete plan to the resolved format's extension:
|
||||
|
||||
```text
|
||||
docs/plans/YYYY-MM-DD-NNN-<type>-<descriptive-name>-plan.<md|html>
|
||||
```
|
||||
|
||||
Extension follows `OUTPUT_FORMAT` from Phase 0.0 — `.md` when markdown, `.html` when HTML. Sequence number `NNN` is derived from existing plan files in `docs/plans/` regardless of extension (count both `.md` and `.html`) to ensure unique daily ordering.
|
||||
|
||||
Compose the plan using the content from `references/plan-sections.md` and the format-specific principles from the rendering reference loaded at Phase 0.0 (`markdown-rendering.md` OR `html-rendering.md`).
|
||||
|
||||
**HTML composition timing.** When `OUTPUT_FORMAT=html`, Phase 5.3 deepening runs before this write completes its final form, but `ce-doc-review` is skipped in HTML mode (its mutation mechanics are markdown-only today — see Phase 5.3.8 format gate in `references/plan-handoff.md`). The HTML artifact reflects deepening synthesis but not doc-review autofixes; this is a known gap until ce-doc-review gains HTML-aware mutation.
|
||||
|
||||
Confirm (use absolute path so the reference is clickable in modern terminals):
|
||||
|
||||
```text
|
||||
Plan written to <absolute path to plan>
|
||||
```
|
||||
|
||||
**Pipeline mode:** If invoked from an automated workflow such as LFG or any `disable-model-invocation` context, skip interactive questions. Make the needed choices automatically and proceed to writing the plan. Pipeline mode forces `OUTPUT_FORMAT=md` at Phase 0.0.
|
||||
|
||||
**CONCEPTS.md gap-fill (only if the file already exists):** If the plan body uses a domain term whose definition is missing from `CONCEPTS.md`, add the entry. **Domain entities, named processes, and status concepts with project-specific meaning only** — not file paths, class names, function signatures, or implementation decisions. `CONCEPTS.md` is a glossary, not a spec or catch-all. Follow the format set by existing entries. Apply silently. Skip entirely if `CONCEPTS.md` does not exist — creation is owned by ce-compound and ce-compound-refresh.
|
||||
|
||||
#### 5.3 Confidence Check and Deepening
|
||||
|
||||
After writing the plan file, automatically evaluate whether the plan needs strengthening.
|
||||
|
||||
**Two deepening modes:**
|
||||
|
||||
- **Auto mode** (default during plan generation): Runs without asking the user for approval. The user sees what is being strengthened but does not need to make a decision. Sub-agent findings are synthesized directly into the plan.
|
||||
- **Interactive mode** (activated by the re-deepen fast path in Phase 0.1): The user explicitly asked to deepen an existing plan. Sub-agent findings are presented individually for review before integration. The user can accept, reject, or discuss each agent's findings. Only accepted findings are synthesized into the plan.
|
||||
|
||||
Interactive mode exists because on-demand deepening is a different user posture — the user already has a plan they are invested in and wants to be surgical about what changes. This applies whether the plan was generated by this skill, written by hand, or produced by another tool.
|
||||
|
||||
`ce-doc-review` and this confidence check are different:
|
||||
- Use the `ce-doc-review` skill when the document needs clarity, simplification, completeness, or scope control
|
||||
- This confidence check strengthens rationale, sequencing, risk treatment, and system-wide thinking when the plan is structurally sound but still needs stronger grounding
|
||||
|
||||
**Pipeline mode:** This phase always runs in auto mode in pipeline/disable-model-invocation contexts. No user interaction needed.
|
||||
|
||||
##### 5.3.1 Classify Plan Depth and Topic Risk
|
||||
|
||||
Determine the plan depth from the document:
|
||||
- **Lightweight** - small, bounded, low ambiguity, usually 2-4 implementation units
|
||||
- **Standard** - moderate complexity, some technical decisions, usually 3-6 units
|
||||
- **Deep** - cross-cutting, high-risk, or strategically important work, usually 4-8 units or phased delivery
|
||||
|
||||
Build a risk profile. Treat these as high-risk signals:
|
||||
- Authentication, authorization, or security-sensitive behavior
|
||||
- Payments, billing, or financial flows
|
||||
- Data migrations, backfills, or persistent data changes
|
||||
- External APIs or third-party integrations
|
||||
- Privacy, compliance, or user data handling
|
||||
- Cross-interface parity or multi-surface behavior
|
||||
- Significant rollout, monitoring, or operational concerns
|
||||
|
||||
##### 5.3.2 Gate: Decide Whether to Deepen
|
||||
|
||||
- **Lightweight** plans usually do not need deepening unless they are high-risk
|
||||
- **Standard** plans often benefit when one or more important sections still look thin
|
||||
- **Deep** or high-risk plans often benefit from a targeted second pass
|
||||
- **Thin local grounding override:** If Phase 1.2 triggered external research because local patterns were thin (fewer than 3 direct examples or adjacent-domain match), always proceed to scoring regardless of how grounded the plan appears. When the plan was built on unfamiliar territory, claims about system behavior are more likely to be assumptions than verified facts. The scoring pass is cheap — if the plan is genuinely solid, scoring finds nothing and exits quickly
|
||||
- **Load-bearing external research override:** If Phase 1.4 marked external research as load-bearing (it materially shaped a KTD, Alternative, Scope boundary, or Risk), always proceed to scoring — **even when local implementation patterns are strong**. A landscape or prior-art finding can shape recommendations the local codebase cannot verify, and the thin-grounding override above would miss it. This enters the scoring pass only; it does not force deepening
|
||||
|
||||
If the plan already appears sufficiently grounded and neither the thin-grounding nor the load-bearing-external-research override applies, report "Confidence check passed — no sections need strengthening", then **load `references/plan-handoff.md` now and execute 5.3.8 → 5.3.9 → 5.4 in sequence**. Document review is mandatory for markdown plans — do not skip it because the confidence check passed. The two tools catch different classes of issues. For HTML plans (`OUTPUT_FORMAT=html`), the plan-handoff 5.3.8 format gate skips ce-doc-review since its mutation mechanics are markdown-only today; the menu summary surfaces that limitation explicitly.
|
||||
|
||||
##### 5.3.3–5.3.7 Deepening Execution
|
||||
|
||||
When deepening is warranted, read `references/deepening-workflow.md` for confidence scoring checklists, section-to-agent dispatch mapping, execution mode selection, research execution, interactive finding review, and plan synthesis instructions. Execute steps 5.3.3 through 5.3.7 from that file, then return here for 5.3.8.
|
||||
|
||||
##### 5.3.8–5.4 Document Review, Final Checks, and Post-Generation Options
|
||||
|
||||
**STOP. Load `references/plan-handoff.md` now before continuing.** It carries the full instructions for 5.3.8 (document review), 5.3.9 (final checks and cleanup), and 5.4 (post-generation handoff, including the Proof HITL flow, post-HITL re-review, and Issue Creation branching). **This load is non-optional** — without it, the agent renders the post-generation menu, captures the user's selection, and stops without firing the routed action. Document review at 5.3.8 runs unconditionally for `OUTPUT_FORMAT=md` regardless of whether the confidence check already ran; for `OUTPUT_FORMAT=html`, plan-handoff's 5.3.8 format gate skips ce-doc-review because its mutation mechanics are markdown-only today. The default mode for markdown is headless (`mode:headless`) — `safe_auto` fixes apply silently, remaining findings surface contextually above the menu, and a deeper interactive review is opt-in via free-form prompt.
|
||||
|
||||
After document review and final checks, print a one-line summary of the headless review state above the menu (e.g., `Doc review applied 3 fixes. 2 decisions, 1 proposed fix, 4 FYI observations remain (1 at P1).`; for HTML plans where 5.3.8 was skipped, print `Doc review skipped — ce-doc-review is markdown-only today; the HTML plan was not reviewed.`), then present the menu. The menu has 5 options when actionable findings remain (`proposed_fixes_count + decisions_count > 0`) and 4 options otherwise — including the FYI-only case AND the HTML-skip case (`skipped_reason: output_format_html`), both of which hide option 2 because ce-doc-review's walkthrough is gated to actionable markdown findings and would have nothing valid to walk through. See `references/plan-handoff.md` for the full rule. Render the 5-option menu as a numbered list in chat per the AGENTS.md narrow exception for legitimate option overflow, with the hint "Pick a number or describe what you want." On platforms whose blocking question tool has no option cap (Codex `request_user_input`, Pi `ask_user`), use the platform's blocking tool; when that tool is unavailable or errors (e.g., Codex edit modes where `request_user_input` is not exposed), fall back to the same numbered-list-in-chat rendering with the "Pick a number or describe what you want." hint. The 4-option case routes through the platform's blocking tool normally (`AskUserQuestion` in Claude Code — call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), with the same numbered-list-in-chat fallback when no blocking tool is available or the call errors. Never silently skip the question.
|
||||
|
||||
**Question:** "Plan ready at `<absolute path to plan>`. What would you like to do next?" (use absolute path so the reference is clickable in modern terminals)
|
||||
|
||||
**Options.** Option 4's label matches the artifact's format. Under exclusive output mode, exactly one of "Open in Proof" or "Open in browser" applies per run — `OUTPUT_FORMAT=md` shows Proof; `OUTPUT_FORMAT=html` shows browser. Proof operates on markdown and cannot ingest HTML; the browser option opens the local `.html` file. Render the option matching the format produced this run.
|
||||
|
||||
1. **Start `/ce-work`** (recommended) - Begin implementing this plan in the current session
|
||||
2. **Run deeper doc review** - Walk through the remaining findings interactively (full ce-doc-review walkthrough)
|
||||
3. **Create Issue** - Create a tracked issue from this plan in your configured issue tracker (GitHub or Linear)
|
||||
4. **Open in Proof (web app) — review and comment to iterate with the agent** - Open the doc in Every's Proof editor, iterate with the agent via comments, or copy a link to share with others. **Render only when `OUTPUT_FORMAT=md`.**
|
||||
4. **Open in browser** - Open the HTML plan file locally for review and sharing. **Render only when `OUTPUT_FORMAT=html`.**
|
||||
5. **Done for now** - Pause; the plan file is saved and can be resumed later
|
||||
|
||||
**Routing.** Act on the user's selection — do not just announce it. Elaborate sub-flows (Proof HITL state machine, Issue Creation tracker detection, post-HITL resync) live in `references/plan-handoff.md`.
|
||||
|
||||
- **Start `/ce-work`** — Invoke the `ce-work` skill via the platform's skill-invocation primitive (`Skill` in Claude Code, `Skill` in Codex, the equivalent on Gemini/Pi), passing the plan path as the skill argument. Do not merely tell the user to type `/ce-work` — fire the invocation now so the plan executes in this session.
|
||||
- **Run deeper doc review** — Re-invoke the `ce-doc-review` skill on the plan path **without** `mode:headless` so the interactive routing question and walkthrough fire. After it returns, re-render this menu with refreshed counts so the user can pick a next-stage action.
|
||||
- **Create Issue** — Detect the project tracker (`gh` for GitHub, `linear` for Linear) and create the issue from the plan file as described under "Issue Creation" in `references/plan-handoff.md`. After creation, display the issue URL and ask whether to proceed to `/ce-work` via the platform's blocking question tool.
|
||||
- **Open in Proof (web app) — review and comment to iterate with the agent** — Load the `ce-proof` skill in HITL-review mode with the plan file as `source file`, the plan title as `doc title`, identity `ai:compound-engineering` / `Compound Engineering`, and recommended next step `/ce-work`. Then follow the post-HITL resync logic in `references/plan-handoff.md`, which handles the four `ce-proof` return statuses, re-runs `ce-doc-review` after material edits, and falls back gracefully on upload failure.
|
||||
- **Open in browser** — Display the absolute path to the `.html` plan file so the user can open it locally. Where the platform exposes a browser-opening primitive (e.g., `open` on macOS, `xdg-open` on Linux, `start` on Windows), the agent may use it; otherwise print the absolute path and let the user open it. Do not invoke `ce-work` from this option — the user picked HTML for review/sharing, not handoff.
|
||||
- **Done for now** — Display a brief confirmation that the plan file is saved and end the turn. Do not start follow-up work without an explicit further user prompt.
|
||||
|
||||
If the user types free-form prompts targeting the findings (e.g., "review", "walk through", "deep review"), route as if they picked `Run deeper doc review` — fire the skill rather than looping back to the menu. For other free-text revisions, accept the input and loop back to this menu after applying the revision.
|
||||
|
||||
**Completion check:** This skill is not complete until the post-generation menu above has been presented, the user has selected an action, and the inline routing for that selection has been executed. Presenting the menu and stopping at the user's selection is not completion — fire the routed action.
|
||||
|
||||
**Pipeline mode exception:** In LFG or any `disable-model-invocation` context, skip the interactive menu and return control to the caller after the plan file is written, confidence check has run, and `ce-doc-review` has run in headless mode (per `references/plan-handoff.md`). Pipeline mode forces `OUTPUT_FORMAT=md` at Phase 0.0, so the 5.3.8 format gate never selects the HTML skip path in pipeline runs.
|
||||
@@ -0,0 +1,249 @@
|
||||
# Deepening Workflow
|
||||
|
||||
This file contains the confidence-check execution path (5.3.3-5.3.7). Load it only when the deepening gate at 5.3.2 determines that deepening is warranted.
|
||||
|
||||
## 5.3.3 Score Confidence Gaps
|
||||
|
||||
Use a checklist-first, risk-weighted scoring pass.
|
||||
|
||||
For each section, compute:
|
||||
- **Trigger count** - number of checklist problems that apply
|
||||
- **Risk bonus** - add 1 if the topic is high-risk and this section is materially relevant to that risk
|
||||
- **Critical-section bonus** - add 1 for `Key Technical Decisions`, `Implementation Units`, `System-Wide Impact`, `Risks & Dependencies`, or `Open Questions` in `Standard` or `Deep` plans
|
||||
|
||||
Treat a section as a candidate if:
|
||||
- it hits **2+ total points**, or
|
||||
- it hits **1+ point** in a high-risk domain and the section is materially important
|
||||
|
||||
Choose only the top **2-5** sections by score. If deepening a lightweight plan (high-risk exception), cap at **1-2** sections.
|
||||
|
||||
If the plan already has a `deepened:` date:
|
||||
- Prefer sections that have not yet been substantially strengthened, if their scores are comparable
|
||||
- Revisit an already-deepened section only when it still scores clearly higher than alternatives
|
||||
|
||||
**Section Checklists:**
|
||||
|
||||
**Requirements**
|
||||
- Requirements are vague or disconnected from implementation units
|
||||
- Success criteria are missing or not reflected downstream
|
||||
- Units do not clearly advance the traced requirements
|
||||
- Origin requirements are not clearly carried forward
|
||||
- Origin A/F/AE IDs (when supplied by the upstream brainstorm) are not preserved where planning decisions touch them, or are referenced inconsistently across Requirements, units, and test scenarios
|
||||
|
||||
**Context & Research / Sources & References**
|
||||
- Relevant repo patterns are named but never used in decisions or implementation units
|
||||
- Cited learnings or references do not materially shape the plan
|
||||
- High-risk work lacks appropriate external or internal grounding
|
||||
- Research is generic instead of tied to this repo or this plan
|
||||
|
||||
**Key Technical Decisions**
|
||||
- A decision is stated without rationale
|
||||
- Rationale does not explain tradeoffs or rejected alternatives
|
||||
- The decision does not connect back to scope, requirements, or origin context
|
||||
- An obvious design fork exists but the plan never addresses why one path won
|
||||
|
||||
**Open Questions**
|
||||
- Product blockers are hidden as assumptions
|
||||
- Planning-owned questions are incorrectly deferred to implementation
|
||||
- Resolved questions have no clear basis in repo context, research, or origin decisions
|
||||
- Deferred items are too vague to be useful later
|
||||
|
||||
**High-Level Technical Design (when present)**
|
||||
- The sketch uses the wrong medium for the work
|
||||
- The sketch contains implementation code rather than pseudo-code
|
||||
- The non-prescriptive framing is missing or weak
|
||||
- The sketch does not connect to the key technical decisions or implementation units
|
||||
|
||||
**High-Level Technical Design (when absent)** *(Standard or Deep plans only)*
|
||||
- The work involves DSL design, API surface design, multi-component integration, complex data flow, or state-heavy lifecycle
|
||||
- Key technical decisions would be easier to validate with a visual or pseudo-code representation
|
||||
- The approach section of implementation units is thin and a higher-level technical design would provide context
|
||||
|
||||
**Implementation Units**
|
||||
- Dependency order is unclear or likely wrong
|
||||
- File paths or test file paths are missing where they should be explicit
|
||||
- Units are too large, too vague, or broken into micro-steps
|
||||
- Approach notes are thin or do not name the pattern to follow
|
||||
- Test scenarios are vague (don't name inputs and expected outcomes), skip applicable categories (e.g., no error paths for a unit with failure modes, no integration scenarios for a unit crossing layers), or are disproportionate to the unit's complexity
|
||||
- Feature-bearing units have blank or missing test scenarios (feature-bearing units require actual test scenarios; the `Test expectation: none` annotation is only valid for non-feature-bearing units)
|
||||
- Verification outcomes are vague or not expressed as observable results
|
||||
- Existing U-IDs were renumbered after a unit was reordered, split, or deleted (U-IDs are stable: never renumber existing IDs; gaps from deletions are preserved; new units take the next unused number)
|
||||
- A unit realizing an origin Key Flow does not cite the F-ID, or a unit enforcing an origin Acceptance Example does not cite the AE-ID, when origin supplies them
|
||||
|
||||
**System-Wide Impact**
|
||||
- Affected interfaces, callbacks, middleware, entry points, or parity surfaces are missing
|
||||
- Failure propagation is underexplored
|
||||
- State lifecycle, caching, or data integrity risks are absent where relevant
|
||||
- Integration coverage is weak for cross-layer work
|
||||
|
||||
**Risks & Dependencies / Documentation / Operational Notes**
|
||||
- Risks are listed without mitigation
|
||||
- Rollout, monitoring, migration, or support implications are missing when warranted
|
||||
- External dependency assumptions are weak or unstated
|
||||
- Security, privacy, performance, or data risks are absent where they obviously apply
|
||||
|
||||
Use the plan's own `Context & Research` and `Sources & References` as evidence. If those sections cite a pattern, learning, or risk that never affects decisions, implementation units, or verification, treat that as a confidence gap.
|
||||
|
||||
## 5.3.4 Report and Dispatch Targeted Research
|
||||
|
||||
Before dispatching agents, report what sections are being strengthened and why:
|
||||
|
||||
```text
|
||||
Strengthening [section names] — [brief reason for each, e.g., "decision rationale is thin", "cross-boundary effects aren't mapped"]
|
||||
```
|
||||
|
||||
For each selected section, choose the smallest useful agent set. Do **not** run every agent. Use at most **1-3 agents per section** and usually no more than **8 agents total**.
|
||||
|
||||
Use fully-qualified agent names inside Task calls.
|
||||
|
||||
**Deterministic Section-to-Agent Mapping:**
|
||||
|
||||
**Requirements / Open Questions classification**
|
||||
- `ce-spec-flow-analyzer` for missing user flows, edge cases, and handoff gaps
|
||||
- `ce-repo-research-analyst` (Scope: `architecture, patterns`) for repo-grounded patterns, conventions, and implementation reality checks
|
||||
|
||||
**Context & Research / Sources & References gaps**
|
||||
- `ce-learnings-researcher` for institutional knowledge and past solved problems
|
||||
- `ce-framework-docs-researcher` for official framework or library behavior
|
||||
- `ce-best-practices-researcher` for current external patterns and industry guidance
|
||||
- `ce-web-researcher` for landscape/prior-art gaps — competitor patterns, market signals, or an unsettled external option set (which library/provider/approach) that recommendations depend on
|
||||
- Add `ce-git-history-analyzer` only when historical rationale or prior art is materially missing
|
||||
|
||||
**Key Technical Decisions**
|
||||
- `ce-architecture-strategist` for design integrity, boundaries, and architectural tradeoffs
|
||||
- Add `ce-framework-docs-researcher` or `ce-best-practices-researcher` when the decision needs external grounding beyond repo evidence
|
||||
|
||||
**High-Level Technical Design**
|
||||
- `ce-architecture-strategist` for validating that the technical design accurately represents the intended approach and identifying gaps
|
||||
- `ce-repo-research-analyst` (Scope: `architecture, patterns`) for grounding the technical design in existing repo patterns and conventions
|
||||
- Add `ce-best-practices-researcher` when the technical design involves a DSL, API surface, or pattern that benefits from external validation
|
||||
|
||||
**Implementation Units / Verification**
|
||||
- `ce-repo-research-analyst` (Scope: `patterns`) for concrete file targets, patterns to follow, and repo-specific sequencing clues
|
||||
- `ce-pattern-recognition-specialist` for consistency, duplication risks, and alignment with existing patterns
|
||||
- Add `ce-spec-flow-analyzer` when sequencing depends on user flow or handoff completeness
|
||||
|
||||
**System-Wide Impact**
|
||||
- `ce-architecture-strategist` for cross-boundary effects, interface surfaces, and architectural knock-on impact
|
||||
- Add the specific specialist that matches the risk:
|
||||
- `ce-performance-oracle` for scalability, latency, throughput, and resource-risk analysis
|
||||
- `ce-security-sentinel` for auth, validation, exploit surfaces, and security boundary review
|
||||
- `ce-data-integrity-guardian` for migrations, persistent state safety, consistency, and data lifecycle risks
|
||||
|
||||
**Risks & Dependencies / Operational Notes**
|
||||
- Use the specialist that matches the actual risk:
|
||||
- `ce-security-sentinel` for security, auth, privacy, and exploit risk
|
||||
- `ce-data-integrity-guardian` for migrations, backfills, persistent data safety, constraints, transaction boundaries, and production data transformation risk (plan context — not the PR-review `ce-data-migration-reviewer` persona)
|
||||
- `ce-deployment-verification-agent` for rollout checklists, rollback planning, and launch verification
|
||||
- `ce-performance-oracle` for capacity, latency, and scaling concerns
|
||||
|
||||
**Agent Prompt Shape:**
|
||||
|
||||
For each selected section, pass:
|
||||
- The scope prefix from the mapping above when the agent supports scoped invocation
|
||||
- A short plan summary
|
||||
- The exact section text
|
||||
- Why the section was selected, including which checklist triggers fired
|
||||
- The plan depth and risk profile
|
||||
- A specific question to answer
|
||||
|
||||
Instruct the agent to return:
|
||||
- findings that change planning quality
|
||||
- stronger rationale, sequencing, verification, risk treatment, or references
|
||||
- no implementation code
|
||||
- no shell commands
|
||||
|
||||
## 5.3.5 Choose Research Execution Mode
|
||||
|
||||
Use the lightest mode that will work:
|
||||
|
||||
- **Direct mode** - Default. Use when the selected section set is small and the parent can safely read the agent outputs inline.
|
||||
- **Artifact-backed mode** - Use only when the selected research scope is large enough that inline returns would create unnecessary context pressure.
|
||||
|
||||
Signals that justify artifact-backed mode:
|
||||
- More than 5 agents are likely to return meaningful findings
|
||||
- The selected section excerpts are long enough that repeating them in multiple agent outputs would be wasteful
|
||||
- The topic is high-risk and likely to attract bulky source-backed analysis
|
||||
|
||||
If artifact-backed mode is not clearly warranted, stay in direct mode.
|
||||
|
||||
Artifact-backed mode uses a per-run OS-temp scratch directory. Create it once before dispatching sub-agents and capture its **absolute path** — pass that absolute path to each sub-agent so they write to it directly. Do not use `.context/`; the artifacts are per-run throwaway that are cleaned up when deepening ends (see 5.3.6b), matching the repo Scratch Space convention for one-shot artifacts. Do not pass unresolved shell-variable strings to sub-agents; they need the resolved absolute path.
|
||||
|
||||
```bash
|
||||
SCRATCH_DIR="$(mktemp -d -t ce-plan-deepen-XXXXXX)"
|
||||
echo "$SCRATCH_DIR"
|
||||
```
|
||||
|
||||
Refer to the echoed absolute path as `<scratch-dir>` throughout the rest of this workflow.
|
||||
|
||||
## 5.3.6 Run Targeted Research
|
||||
|
||||
Launch the selected agents in parallel using the execution mode chosen above. If the current platform does not support parallel dispatch, run them sequentially instead. Omit the `mode` parameter when dispatching so the user's configured permission settings apply.
|
||||
|
||||
Prefer local repo and institutional evidence first. Use external research only when the gap cannot be closed responsibly from repo context or already-cited sources.
|
||||
|
||||
If a selected section can be improved by reading the origin document more carefully, do that before dispatching external agents.
|
||||
|
||||
**Direct mode:** Have each selected agent return its findings directly to the parent. Keep the return payload focused: strongest findings only, the evidence or sources that matter, the concrete planning improvement implied by the finding.
|
||||
|
||||
**Artifact-backed mode:** For each selected agent, pass the absolute `<scratch-dir>` path captured earlier and instruct the agent to write one compact artifact file inside that directory, then return only a short completion summary. Each artifact should contain: target section, why selected, 3-7 findings, source-backed rationale, the specific plan change implied by each finding. No implementation code, no shell commands.
|
||||
|
||||
If an artifact is missing or clearly malformed, re-run that agent or fall back to direct-mode reasoning for that section.
|
||||
|
||||
If agent outputs conflict:
|
||||
- Prefer repo-grounded and origin-grounded evidence over generic advice
|
||||
- Prefer official framework documentation over secondary best-practice summaries when the conflict is about library behavior
|
||||
- If a real tradeoff remains, record it explicitly in the plan
|
||||
|
||||
## 5.3.6b Interactive Finding Review (Interactive Mode Only)
|
||||
|
||||
Skip this step in auto mode — proceed directly to 5.3.7.
|
||||
|
||||
In interactive mode, present each agent's findings to the user before integration. For each agent that returned findings:
|
||||
|
||||
1. **Summarize the agent and its target section** — e.g., "The ce-architecture-strategist reviewed Key Technical Decisions and found:"
|
||||
2. **Present the findings concisely** — bullet the key points, not the raw agent output. Include enough context for the user to evaluate: what the agent found, what evidence supports it, and what plan change it implies.
|
||||
3. **Ask the user** using the platform's blocking question tool when available (see Interaction Method):
|
||||
- **Accept** — integrate these findings into the plan
|
||||
- **Reject** — discard these findings entirely
|
||||
- **Discuss** — the user wants to talk through the findings before deciding
|
||||
|
||||
If the user chooses "Discuss", engage in brief dialogue about the findings and then re-ask with only accept/reject (no discuss option on the second ask). The user makes a deliberate choice either way.
|
||||
|
||||
When presenting findings from multiple agents targeting the same section, present them one agent at a time so the user can make independent decisions. Do not merge findings from different agents before showing them.
|
||||
|
||||
After all agents have been reviewed, carry only the accepted findings forward to 5.3.7.
|
||||
|
||||
If the user accepted no findings, report "No findings accepted — plan unchanged." Then proceed directly to Phase 5.4 (skip document-review and synthesis — the plan was not modified). This interactive-mode-only skip does not apply in auto mode; auto mode always proceeds through 5.3.7 and 5.3.8. No explicit scratch cleanup needed — `$SCRATCH_DIR` is OS temp and will be cleaned up by the OS; leaving it in place preserves the rejected agent artifacts for debugging.
|
||||
|
||||
If findings were accepted and the plan was modified, proceed through 5.3.7 and 5.3.8 as normal — document-review acts as a quality gate on the changes.
|
||||
|
||||
## 5.3.7 Synthesize and Update the Plan
|
||||
|
||||
Strengthen only the selected sections. Keep the plan coherent and preserve its overall structure.
|
||||
|
||||
**In interactive mode:** Only integrate findings the user accepted in 5.3.6b. If some findings from different agents touch the same section, reconcile them coherently but do not reintroduce rejected findings.
|
||||
|
||||
Allowed changes:
|
||||
- Clarify or strengthen decision rationale
|
||||
- Tighten requirements trace or origin fidelity
|
||||
- Reorder or split implementation units when sequencing is weak — but **never renumber existing U-IDs**. Reordering preserves U-IDs in their new order (e.g., U1, U3, U5 reordered is correct; renumbering to U1, U2, U3 is not). Splitting keeps the original U-ID on the original concept and assigns the next unused number to the new unit. Renumbering breaks ce-work blocker and verification references that were written against the original IDs
|
||||
- Add missing pattern references, file/test paths, or verification outcomes
|
||||
- Expand system-wide impact, risks, or rollout treatment where justified
|
||||
- Reclassify open questions between `Resolved During Planning` and `Deferred to Implementation` when evidence supports the change
|
||||
- Strengthen, replace, or add a High-Level Technical Design section when the work warrants it and the current representation is weak
|
||||
- Strengthen or add per-unit technical design fields where the unit's approach is non-obvious
|
||||
- Add or update `deepened: YYYY-MM-DD` in frontmatter when the plan was substantively improved
|
||||
|
||||
Do **not**:
|
||||
- Add implementation code — no imports, exact method signatures, or framework-specific syntax. Pseudo-code sketches and DSL grammars are allowed
|
||||
- Add git commands, commit choreography, or exact test command recipes
|
||||
- Add generic `Research Insights` subsections everywhere
|
||||
- Rewrite the entire plan from scratch
|
||||
- Invent new product requirements, scope changes, or success criteria without surfacing them explicitly
|
||||
- Renumber existing U-IDs as part of reordering, splitting, deletion, or "tidying" the unit list. Deepening is the most likely accidental-renumber vector — preserve U-IDs even when the new order would look cleaner with sequential numbering
|
||||
|
||||
If research reveals a product-level ambiguity that should change behavior or scope:
|
||||
- Do not silently decide it here
|
||||
- Record it under `Open Questions`
|
||||
- Recommend `ce-brainstorm` if the gap is truly product-defining
|
||||
@@ -0,0 +1,538 @@
|
||||
# HTML Rendering
|
||||
|
||||
This is a format-rendering reference — it describes how to render any
|
||||
artifact in HTML, independent of which skill is producing it.
|
||||
|
||||
It is paired with a section contract (`plan-sections.md`,
|
||||
`brainstorm-sections.md`, etc.) that describes *what* the artifact contains.
|
||||
This reference describes *how* HTML specifically presents it. The same
|
||||
content rendered by different skills shares the same HTML principles.
|
||||
|
||||
The HTML artifact is the *only* artifact the skill produces for that run —
|
||||
output mode is exclusive (markdown OR HTML, never both). Downstream
|
||||
consumers that read HTML today (`ce-work`, human readers) do so directly;
|
||||
the agent-consumability rules below make that work. `ce-doc-review` is
|
||||
*not* currently an HTML consumer — its mutation mechanics are markdown-only,
|
||||
so the ce-plan handoff gates the 5.3.8 doc-review pass to `OUTPUT_FORMAT=md`
|
||||
runs and skips it for HTML.
|
||||
|
||||
## Hard invariants
|
||||
|
||||
These hold regardless of which skill produced the artifact.
|
||||
|
||||
- **Single self-contained HTML5 file.** No companion `.css`, `.js`, or
|
||||
`.svg` files. CSS lives in `<style>`. SVG lives inline. Images are
|
||||
base64 data URIs or inline SVG. The one permitted exception is a
|
||||
`<link rel="stylesheet">` to a CDN webfont CSS endpoint (Google Fonts,
|
||||
Bunny Fonts, etc.), paired with an offline-readable fallback font stack
|
||||
so the doc remains readable if the CDN is unreachable.
|
||||
- **All metadata appears as visible text — single source of truth.**
|
||||
The artifact's metadata (title, type, status, date, etc. — exact
|
||||
fields per-skill, defined in the section contract) renders as visible
|
||||
HTML elements that downstream agents and humans read. No hidden
|
||||
machine-readable copy in any form: no `<script type="application/json">`
|
||||
frontmatter block, no `data-*` attribute mirror, and no
|
||||
`<meta name="status">` / `<meta name="created">` / `<meta name="origin">`
|
||||
in `<head>` duplicating the same values that appear in the visible
|
||||
header. One representation for each value — drift across two copies is
|
||||
the failure this rule prevents.
|
||||
|
||||
The text-and-attribute redundancy in `<time datetime="2026-05-12">2026-05-12</time>`
|
||||
is acceptable because the attribute is a parser hint, not a hidden copy.
|
||||
- **Editable status renders as `<span class="status">{value}</span>`.**
|
||||
Downstream tooling (`ce-work` shipping flip, future HTML-aware
|
||||
consumers) finds and rewrites status by selector. Embedding the
|
||||
status value inside a header `<dl>` cell (`<dt>Status</dt><dd>active</dd>`),
|
||||
inside a `<meta>` tag, or as visible text without the `class="status"`
|
||||
hook all break the flip mechanic — the consumer either can't locate
|
||||
the value or can't disambiguate it from prose. The status span may
|
||||
sit anywhere in the doc (inside the header metadata, in a stats
|
||||
strip, in a hero banner); placement is a visual choice, the selector
|
||||
shape is the contract.
|
||||
- **Stable IDs as anchor IDs AND visible text.** Every ID-bearing item
|
||||
(R-IDs, U-IDs, A-IDs, F-IDs, AE-IDs, KTDs) gets `id="r1"` on its
|
||||
element AND appears as visible text inside the element (e.g., the
|
||||
text "R1." inside the table cell or heading). Downstream agents find
|
||||
the ID in source the same way they find it in markdown.
|
||||
- **Source / composition signal.** A visible footer at the bottom of
|
||||
the doc names the composition timestamp and the source identifier
|
||||
(the user prompt context, the upstream brainstorm doc when one
|
||||
exists, or just the composing skill name when there's no external
|
||||
source). Example shape:
|
||||
`<footer class="composition-signal">Composed 2026-05-17T14:23Z by ce-plan from <code>docs/brainstorms/...-requirements.md</code></footer>`.
|
||||
Under exclusive output mode this signal is the artifact's own
|
||||
provenance — there's no markdown sibling to reference. Omitting it
|
||||
leaves readers unable to tell how stale the rendering is.
|
||||
- **ASCII identifiers.** Class names, element IDs, data attribute names
|
||||
are ASCII-only.
|
||||
|
||||
## Precedence stack for style preferences
|
||||
|
||||
Honor user style preferences in this order (highest to lowest):
|
||||
|
||||
1. **In-session conversation** — explicit direction the user gave this run.
|
||||
2. **Preferred stylesheet reference** named in loaded agent-instruction
|
||||
context (typically `AGENTS.md` / `CLAUDE.md`, but scan loaded context;
|
||||
don't enumerate locations). The reference may be a file path
|
||||
(`docs/style.css`), a URL, a named library ("Tailwind"), or a style
|
||||
brand ("Stripe docs"). Agent-instruction files carry deliberate
|
||||
agent-aware preferences, so this tier sits above DESIGN.md.
|
||||
3. **DESIGN.md** discovered on the filesystem (see "DESIGN.md discovery"
|
||||
below).
|
||||
4. **Fallback default** — the opinionated palette / typography choices the
|
||||
agent makes when no preference exists.
|
||||
|
||||
### Active-recall at compose time
|
||||
|
||||
Before writing the CSS, scan loaded context for any stylesheet reference
|
||||
the user has indicated for documents like this. If found and inlinable
|
||||
(short local file, fetchable URL within budget), inline it into `<style>`.
|
||||
If found but not inlinable (large framework, paywalled stylesheet, named
|
||||
system without a fetchable source), compose CSS in its spirit — typography,
|
||||
color, density cues drawn from the named system. Only fall back to the
|
||||
default style when no preference signal exists.
|
||||
|
||||
The single-file invariant is preserved either way. External
|
||||
`<link rel="stylesheet">` is permitted only for CDN webfont CSS (with the
|
||||
offline fallback font stack); never link to an external stylesheet
|
||||
carrying layout, color, or typography rules the doc cannot read offline.
|
||||
|
||||
### DESIGN.md discovery
|
||||
|
||||
When tier 3 of the precedence stack applies, look for a DESIGN.md file in
|
||||
these locations, first match wins:
|
||||
|
||||
1. Worktree root (resolve via `git rev-parse --show-toplevel`).
|
||||
2. `docs/DESIGN.md`.
|
||||
3. `.compound-engineering/DESIGN.md`.
|
||||
|
||||
Read once at compose time. Absent → fall through to the fallback default.
|
||||
|
||||
Worktree-root only — do not fall through to a main checkout. Users
|
||||
working from a worktree who want HTML defaults can add DESIGN.md to the
|
||||
worktree.
|
||||
|
||||
**DESIGN.md is a partial override, not all-or-nothing.** Real
|
||||
DESIGN.md files vary widely: some are token tables, some are CSS
|
||||
variables, some are prose; most cover a subset of what HTML composition
|
||||
needs. Apply the tokens that fit a long-form text doc — typography roles,
|
||||
text colors, contrast targets, border-radius scale, elevation primitives,
|
||||
muted-vs-accent split. Skip the rest. Three specific failure modes to
|
||||
defend against:
|
||||
|
||||
- **Scope mismatch (product UI vs doc surface).** A DESIGN.md aimed at
|
||||
product marketing or app UI may name page-surface colors, button
|
||||
states, input borders, or hero backgrounds that are tied to *that*
|
||||
surface, not to a generic doc. Page-surface colors are the canonical
|
||||
trap — `--surface: #c0f0fb` belongs on the product's marketing page,
|
||||
not on every plan or requirements doc the team writes. Extract the
|
||||
principle (the design language uses a tinted surface) rather than the
|
||||
literal value when the token is product-UI-scoped. Apply literal
|
||||
values only when the token is generic enough to transfer (text color,
|
||||
type scale ratio, radius scale, contrast ratio).
|
||||
- **Partial coverage.** When DESIGN.md defines some categories but not
|
||||
others (e.g., colors but no spacing scale, typography but no
|
||||
elevation), use DESIGN.md for what it covers and the fallback default
|
||||
for what it doesn't. Do not require DESIGN.md to be complete before
|
||||
honoring it.
|
||||
- **Named font without a fetchable source.** When DESIGN.md names a
|
||||
font (e.g., "Signifier", "Every") without a CDN URL or local
|
||||
`@font-face` source the agent can inline, treat the name as a hint
|
||||
about the design intent, not a literal directive. Emit a system-font
|
||||
stack in the same family (serif vs sans vs mono) and pick a weight
|
||||
that matches the intent. The single-file invariant still holds; do
|
||||
not link to an external stylesheet to fetch the named font.
|
||||
- **Typography-scale mismatch.** DESIGN.md typography tokens are often
|
||||
sized for product UI — marketing pages, app screens, hero sections —
|
||||
with body text at 18-20px and headings at 32-52px. A long-form doc
|
||||
surface needs body at ~14-16px and headings at ~1.2-1.6× body. When
|
||||
the DESIGN.md size scale looks product-scaled, use the **family**,
|
||||
**weight**, and **OpenType feature** assignments (these carry the
|
||||
design language) and pick the agent's own **size scale** for the doc
|
||||
surface. Apply DESIGN.md sizes literally only when the tokens are
|
||||
clearly doc-scaled — body tokens at 14-16px, headings under ~32px.
|
||||
|
||||
## Format principles
|
||||
|
||||
These shape what "good" HTML looks like; the agent applies them per
|
||||
artifact based on content.
|
||||
|
||||
### Readable measure, not full bleed
|
||||
|
||||
Long-form text is unreadable at full viewport width — past ~80 characters
|
||||
per line the eye loses the return sweep and scanning slows. As a
|
||||
fallback-default (precedence tier 4, overridden by in-session direction or
|
||||
DESIGN.md), center the document in a content container and hold prose to a
|
||||
comfortable measure.
|
||||
|
||||
- **Page container.** A centered column with a max-width in the ~820-960px
|
||||
band (`margin-inline: auto`) keeps the doc off the far edges of wide
|
||||
monitors while leaving room for the format's richer shapes.
|
||||
- **Prose measure.** Hold running paragraphs to roughly 65-80 characters
|
||||
(`max-width: ~70ch` on text blocks). The named test: read a paragraph at
|
||||
full window width on a wide display — if the return sweep to the next
|
||||
line is effortful, the measure is too wide.
|
||||
- **Let wide content break out.** Tables, diagrams, and side-by-side
|
||||
columns may use the full container width (or wider) when the content
|
||||
needs it — the measure constraint is for prose, not for everything.
|
||||
|
||||
Express the constraint in `ch`/`rem` rather than a single hardcoded pixel
|
||||
value so it survives font-size and DESIGN.md overrides. DESIGN.md or an
|
||||
in-session instruction overrides these values; this is the fallback when no
|
||||
layout preference exists.
|
||||
|
||||
### Markdown source is content, not design
|
||||
|
||||
When markdown (or markdown-shaped chat context) is part of the input, use
|
||||
it for semantic content — what the doc is about, what sections exist,
|
||||
what facts each section establishes. Do NOT treat its bullet-vs-table
|
||||
presentation choices as authoritative; re-choose the rendering per
|
||||
content shape in HTML's richer affordance space. If the markdown rendered
|
||||
13 requirements as a bulleted list, that does NOT mean HTML must render
|
||||
them as a list — ask whether 13 items sharing `ID + body` shape deserve
|
||||
a table.
|
||||
|
||||
### Prose is authoritative
|
||||
|
||||
When a visualization disagrees with the surrounding prose, the prose
|
||||
governs. If they diverge, the visualization is wrong.
|
||||
|
||||
### Hyperlink the reference index
|
||||
|
||||
When the doc has a Sources & References (or equivalent reference-index)
|
||||
section, hyperlink each entry to its canonical destination so readers
|
||||
can open it directly. A long bare-text list of paths and ticket IDs is
|
||||
the format's biggest unforced UX miss — the reader has to copy-paste
|
||||
every entry into a browser or IDE.
|
||||
|
||||
Resolve the repo's GitHub URL once at compose time:
|
||||
|
||||
```bash
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
Apply linking to three reference shapes:
|
||||
|
||||
- **Repo-relative code/doc paths** (`services/foo.ts`,
|
||||
`docs/solutions/bar.md`) → `<repo-url>/blob/main/<path>`.
|
||||
- **Named GitHub PRs/issues** (`PR #636`, `issue #1048`) →
|
||||
`<repo-url>/pull/636` or `<repo-url>/issues/1048`.
|
||||
- **Named external trackers** (Linear `ESP-1705`, Jira `PROJ-123`) →
|
||||
link only when the workspace URL is established in loaded context
|
||||
(e.g., a `linear.app/<workspace>/...` URL appeared earlier in the
|
||||
session or in `AGENTS.md`); otherwise leave as text.
|
||||
|
||||
**Do not invent URLs.** If `origin` isn't a GitHub URL (GitLab,
|
||||
Bitbucket, internal host) and the equivalent main-tree URL pattern
|
||||
isn't obvious, leave entries as `<code>` text. If the external
|
||||
tracker workspace isn't established, leave as text. A broken or
|
||||
guessed link is worse than no link.
|
||||
|
||||
**Scope: reference index only, not inline prose.** Inline `<code>`
|
||||
mentions of paths or PRs inside paragraph prose stay as code or text.
|
||||
Linking every mention would clutter; readers expect clickable jumps
|
||||
where the doc presents itself as a reference index.
|
||||
|
||||
### Text contrast is local
|
||||
|
||||
Every text-on-background pairing must hold up on its own. A color that
|
||||
works for prose on the page background does not automatically work for
|
||||
a small label inside a tinted container. The most common violation:
|
||||
applying a generic "muted" text variable (calibrated for prose-on-bg) to
|
||||
secondary text inside an accent-soft / warn-soft / info-soft container.
|
||||
|
||||
Test by reading each filled shape's labels at the rendered scale. If the
|
||||
subtitle or secondary text feels washed-out against the fill, the choice
|
||||
is wrong for that local context — pick a color from the same family as
|
||||
the fill (accent-text for accent-soft, etc.) or drop the muting entirely
|
||||
and rely on font-size and weight for hierarchy.
|
||||
|
||||
### Body bold not colored by default
|
||||
|
||||
Reserve accent text color for status chips, ID chips, links, and section
|
||||
borders. Do NOT color `<strong>` in body content by default. Bold weight
|
||||
already carries emphasis; applying accent color to every `<strong>` in a
|
||||
long list overwhelms the eye, especially in dark mode. CSS should leave
|
||||
`strong` at `color: inherit` unless a specific surface (status pill, ID
|
||||
chip) is being styled.
|
||||
|
||||
### No JS framework runtimes
|
||||
|
||||
A small inline `<script>` for active-section TOC tracking or anchor-
|
||||
permalink behavior is acceptable. React, Vue, Svelte, or any framework
|
||||
runtime is not. The single-file invariant doesn't permit framework
|
||||
bundles, and the artifact's longevity doesn't warrant a build dependency.
|
||||
|
||||
## Section anatomy
|
||||
|
||||
How section types commonly render in HTML. These are patterns, not
|
||||
contracts — the agent picks shapes that fit the content.
|
||||
|
||||
- **Summary / Problem Frame** — semantic `<section>` with prose
|
||||
paragraphs. Optionally precede with an eyebrow label (small-caps tag
|
||||
above the title) for editorial polish.
|
||||
- **Requirements** — `<table>` is the default at 5+ uniform items;
|
||||
bullets at smaller counts. Concern-grouping takes precedence over the
|
||||
flat-table default: when requirements span distinct concerns, group them
|
||||
under bold inline headers (or per-group sections) first, then apply the
|
||||
5+ table default *within* each group rather than flattening the whole
|
||||
section into one table. Each row has the R-ID as visible text in
|
||||
its own column. Consider adding a "covered by" column for reverse
|
||||
traceability when ID-anchored items have downstream references in
|
||||
the same doc.
|
||||
- **Implementation Units** — repeating `<article>` cards with a stable
|
||||
ID chip (visible "U1" text), a metadata strip (`<dl>` with field
|
||||
labels and values for Goal, Files, Dependencies), and secondary
|
||||
content (Approach, Test Scenarios, Verification, Patterns to Follow)
|
||||
inside `<details>` collapsibles, **default-closed**. At 3+ units the
|
||||
default-closed rule is load-bearing — rendering all units fully
|
||||
expanded turns the doc into one continuous scroll where the reader
|
||||
can't see the unit list at a glance. The metadata strip is the
|
||||
primary always-visible surface; subsection labels (`<summary>`) are
|
||||
clickable affordances for readers to expand on demand. A single unit
|
||||
with no secondary content can skip `<details>` entirely; the rule
|
||||
fires when content exists to hide. The `<dl>` strip is for *descriptive*
|
||||
fields (Goal, Files, Dependencies). A *directive* field — `Execution
|
||||
note` is the canonical case, carrying a procedural instruction the
|
||||
implementer must act on (e.g. "start with a failing integration test") —
|
||||
does not belong in the strip, where it renders as a passive pair styled
|
||||
like a date and gets skimmed past. Render it as an advisory callout (see
|
||||
Tinted callout cards) so its visual weight matches its actionability. The
|
||||
test: descriptive value -> metadata pair; something the reader must act
|
||||
on -> callout.
|
||||
- **Key Technical Decisions** — repeating cards with the decision ID,
|
||||
bold decision title (often with inline code for technical
|
||||
identifiers), and prose rationale. Flat cards (not collapsibles) —
|
||||
these are reference material readers scan, not drill into.
|
||||
- **Risks** — color-coded cards with status eyebrow (e.g., "RISK ·
|
||||
MITIGATED" / "OPEN · DEFERRED FOLLOW-UP") and prose body. Color of
|
||||
the left-border or accent communicates status at a glance.
|
||||
- **Scope Boundaries** — callout cards with color-coded left borders
|
||||
(in-scope vs deferred vs outside) when the distinction is meaningful.
|
||||
|
||||
The agent picks more elaborate or simpler shapes based on what each
|
||||
specific artifact's content needs.
|
||||
|
||||
## Diagrams
|
||||
|
||||
When the section contract calls for a diagram (architecture, sequence,
|
||||
flowchart, state machine, swim lane, data-flow, quantitative
|
||||
comparison), HTML renders it as **inline SVG**. The agent picks the
|
||||
shape that conveys the content fastest — there is no fixed catalog of
|
||||
"approved" diagram types. If the content is quantitative comparison
|
||||
across categories, a bar chart is the right shape; if it's component
|
||||
relationships, a topology diagram; if it's process flow across
|
||||
participants, a swim lane; etc.
|
||||
|
||||
**Conceptual diagrams are not wireframes.** The wireframe affordance below
|
||||
is scoped to brainstorm requirements docs about *visual products* and is
|
||||
excluded for non-visual systems. That exclusion is about wireframes only —
|
||||
a brainstorm about a data model, schema, agent workflow, or migration is
|
||||
still free to use a conceptual diagram (a before/after field map, a
|
||||
source-of-truth fan-out, a state diagram). Don't let the wireframe
|
||||
exclusion suppress a conceptual diagram the content warrants.
|
||||
|
||||
**Diagrams complement prose; they never replace it.** A diagram is an
|
||||
accelerant placed next to the prose it illustrates, not a substitute. The
|
||||
IDed prose stays complete and standalone — a reader who ignores every
|
||||
diagram still gets the full content in text, and a text-reading downstream
|
||||
agent (which does not parse SVG geometry) is never left with a relationship
|
||||
that exists only in the picture. This extends the prose-is-authoritative
|
||||
rule above: prose governs not only on disagreement but on completeness, so
|
||||
adding a diagram is not license to thin the prose it depicts.
|
||||
|
||||
### Layout legibility for hand-authored SVG
|
||||
|
||||
The agent designs SVG coordinates without rendering — layouts that look
|
||||
fine in source can collide in practice. Before emitting, trace each
|
||||
labeled arrow and each text label:
|
||||
|
||||
- **No arrow path passes through a text label.** If an arrow line or
|
||||
curve crosses a label's bounding box, the text reads as struck-through
|
||||
and the arrow reads as terminating at the wrong element. Fix by
|
||||
re-routing the arrow, moving the label, or applying
|
||||
`paint-order: stroke fill` with a stroke color matching the diagram
|
||||
background to halo the label. The halo width is a judgment call:
|
||||
narrow enough not to bleed into glyph strokes (a halo whose width
|
||||
approaches the glyph's own stroke width muddies the text color), wide
|
||||
enough to mask underlying arrows (at least the arrow's stroke width
|
||||
plus a hairline). Verify by inspecting rendered text at the target
|
||||
font size — if glyphs look thicker or more colored-toward-halo than
|
||||
the same text outside the diagram, the halo is too wide.
|
||||
- **Arrow labels sit adjacent to the arrow's midpoint** (typically
|
||||
within ~10-15px above or beside the line they describe). A label
|
||||
floating at the diagram's edge that readers have to trace back to an
|
||||
arrow is broken — readers will misread.
|
||||
- **Avoid long curves that traverse the diagram** to connect a
|
||||
component on one side to one on the other. If A and D need a labeled
|
||||
connection across a multi-component layout, prefer reordering boxes
|
||||
so A and D are adjacent, numbered step badges next to each
|
||||
participant that the caption ties together, or a short
|
||||
labeled-channel notation — rather than one curve crossing multiple
|
||||
unrelated elements.
|
||||
- **Differentiate diagram shapes by geometry first, by fill semantics
|
||||
second.** Geometry (diamond = decision, rect = step, oval =
|
||||
start/end, parallelogram = data) carries the role unambiguously.
|
||||
Fill semantics (accent-soft for highlighted path, warn-soft for
|
||||
fallthrough) carry meaning. Resist introducing additional neutral-tint
|
||||
tiers (a slightly-lighter grey to mark "decision shapes are different
|
||||
from boxes") — when geometry already differentiates, an additional
|
||||
luminance tier adds no information and creates fragility: small RGB
|
||||
deltas survive native browser rendering but can be flattened or
|
||||
inverted inconsistently by dark-mode extensions, accessibility
|
||||
plugins, or printing.
|
||||
|
||||
### Plan architecture diagrams are not directional sketches
|
||||
|
||||
Do not add hedging captions or section preambles to plan SVG diagrams —
|
||||
phrases like "directional guidance for review, not implementation
|
||||
specification" do not belong on plan diagrams or on unit-card
|
||||
technical-design subsections. Plan diagrams render the same authoritative
|
||||
content as the surrounding prose; the prose-is-authoritative rule
|
||||
already governs disagreement. Hedging language is reserved for the
|
||||
wireframe affordance below, which carries a *required* directional
|
||||
caption because the wireframe is explicitly NOT a spec.
|
||||
|
||||
## Wireframe mockups (requirements docs only)
|
||||
|
||||
When a brainstorm requirements document describes a user-facing visual
|
||||
surface (UI feature, screen layout, screen flow, component placement),
|
||||
the HTML rendering may include a wireframe mockup. This affordance applies
|
||||
ONLY to brainstorm requirements docs that describe visual products — not
|
||||
to plan artifacts, and not to brainstorms about non-visual systems (API
|
||||
design, agent workflows, infrastructure).
|
||||
|
||||
When a wireframe is included:
|
||||
|
||||
- **Fidelity ceiling: wireframe, not mockup.** Gray boxes for layout
|
||||
regions, text labels for content placeholders, intentional placeholder
|
||||
copy (`[Product name]`, `[CTA label]`, `[user avatar]`). No
|
||||
pixel-perfect colors, no exact typography choices, no specific
|
||||
component-library references. The wireframe communicates spatial
|
||||
arrangement and structure, not visual style.
|
||||
- **Static only.** Inline SVG or simple HTML/CSS for layout. No JS
|
||||
interaction, no working form fields, no state changes, no live data.
|
||||
- **Anti-padding.** One wireframe per distinct visual concept.
|
||||
- **Mandatory directional caption.** Every wireframe carries an explicit
|
||||
"directional, not the spec" note adjacent to it. Required wording (or
|
||||
close paraphrase): *"Directional only — illustrates the intended
|
||||
user-facing shape. Exact colors, spacing, copy, and component choices
|
||||
are placeholders for review, not requirements."*
|
||||
|
||||
Without this caption the wireframe risks being read as a binding visual
|
||||
spec, which the affordance is explicitly designed to avoid.
|
||||
|
||||
## Affordance idioms
|
||||
|
||||
Common HTML affordances the agent can reach for when content benefits.
|
||||
These are examples, not requirements — the agent picks what each
|
||||
artifact's content warrants. Other affordances not listed here are
|
||||
fine when the content suggests them.
|
||||
|
||||
- **Sticky TOC sidebar with active-section indicator** — available when
|
||||
the agent judges navigation will materially help and the
|
||||
implementation is reliable: two-column layout on desktop, collapsed
|
||||
to top-of-page on mobile, paired with a small inline
|
||||
`IntersectionObserver` script that toggles `.active` on the matching
|
||||
nav anchor. Trade-off: a broken sticky TOC (layout collisions,
|
||||
active-section state drift, dark-mode CSS issues) is worse than a
|
||||
static top-of-doc TOC. For most long docs, default-closed `<details>`
|
||||
on repeating cards (see Implementation Units anatomy) already cuts
|
||||
the visible scroll length enough that a static TOC works — reach for
|
||||
sticky only when collapsibles alone don't solve the navigation
|
||||
problem.
|
||||
- **Within-section sub-nav** for sections containing 6+ repeating cards
|
||||
(Implementation Units, KTDs, Risks at large counts). A short list of
|
||||
card-anchor links (`<ul>` of `<a href="#u1">U1. ...</a>`) rendered at
|
||||
the top of the section gives readers a jump table — no JS needed.
|
||||
Lower-complexity alternative to the sticky TOC for the specific case
|
||||
of long card sections.
|
||||
- **Eyebrow labels** (small-caps tag above section titles) for
|
||||
editorial polish, especially when section titles are narrative
|
||||
rather than literal.
|
||||
- **Stats strip** at the top of the doc when the artifact has 3+
|
||||
quantifiable signals worth surfacing at a glance.
|
||||
- **`<details>` + `<summary>`** for collapsible secondary content
|
||||
inside repeating cards. All collapsibles start closed — `open`
|
||||
attribute should not appear on any `<details>` inside repeating
|
||||
cards by default.
|
||||
- **Side-by-side columns** for parallel content (Request / Response,
|
||||
Before / After, Two alternatives).
|
||||
- **Tinted callout cards** for content that is "different in kind"
|
||||
(Deferred, Open Questions, advisory notes, unit-level execution notes)
|
||||
— color-coded left borders communicate kind at a glance.
|
||||
|
||||
## Agent-consumability rules
|
||||
|
||||
Downstream agents that read HTML today (`ce-work`, future consumers) read
|
||||
the HTML file as text linearly, not via DOM extraction. `ce-doc-review` is
|
||||
not a current HTML consumer (see opening note). Compose so semantic
|
||||
understanding is reachable in source:
|
||||
|
||||
- **Use semantic HTML over `<div>` soup.** `<article>` per unit card,
|
||||
`<dl>` for metadata pairs, `<table>` for tabular content, `<details>`
|
||||
/ `<summary>` for collapsibles, `<section>` for top-level doc
|
||||
sections. Structure markers carry meaning to a text-reading agent.
|
||||
- **Render field labels as visible text, not as attributes.** Emit
|
||||
`<dt>GOAL</dt><dd>...</dd>`, not `<dd data-field="goal">...</dd>`.
|
||||
The label is the semantic anchor.
|
||||
- **Keep U-IDs, R-IDs, and similar as visible text** in headings and
|
||||
table cells, not only as `id=""` attributes. The agent finds "U1." in
|
||||
source the same way it finds "U1." in markdown.
|
||||
- **Match section heading vocabulary to what the section contract
|
||||
defines.** When the section contract says "Implementation Units," the
|
||||
HTML heading is "Implementation Units" — not "How we'll build it,"
|
||||
even if the narrative version reads better. Section heading
|
||||
vocabulary is the contract downstream consumers grep for. (Editorial
|
||||
re-titles can appear as eyebrow labels, sub-headings, or visual
|
||||
framing — but the load-bearing section heading matches the contract
|
||||
name.)
|
||||
- **All semantic content lives in actual HTML text.** No CSS `::before
|
||||
{ content: "..." }` carrying meaning, no background images as
|
||||
content, no semantic info that only renders. Whatever the agent sees
|
||||
in source is what it knows.
|
||||
- **Stable structure is the public API.** Element types, the ID and
|
||||
label scheme, and the field-label vocabulary do not break across
|
||||
versions. Visual styling can change freely.
|
||||
|
||||
## Post-compose audit
|
||||
|
||||
Before returning the artifact, scan it for common slips:
|
||||
|
||||
- **Single self-contained file.** No companion `.css` / `.js` / `.svg`.
|
||||
- **No hidden machine-readable metadata copy.** No
|
||||
`<script type="application/json">` frontmatter block, no `data-*`
|
||||
attributes mirroring visible values, **no `<meta name="status">` /
|
||||
`<meta name="created">` / `<meta name="origin">` etc. in `<head>`
|
||||
duplicating the visible header**. Metadata lives in visible text;
|
||||
one source of truth per value.
|
||||
- **Status renders as `<span class="status">{value}</span>`** so
|
||||
downstream tooling can flip `active → completed` by selector.
|
||||
- **All stable IDs** appear as both `id=""` and visible text.
|
||||
- **Section heading vocabulary** matches the section contract names
|
||||
(downstream agents grep these).
|
||||
- **Source / composition signal** is present as a visible footer at
|
||||
the bottom of the doc (composition timestamp + source identifier).
|
||||
- **Repeating cards with 3+ instances put secondary content inside
|
||||
default-closed `<details>`.** Fully-expanded unit cards in a long
|
||||
Implementation Units section is a failure mode — the reader can't see
|
||||
the unit list at a glance. Verify by skimming the rendered units:
|
||||
each `<article>` should render as its ID + title + metadata strip
|
||||
with collapsibles below, not as one long block.
|
||||
- **Within-section sub-nav** is present for sections with 6+ repeating
|
||||
cards.
|
||||
- **Body `<strong>`** is not colored with accent palette.
|
||||
- **`<details>`** inside repeating cards have no `open` attribute.
|
||||
- **Diagram labels** are legible — no arrow paths crossing text,
|
||||
halo width appropriate for font size.
|
||||
- **Diagrams complement prose, not replace it.** Every relationship a
|
||||
diagram conveys is also present in the surrounding IDed prose; no
|
||||
content lives only in an SVG.
|
||||
- **No JS framework runtimes** included. Small inline `<script>` for
|
||||
active-section TOC tracking or anchor-permalink behavior is the only
|
||||
acceptable JS.
|
||||
- **Each heading level** is visually distinct from others and from
|
||||
inline bold.
|
||||
- **No template placeholders** (`{skill}`, `<value>`, `[plan title]`)
|
||||
leaked into output.
|
||||
- **No process exhaust** callouts in the artifact.
|
||||
@@ -0,0 +1,207 @@
|
||||
# Markdown Rendering
|
||||
|
||||
This is a format-rendering reference — it describes how to render any
|
||||
artifact in markdown, independent of which skill is producing it.
|
||||
|
||||
It is paired with a section contract (`plan-sections.md`,
|
||||
`brainstorm-sections.md`, etc.) that describes *what* the artifact contains.
|
||||
This reference describes *how* markdown specifically presents it. The same
|
||||
content rendered by different skills shares the same markdown principles.
|
||||
|
||||
## Hard invariants
|
||||
|
||||
These hold regardless of which skill produced the artifact.
|
||||
|
||||
- **YAML frontmatter at the top of the file.** Standard `---` delimited block
|
||||
containing the artifact's stable metadata (title, status, date, type, etc.
|
||||
— exact fields are per-skill, defined in the section contract). Editable
|
||||
in place; tools and agents that do status flips (`active → completed`)
|
||||
update the YAML directly.
|
||||
- **ASCII identifiers in anchors.** Markdown headings auto-generate anchors
|
||||
from the heading text. Keep headings ASCII so anchors are predictable
|
||||
(`#implementation-units`, not `#implementación-units`).
|
||||
- **Repo-relative paths for file references.** Always. Never absolute paths
|
||||
— they break portability across machines, worktrees, teammates.
|
||||
- **No HTML mixed in.** Keep the markdown pure. No `<div>`, no `<details>`,
|
||||
no inline `<style>`. If a layout idea only works as HTML, defer it to the
|
||||
HTML rendering. Markdown stays markdown.
|
||||
|
||||
## Format principles
|
||||
|
||||
These shape what "good" markdown looks like; the agent applies them per
|
||||
artifact based on content shape.
|
||||
|
||||
### ID prefix format
|
||||
|
||||
Stable IDs (R, U, A, F, AE, KTD) appear as plain prefixes at the start of
|
||||
the bullet or heading — do NOT bold the prefix. The prefix is visually
|
||||
distinctive on its own; bolding it inflates visual noise.
|
||||
|
||||
```markdown
|
||||
- R1. The plan returns paginated sessions. ← right
|
||||
- **R1.** The plan returns paginated sessions. ← wrong (bolded prefix)
|
||||
```
|
||||
|
||||
Same applies to unit headings: `### U1. Cloak detection in preflight contract`.
|
||||
|
||||
### Content shape: prose vs bullets vs tables
|
||||
|
||||
The same content can be rendered three ways; the agent picks per content
|
||||
shape, not by template default.
|
||||
|
||||
- **Prose** when the content has narrative flow (motivation, decision
|
||||
rationale, problem framing). Bullets fragment narrative into
|
||||
disconnected pieces.
|
||||
- **Bullets** when items share a parallel shape but each carries enough
|
||||
prose to not fit a table cell.
|
||||
- **Tables** when 5+ items share uniform structure (`ID + body`,
|
||||
`name + value`, `decision + rationale`, `risk + mitigation`). Tables
|
||||
scan faster at that scale and unlock additional columns (status,
|
||||
traceability, severity) that bullets can't accommodate cleanly.
|
||||
|
||||
The test: which shape would a reader scan fastest for this content? If
|
||||
items have parallel structure and 5+ instances, table. If items are 3-5
|
||||
and each has a few lines of prose, bullets. If the content is a single
|
||||
narrative thought, prose.
|
||||
|
||||
### Bold leader labels within bullets
|
||||
|
||||
When a bullet has substructure that benefits from named fields (Key Flows
|
||||
with Trigger / Actors / Steps / Outcome, Acceptance Examples with Covers
|
||||
/ Given / When / Then), use bold leader labels at the start of nested
|
||||
bullets — not deeper heading levels.
|
||||
|
||||
```markdown
|
||||
- F1. Anonymous capture
|
||||
- **Trigger:** Agent enters Step 2a with no session.
|
||||
- **Actors:** A1, A2
|
||||
- **Steps:** Preflight detects cloak; agent launches; capture proceeds.
|
||||
- **Covered by:** R1, R2, R5
|
||||
```
|
||||
|
||||
This gives the bullet structure without needing H4/H5 headings that would
|
||||
clutter the doc and break TOC generation.
|
||||
|
||||
### Section separators
|
||||
|
||||
For substantial artifacts, use horizontal rules (`---`) between top-level
|
||||
H2 sections. Omit for short docs where separators would dominate.
|
||||
|
||||
### Tables for genuinely comparative info only
|
||||
|
||||
Use tables for the uniform-shape case in "Content shape" above. Don't use
|
||||
tables to render content lists that are really bullets — markdown tables
|
||||
are noisier in raw form and worse for diffs.
|
||||
|
||||
## Section anatomy
|
||||
|
||||
How section types commonly render in markdown. These are patterns, not
|
||||
contracts — the agent picks the shape that fits the content.
|
||||
|
||||
- **Summary / Problem Frame** — prose paragraphs.
|
||||
- **Requirements** — bullets with `R<N>.` prefix. When requirements span
|
||||
more than one concern, grouping under bold inline headers is the default
|
||||
shape, not optional polish (group by capability, not by discussion order);
|
||||
render a flat list only when every requirement is about the same thing.
|
||||
When requirements have status, traceability, or severity that warrant
|
||||
additional columns, escalate to a table.
|
||||
- **Implementation Units** — H3 heading per unit with `U<N>.` prefix.
|
||||
Fields (Goal, Files, Patterns, Test Scenarios, Verification) render as
|
||||
bullets with bold leader labels, or as sub-headings if the field has
|
||||
multi-paragraph content.
|
||||
- **Key Technical Decisions** — bullets with bold decision name + prose
|
||||
rationale, or numbered KTD-N pattern when traceability matters.
|
||||
- **Key Flows / Acceptance Examples** — bullets with bold leader labels
|
||||
(Trigger / Actors / Steps / Outcome / Covers / Given-When-Then).
|
||||
- **Scope Boundaries** — bullets, optionally split into "Deferred for
|
||||
later" / "Outside this product's identity" sub-headings when the
|
||||
positioning distinction matters.
|
||||
|
||||
The agent picks more elaborate or simpler shapes based on what each
|
||||
specific artifact's content needs.
|
||||
|
||||
## Diagrams
|
||||
|
||||
When the section contract calls for a diagram (architecture, sequence,
|
||||
flowchart, state machine, swim lane, data-flow), markdown renders it as
|
||||
a fenced mermaid block:
|
||||
|
||||
```markdown
|
||||
` ``mermaid
|
||||
flowchart TB
|
||||
A[Start] --> B{Decision}
|
||||
B -->|yes| C[Action]
|
||||
B -->|no| D[Other action]
|
||||
` ``
|
||||
```
|
||||
|
||||
(`TB` direction default — keeps diagrams narrow in source view and in
|
||||
narrow rendered viewports.)
|
||||
|
||||
Markdown's diagram affordances are limited compared to HTML. For
|
||||
quantitative comparisons (bar charts, scatter plots) markdown has no
|
||||
native equivalent — use a table with the data and let prose or caption
|
||||
carry the interpretation. The richer visualization happens in the HTML
|
||||
rendering.
|
||||
|
||||
## Inline code and code blocks
|
||||
|
||||
- **Inline code** for identifiers (variable names, function names,
|
||||
flag names, file paths, IDs that aren't section anchors).
|
||||
- **Fenced code blocks** with language tag for code, shell commands,
|
||||
API request/response samples. Always specify the language for syntax
|
||||
highlighting and accessibility.
|
||||
|
||||
```markdown
|
||||
The flag `--cdp-url` accepts a URL.
|
||||
|
||||
` ``bash
|
||||
browser-use --cdp-url http://localhost:9222
|
||||
` ``
|
||||
```
|
||||
|
||||
## No process exhaust
|
||||
|
||||
Engineering process metadata stays out of the artifact:
|
||||
|
||||
- No "captured at Phase X" notes
|
||||
- No `## Next Steps` pointing to the next skill
|
||||
- No italic provenance lines ("*Brainstorm completed 2026-05-13*")
|
||||
- No engineering-flow shepherding ("Now read this file:", "Next, run that
|
||||
command:")
|
||||
|
||||
This information belongs in commit messages, tool output, and agent
|
||||
transcripts — not in the artifact a reader returns to weeks later.
|
||||
|
||||
## Frontmatter shape
|
||||
|
||||
Per-skill frontmatter fields are defined in each skill's section contract
|
||||
(`plan-sections.md` lists plan frontmatter; `brainstorm-sections.md` lists
|
||||
brainstorm frontmatter). Common rules:
|
||||
|
||||
- YAML at the top of the file, delimited by `---` on its own line above
|
||||
and below.
|
||||
- Field names in lowercase snake_case (`status`, `created_at`, not
|
||||
`Status`, `CreatedAt`).
|
||||
- **Status lifecycle is per-contract.** When the section contract
|
||||
defines a `status` field with a lifecycle (plans use
|
||||
`active → completed`, flipped by ce-work at shipping time via direct
|
||||
YAML edit), it is editable in place. When the section contract does
|
||||
not define a status lifecycle (brainstorms, for example, have no
|
||||
`active → completed` flip — they are upstream of plans and
|
||||
referenced via the plan's `origin:`), do not introduce one.
|
||||
- Stable across artifact revisions — never rename or repurpose a field.
|
||||
|
||||
## Post-write audit
|
||||
|
||||
Before declaring the markdown file written, scan it for these common
|
||||
slips:
|
||||
|
||||
- All stable IDs are plain-prefix format, not bolded.
|
||||
- No HTML elements mixed in.
|
||||
- All file paths are repo-relative.
|
||||
- Horizontal rule separators between H2s (for Standard / Deep artifacts).
|
||||
- No process exhaust (Phase X notes, Next Steps pointers, provenance
|
||||
lines).
|
||||
- Tables only where 5+ uniform-shape items justify them.
|
||||
- Frontmatter has all the per-skill required fields with reasonable values.
|
||||
@@ -0,0 +1,126 @@
|
||||
# Plan Handoff
|
||||
|
||||
This file contains post-plan-writing instructions: document review, post-generation options, and issue creation. Load it after the plan file has been written and the confidence check (5.3.1-5.3.7) is complete.
|
||||
|
||||
## 5.3.8 Document Review
|
||||
|
||||
**Format gate.** This phase runs only when `OUTPUT_FORMAT=md` (resolved in SKILL.md Phase 0.0). `ce-doc-review`'s mutation mechanics are markdown-specific — its walkthrough applies `gated_auto`/`manual` fixes as "single-file markdown changes" via the platform's edit tool, and its Append-to-Open-Questions flow inserts `##`/`###` markdown headings (see `references/walkthrough.md` and `references/open-questions-defer.md` in the ce-doc-review skill). Running those mutators against an HTML artifact would produce malformed output. Until ce-doc-review gains HTML-aware mutation, HTML plans skip this phase entirely.
|
||||
|
||||
**When `OUTPUT_FORMAT=html`:** Skip the ce-doc-review invocation. Capture a synthetic "skipped" envelope so the menu summary line in 5.4 can name the limitation explicitly:
|
||||
- `fixes_applied = 0`
|
||||
- `proposed_fixes_count = 0`, `decisions_count = 0`, `fyi_count = 0`
|
||||
- `skipped_reason = "output_format_html"`
|
||||
|
||||
Then proceed directly to Final Checks (5.3.9). Do not block on this — the confidence check at 5.3 already strengthened the plan. Free-form requests for review in the post-generation menu will be declined for HTML runs with a prompt to switch to `output:md` (see 5.4); review is not available for HTML plans until ce-doc-review gains HTML-aware mutation.
|
||||
|
||||
**When `OUTPUT_FORMAT=md`:** Run the `ce-doc-review` skill with `mode:headless` on the plan file. Pass `mode:headless <plan-path>` as the skill arguments. When this step is reached for a markdown plan, it is mandatory — do not skip it because the confidence check already ran. The two tools catch different classes of issues.
|
||||
|
||||
Headless is the default at this phase because most users want to start work after planning, not adjudicate every reviewer concern up front. Headless applies `safe_auto` fixes silently and returns structured findings text — no walkthrough, no per-finding routing, no blocking prompts. The post-generation menu (see 5.4) offers `Run deeper doc review` as a first-class option so users can opt into the full interactive walkthrough when they want it.
|
||||
|
||||
The confidence check and ce-doc-review are complementary:
|
||||
- The confidence check strengthens rationale, sequencing, risk treatment, and grounding
|
||||
- Document-review checks coherence, feasibility, scope alignment, and surfaces role-specific issues
|
||||
|
||||
Capture the headless envelope so it can drive the contextual summary above the post-generation menu:
|
||||
- The number of fixes auto-applied
|
||||
- The count of remaining findings, broken out by user-facing bucket (proposed fixes, decisions, FYI observations)
|
||||
- The severity breakdown of decisions and proposed fixes (specifically the P0/P1 count, since those benefit from explicit user attention)
|
||||
|
||||
When ce-doc-review returns "Review complete", proceed to Final Checks.
|
||||
|
||||
**Pipeline mode:** Pipeline runs (LFG or any `disable-model-invocation` context) force `OUTPUT_FORMAT=md` at Phase 0.0, so the format gate above never selects the HTML skip path in pipeline mode. Pipeline runs always invoke `ce-doc-review` with `mode:headless` and the plan path — the headless mode is identical to the interactive default at this phase. No further routing is offered in pipeline mode; the caller decides what to do with the returned findings. Address any P0/P1 findings before returning control to the caller.
|
||||
|
||||
## 5.3.9 Final Checks and Cleanup
|
||||
|
||||
Before proceeding to post-generation options:
|
||||
- Confirm the plan is stronger in specific ways, not merely longer
|
||||
- Confirm the planning boundary is intact
|
||||
- Confirm origin decisions were preserved when an origin document exists
|
||||
|
||||
If artifact-backed mode was used:
|
||||
- Clean up the temporary scratch directory after the plan is safely updated
|
||||
- If cleanup is not practical on the current platform, note where the artifacts were left
|
||||
|
||||
**Format-specific composition.** When `OUTPUT_FORMAT=html` (resolved in SKILL.md Phase 0.0), the plan is written as a single self-contained `.html` file — there is no markdown sibling. Read `references/html-rendering.md` for composition rules: invariants, precedence stack, format principles, agent-consumability rules, and the post-compose audit. The `.html` file is the artifact downstream consumers (ce-work, human readers) read. `ce-doc-review` is not a current HTML consumer — its mutation mechanics are markdown-only today, and HTML plans skip the 5.3.8 doc-review pass until that gap closes.
|
||||
|
||||
When `OUTPUT_FORMAT=md`, write the markdown directly per `references/markdown-rendering.md`. No HTML is composed.
|
||||
|
||||
After all mutations in this run have settled (initial write, deepening synthesis, ce-doc-review `safe_auto` fixes when `OUTPUT_FORMAT=md`, HITL Proof resync if any), the artifact at its single path reflects the final state. HTML runs skip the ce-doc-review autofix step (see 5.3.8 format gate).
|
||||
|
||||
## 5.4 Post-Generation Options
|
||||
|
||||
**Pipeline mode:** If invoked from an automated workflow such as LFG or any `disable-model-invocation` context, skip the interactive menu below and return control to the caller immediately. The plan file has already been written, the confidence check has already run, and ce-doc-review has already run — the caller (e.g., lfg) determines the next step.
|
||||
|
||||
**Path format:** Use absolute paths for chat-output file references — relative paths are not auto-linked as clickable in most terminals.
|
||||
|
||||
**Summary line above the menu (always):** Print a single concise line summarizing the headless review state — e.g., `Doc review applied 3 fixes. 2 decisions, 1 proposed fix, 4 FYI observations remain (1 at P1).` When no fixes were applied and no findings remain, print `Doc review clean — no fixes needed.` When the envelope carries `skipped_reason: output_format_html` (HTML run, per Phase 5.3.8 format gate), print `Doc review skipped — ce-doc-review is markdown-only today; the HTML plan was not reviewed.` so the user knows the autofix pass did not run on this artifact. This line establishes what the autofix pass did (or didn't) so the user has the context to choose between the menu options below.
|
||||
|
||||
**Question:** "Plan ready at `<absolute path to plan>`. What would you like to do next?"
|
||||
|
||||
**Options:**
|
||||
1. **Start `/ce-work`** (recommended) - Begin implementing this plan in the current session
|
||||
2. **Run deeper doc review** - Walk through the remaining findings interactively (full ce-doc-review walkthrough)
|
||||
3. **Create Issue** - Create a tracked issue from this plan in your configured issue tracker (GitHub or Linear)
|
||||
4. **Open in Proof (web app) — review and comment to iterate with the agent** - Open the doc in Every's Proof editor, iterate with the agent via comments, or copy a link to share with others. **Render only when `OUTPUT_FORMAT=md`.**
|
||||
4. **Open in browser** - Open the HTML plan file locally for review and sharing. **Render only when `OUTPUT_FORMAT=html`.**
|
||||
5. **Done for now** - Pause; the plan file is saved and can be resumed later
|
||||
|
||||
**Option 4 format-keyed label.** Under exclusive output mode, the plan exists as exactly one artifact — `.md` or `.html`, never both. Render the option 4 label matching the produced format. Proof operates on markdown plans (it ingests the `.md` source and rewrites markdown), so it does not apply to HTML runs; the browser option opens the local `.html` file directly. `/ce-work` remains the recommended option in both modes — `ce-work` reads either format (see the ce-work skill's plan-input handling).
|
||||
|
||||
**Menu rendering:** The menu has 5 options, which exceeds the `AskUserQuestion` 4-option cap. Per the AGENTS.md narrow exception for legitimate option overflow, render this menu as a numbered list in chat with the hint "Pick a number or describe what you want." rather than trimming to fit the cap. Each option is a distinct destination/workflow and none are removable without losing real user choice (deeper review, issue creation, Proof, ce-work, and pause are each separately requested in practice). On platforms where blocking question tools have no option cap (e.g., Codex `request_user_input`, Pi `ask_user`), use the platform's blocking tool with all 5 options. When the platform's blocking tool is unavailable or errors (e.g., Codex edit modes where `request_user_input` is not exposed, or `ask_user` returns no match), fall back to the same numbered-list-in-chat rendering with the "Pick a number or describe what you want." hint — the same fallback the `AskUserQuestion` overflow path uses. Never silently skip the question.
|
||||
|
||||
**Hide `Run deeper doc review` when no actionable findings remain or doc review was skipped.** Show option 2 only when the headless envelope reports `proposed_fixes_count + decisions_count > 0` — i.e., at least one `gated_auto` or `manual` finding at confidence anchor `75` or `100`. Drop the option in any other case, including FYI-only state. FYI observations (anchor `50`) do not enter `ce-doc-review`'s interactive routing question or walkthrough — that flow is gated to actionable findings — so a `Run deeper doc review` option that only has FYIs to show is a dead-end: ce-doc-review would re-dispatch the persona team, find the same FYIs, skip the routing question, and fall through to the terminal question with nothing to walk through. The user paid the dispatch cost for no engagement surface. **Also drop option 2 when the envelope carries `skipped_reason: output_format_html`** — ce-doc-review's mutation mechanics are markdown-only today (see Phase 5.3.8 format gate), so a `Run deeper doc review` option on an HTML plan would route into the same markdown-oriented walkthrough the gate exists to prevent. When option 2 is dropped, the menu becomes 4 options (1, 3, 4, 5 above), falls back to `AskUserQuestion` on Claude Code, and renumbers 1-4 in display so users see a clean sequence. The summary line above the menu still names the FYI count when present (`Doc review applied 3 fixes. 2 FYI observations remain.`) so the user sees what was found, even though there is no menu action attached to it — the FYIs are visible in the headless envelope text the menu rendered alongside.
|
||||
|
||||
Based on selection (the bare per-option routing is also stated inline in the SKILL.md so it cannot be missed when this reference is not loaded; the elaborate sub-flows below are the reason this reference still exists):
|
||||
- **Start `/ce-work`** -> Invoke the `ce-work` skill via the platform's skill-invocation primitive (`Skill` in Claude Code, `Skill` in Codex, the equivalent on Gemini/Pi), passing the plan path as the skill argument. Do not merely tell the user to type `/ce-work` — fire the invocation now so the plan executes in this session.
|
||||
- **Run deeper doc review** -> Re-invoke the `ce-doc-review` skill on the plan path **without** `mode:headless` so the interactive routing question and walkthrough fire. The headless pass already applied `safe_auto` fixes and recorded its findings in the session, so the interactive pass picks up where headless stopped — its R29 suppression rule prevents prior-round Skipped/Deferred entries from re-raising. After it returns, re-render this menu with the refreshed counts so the user can pick what to do next.
|
||||
- **Create Issue** -> Follow the Issue Creation section below
|
||||
- **Open in Proof (web app) — review and comment to iterate with the agent** -> Load the `ce-proof` skill in HITL-review mode with:
|
||||
- source file: `docs/plans/<plan_filename>.md`
|
||||
- doc title: `Plan: <plan title from frontmatter>`
|
||||
- identity: `ai:compound-engineering` / `Compound Engineering`
|
||||
- recommended next step: `/ce-work` (shown in the ce-proof skill's final terminal output)
|
||||
|
||||
Follow `references/hitl-review.md` in the ce-proof skill. It uploads the plan, prompts the user for review in Proof's web UI, ingests filtered comment threads, applies agreed edits through the current Proof edit APIs, replies/resolves in-thread, and syncs the final markdown back to the plan file atomically on proceed.
|
||||
|
||||
Note: the Proof flow only runs when `OUTPUT_FORMAT=md` (the menu only renders this option then). Proof ingests markdown; HTML plans use the local browser option instead.
|
||||
|
||||
When the ce-proof skill returns:
|
||||
- `status: proceeded` with `localSynced: true` -> the plan on disk now reflects the review. Re-run `ce-doc-review` on the updated plan before re-rendering the menu — HITL can materially rewrite the plan body, so the prior ce-doc-review pass no longer covers the current file and section 5.3.8 requires a review before any handoff option is offered. Then return to the post-generation options with the refreshed residual findings.
|
||||
- `status: proceeded` with `localSynced: false` -> the reviewed version lives in Proof at `docUrl` but the local copy is stale. Offer to pull the Proof doc to `localPath` using the ce-proof skill's Pull workflow. If the pull happened, re-run `ce-doc-review` on the pulled file before re-rendering the options (same 5.3.8 rationale — the local plan was materially updated by the pull). If the pull was declined, include a one-line note above the menu that `<localPath>` is stale vs. Proof — otherwise `Start /ce-work` or `Create Issue` will silently use the pre-review copy.
|
||||
- `status: done_for_now` -> the plan on disk may be stale if the user edited in Proof before leaving. Offer to pull the Proof doc to `localPath` so the local plan file stays in sync. If the pull happened, re-run `ce-doc-review` on the pulled file before re-rendering the options (same 5.3.8 rationale). If the pull was declined, include the stale-local note above the menu. `done_for_now` means the user stopped the HITL loop — it does not mean they ended the whole plan session; they may still want to start work or create an issue.
|
||||
- `status: aborted` -> fall back to the options without changes.
|
||||
|
||||
If the initial upload fails (network error, Proof API down), retry once after a short wait. If it still fails, tell the user the upload didn't succeed and briefly explain why, then return to the options — don't leave them wondering why the option did nothing.
|
||||
- **Open in browser** -> Display the absolute path to the `.html` plan file so the user can open it locally. Where the platform exposes a browser-opening primitive (e.g., `open` on macOS, `xdg-open` on Linux, `start` on Windows), the agent may invoke it directly; otherwise print the absolute path and let the user open it. After the path is displayed (or the browser is opened), return to the post-generation options so the user can pick a follow-up action.
|
||||
- **Done for now** -> Display a brief confirmation that the plan file is saved and end the turn. Do not start follow-up work without an explicit further user prompt.
|
||||
- **Free-form prompts that target the findings** (e.g., the user types "review", "walk through", "deep review" instead of picking a numbered option) -> route as if they had picked `Run deeper doc review`. Do not loop back to the menu without firing the deeper review. **Exception:** when the envelope carries `skipped_reason: output_format_html`, do not fire ce-doc-review — instead, reply once with `ce-doc-review is markdown-only today; the HTML plan can't be reviewed without HTML-aware mutation support. Switch to /ce-plan output:md to regenerate as markdown if you want a review pass.` and loop back to the menu.
|
||||
- **Other free-form input** -> Accept revisions to the plan and loop back to options.
|
||||
|
||||
## Issue Creation
|
||||
|
||||
When the user selects "Create Issue", detect their project tracker:
|
||||
|
||||
1. Read `AGENTS.md` (or `CLAUDE.md` for compatibility) at the repo root and look for `project_tracker: github` or `project_tracker: linear`.
|
||||
2. If `project_tracker: github`:
|
||||
|
||||
```bash
|
||||
gh issue create --title "<type>: <title>" --body-file <plan_path>
|
||||
```
|
||||
|
||||
3. If `project_tracker: linear`:
|
||||
|
||||
```bash
|
||||
linear issue create --title "<title>" --description "$(cat <plan_path>)"
|
||||
```
|
||||
|
||||
4. If no tracker is configured, ask the user which tracker they use with the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to asking in chat only when no blocking tool exists or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip. Options: `GitHub`, `Linear`, `Skip`. Then:
|
||||
- Proceed with the chosen tracker's command above
|
||||
- Offer to persist the choice by adding `project_tracker: <value>` to `AGENTS.md`, where `<value>` is the lowercase tracker key (`github` or `linear`) — not the display label — so future runs match the detector in step 1 and skip this prompt
|
||||
- If `Skip`, return to the options without creating an issue
|
||||
|
||||
5. If the detected tracker's CLI is not installed or not authenticated, surface a clear error (e.g., "`gh` CLI not found — install it or create the issue manually") and return to the options.
|
||||
|
||||
After issue creation:
|
||||
- Display the issue URL
|
||||
- Ask whether to proceed to `/ce-work` using the platform's blocking question tool
|
||||
@@ -0,0 +1,240 @@
|
||||
# Plan Sections
|
||||
|
||||
This reference describes what makes a great implementation plan. It does NOT
|
||||
prescribe how the plan looks on the page — rendering is handled by the
|
||||
format-specific references (`markdown-rendering.md`, `html-rendering.md`).
|
||||
|
||||
## The outcome
|
||||
|
||||
A great plan enables three audiences to act:
|
||||
|
||||
- **The implementing agent** (`ce-work` or a human) starts from an informed
|
||||
baseline — load-bearing decisions are named, research breadcrumbs orient
|
||||
their own investigation, unit boundaries are clear. The plan gives the
|
||||
implementer a starting point, not a substitute for their own investigation.
|
||||
- **The reviewer** identifies the load-bearing decisions and the boundaries
|
||||
of what's being changed in one pass.
|
||||
- **The future reader** (anyone returning months later) traces why the work
|
||||
was done, what shaped it, and where the artifacts live.
|
||||
|
||||
Sections earn their place by serving one of these audiences. Omit padding.
|
||||
|
||||
## Decide whether a plan doc is warranted at all
|
||||
|
||||
Not every invocation of `ce-plan` should produce a plan document. For
|
||||
genuinely atomic work, the doc is ceremony — the implementer (whether
|
||||
`ce-work` or a human) can act directly without IDed units, KTDs, or
|
||||
Requirements as a checklist.
|
||||
|
||||
**Bias toward producing a plan.** The risk asymmetry favors writing one:
|
||||
a thin plan doc for small work is mild ceremony, but skipping a plan when
|
||||
one was warranted costs the implementer real time (reinvented decisions,
|
||||
lost unit boundaries, no IDed requirements to verify against). When unsure,
|
||||
write the plan.
|
||||
|
||||
**Skip plan creation only when ALL of these hold:**
|
||||
|
||||
- The work is **atomic** — fits in one commit, no meaningful unit boundaries
|
||||
to break out independently.
|
||||
- There are **no design choices that constrain implementation** — no
|
||||
Key Technical Decisions worth recording. If the work needs the implementer
|
||||
to make a choice between two approaches, those approaches are KTDs and
|
||||
a plan is warranted.
|
||||
- There are **no scope boundaries worth pinning** in writing — the work
|
||||
scope is self-evident from the user's request.
|
||||
- **No upstream artifact** (a brainstorm with R-IDs, an incident report,
|
||||
a deferred-follow-up item from a prior plan) needs traceability through
|
||||
this plan.
|
||||
|
||||
**Stress test the "looks atomic" case.** Many requests look atomic at first
|
||||
glance but hide design decisions:
|
||||
|
||||
- *"Add caching to this endpoint"* — sounds atomic, but TTL, invalidation,
|
||||
cache key shape, and backend selection are all KTDs. Write the plan.
|
||||
- *"Migrate from package A to package B"* — sounds mechanical, but
|
||||
semantic differences between the packages create migration KTDs. Write
|
||||
the plan.
|
||||
- *"Add rate limiting"* — sounds small, but algorithm, scope, and
|
||||
configurability are all KTDs. Write the plan.
|
||||
|
||||
vs. genuine skip cases:
|
||||
|
||||
- *"Fix typo in README line 47"* — atomic, no KTDs, skip the plan.
|
||||
- *"Rename `oldFn` to `newFn` across the repo"* — mechanical, no design
|
||||
choices, skip the plan.
|
||||
- *"Bump dependency X to v2.3.1"* — mechanical, skip the plan (unless the
|
||||
bump introduces breaking changes that warrant unit-by-unit migration).
|
||||
|
||||
When skipping the plan doc, the work proceeds directly to `ce-work` or to
|
||||
implementation, and any decisions made along the way land in the commit
|
||||
message or `docs/solutions/` if they're worth carrying forward.
|
||||
|
||||
## Hard floor
|
||||
|
||||
When a plan doc is warranted, these sections are present. They carry the
|
||||
contracts downstream consumers depend on.
|
||||
|
||||
- **Summary** — what the plan proposes, in 1-3 lines. Forward-looking; orients
|
||||
the reader before they invest in detail.
|
||||
- **Problem Frame** — why the work is being done. Backward-looking /
|
||||
situational. May merge with Summary for compact plans where the motivation
|
||||
is a single sentence.
|
||||
- **Requirements** (with stable R-IDs) — what must be true after the work
|
||||
ships. Reviewer's checklist; downstream code review verifies against these.
|
||||
- **Key Technical Decisions** (KTDs) — the load-bearing choices that constrain
|
||||
implementation. Each entry is `<decision>: <rationale>`. Without these, the
|
||||
implementer can't tell which design choices are open and which are pinned.
|
||||
- **Implementation Units** (with stable U-IDs) — the discrete units of work,
|
||||
sized so each is independently landable. `ce-work` consumes these to
|
||||
execute. For trivial single-step plans the work may collapse into Summary
|
||||
prose and U-IDs may be omitted; this is rare.
|
||||
|
||||
## Include when material
|
||||
|
||||
These sections are present when they carry information that isn't covered
|
||||
elsewhere. The test is not "is this a substantial plan?" — it is
|
||||
*"does this specific plan have content this section would surface?"* Filling
|
||||
a section with placeholder prose is worse than omitting it.
|
||||
|
||||
- **High-Level Technical Design** — include when the technical approach has
|
||||
shape that prose alone doesn't carry well: architecture across components,
|
||||
sequencing across processes, state machines, branching gates.
|
||||
Visualizations (component topology, sequence, swim lane, flowchart,
|
||||
data-flow) typically live here. Skip when the approach is a one-paragraph
|
||||
pattern application that the prose itself conveys.
|
||||
|
||||
- **Scope Boundaries** — include when scope is contested, when there are
|
||||
tempting non-goals worth naming explicitly, or when "deferred for later"
|
||||
needs distinguishing from "outside the product's identity." Skip when scope
|
||||
is obvious from Requirements alone.
|
||||
|
||||
- **Open Questions** — include when there are genuinely unresolved items that
|
||||
block planning or implementation. Skip when the plan is complete; an empty
|
||||
"Open Questions: none" section signals false uncertainty.
|
||||
|
||||
- **System-Wide Impact** — include when the change affects cross-cutting
|
||||
concerns (data lifecycles, auth boundaries, performance posture, cardinal
|
||||
rules, shared infrastructure). Skip for changes localized to one component
|
||||
where the impact is self-evident.
|
||||
|
||||
- **Risks & Dependencies** — include when there are real risks worth flagging
|
||||
(external service changes, version pins under churn, behavioral assumptions
|
||||
worth highlighting) or material upstream dependencies. Skip for low-risk
|
||||
localized work.
|
||||
|
||||
- **Acceptance Examples** — include when any requirement has a state-dependent
|
||||
or conditional shape ("When X, Y") where the prose alone leaves ambiguity
|
||||
about edge cases. Skip when all requirements are unconditional and
|
||||
unambiguous.
|
||||
|
||||
- **Documentation / Operational Notes** — include when documentation,
|
||||
monitoring, runbooks, or rollout steps need explicit notes. Skip when the
|
||||
work is purely internal and uses existing operational scaffolding without
|
||||
modification.
|
||||
|
||||
- **Sources / Research** — surface the research that orients the implementer
|
||||
or justifies load-bearing choices. The test: *"if I were the implementer
|
||||
reading this cold, would this breadcrumb help me make better choices?"*
|
||||
Yes → surface (code locations like `services/convex/reports.ts:174-176`,
|
||||
external docs, RFCs, constraints, prior plans — the category is inclusive,
|
||||
not enumerated). Process exhaust (reading the user's prompt, glancing at
|
||||
obvious entry points, restating prose) → omit. Surface inline next to the
|
||||
KTD or unit it justifies, or as a dedicated section — both shapes work.
|
||||
|
||||
## Agent agency
|
||||
|
||||
The catalog is a floor, not a ceiling. When the plan's content doesn't fit
|
||||
any catalog section, introduce a new one — don't force the content into a
|
||||
section it doesn't belong in. Content drives section choices, not vice
|
||||
versa.
|
||||
|
||||
The agent also picks per artifact:
|
||||
|
||||
- Whether Problem Frame merges into Summary
|
||||
- Sub-groupings (Requirements by capability, KTDs by component, Units phased
|
||||
into milestones)
|
||||
- How much detail each section carries
|
||||
- Whether HTD has one diagram, several, or none — and whether visualizations
|
||||
live in HTD or embedded in other sections
|
||||
|
||||
## Plan metadata fields
|
||||
|
||||
Every plan carries a small set of stable metadata fields that downstream
|
||||
tooling depends on. The contract is format-independent: in markdown these
|
||||
fields appear as YAML frontmatter at the top of the file; in HTML they
|
||||
appear as visible header text (typically a `<dl>` of `<dt>`/`<dd>` pairs or
|
||||
a stats strip). Field names and semantics are the same across both formats
|
||||
so consumers can locate them without knowing which format produced the
|
||||
plan.
|
||||
|
||||
### Required
|
||||
|
||||
- **`title`** — verbatim plan title. Matches the H1 (markdown) or document
|
||||
`<h1>` (HTML) so file metadata and visible heading don't drift.
|
||||
- **`type`** — conventional-commit-prefix-aligned classification (`feat`,
|
||||
`fix`, `refactor`, `chore`, `docs`, `perf`, `test`, etc.). Carries the
|
||||
intent the eventual commit message should reflect.
|
||||
- **`status`** — `active` on creation; `ce-work` flips to `completed` on
|
||||
ship. `ce-plan`'s Phase 0.1 resume fast path keys on `active`. In HTML,
|
||||
status MUST render as `<span class="status">{value}</span>` so the flip
|
||||
mechanic can locate and rewrite it by selector (see
|
||||
`references/html-rendering.md`).
|
||||
- **`date`** — creation date in ISO 8601 (`YYYY-MM-DD`), ASCII digits only.
|
||||
|
||||
### Optional but well-known
|
||||
|
||||
These fields are not required, but when set they have fixed names and
|
||||
semantics so downstream tooling can rely on them:
|
||||
|
||||
- **`origin`** — repo-relative path to an upstream brainstorm requirements
|
||||
doc (e.g., `docs/brainstorms/2026-05-12-pagination-requirements.md`).
|
||||
Set when planning from an upstream brainstorm; carried for traceability
|
||||
and re-resolved when `ce-plan` re-deepens. The HITL Proof flow uses
|
||||
`origin` to trace back to the source brainstorm.
|
||||
- **`deepened`** — ISO 8601 date marking the first time the confidence
|
||||
check substantively strengthened the plan. Presence affects Phase 0.1
|
||||
resume fast-path logic (see `references/deepening-workflow.md`).
|
||||
|
||||
Field names are stable across plan revisions — never rename a field or
|
||||
repurpose its semantics. Agents composing new plans MUST use these exact
|
||||
names; adding new fields is fine, but renaming `status` to `state` or
|
||||
`origin` to `source` breaks the downstream consumers above.
|
||||
|
||||
## ID and content rules
|
||||
|
||||
These apply regardless of rendering format.
|
||||
|
||||
- **Stable IDs.** R-IDs (Requirements), U-IDs (Implementation Units), A-IDs
|
||||
(if Actors fire), F-IDs (if Flows fire), AE-IDs (if Acceptance Examples
|
||||
fire). IDs are stable across plan revisions — never renumber to "clean
|
||||
up gaps."
|
||||
- **Plain prefix.** `R1.`, `U1.` as bullet prefixes. Do not bold; the prefix
|
||||
is visually distinctive on its own.
|
||||
- **Repo-relative paths.** Always. Never absolute paths in plan content;
|
||||
they break portability across machines, worktrees, teammates.
|
||||
- **No process exhaust.** No "captured at Phase X" notes, no `## Next Steps`
|
||||
pointing to the next skill, no italic provenance lines. Engineering process
|
||||
metadata belongs in commit messages and tool output, not the artifact.
|
||||
- **Group Requirements by concern when they span distinct logical areas.**
|
||||
The trigger is distinct concerns, not item count — even four requirements
|
||||
benefit from grouping if they cover three different topics. Skip grouping
|
||||
only when all requirements are genuinely about the same thing; a long flat
|
||||
list is a smell that subgroups were missed. Group by capability (e.g.,
|
||||
"Packaging", "Migration and compatibility", "Contributor workflow"), not by
|
||||
the order requirements were discussed. R-IDs stay continuous across groups
|
||||
(R1, R2 in the first group; R3, R4 in the second; never restart at R1 per
|
||||
group).
|
||||
|
||||
## Rendering
|
||||
|
||||
The format-specific references describe how to render these sections in each
|
||||
output format:
|
||||
|
||||
- **Markdown rendering:** `references/markdown-rendering.md`
|
||||
- **HTML rendering:** `references/html-rendering.md`
|
||||
|
||||
This reference (`plan-sections.md`) is about WHAT the plan contains;
|
||||
rendering references are about HOW each format presents it. The plan is
|
||||
written in one format — markdown OR HTML, never both — based on the
|
||||
resolved output mode. The section catalog is the same regardless of
|
||||
format.
|
||||
@@ -0,0 +1,396 @@
|
||||
# Scoping Synthesis
|
||||
|
||||
**Scoping synthesis ≠ plan doc.** The scoping synthesis is the scope/decisions checkpoint that plan-write (Phase 5.2) consumes as input. It surfaces decisions the agent CAN make at synthesis time: scope-level (does this plan cover the full brainstorm or narrow to a subset?), posture (extend existing pattern vs. introduce new abstraction), test approach. It does NOT surface decisions plan-write produces: PR count, commit/branch sequencing, effort or time estimates, Implementation Unit lists, exact file paths, test command recipes. If the synthesis claims any of those, it has leaked plan-write thinking and must be re-cut to scope-decisions only. Even when the agent has formed plan-write opinions earlier in the session, the synthesis stays at scope altitude — the user is being asked to affirm scope, not to rubber-stamp implementation.
|
||||
|
||||
**Two-stage shape: internal draft, then chat-time synthesis.** The synthesis is composed in two stages. Stage 1 is an internal three-bucket draft (Stated / Inferred / Out of scope) the agent uses to think comprehensively about scope. Stage 2 is the compressed chat-time output: a tier-shaped summary plus "Call outs" (zero or more, capped by plan depth — see the cap table under "How many call-outs are right?") — the specific forks where the user might redirect. The user only sees stage 2. The internal draft still informs the plan body via the doc-shape routing below; it just doesn't reach the user verbatim. This split exists because the comprehensive audit shape produced too much detail for the user to weigh in on, even when the granularity rules were followed.
|
||||
|
||||
**Three-bucket structure is the internal draft, not the user-facing artifact.** It does its scope-thinking job during stage 1 and dissolves when Phase 5.2 writes the plan: Stated content informs Requirements, Inferred content informs Key Technical Decisions / Implementation Units (interactive mode) or `## Assumptions` (non-interactive mode), Out-of-scope content informs Scope Boundaries. The plan has no parallel `## Synthesis` section — only the stage-2 summary embeds, as `## Summary`. See "Doc shape after confirmation" below for the routing.
|
||||
|
||||
This content is loaded when a synthesis-summary phase fires in ce-plan. There are two variants — they share structure but differ in timing and content focus:
|
||||
|
||||
- **Solo variant** (Phase 0.7): fires after Phase 0.4 bootstrap and Phase 0.6 depth classification, before Phase 1 research begins. Catches scope misinterpretation before sub-agent dispatch is spent. Full breadth — problem frame, intended behavior, success criteria, in/out scope.
|
||||
- **Brainstorm-sourced variant** (Phase 5.1.5): fires after Phase 1 research, before Phase 5.2 plan-write. Focuses on plan-time decisions (which files/modules to touch, which patterns extended vs. introduced new, test scope, refactor scope). Brainstorm-validated WHAT is assumed and not re-stated.
|
||||
|
||||
Both variants share the two-stage shape, the keep test for call-outs, soft-cut behavior, and the doc-shape routing. In non-interactive (headless) mode, both compose the internal draft and skip stage 2 — the user-facing compression is moot when there is no synchronous user. The internal draft dissolves into the plan body the same way, with Inferred bets routing to a `## Assumptions` section. See "Headless mode (shared)" below for the full routing.
|
||||
|
||||
---
|
||||
|
||||
## Stage 1: internal three-bucket draft (shared)
|
||||
|
||||
The internal draft is structured in three labeled buckets. Items may appear in two buckets when meaningfully both — flag the inclusion-then-exclusion as Inferred so the reasoning is captured.
|
||||
|
||||
- **Stated** — what the user said directly (in the original prompt, prior conversation, dialogue answers, or the upstream brainstorm doc when present). Items here have explicit user-language anchors.
|
||||
- **Inferred** — what the agent assumed to fill gaps. Scope boundaries the user never explicitly named, success criteria extrapolated from intent, technical assumptions made because the brief interview didn't probe them. The Inferred list is the most actionable bucket — items here are the agent's bets that the user can correct.
|
||||
- **Out of scope** — deliberately excluded items. Adjacent work the agent considered but decided not to include, refactors, nice-to-haves, future-work items.
|
||||
|
||||
This draft is internal. Do not paste it verbatim into chat. Compose it as a thinking step, then derive stage 2 from it.
|
||||
|
||||
---
|
||||
|
||||
## Stage 2: chat-time scoping synthesis
|
||||
|
||||
Stage 2 is what the user actually sees. The shape differs between variants because they serve different purposes — brainstorm-sourced plans inherit a validated WHAT and surface plan-specific HOW; solo plans have no upstream and the synthesis is the WHAT.
|
||||
|
||||
### Brainstorm-sourced shape (Phase 5.1.5)
|
||||
|
||||
Two content sections plus call-outs:
|
||||
|
||||
1. **Brainstorm-scope restatement** (1-2 sentences, prose). Restates the brainstorm's scope as orientation. The user wrote this content, but the synthesis may be read days later or in parallel with other plans — the restatement is the topic anchor that says "this is the artifact we're planning against." Stay in the brainstorm's own vocabulary. Do NOT enumerate Implementation Units, restate constraints back at the user, or list acceptance examples.
|
||||
|
||||
2. **Plan-specific scoping decisions** (prose, or bullets when multi-faceted). Scope-level commitments the agent made that the brainstorm did not: does this plan cover the full brainstorm scope or narrow to a subset; are adjacent refactors pulled in or held out; what test scope at scenario level (which sites, which acceptance examples). Each item must pass the **affirmability test** — the user can affirm or redirect it without reading code. This section is scope claims at affirm-or-redirect level, NOT a description of where the implementation reaches, NOT PR count or commit sequencing, NOT Implementation Unit lists, NOT exact file paths or test commands — those are all plan-write outputs the synthesis cannot honestly claim. If the plan covers the full brainstorm scope with no narrowing, expansions, or adjacent work, this section stays short ("This plan covers the full brainstorm scope; test scope is X").
|
||||
|
||||
3. **Call outs** (zero or more, capped by plan depth — see "How many call-outs are right?" below). Each a real fork where the user's input materially changes the plan. Omit the "Call outs:" header entirely when zero forks survived the keep test.
|
||||
|
||||
### Solo shape (Phase 0.7)
|
||||
|
||||
No upstream document; the synthesis itself is the scope claim:
|
||||
|
||||
1. **Scope claim** (prose, or bullets when multi-faceted). What the agent is planning to build, at affirm-or-redirect level — names what's in and what's out. NOT an enumeration of Implementation Units the plan will contain.
|
||||
|
||||
2. **Call outs** (zero or more, capped by plan depth). Same as brainstorm-sourced.
|
||||
|
||||
### Shape budgets
|
||||
|
||||
Tier-aware budgets are **ceilings, not targets**. Less is correct when there isn't more to say — filling the budget produces noise.
|
||||
|
||||
| Plan depth | Restatement (brainstorm-sourced) | Plan-specific scoping (brainstorm-sourced) / Scope claim (solo) |
|
||||
|---|---|---|
|
||||
| Lightweight | 1 sentence | 1-3 lines prose |
|
||||
| Standard | 1-2 sentences | up to 3-5 lines or 2-4 bullets |
|
||||
| Deep | 1-2 sentences | up to 4-6 lines or 3-6 bullets |
|
||||
|
||||
Form within each section (prose, bullets, mix) follows whatever communicates best.
|
||||
|
||||
### Shared rules
|
||||
|
||||
- **No "Stated" bucket in chat** (the orientation or scope-claim covers it).
|
||||
- **No "Out of scope" bucket as a separate list** — fold a non-obvious exclusion into a call-out when it survives the keep test, otherwise drop it.
|
||||
- **Source-document vocabulary.** When a brainstorm exists, use its terms. Don't invent agent-coded shorthand (e.g., "skill-instruction shape", "hooks engine selection at Step 2a entry"). When referencing acceptance examples, requirements, or flows, name them in plain terms ("the install-prompt acceptance case") — never use bare IDs.
|
||||
|
||||
- **Pre-emit mechanical checks.** Before emitting the synthesis, scan the output:
|
||||
- **Bare ID references** (`AE\d+`, `R\d+`, `F\d+`, `A\d+`, `U\d+`) → replace with plain names. Mixed forms (case named AND ID cited) still violate the rule because the ID adds noise without information.
|
||||
- **File paths** (`path/like.md`, `path/like.py`, `internal/cli/...`, `skills/.../...`, etc.) → cut unless the path IS the topic of an explicit fork in the call-outs. Allowed: "cleanup hook in the existing archive step vs. a new dedicated phase" (where the path is implicit in the decision). Forbidden: paths listed to demonstrate completeness, preview Implementation Units, or describe where the implementation reaches. The synthesis names *what* the plan targets, not *where* the code lives.
|
||||
|
||||
### The keep test for each call-out
|
||||
|
||||
Before keeping a candidate call-out from the internal draft, run the **affirmability test**: would the user need to look at code to evaluate this? If yes, it is plan-body content — cut. If no, apply the keep test — one of the following must be true:
|
||||
|
||||
- **Real fork**: another reasonable agent might choose differently on this dimension (extend pattern X vs. introduce abstraction Y; scan source A vs. source B; etc.)
|
||||
- **Non-obvious behavioral choice**: a default the agent picked that the user would not see by reading the summary alone, but that materially affects what the plan does (e.g., "scans the working-dir snapshot before the copy step" — the user would not infer the scan target from a description of the gate's purpose)
|
||||
- **Non-obvious exclusion**: an item was deliberately excluded that the user might want to add back in
|
||||
- **Cheap-now-expensive-later correction**: a bet the user is well-placed to redirect now that would be expensive to undo after research or plan-write
|
||||
|
||||
Cut anything else, including:
|
||||
|
||||
- Mechanical items where there is no real alternative (e.g., "no new dependencies" when the work clearly does not need any)
|
||||
- Implementation choices that will be settled during the work (e.g., regex precision tuned during impl)
|
||||
- Items already implied by the summary
|
||||
|
||||
### The detail test (per call-out and per summary bullet)
|
||||
|
||||
After the keep test, every surviving item runs the **detail test**: 1-2 lines max, conversational not documentary. A call-out or summary bullet that runs to 4+ lines of dense prose is naming an implementation consequence rather than a decision — re-cut at higher abstraction.
|
||||
|
||||
The keep test addresses *which* items survive. The detail test addresses *how much* each surviving item says. Without it, the count cap is gameable: an agent can hit "3 call-outs" while each call-out is a 6-line paragraph, and the synthesis reads as a doc preview instead of a checkpoint.
|
||||
|
||||
### How many call-outs are right?
|
||||
|
||||
The cap is heuristic, not law. The real discipline is the keep test on each candidate. Typical bounds by plan depth:
|
||||
|
||||
| Plan depth | Typical | Cap |
|
||||
|---|---|---|
|
||||
| Lightweight | 0-2 | 3 |
|
||||
| Standard | 1-3 | 4 |
|
||||
| Deep | 2-5 | 6 |
|
||||
|
||||
**If the stage-2 pass exceeds the tier cap, OR any call-out or summary bullet runs to 4+ lines of dense prose, the synthesis is misshapen — do not raise the cap or accept the bloat, re-cut at a higher level of abstraction.** Almost always, 2-3 of those call-outs are sub-decisions of one larger fork (file path, flag name, JSON key behavior, and dependency choice are usually four facets of one "how to extend the existing scaffold" decision, not four independent forks). Collapse related call-outs into a single decision named at the level the user actually weighs in on. The user's job is to redirect forks, not to validate every implementation consequence of a fork they have already implicitly agreed to by accepting the higher-level decision.
|
||||
|
||||
A useful test: read the call-outs aloud. If two or more sound like "and also" extensions of the same idea, they belong as one.
|
||||
|
||||
### Anti-patterns in call-outs
|
||||
|
||||
Each anti-pattern below produces a call-out that fails the affirmability test. If a candidate call-out matches one of these, it is plan-body content — cut, do not rephrase.
|
||||
|
||||
- Names a file path or module name (`internal/artifacts/pii.go`)
|
||||
- Names a flag, env var, or exact env value (`--accept-redaction-list=<finding-id,...>`)
|
||||
- Specifies a JSON shape, response format, or exact data structure
|
||||
- Names HTTP status codes, event names, or exact error wording
|
||||
- Describes implementation flow ("first X, then Y, then Z")
|
||||
- Names exact method signatures, call graphs, or SQL syntax
|
||||
- States a mechanical choice with no real alternative ("uses stdlib regexp")
|
||||
|
||||
The line-number, signature, and code-spec rules are not new — they have always been forbidden in Inferred bullets. They apply equally to call-outs, which are now the user-facing surface.
|
||||
|
||||
---
|
||||
|
||||
## When to skip the blocking confirmation
|
||||
|
||||
The auto-proceed path (announce without waiting for user confirmation) fires only when **plan depth is Lightweight AND zero call-outs survive the keep test**. For Standard or Deep plans, always fire the confirmation gate even when zero call-outs survive — substance earns the checkpoint, not interaction history. A Deep plan with rich silent decisions and a 1-3 line summary is exactly the case where rubber-stamping is most likely; the explicit confirmation request gives the user a real chance to push back before research or plan-write proceeds.
|
||||
|
||||
When auto-proceed applies (Lightweight + zero call-outs), emit a one-line announcement and continue:
|
||||
|
||||
```
|
||||
Planning: [1-3 line summary]
|
||||
|
||||
No open decisions to weigh in on — proceeding to [research / plan-write]. Interrupt if I have the scope wrong.
|
||||
```
|
||||
|
||||
The announcement is mandatory when skipping — silent proceeding is not allowed. The "why" (no forks worth flagging) must be visible.
|
||||
|
||||
For Standard/Deep with zero call-outs, the confirmation template still fires; the "Call outs:" header is simply omitted. The user gets the summary plus the explicit confirmation request.
|
||||
|
||||
---
|
||||
|
||||
## Synthesis structural discipline (shared)
|
||||
|
||||
Both variants share these structural rules. They address failure modes where the synthesis becomes a Phase 5.2 (plan-write) preview instead of a scope checkpoint.
|
||||
|
||||
**Summary leads, call-outs follow** — not the reverse, and no separate framing block above. Putting extensive content ABOVE the synthesis (an approach pitch, files-touched bullets, rationale block) inverts the structure: the synthesis becomes a footnote to the proposal instead of the proposal being a tier-budgeted summary the call-outs depend on.
|
||||
|
||||
**Anti-pattern: synthesis as plan-pitch.** Plan-body content — file paths, code shapes, sentinel strings, exact error messages, "Recommendation" / "Behavior when X" / "Why this shape" rationale — does not belong in chat output regardless of where it appears: not in a block above the call-outs, not inside the summary, and not nested in a call-out's commentary or sub-bullets. The position rule and the content rule are independent: a structurally-legal placement (inside a call-out bullet) does not legitimize plan-body content. If you find yourself writing it anywhere, stop. That content is Phase 5.2 (plan-write) territory — it belongs in the plan body the next phase will write, not in the synthesis presentation. The synthesis is a scope/decisions checkpoint: a tier-budgeted summary plus call-outs bounded by the tiered cap (see "How many call-outs are right?"). Implementation detail leaking into the synthesis (anywhere) is a sign Phases 1-4 (research and structuring) and Phase 5.2 (plan-write) have collapsed into the synthesis-confirmation step.
|
||||
|
||||
**Anti-pattern: numerical attestation.** "All nine requirements covered," "all three flows in scope," "five acceptance examples addressed," counts of files or test scenarios. These are the agent showing its work or attesting completeness, not naming scope decisions. "Covers the full brainstorm scope" already conveys the claim; the count adds nothing the user can affirm or redirect. Cut the numbers; keep the scope claim.
|
||||
|
||||
**A revision is not a confirmation.** After any user revision (even a trivially-understood swap), integrate the change, re-present the revised stage 2 with the change reflected, and wait for explicit confirmation before writing the plan. The loop is:
|
||||
|
||||
1. Present stage 2 → user responds
|
||||
2. User confirms → write the plan
|
||||
3. User revises → integrate, re-present revised stage 2, return to step 1
|
||||
|
||||
Plan-write (Phase 5.2) fires only on explicit confirm or after the soft-cut blocking question's "proceed" option. Never write immediately after a revision, even when the revision is small enough that the agent feels it understood — the confirmation step is what makes the synthesis **confirmed** rather than "agent's last proposal."
|
||||
|
||||
---
|
||||
|
||||
## Granularity: name the decision; don't expand it (shared)
|
||||
|
||||
Each call-out should be affirmable or rejectable by the user **without reading code**. Name the decision at the granularity that lets the user say "yes" or "I want X instead." Anything more specific is plan-body content — Phase 5.2's job, not synthesis's.
|
||||
|
||||
**Allowed** (when these ARE the decisions being made):
|
||||
- File / module names — "skip filter in the matcher" when "where to put it" is the choice
|
||||
- Pattern names — "extends the existing event-skip pattern" when "extend vs. introduce" is the choice
|
||||
- Column / table names — "user-TZ" or "destination-calendar TZ" when "which source" is the choice
|
||||
- Approach posture — "DB-side query with Google-side fallback" when "which strategy" is the choice
|
||||
|
||||
**Not allowed** (always plan-body, regardless of variant):
|
||||
- Line numbers (`route.ts:249-255`)
|
||||
- Exact method signatures, call graphs, or implementation flow ("at the top, before include/exclude evaluation, returning ...")
|
||||
- Exact JSON / response shapes (`{pause, cleanup: {eventsDeleted, eventsFailed, errors}}`)
|
||||
- HTTP status codes (`409`, `404`, `403`)
|
||||
- Exact event / activity-log / type names (`userPauseSet/userPauseEdited/...`)
|
||||
- Exact wording of error messages or UI labels
|
||||
- SQL syntax or query bodies
|
||||
|
||||
The line is drawn slightly differently per variant. **Solo (Phase 0.7)** stays at the higher level — brainstorm's WHAT hasn't been validated yet, so file/module names are usually too specific; talk in terms of "the rule entity," not "syncRules table." **Brainstorm-sourced (Phase 5.1.5)** allows the file / module / pattern / column level when those ARE plan-time decisions, but not implementation flow specifics.
|
||||
|
||||
### Bad-vs-good examples
|
||||
|
||||
| Plan-body in call-out (wrong) | Decision-level (right) |
|
||||
|---|---|
|
||||
| Timezone source: `users.timezone` (IANA), fallback to destination calendar TZ if null. Research found `useTimezoneSync` and `ProtectionStatsCalculator` establish the pattern. | Timezone source: user-TZ (reverses brainstorm's tentative lean — research found established infra and pattern precedent) |
|
||||
| Skip filter goes in `RuleMatcher.eventMatchesRule` at the top, before include/exclude evaluation, using the existing `filteredReason` mechanism. | Skip filter extends the existing event-skip pattern in the matcher (vs. introducing a new mechanism) |
|
||||
| Reactivation guard: explicit safety in `[ruleId]/route.ts` PATCH — when `isActive: false → true`, the existing handler clears `status/pausedAt/pausedReason`. | Reactivation guard: pause window state preserved through the isActive toggle's existing system-pause-clearing path |
|
||||
| Partial cleanup failure response: `{pause, cleanup: {eventsDeleted, eventsFailed, errors}}`; pause window persists regardless of cleanup outcome. | Partial cleanup failure: pause window persists; partial-failure response mirrors the existing rule-edit precedent |
|
||||
|
||||
The test: a scanner reading a call-out should affirm or reject it without needing to read code. If they would have to look up a column name, method name, or call graph to evaluate the call-out, the granularity is wrong — that's plan-body content.
|
||||
|
||||
### Worked example: compression from internal draft to call-outs
|
||||
|
||||
For a PII redaction gate proposal where the internal draft had 4 Stated items, 7 Inferred items, and 3 Out-of-scope items, the compressed stage 2 looks like:
|
||||
|
||||
```
|
||||
Planning a mechanical PII redaction gate before promote (the unguarded leak path from the amazon-orders retro) and alongside the existing vendor-prefix scanner at publish. Phase-1 detectors are shape-only — card last-4, postal address, JSON person names. Default halts; per-finding ack via flag.
|
||||
|
||||
**Call outs:**
|
||||
- Person-name filter works by JSON key (allowlist of attribution keys: `printer`, `printer_name`, `owner_name`, `author`), not by name value.
|
||||
- Promote scans the working-dir snapshot before the copy step, not the staged copy.
|
||||
- Publish combines PII + vendor-prefix findings into one report, not fail-fast on first.
|
||||
|
||||
Confirm and I'll proceed to research, drawing on this scope.
|
||||
```
|
||||
|
||||
What got cut from the internal draft and why:
|
||||
|
||||
- "Module name: `internal/artifacts/pii.go`" — plan-body content (file path), fails affirmability test
|
||||
- "Flag name: `--accept-redaction-list=<finding-id,...>`" — plan-body content (exact flag string), fails affirmability test
|
||||
- "No new dependencies — stdlib regexp + filepath.WalkDir only" — mechanical, no real alternative
|
||||
- "Detector regex precision tuned during implementation" — deferred-impl, not a plan-time fork
|
||||
- All three Out-of-scope items — either restated in prose ("defer to #960") or implicitly excluded by scope
|
||||
|
||||
What survived: three real forks where another reasonable agent might choose differently and the user can correct cheaply now. Each is affirmable in one sentence without reading code.
|
||||
|
||||
---
|
||||
|
||||
## Solo variant (Phase 0.7)
|
||||
|
||||
Fires only when:
|
||||
- Phase 0.2 found no upstream brainstorm doc
|
||||
- AND Phase 0.4 stayed in ce-plan (did not route to ce-debug, ce-work, or universal-planning)
|
||||
- AND Phase 0.5 cleared (no unresolved blockers)
|
||||
- AND not on Phase 0.1 fast paths (resume normal, deepen-intent)
|
||||
|
||||
Each guard is an explicit conditional in SKILL.md, not implicit. R2 solo does NOT fire on resume/deepen, route-out, or brainstorm-sourced paths.
|
||||
|
||||
**Content focus**: full-breadth internal draft. Phase 0.4 bootstrap is brief by design ("ask one or two clarifying questions"), so the agent has made substantial inferences before Phase 0.7 fires. The Inferred bucket in the internal draft is especially load-bearing here — the agent's bets are widest. Stage 2 compression still applies: most of those inferences will not survive the keep test, and that is correct — the user should only see the forks they can meaningfully redirect.
|
||||
|
||||
**Counter-warning for rich-context invocations.** When the inference source is *not* just Phase 0.4 bootstrap — e.g., a prior in-conversation validation agent, completed sibling work units earlier in the same session, or a planning artifact already in the conversation — the temptation is to dump that material into call-outs verbatim. The granularity rules tighten in this case, not loosen: the agent has more material to compress, not more material to expose. A bet that's already been validated upstream is **Stated** (internal), not Inferred (internal); a bet whose specifics belong in plan-body is named at decision-level in the call-out regardless of how much detail upstream context provided. If recent turns produced detailed code, file paths, or research artifacts, expect the internal draft to over-share and compress proactively before stage 2.
|
||||
|
||||
**Why pre-research, not pre-write**: research effort would be wasted if scope is wrong. Catching scope errors before sub-agent dispatch (Phase 1.1's repo-research-analyst, learnings-researcher, etc.) saves token and time cost.
|
||||
|
||||
### Stage 2 template (solo)
|
||||
|
||||
**Summary discipline (required):** describe **what scope the plan will target**, forward-looking (what *will* be planned), not retrospective. The summary's job is to help the user pattern-match against intent before reading call-outs — solo invocation has minimal pre-write dialogue, so the summary is especially load-bearing here. Form (prose, bullets, mix) and length follow the tier budget in "Stage 2: chat-time scoping synthesis" above; detail test applies per bullet.
|
||||
|
||||
**Anti-fluff guidance:** lead with the actual thing being planned in plain words. No qualifiers ("comprehensive," "thoughtful," "substantive"). No re-stating the user's prompt. If the scope cannot be said within the tier budget without filler, the synthesis isn't ready yet.
|
||||
|
||||
**Confirmation template (fires for Standard/Deep regardless of call-out count, or for any tier with one or more call-outs surviving):**
|
||||
|
||||
```
|
||||
Based on your request and our brief discussion, here's the scope I'm proposing to plan against:
|
||||
|
||||
[scope claim — what the plan will target, what it will not; affirm-or-redirect level; NOT an enumeration of Implementation Units]
|
||||
|
||||
**Call outs:** (omit this header when zero forks survived the keep test)
|
||||
- [decision-level fork in 1-2 lines: name the choice and optional one-clause trade-off in parens. NO multi-sentence rationale, NO "my default is X" pitch — those belong in Key Technical Decisions in the plan body, not the synthesis]
|
||||
|
||||
Confirm and I'll proceed to research, drawing on this scope. (You can also redirect to /ce-brainstorm if this is bigger than you initially thought — I'll stop here and load it for you.)
|
||||
```
|
||||
|
||||
**Auto-proceed template (fires only for Lightweight with zero call-outs):**
|
||||
|
||||
```
|
||||
Planning: [1-3 line scope claim]
|
||||
|
||||
No open decisions to weigh in on — proceeding to research. Interrupt if I have the scope wrong.
|
||||
```
|
||||
|
||||
Then continue to Phase 1 without waiting. Use prose for any user response that does arrive (no `AskUserQuestion` menu). Justification is Interaction Rule 5(a) in SKILL.md.
|
||||
|
||||
---
|
||||
|
||||
## Brainstorm-sourced variant (Phase 5.1.5)
|
||||
|
||||
Fires only when:
|
||||
- Phase 0.2 found upstream brainstorm doc (brainstorm-sourced invocation)
|
||||
- AND not on Phase 0.1 fast paths
|
||||
|
||||
**Content focus**: plan-time decisions only. The brainstorm + R1 synthesis already validated WHAT to build; the internal draft and stage 2 surface HOW the plan will execute that work — decisions the brainstorm did not make.
|
||||
|
||||
Items to surface in the internal draft:
|
||||
- **Files/modules to touch (and not touch)** — what the implementation reaches into
|
||||
- **Patterns extended vs. introduced new** — architectural decisions the agent made within confirmed scope (R2's content focus, not bias toward either direction)
|
||||
- **Test scope** — which existing-but-untested code is in/out of test scope for this work
|
||||
- **Refactor scope** — adjacent cleanup, if any, going to deferred items vs. active diff
|
||||
- **Cross-cutting impact** — auth, migrations, shared types when they're touched
|
||||
|
||||
Most of these will not survive the keep test as separate call-outs. Surface only the forks where another reasonable agent might choose differently and the user can correct cheaply now.
|
||||
|
||||
**Reads from doc body, not a synthesis section**: brainstorm docs do not have a `## Synthesis` section (the synthesis is a chat-time artifact in ce-brainstorm; only the prose summary embeds, as `## Summary`). Phase 5.1.5 derives plan-time decisions from the brainstorm doc's body sections — Summary, Problem Frame, Requirements, Key Decisions, Scope Boundaries — plus Phase 1 research. Older brainstorms that may have a legacy `## Synthesis` section work fine; that content is treated as supplementary, not authoritative, with the body sections taking precedence.
|
||||
|
||||
**Why pre-write, not pre-research**: brainstorm doc + R1 synthesis already validated WHAT, so research is well-targeted. Plan-time decisions emerge during research and structuring (Phases 1-4), so pre-write catches them at the latest cheap moment — before Phase 5.2 commits the plan to disk.
|
||||
|
||||
### Stage 2 template (brainstorm-sourced)
|
||||
|
||||
**Summary discipline (required):** describe **how the implementation approaches the work** at a high level — files/modules touched, patterns extended vs. introduced, scope boundaries the plan honors. Forward-looking (what *will* be in the plan), not retrospective. Brainstorm-validated WHAT is assumed; the summary covers HOW. Form (prose, bullets, mix) and length follow the tier budget in "Stage 2: chat-time scoping synthesis" above; detail test applies per bullet.
|
||||
|
||||
**Anti-fluff guidance:** lead with the actual implementation shape in plain words. No qualifiers, no re-stating the brainstorm's WHAT. If the summary just restates the brainstorm's Problem Frame, rewrite it to focus on plan-time decisions.
|
||||
|
||||
**Confirmation template (fires for Standard/Deep regardless of call-out count, or for any tier with one or more call-outs surviving):**
|
||||
|
||||
```
|
||||
The brainstorm scopes [1-2 sentence restatement of the brainstorm's scope as orientation; in the brainstorm's own vocabulary; NOT an enumeration of Implementation Units, constraints, or acceptance examples].
|
||||
|
||||
This plan [plan-specific scoping: what's covered vs. deferred vs. expanded relative to the brainstorm; test scope; any adjacent refactors pulled in or held out. Prose or bullets per substance].
|
||||
|
||||
**Call outs:** (omit this header when zero forks survived the keep test)
|
||||
- [plan-time fork in 1-2 lines: name the choice and optional one-clause trade-off in parens. NO multi-sentence rationale, NO "my default is X" pitch — those belong in Key Technical Decisions in the plan body, not the synthesis]
|
||||
|
||||
Confirm and I'll write the plan next, drawing on the brainstorm, research, and this synthesis.
|
||||
```
|
||||
|
||||
**Auto-proceed template (fires only for Lightweight with zero call-outs):**
|
||||
|
||||
```
|
||||
Planning [brief brainstorm-scope restatement] — [plan-specific shape in one clause].
|
||||
|
||||
No open decisions to weigh in on — proceeding to plan-write. Interrupt if I have the scope wrong.
|
||||
```
|
||||
|
||||
Then continue to Phase 5.2 without waiting. Use prose for any user response that does arrive. Justification is Interaction Rule 5(a).
|
||||
|
||||
---
|
||||
|
||||
## Soft-cut on circularity (shared)
|
||||
|
||||
Track which call-outs the user touched per round. The soft-cut blocking question fires **only when the same call-out is revised twice** (or a third-round revision targets a call-out already revised in round two). New-call-out revisions across rounds proceed without limit.
|
||||
|
||||
**Identity across rounds is by decision dimension, not surface wording.** A revision may cause stage 2 to re-derive — the same underlying fork can come back rephrased, merged with another call-out, or split into two. "Same call-out" means the same decision being made (e.g., "where does the scan run" stays one decision whether it's worded as "promote scans the working-dir snapshot" or "scan target: pre-copy working dir"). When a re-cut collapses multiple prior call-outs into one, the new combined call-out inherits the "touched" status of any of its constituents — soft-cut fires if any of those underlying decisions was already revised once before.
|
||||
|
||||
When the soft-cut fires, use the platform's blocking question tool with two options:
|
||||
|
||||
- `Proceed and continue to [research / plan-write]`
|
||||
- `Hold off — keep discussing before continuing`
|
||||
|
||||
Fall back to numbered list in chat only when no blocking tool exists or the call errors. Never silently skip.
|
||||
|
||||
---
|
||||
|
||||
## Headless mode (shared)
|
||||
|
||||
When the skill is invoked from an automated workflow such as LFG or any `disable-model-invocation` context, the skill runs in non-interactive mode (no synchronous user). The artifact is read by downstream skills (ce-doc-review, ce-work) and human reviewers (PR review).
|
||||
|
||||
**Stage 2 is moot in headless mode.** Compose the internal draft (stage 1) as usual, but skip the chat-time compression — there is no synchronous user to confirm to, no call-outs to derive, no auto-proceed announcement. Route the internal draft directly into the plan body via the doc-shape table below.
|
||||
|
||||
**Per-variant behavior** (the timing matters for which phases follow):
|
||||
|
||||
- **Solo variant (Phase 0.7)**: fires *before* research. Compose the internal draft and continue to Phase 1 research as normal. Inferred content is held until plan-write (Phase 5.2), where it routes to `## Assumptions`.
|
||||
- **Brainstorm-sourced variant (Phase 5.1.5)**: fires *after* research, before plan-write. Compose the internal draft and proceed to Phase 5.2 plan-write. Inferred content routes to `## Assumptions`.
|
||||
|
||||
**Shared behavior across both variants:**
|
||||
|
||||
- **No user prompt; no stage 2; no auto-proceed announcement.** All three are moot.
|
||||
- **Route internal-draft content with mode-aware shape:**
|
||||
- **Stated** content → Requirements (user-stated constraints, traced to origin's R-IDs when present)
|
||||
- **Out-of-scope** content → Scope Boundaries
|
||||
- **Inferred** content → `## Assumptions` section in the plan — explicitly labeled as un-validated agent bets. Do NOT route Inferred items into Key Technical Decisions or Implementation Units; that would make un-validated bets indistinguishable from user-confirmed decisions.
|
||||
|
||||
The `## Assumptions` section appears in non-interactive plans only. Interactive plans don't need it (Inferred bets either get user-corrected via call-outs and become Key Technical Decisions, are revised away, or were judged not-fork material by the keep test and dissolved into Implementation Units silently).
|
||||
|
||||
This restores the audit visibility the original design intended (un-validated bets must not propagate as authoritative content), but surfaces them under their own label rather than hiding them. Downstream review (ce-doc-review, ce-work, human PR review) can scrutinize Assumptions specifically.
|
||||
|
||||
---
|
||||
|
||||
## Self-redirect (shared)
|
||||
|
||||
If the user response indicates they're in the wrong skill or want a different workflow:
|
||||
|
||||
- **Solo variant**: common redirects include "this is bigger than I thought — let me brainstorm first" (suggest `/ce-brainstorm`), "this is just a fix, no plan needed" (suggest `/ce-work`), or "I need to investigate first" (suggest `/ce-debug`).
|
||||
- **Brainstorm-sourced variant**: less common, but possible — "actually this scope is wrong, take it back to brainstorm" (suggest `/ce-brainstorm` to revise the upstream doc).
|
||||
|
||||
In either case: stop ce-plan, suggest the alternative skill, offer to load it in-session. Don't push back or argue — the user's redirect signal is the deliberate choice.
|
||||
|
||||
---
|
||||
|
||||
## Doc shape after confirmation
|
||||
|
||||
After user confirmation (or after the soft-cut decision proceeds), Phase 5.2 writes the plan doc. The internal draft does NOT carry into the plan as a `## Synthesis` section. Only the stage-2 summary embeds, replacing the existing `## Overview` slot in the plan template (renamed to `## Summary` for terminology consistency). Internal-draft content dissolves into the plan's body sections:
|
||||
|
||||
| Internal-draft element | Where it goes in the plan |
|
||||
|---|---|
|
||||
| Summary (stage 2) | `## Summary` (1-3 lines prose, forward-looking) — rewrite to plan convention if the chat-time summary used bullets. Solo variant: scope being targeted. Brainstorm-sourced: implementation approach |
|
||||
| Stated bullets | `## Requirements` (R-IDs) and where relevant `## Problem Frame` for narrative context |
|
||||
| Inferred bullets | `## Key Technical Decisions` (with rationale) and Implementation Units when the bet drives a structural choice. In non-interactive mode, route to `## Assumptions` instead — see Headless mode above. |
|
||||
| Out-of-scope bullets | `## Scope Boundaries` — including the `### Deferred to Follow-Up Work` subsection when relevant |
|
||||
|
||||
No italic capture-context note (e.g., "Captured at Phase 0.7..."). It would leak engineering process into an artifact whose readers do not need that signal.
|
||||
|
||||
The plan's `## Summary` and `## Problem Frame` must serve distinct purposes: Summary answers "what is this plan proposing?" (forward-looking, 1-3 lines); Problem Frame answers "why does this proposal exist?" (backward-looking, paragraphs). Don't restate the proposal in Problem Frame; don't pad Summary with situational context.
|
||||
|
||||
---
|
||||
|
||||
## What does NOT belong in the synthesis
|
||||
|
||||
- Implementation code (no imports, exact method signatures, framework-specific syntax, JSON shapes, exact error message wording) — in chat output OR in the internal draft
|
||||
- Re-statement of the entire brainstorm doc — the synthesis is plan-perspective, not a copy
|
||||
- Defensive what-ifs and hedges — if a concern is real, state it as Inferred (internal); if speculation, drop it
|
||||
- The internal three-bucket draft pasted into chat as a verbatim user-facing artifact — that was the old shape and the volume problem it produced is why stage 2 exists. Compose internally, derive call-outs, present compressed
|
||||
- Open questions surfaced outside the buckets/call-outs — by synthesis time, every scope-shaping question must be in **Stated** (internal — asked and answered earlier), **Inferred** (internal — agent's bet for correction, surfaces as a call-out if it survives the keep test), or **Out** (internal — deliberately excluded). There is no fourth status
|
||||
- Floating questions adjacent to stage 2 — if a question genuinely cannot be defaulted, pause synthesis and resolve it before presenting. Pick the question shape that matches: a blocking multiple-choice tool when options are bounded and meaningfully distinct, prose when option sets would bias the answer per Interaction Rule 5(a). Integrate the answer, then present stage 2. Never present stage 2 with adjacent floating questions — that gives the user no clear resolution path
|
||||
@@ -0,0 +1,167 @@
|
||||
# Universal Planning Workflow
|
||||
|
||||
This file is loaded when ce-plan detects a non-software task (Phase 0.1b). It replaces the software-specific phases (0.2 through 5.1) with a domain-agnostic planning workflow.
|
||||
|
||||
## Before starting: verify classification
|
||||
|
||||
The detection stub in SKILL.md routes here for anything that isn't clearly software. Verify the classification is correct before proceeding:
|
||||
|
||||
- **Is this actually a software task?** The key distinction is task-type, not topic-domain. A study guide about Rust is non-software (producing educational content). A Rust library refactor is software (modifying code). If this is actually software, return to Phase 0.2 in the main SKILL.md.
|
||||
- **Is this a trivial single-fact lookup?** Only a question answerable from one fact with no research, retrieval, or judgment skips planning — answer it directly and stop, in the user's terms. Do not narrate that it "isn't a planning task" or explain the routing; that is process exhaust (see Veil of value below). Examples: "zsh: command not found: brew", "what's the capital of France." A question that needs multiple steps, any retrieval, or synthesis to answer well does **not** qualify: it is an answer-seeking task (see Disposition below), not a quick-help exit. When unsure, do not exit.
|
||||
- **Pipeline mode?** If invoked from LFG or any `disable-model-invocation` context: output "This is a non-software task. The LFG pipeline requires ce-work, which only supports software tasks. Use `/ce-plan` directly for non-software planning." and stop.
|
||||
|
||||
Once past these checks, commit to the task — do not bail because it looks like a "lookup" or "research question." The user invoked the planning tool on purpose. Then choose the disposition below.
|
||||
|
||||
---
|
||||
|
||||
## Disposition: plan-seeking vs. answer-seeking
|
||||
|
||||
Two kinds of task land here, with different deliverables:
|
||||
|
||||
- **Plan-seeking** — the deliverable is a *plan*: a trip itinerary, a study curriculum, an event runbook, a project plan. The plan is the artifact, saved or shared. → Follow Steps 1-3 below.
|
||||
- **Answer-seeking** — the deliverable is an *answer*: an investigative or analytical question ("how often does X happen — is it a big deal?", "how does our approach compare to Y?", "should we Z?"). No one wants a saved plan document for this; planning is the means to a good answer, not the output. → Follow the **Answer-seeking flow** below; skip the Step 3 artifact handling.
|
||||
|
||||
If a request blends both ("research X, then plan Y"), do the answer-seeking research first, then produce the plan artifact.
|
||||
|
||||
Commit to one disposition before reading further, and follow only that flow: a plan-seeking task still produces its plan document (Steps 1-3) and does not stop at a chat answer; an answer-seeking task does not write a plan file.
|
||||
|
||||
---
|
||||
|
||||
## Answer-seeking flow
|
||||
|
||||
The planning instinct still applies — but the plan is *working scaffold*, not an artifact. State it in chat to steer the work and show the human the approach; execute it; discard it. No plan file is written.
|
||||
|
||||
### State a brief plan-of-attack, then proceed
|
||||
|
||||
Say how the question will be answered, right-sized to it: a light question gets a one-line approach; a multi-part analytical question gets a short bulleted plan (a few steps). This is **non-blocking** — announce the approach and continue immediately. Do not ask the user to approve the plan; the stated approach is itself the checkpoint, and the user can interrupt if the framing is wrong. Stop to ask only on a genuine fork the agent cannot resolve (e.g., "his personal account or the org's?").
|
||||
|
||||
### Execute the plan
|
||||
|
||||
Carry out the approach. When the answer depends on facts the model can't reliably supply from memory — current data, recent events, specifics that drift — gather them using the **Research decomposition pattern** under Step 1 below (decompose into focused questions, dispatch in parallel via the platform's subagent/web primitive, collate). Skip research for anything the model already knows well.
|
||||
|
||||
**Ground answers about the user's own code, repo, or named artifacts in the actual sources — not memory.** When the question references local code, a specific file, a named CLI or service, or "our X", read those sources first (and any resource the user named — see Core Principle 8 in SKILL.md). "The model already knows the topic" covers general knowledge only, never the contents of the user's codebase: a comparison or recommendation about local code that was never read is ungrounded. Inspect, then answer.
|
||||
|
||||
**Execution here is research and analysis only — never code.** Reading code and artifacts to understand them is in-bounds research; writing or running code to change the system is not — that belongs in `ce-work`. This keeps the planning/execution boundary intact.
|
||||
|
||||
### Deliver the answer
|
||||
|
||||
Answer in chat. Do **not** write a plan file and do **not** run the Step 3 save/share menu by default. If the investigation produced something the user might want to keep (a comparison table, a sourced summary), offer to save it; otherwise just give the answer. In headless or non-interactive runs, skip the offer and deliver the answer.
|
||||
|
||||
### Veil of value: what to surface, what to hide
|
||||
|
||||
The plan-of-attack and the answer are for the caller; the skill's internal machinery is not. Edit for relevance the way an expert consultant does — they tell you their thinking about your problem, not which template their back office applied.
|
||||
|
||||
- **Surface** (question-domain — reads as value): the approach to the user's actual question, in the user's terms.
|
||||
- **Hide** (skill-domain — process exhaust): which skill, mode, or phase is running; whether a plan file was or wasn't written; the routing or disposition decision itself.
|
||||
- **Never hide** (audit content — affects trust in the answer): caveats, gaps, and uncertainty. "I could only pull his last ~100 stars, so this is partial" or "this is my read, not a hard signal" is not junk — it is what a good assistant surfaces. The veil hides plumbing, never the limits of the answer.
|
||||
|
||||
Register example, for "how often does he star things — is this a big deal?":
|
||||
|
||||
> Wrong: "Quick note first: /ce-plan builds implementation plans, so I ignored the template and just answered the question. Here's what the data says..."
|
||||
|
||||
Leaks the skill's name, narrates an internal routing decision, apologizes for deviating — the caller sees the seams of the tool.
|
||||
|
||||
> Right: "Let me size this up — I'll check how active a starrer he is overall, his recent cadence, and the kinds of repos he tends to star, then weigh where this one lands. [gathers data] Yes, this is a real signal: ..."
|
||||
|
||||
Same underlying process; none of the machinery surfaces. The caller sees thinking about their question.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Assess Ambiguity and Research Need
|
||||
|
||||
Evaluate two things before planning:
|
||||
|
||||
**Would 1-3 quick questions meaningfully improve this plan?**
|
||||
|
||||
- **Default: ask 1-3 questions** via Step 1b when the answers would change the plan's structure or content. Always include a final option like "Skip — just make the plan with reasonable assumptions" so the user can opt out instantly.
|
||||
- **Skip questions entirely** only when the request already specifies all major variables or the task is simple enough that reasonable assumptions cover it well.
|
||||
|
||||
**Research need — does this plan depend on facts that change faster than training data?**
|
||||
|
||||
| Research need | Signals | Action |
|
||||
|--------------|---------|--------|
|
||||
| **None** | Generic, timeless, or conceptual plan (study curriculum methodology, project management approach, personal goal breakdown) | Skip research. Model knowledge is sufficient. After structuring the plan, offer: "I based this on general knowledge. Want me to search for [specific thing research would improve]?" — e.g., sourced recipes, current product recommendations, expert frameworks. Only if the user accepts. |
|
||||
| **Recommended** | Plan references specific locations, venues, dates, prices, schedules, seasonal availability, or current events — anything where stale information would break the plan (closed restaurants, changed prices, cancelled events, wrong seasonal dates). | Research before planning. Decompose into 2-5 focused research questions and dispatch parallel web searches. In Claude Code, use the Agent tool with `model: "haiku"` for each search to reduce cost. Collate findings before structuring the plan. |
|
||||
|
||||
When research is recommended, do it — don't just offer. Stale recommendations (closed restaurants, rethemed attractions, outdated prices) are worse than no recommendations. The user invoked `/ce-plan` because they want a good plan, not a disclaimer about training data.
|
||||
|
||||
**Research decomposition pattern:**
|
||||
1. Identify 2-5 independent research questions based on the task. Good questions target facts the model is least confident about: current prices, hours, availability, recent changes, seasonal specifics.
|
||||
2. Dispatch parallel research. Prefer user-named surfaces first per Core Principle 8 in SKILL.md; fall back to web search for questions those surfaces don't cover.
|
||||
3. Collate findings into a brief research summary before proceeding to planning.
|
||||
|
||||
Example for "plan a date night in Seattle this Saturday":
|
||||
- "Best restaurants open late Saturday in Capitol Hill Seattle 2026"
|
||||
- "Events happening in Seattle [specific date]"
|
||||
- "Seattle waterfront current status and hours"
|
||||
|
||||
## Step 1b: Focused Q&A
|
||||
|
||||
Ask up to 3 questions targeting the unknowns that would most change the plan. Use the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
**How to ask well:**
|
||||
- Offer informed options, not open-ended blanks. Instead of "When are you going?", try "Mid-week visits have 30-40% shorter lines — are you flexible on timing?" The question should give the user a frame of reference, not just extract information.
|
||||
- Use multi-select when several independent choices can be captured in one question. This is compact and respects the user's time.
|
||||
- Always include a final option like **"Skip — just make the plan with reasonable assumptions"** so the user can opt out at any point.
|
||||
|
||||
Focus on the unknowns specific to this task that would change what the plan recommends or how it's structured. Do not ask more than 3 — after that, proceed with assumptions for anything remaining.
|
||||
|
||||
## Step 2: Structure the Plan
|
||||
|
||||
Create a structured plan guided by these quality principles. Do NOT use the software plan template (implementation units, test scenarios, file paths, etc.).
|
||||
|
||||
### Format: when to prescribe vs. present options
|
||||
|
||||
Not every plan should be a single linear path. Match the format to the task:
|
||||
|
||||
| Task type | Best format | Why |
|
||||
|-----------|------------|-----|
|
||||
| **High personal preference** (food, entertainment, activities, gifts) | Curated options per category — present 2-3 choices and let the user compose | Preferences vary; a single pick may miss. Options respect the user's taste. |
|
||||
| **Logical sequence** (study plan, project timeline, multi-day trip logistics) | Single prescriptive path with clear ordering | Sequencing matters; options at each step create decision paralysis. |
|
||||
| **Hybrid** (event with fixed structure but variable details) | Fixed structure with choice points marked | The skeleton is set but specific vendors/venues/activities are options. |
|
||||
|
||||
Example: A date night plan should present 2-3 restaurant options, 2-3 activity options, and a suggested flow — not pick one restaurant and build the whole evening around it. A study plan should prescribe a single weekly progression — not present 3 different curricula to choose from.
|
||||
|
||||
### Formatting: bullets over prose
|
||||
|
||||
- Prefer bullets and tables for actionable content (steps, options, logistics, budgets)
|
||||
- Use prose only for context, rationale, or explanations that connect the dots
|
||||
- Plans are for scanning and executing, not reading cover-to-cover
|
||||
|
||||
### Quality principles
|
||||
|
||||
- **Actionable steps**: Each step is specific enough to execute without further research
|
||||
- **Sequenced by dependency**: Steps are in the right order, with dependencies noted
|
||||
- **Time-aware**: When relevant, include timing, durations, deadlines, or phases
|
||||
- **Resource-identified**: Specify what's needed — tools, materials, people, budget, locations
|
||||
- **Contingency-aware**: For important decisions, note alternatives or what to do if plans change
|
||||
- **Appropriately detailed**: Match detail to task complexity. A weekend trip needs less structure than a 3-month curriculum. A dinner plan should be concise, not a 200-line document.
|
||||
- **Domain-appropriate format**: Choose a structure that fits the domain:
|
||||
- Itinerary for travel (day-by-day, with times and locations)
|
||||
- Syllabus or curriculum for study plans (topics, resources, milestones)
|
||||
- Runbook for events (timeline, responsibilities, logistics)
|
||||
- Project plan for business or operational tasks (phases, owners, deliverables)
|
||||
- Research plan for investigations (questions, methods, sources)
|
||||
- Options menu for preference-driven tasks (curated picks per category)
|
||||
|
||||
## Step 3: Save or Share
|
||||
|
||||
After structuring the plan, ask the user how they want to receive it using the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
**Question:** "Plan ready. How would you like to receive it?"
|
||||
|
||||
**Options:**
|
||||
|
||||
1. **Save to disk** — Write the plan as a markdown file. Ask where:
|
||||
- `docs/plans/` (only show if this directory exists)
|
||||
- Current working directory
|
||||
- `/tmp`
|
||||
- A custom path
|
||||
- Use filename convention: `YYYY-MM-DD-<descriptive-name>-plan.md`
|
||||
- Start the document with a `# Title` heading, followed by `Created: YYYY-MM-DD` on the next line. No YAML frontmatter.
|
||||
|
||||
2. **Open in Proof (web app) — review and comment to iterate with the agent** — Open the doc in Every's Proof editor, iterate with the agent via comments, or copy a link to share with others. Load the `ce-proof` skill to create and open the document.
|
||||
|
||||
3. **Save to disk AND open in Proof** — Do both: write the markdown file to disk and open the doc in Proof for review.
|
||||
|
||||
Do not offer `/ce-work` (software-only) or issue creation (not applicable to non-software plans).
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: ce-strategy
|
||||
description: "Create or maintain STRATEGY.md - the product's target problem, approach, users, key metrics, and tracks of work. Use when starting a new product, updating direction, or when prompts like 'write our strategy', 'update the roadmap', 'what are we working on', or 'set up the strategy doc' come up. Also triggers when ce-ideate, ce-brainstorm, or ce-plan need upstream grounding and no strategy doc exists yet."
|
||||
argument-hint: "[optional: section to revisit, e.g. 'metrics' or 'approach']"
|
||||
---
|
||||
|
||||
# Product Strategy
|
||||
|
||||
**Note: The current year is 2026.** Use this when dating the strategy document.
|
||||
|
||||
`ce-strategy` produces and maintains `STRATEGY.md` - a short, durable anchor document that captures what the product is, who it serves, how it succeeds, and where the team is investing. It lives at the repo root as a canonical, well-known file (peer of `README.md`). Downstream skills (`ce-ideate`, `ce-brainstorm`, `ce-plan`) read it as grounding when it exists.
|
||||
|
||||
The document is short and structured on purpose. Good answers to a handful of sharp questions produce a better strategy than any amount of prose. This skill asks those questions, pushes back on weak answers, and writes the doc.
|
||||
|
||||
## Interaction Method
|
||||
|
||||
Default to the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
|
||||
|
||||
Ask one question at a time. Prefer free-form responses for the substantive sections (problem, approach, persona); reserve single-select for routing decisions (which section to revisit). Each option label must be self-contained.
|
||||
|
||||
## Focus Hint
|
||||
|
||||
<focus_hint> #$ARGUMENTS </focus_hint>
|
||||
|
||||
Interpret any argument as an optional focus: a section name to revisit (`metrics`, `approach`, `tracks`) or a scope hint. With no argument, proceed open-ended and let the file state decide the path.
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Anchor, not plan.** Strategy is what the product is and why. Features belong in `ce-brainstorm`; schedules belong in the issue tracker. Do not let either creep into the doc.
|
||||
2. **Rigor in the questions, not the headings.** The section headers are plain English. The interview questions enforce strategy discipline.
|
||||
3. **Short is a feature.** The template is constrained. Adding sections costs more than it looks like. Push back on expansion.
|
||||
4. **Durable across runs.** This skill is rerunnable. On a second run it updates in place, preserves what is working, and only challenges sections that look stale or weak.
|
||||
|
||||
## Execution Flow
|
||||
|
||||
### Phase 0: Route by File State
|
||||
|
||||
Read `STRATEGY.md` using the native file-read tool.
|
||||
|
||||
- **File does not exist** -> First run. Go to Phase 1.
|
||||
- **File exists and argument names a specific section** -> Targeted update. Go to Phase 2.
|
||||
- **File exists, no argument** -> Ask which section(s) to revisit, then Phase 2.
|
||||
|
||||
Announce the path in one line: "Strategy doc not found - let's write it." or "Found existing strategy - let's review and update."
|
||||
|
||||
### Phase 1: First-Run Interview
|
||||
|
||||
Read `references/interview.md`. This load is non-optional - the pushback rules, anti-pattern examples, and quality bar for each section live there. Improvising from memory produces a passive transcription instead of a strategy doc.
|
||||
|
||||
Run the interview in the section order of the final document:
|
||||
|
||||
1. Target problem
|
||||
2. Our approach
|
||||
3. Who it's for
|
||||
4. Key metrics
|
||||
5. Tracks
|
||||
6. Milestones (optional)
|
||||
7. Not working on (optional)
|
||||
8. Marketing (optional)
|
||||
|
||||
For each section, ask the opening question, apply the pushback rules, and capture the final answer in the user's own language. Do not skip the pushback step - it is the core of the skill. Two rounds of pushback per section maximum; capture what the user has given after that and note the section is worth revisiting on the next run.
|
||||
|
||||
When all required sections (1-5) are captured, read `references/strategy-template.md`, fill it in, and present the full draft in chat before writing. Offer one round of edits. Then write to `STRATEGY.md`.
|
||||
|
||||
### Phase 2: Update Run
|
||||
|
||||
Read the existing `STRATEGY.md` thoroughly. Summarize current state in 3-5 lines so the user sees what is on file.
|
||||
|
||||
If the argument named a specific section, jump to that section in `references/interview.md`. Preserve all other sections exactly. Apply pushback as if this were a first run - do not rubber-stamp existing weak content just because it is already written.
|
||||
|
||||
If no specific target, ask the user which section to revisit using the blocking question tool. Options:
|
||||
|
||||
- "Target problem"
|
||||
- "Our approach"
|
||||
- "Who it's for"
|
||||
- "Metrics, tracks, or other"
|
||||
|
||||
For each revisited section, re-interview with full pushback. For sections the user confirms are still accurate, leave them untouched. Update the `last_updated` value in the YAML frontmatter to today's ISO date.
|
||||
|
||||
Write the updated doc back to `STRATEGY.md`.
|
||||
|
||||
### Phase 3: Downstream Handoff
|
||||
|
||||
After writing, note in one line where the file lives and that `ce-ideate`, `ce-brainstorm`, and `ce-plan` will pick it up as grounding on their next run.
|
||||
|
||||
If no downstream skill has run yet on this repo, suggest `ce-ideate` or `ce-brainstorm` skills as a next step.
|
||||
|
||||
## What This Skill Does Not Do
|
||||
|
||||
- Does not update the issue tracker or reconcile in-flight work. Strategy is the doc; execution lives elsewhere.
|
||||
- Does not prioritize the backlog. Prioritization is a separate workflow.
|
||||
- Does not write product requirements or implementation plans - those are `ce-brainstorm` and `ce-plan`.
|
||||
- Does not compute metric values. It records which metrics matter and where they live, not what they read today.
|
||||
|
||||
## Learn More
|
||||
|
||||
The "Target problem / Our approach / Tracks" structure is informed by Richard Rumelt's *Good Strategy Bad Strategy* - specifically his kernel of diagnosis, guiding policy, and coherent action. The interview questions in `references/interview.md` are designed to push past the patterns he calls "bad strategy": fluff, goals dressed up as strategy, and feature lists in place of a guiding choice. The book is the recommended follow-up reading if the distinction between a slogan and a strategy is not yet sharp.
|
||||
@@ -0,0 +1,143 @@
|
||||
# Strategy Interview
|
||||
|
||||
Loaded by `SKILL.md` at the start of Phase 1 and revisited per-section in Phase 2. Every section below maps one-to-one to a section in `strategy-template.md`.
|
||||
|
||||
For each section: ask the opening question, evaluate the answer against the quality bar, push back when it falls into a named anti-pattern, and capture the final answer in the user's own language.
|
||||
|
||||
## Overall Rules
|
||||
|
||||
1. **Ask, don't prescribe.** Do not offer menu options for open answers (problem, approach, persona). Use free-form responses. Reserve multi-select for routing decisions.
|
||||
2. **Push back once, maybe twice.** If the first answer is weak, name the specific issue and ask a sharper question. If the second answer is still weak, capture what the user has given and note in the draft that the section is worth revisiting. Do not let the interview spiral.
|
||||
3. **Quote the user back at them.** When challenging an answer, use the user's own words verbatim. Paraphrasing softens the challenge and is easier to dismiss.
|
||||
4. **Keep each answer to 1-3 sentences.** Longer answers are usually hiding something vague. If the user writes a paragraph, ask them to pick the sentence that matters most.
|
||||
5. **Don't leak the anti-pattern names.** The user does not need to hear "that's a vanity metric" - just ask the sharper question that follows.
|
||||
|
||||
---
|
||||
|
||||
## 1. Target Problem
|
||||
|
||||
**Opening question:** "What's the core problem this product solves - and what makes that problem hard?"
|
||||
|
||||
Strong answers name a specific situation the target user is in, identify what makes the situation hard *right now* (a crux, a constraint, something that isn't easy to route around), and are falsifiable - you could imagine the problem being absent and know the difference.
|
||||
|
||||
**Anti-patterns and pushback:**
|
||||
|
||||
- **Goal stated as problem** ("the problem is we need to grow revenue") -> "That's a goal, not a problem. What's in the world that's making that goal hard to achieve? Whose situation are you changing?"
|
||||
- **Vague wish** ("people need better tools for X") -> "Whose situation specifically? Doing what? What do they try today, and why doesn't it work?"
|
||||
- **Symptom, not cause** ("users churn after 30 days") -> "That's a symptom. What's happening in their world that makes them stop caring? What's the underlying condition?"
|
||||
- **Too broad** ("communication at work is broken") -> "That's a civilization-scale problem. Narrow it to a situation you can actually affect - which users, doing what, when does it hurt most?"
|
||||
- **Feature-shaped** ("there's no good way to do [specific workflow] with AI") -> "That's a missing feature, not the underlying problem. What outcome do users want that the feature would give them?"
|
||||
|
||||
**Capture:** One or two sentences naming the user's situation and the crux. No solution language.
|
||||
|
||||
---
|
||||
|
||||
## 2. Our Approach
|
||||
|
||||
**Opening question:** "Given that problem, what's your approach - the commitment or principle that makes it tractable?"
|
||||
|
||||
This is the guiding choice: how the product competes or operates, so that many downstream decisions become easier. It is not the product and it is not a feature list.
|
||||
|
||||
Strong answers are a choice (implying alternatives explicitly *not* pursued), are general enough to direct many decisions but specific enough to rule things out, and sound more like "we win by [doing X differently]" than "we do [a list of things]".
|
||||
|
||||
**Anti-patterns and pushback:**
|
||||
|
||||
- **Fluff / values** ("we're customer-obsessed and move fast") -> "Those are values, not an approach. What are you doing *differently* from the other products users could pick? If the answer applies to any company, it's not your approach."
|
||||
- **Feature list** ("we're building AI-powered X, Y, and Z") -> "That's a feature list. What's the underlying bet that makes you pick those features over others? What principle is guiding what you ship?"
|
||||
- **Product description as approach** ("we use AI to draft replies") -> "That's what the product does, but what's the *choice* inside it? Every competitor will say the same thing. Your approach should name what you're doing that the obvious alternative isn't - is it a grounding choice, a trust-building commitment, a workflow bet? What are you betting on that they're not?"
|
||||
- **Goal restated** ("our approach is to be the market leader") -> "That's still the goal. How does the product win? What choice are you making that competitors aren't?"
|
||||
- **Multiple approaches at once** ("we're going deep on enterprise, self-serve, and a consumer app") -> "Pick one as the guiding approach. The others may still get work, but one of them organizes the rest. Which is it?"
|
||||
- **Doesn't connect to the problem** (problem: "users can't trust AI output"; approach: "build a fast, beautiful UI") -> "How does that approach solve the problem you named? If there's no line between them, one of the two is wrong."
|
||||
|
||||
**Capture:** One or two sentences. Ideally ends with or implies "...so that [outcome tied to the problem]".
|
||||
|
||||
---
|
||||
|
||||
## 3. Who It's For
|
||||
|
||||
**Opening question:** "Who is the primary user, and what job are they hiring this product to do?"
|
||||
|
||||
Jobs-to-be-done framing - the user isn't a demographic, they're someone in a situation trying to make progress.
|
||||
|
||||
Strong answers name one primary persona (additional personas allowed but secondary), identify them by role or situation rather than demographic, and state a concrete job as a verb phrase.
|
||||
|
||||
**Anti-patterns and pushback:**
|
||||
|
||||
- **Too many primary personas** ("it's for founders, PMs, engineers, and designers") -> "If it's for everyone, it's for no one. Who matters most? The others can still benefit, but one of them drives the product decisions."
|
||||
- **Demographic framing** ("25-45 year old professionals") -> "That's a demographic, not a user. What are they trying to do that makes them pick up this product?"
|
||||
- **Role without situation** ("PMs") -> "PMs doing what? Running a roadmap review? Writing a spec at midnight? Convincing a skeptical eng lead? The situation is where the product matters."
|
||||
- **Generic job** ("they want to be more productive") -> "Productive at what specifically? They're hiring this product to do *what*? The more specific, the better the product decisions downstream."
|
||||
|
||||
**Capture:** Persona name plus JTBD sentence. Example: "Solo founders running their own roadmap. They're hiring the product to keep strategy and execution aligned without a PM on staff."
|
||||
|
||||
---
|
||||
|
||||
## 4. Key Metrics
|
||||
|
||||
**Opening question:** "What 3-5 metrics will tell you whether the approach is working?"
|
||||
|
||||
Metrics are the feedback loop. Bad metrics create the illusion of progress while the product gets worse.
|
||||
|
||||
Strong answers stay at 3-5 (not 10), mix leading and lagging (something that moves weekly and something that moves quarterly), and could plausibly regress if the product got worse.
|
||||
|
||||
**Anti-patterns and pushback:**
|
||||
|
||||
- **Vanity metrics** ("total signups, total pageviews, cumulative users") -> "Those can all go up while the product gets worse. What moves when users actually get value?"
|
||||
- **Too many** ("here are 12 metrics we watch") -> "A dashboard isn't a strategy. Pick the 3-5 you'd stake the quarter on. What are the others telling you that those don't?"
|
||||
- **Outputs, not outcomes** ("ship velocity, deploys per week") -> "Those measure the team, not the product. If the team doubled velocity but users didn't care, would you call it a win?"
|
||||
- **Can only go up** ("cumulative hours saved") -> "A metric that can only go up doesn't tell you much. What's the rate, the ratio, or the thing that can regress?"
|
||||
- **Unmeasurable** ("user delight") -> "How specifically? If you can't define how you'd check it on a Tuesday, it's aspirational, not a metric."
|
||||
|
||||
**Capture:** A list of 3-5. Each with a one-line definition. Note where each is measured (analytics, DB, qualitative, etc.) if known. If measurement is undefined, ask: "Where does this metric live today? If nowhere, is this something you can start measuring?"
|
||||
|
||||
---
|
||||
|
||||
## 5. Tracks
|
||||
|
||||
**Opening question:** "What are the 2-4 tracks of work you're investing in to execute the approach?"
|
||||
|
||||
Tracks are the coherent-actions half of the strategy kernel - concrete areas of investment that flow from the approach. They are not feature lists and not personal todo items. Each track is a named *domain of work*.
|
||||
|
||||
Strong answers stay at 2-4 (not 8, not 1), connect clearly back to the approach, and are broad enough that multiple features live inside each one.
|
||||
|
||||
**Anti-patterns and pushback:**
|
||||
|
||||
- **Feature list in disguise** ("track 1: Slack integration; track 2: mobile app; track 3: dark mode") -> "Those are features. What's the *investment area* each one lives inside? 'Integrations' might be one track, with Slack, Teams, and Discord as candidates inside it."
|
||||
- **Too many tracks** ("we have 7 tracks this quarter") -> "With 7 tracks, every track is starved for attention. Which 3 are load-bearing? The others either fold in or drop."
|
||||
- **Doesn't connect to approach** (approach: "win by being the easiest to onboard"; track: "enterprise SSO") -> "How does that track serve the approach? If it's a separate bet, name it as one. If it's load-bearing for onboarding, explain the link."
|
||||
- **Too vague** ("improve the product") -> "Every track is 'improve the product.' What's the specific investment area that's different from the others?"
|
||||
- **One track only** -> "With one track, there's no real choice being made. What are the 2-3 things the product needs to be good at, and how are they different?"
|
||||
|
||||
**Capture:** 2-4 tracks. For each: a name, a one-line purpose, and a short note on why this serves the approach.
|
||||
|
||||
---
|
||||
|
||||
## 6. Milestones (optional)
|
||||
|
||||
**Opening question:** "Are there any dated milestones worth anchoring - a launch, a fundraise, a conference, a renewal? Skip if none apply."
|
||||
|
||||
Only capture externally visible, real milestones. Avoid turning this into an internal schedule.
|
||||
|
||||
Default is to skip. Do not push the user to invent milestones. If they name some, capture them verbatim with dates.
|
||||
|
||||
---
|
||||
|
||||
## 7. Not Working On (optional)
|
||||
|
||||
**Opening question:** "Is there anything you've explicitly decided *not* to do right now that's worth naming? This is for things the team keeps being tempted by."
|
||||
|
||||
Clarity tool, not a blocker list. Skip by default. If the user names items, one sentence each. Do not encourage a long list.
|
||||
|
||||
---
|
||||
|
||||
## 8. Marketing (optional)
|
||||
|
||||
**Opening question:** "Any positioning or narrative language you want the doc to carry - a one-liner, a tagline, a key message? Skip if not yet."
|
||||
|
||||
Skip by default. Keep to 2-3 lines if present.
|
||||
|
||||
---
|
||||
|
||||
## After the Interview
|
||||
|
||||
Once sections 1-5 are captured (and any optional sections the user engaged with), read `strategy-template.md` and fill it in. Present the full draft in chat before writing. Offer one edit round. Then write to `STRATEGY.md`.
|
||||
@@ -0,0 +1,89 @@
|
||||
# Strategy Template
|
||||
|
||||
Loaded by `SKILL.md` after the interview is complete. Fill it in using the captured answers and write to `STRATEGY.md`.
|
||||
|
||||
## Rules for filling in
|
||||
|
||||
- Use the user's own language where possible. Do not paraphrase into generic PM-speak.
|
||||
- Each section stays compact. The whole doc should read in under 5 minutes.
|
||||
- Section order is locked. Do not add new top-level sections.
|
||||
- Optional sections: delete entirely if unused. Do not leave empty headers.
|
||||
- Set `last_updated` in the YAML frontmatter to today's ISO date (YYYY-MM-DD). Do not duplicate the date in prose.
|
||||
- Set `name` in the frontmatter to the product or initiative name (the same value used in the H1 title).
|
||||
|
||||
## Template
|
||||
|
||||
The block below is the literal file to write (minus this line and the fences). Replace every `{{placeholder}}` with the captured answer. Delete any optional section whose placeholder wasn't answered.
|
||||
|
||||
~~~markdown
|
||||
---
|
||||
name: {{product_name}}
|
||||
last_updated: {{YYYY-MM-DD}}
|
||||
---
|
||||
|
||||
# {{product_name}} Strategy
|
||||
|
||||
## Target problem
|
||||
|
||||
{{1-2 sentence diagnosis. Names the user situation and the crux that makes it hard. No solution language.}}
|
||||
|
||||
## Our approach
|
||||
|
||||
{{1-2 sentence guiding policy. What this product commits to, so that the target problem becomes tractable.}}
|
||||
|
||||
## Who it's for
|
||||
|
||||
**Primary:** {{Persona name}} - {{one-sentence JTBD, e.g. "They're hiring {{product_name}} to..."}}
|
||||
|
||||
<!-- Duplicate the block above for additional personas only if truly necessary. Fewer is better. -->
|
||||
|
||||
## Key metrics
|
||||
|
||||
- **{{metric 1 name}}** - {{one-line definition; where it's measured}}
|
||||
- **{{metric 2 name}}** - {{...}}
|
||||
- **{{metric 3 name}}** - {{...}}
|
||||
|
||||
<!-- 3-5 total. Stop at 5. -->
|
||||
|
||||
## Tracks
|
||||
|
||||
### {{Track 1 name}}
|
||||
|
||||
{{One line: what this track is - the investment area, not a feature list.}}
|
||||
|
||||
_Why it serves the approach:_ {{one line}}
|
||||
|
||||
<!-- Duplicate the block above for 2-4 tracks total. If you can't keep it to 4, something is wrong - fold related tracks together. -->
|
||||
|
||||
## Milestones
|
||||
|
||||
- **{{YYYY-MM-DD}}** - {{milestone}}
|
||||
|
||||
<!-- Optional. Delete the section if unused. Only externally visible milestones: launches, fundraises, conferences, renewals. -->
|
||||
|
||||
## Not working on
|
||||
|
||||
- {{one line per item}}
|
||||
|
||||
<!-- Optional. Delete the section if unused. Use only for things the team keeps being tempted by. -->
|
||||
|
||||
## Marketing
|
||||
|
||||
**One-liner:** {{single-sentence pitch}}
|
||||
|
||||
**Key message:** {{2-3 lines if useful}}
|
||||
|
||||
<!-- Optional. Delete the section if unused. -->
|
||||
~~~
|
||||
|
||||
## Post-write checklist
|
||||
|
||||
Before confirming the write, scan the draft for:
|
||||
|
||||
- [ ] Frontmatter present at the top with `name` and `last_updated` keys.
|
||||
- [ ] `last_updated` carries today's date in ISO format (YYYY-MM-DD).
|
||||
- [ ] No section has more than 4 sentences except Tracks (where each track has its own short block).
|
||||
- [ ] No placeholders remain (`{{...}}`).
|
||||
- [ ] Optional sections with no content have been deleted, not left empty.
|
||||
- [ ] Metric count is between 3 and 5. Track count is between 2 and 4.
|
||||
- [ ] Target problem and Our approach are connected - one clearly responds to the other.
|
||||
@@ -0,0 +1,369 @@
|
||||
---
|
||||
name: ce-work
|
||||
description: Execute work efficiently while maintaining quality and finishing features
|
||||
argument-hint: "[Plan doc path or description of work. Blank to auto use latest plan doc]"
|
||||
---
|
||||
|
||||
# Work Execution Command
|
||||
|
||||
Execute work efficiently while maintaining quality and finishing features.
|
||||
|
||||
## Introduction
|
||||
|
||||
This command takes a work document (plan or specification) or a bare prompt describing the work, and executes it systematically. The focus is on **shipping complete features** by understanding requirements quickly, following existing patterns, and maintaining quality throughout.
|
||||
|
||||
## Input Document
|
||||
|
||||
<input_document> #$ARGUMENTS </input_document>
|
||||
|
||||
## Execution Workflow
|
||||
|
||||
### Phase 0: Input Triage
|
||||
|
||||
Determine how to proceed based on what was provided in `<input_document>`.
|
||||
|
||||
**Plan document** (input is a file path to an existing plan or specification) → skip to Phase 1.
|
||||
|
||||
**Bare prompt** (input is a description of work, not a file path):
|
||||
|
||||
1. **Scan the work area**
|
||||
|
||||
- Identify files likely to change based on the prompt
|
||||
- Find existing test files for those areas (search for test/spec files that import, reference, or share names with the implementation files)
|
||||
- Note local patterns and conventions in the affected areas
|
||||
|
||||
2. **Assess complexity and route**
|
||||
|
||||
| Complexity | Signals | Action |
|
||||
|-----------|---------|--------|
|
||||
| **Trivial** | 1-2 files, no behavioral change (typo, config, rename) | Proceed to Phase 1 step 2 (environment setup), then implement directly — no task list, no execution loop. Apply Test Discovery if the change touches behavior-bearing code |
|
||||
| **Small / Medium** | Clear scope, under ~10 files | Build a task list from discovery. Proceed to Phase 1 step 2 |
|
||||
| **Large** | Cross-cutting, architectural decisions, 10+ files, touches auth/payments/migrations | Inform the user this would benefit from `/ce-brainstorm` or `/ce-plan` to surface edge cases and scope boundaries. Honor their choice. If proceeding, build a task list and continue to Phase 1 step 2 |
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: Quick Start
|
||||
|
||||
1. **Read Plan and Clarify** _(skip if arriving from Phase 0 with a bare prompt)_
|
||||
|
||||
- Read the work document completely. Plans may be markdown (`.md`) or HTML (`.html`) — both formats are read as text linearly. HTML plans carry the same section names and IDs as markdown plans, just wrapped in semantic HTML elements (`<section>`, `<article>`, etc.); section-finding works the same way (substring match on section names, ignoring HTML wrapper noise).
|
||||
- When auto-detecting the latest plan (blank invocation), glob `docs/plans/*.md` AND `docs/plans/*.html` and pick the most recent regardless of extension.
|
||||
- Treat the plan as a decision artifact, not an execution script
|
||||
- If the plan includes sections such as `Implementation Units`, `Work Breakdown`, `Requirements` (or legacy `Requirements Trace`), `Files`, `Test Scenarios`, or `Verification`, use those as the primary source material for execution
|
||||
- Check for `Execution note` on each implementation unit — these carry the plan's execution posture signal for that unit (for example, test-first or characterization-first). Note them when creating tasks.
|
||||
- Check for a `Deferred to Implementation` or `Implementation-Time Unknowns` section — these are questions the planner intentionally left for you to resolve during execution. Note them before starting so they inform your approach rather than surprising you mid-task
|
||||
- Check for a `Scope Boundaries` section — these are explicit non-goals. Refer back to them if implementation starts pulling you toward adjacent work
|
||||
- Review any references or links provided in the plan
|
||||
- If the user explicitly asks for TDD, test-first, or characterization-first execution in this session, honor that request even if the plan has no `Execution note`
|
||||
- If anything is unclear or ambiguous, ask clarifying questions now
|
||||
- If clarifying questions were needed above, get user approval on the resolved answers. If no clarifications were needed, proceed without a separate approval step — plan scope is the plan's authority, not something to renegotiate
|
||||
- **Do not skip this** - better to ask questions now than build the wrong thing
|
||||
- **Do not edit the plan body during execution.** The plan is a decision artifact; progress lives in git commits and the task tracker. The only plan mutation during ce-work is the final `status: active → completed` flip at shipping (see `references/shipping-workflow.md` Phase 4 Step 2). Legacy plans may contain `- [ ]` / `- [x]` marks on unit headings — ignore them as state; per-unit completion is determined during execution by reading the current file state.
|
||||
|
||||
2. **Setup Environment**
|
||||
|
||||
First, check the current branch:
|
||||
|
||||
```bash
|
||||
current_branch=$(git branch --show-current)
|
||||
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
|
||||
|
||||
# Fallback if remote HEAD isn't set
|
||||
if [ -z "$default_branch" ]; then
|
||||
default_branch=$(git rev-parse --verify origin/main >/dev/null 2>&1 && echo "main" || echo "master")
|
||||
fi
|
||||
```
|
||||
|
||||
**If already on a feature branch** (not the default branch):
|
||||
|
||||
First, check whether the branch name is **meaningful** — a name like `feat/crowd-sniff` or `fix/email-validation` tells future readers what the work is about. Auto-generated worktree names (e.g., `worktree-jolly-beaming-raven`) or other opaque names do not.
|
||||
|
||||
If the branch name is meaningless or auto-generated, suggest renaming it before continuing:
|
||||
```bash
|
||||
git branch -m <meaningful-name>
|
||||
```
|
||||
Derive the new name from the plan title or work description (e.g., `feat/crowd-sniff`). Present the rename as a recommended option alongside continuing as-is.
|
||||
|
||||
Then ask: "Continue working on `[current_branch]`, or create a new branch?"
|
||||
- If continuing (with or without rename), proceed to step 3
|
||||
- If creating new, follow Option A or B below
|
||||
|
||||
**If on the default branch**, choose how to proceed:
|
||||
|
||||
**Option A: Create a new branch**
|
||||
```bash
|
||||
git pull origin [default_branch]
|
||||
git checkout -b feature-branch-name
|
||||
```
|
||||
Use a meaningful name based on the work (e.g., `feat/user-authentication`, `fix/email-validation`).
|
||||
|
||||
**Option B: Use a worktree (recommended for parallel development)**
|
||||
```bash
|
||||
skill: ce-worktree
|
||||
# The skill will create a new branch from the default branch in an isolated worktree
|
||||
```
|
||||
|
||||
**Option C: Continue on the default branch**
|
||||
- Requires explicit user confirmation
|
||||
- Only proceed after user explicitly says "yes, commit to [default_branch]"
|
||||
- Never commit directly to the default branch without explicit permission
|
||||
|
||||
**Recommendation**: Use worktree if:
|
||||
- You want to work on multiple features simultaneously
|
||||
- You want to keep the default branch clean while experimenting
|
||||
- You plan to switch between branches frequently
|
||||
|
||||
3. **Create Task List** _(skip if Phase 0 already built one, or if Phase 0 routed as Trivial)_
|
||||
- Use the platform's task tracking tool (`TaskCreate`/`TaskUpdate`/`TaskList` in Claude Code, `update_plan` in Codex, or the equivalent on other harnesses) to break the plan into actionable tasks
|
||||
- Derive tasks from the plan's implementation units, dependencies, files, test targets, and verification criteria
|
||||
- When the plan defines U-IDs for Implementation Units, preserve the unit's U-ID as a prefix in the task subject (e.g., "U3: Add parser coverage"). This keeps blocker references, deferred-work notes, and final summaries anchored to the same identifier the plan uses, so progress and traceability remain unambiguous across plan edits
|
||||
- Carry each unit's `Execution note` into the task when present
|
||||
- For each unit, read the `Patterns to follow` field before implementing — these point to specific files or conventions to mirror
|
||||
- Use each unit's `Verification` field as the primary "done" signal for that task
|
||||
- Do not expect the plan to contain implementation code, micro-step TDD instructions, or exact shell commands
|
||||
- Include dependencies between tasks
|
||||
- Prioritize based on what needs to be done first
|
||||
- Include testing and quality check tasks
|
||||
- Keep tasks specific and completable
|
||||
|
||||
4. **Choose Execution Strategy**
|
||||
|
||||
After creating the task list, decide how to execute based on the plan's size and dependency structure:
|
||||
|
||||
| Strategy | When to use |
|
||||
|----------|-------------|
|
||||
| **Inline** | 1-2 small tasks, or tasks needing user interaction mid-flight. **Default for bare-prompt work** — bare prompts rarely produce enough structured context to justify subagent dispatch |
|
||||
| **Serial subagents** | 3+ tasks with dependencies between them. Each subagent gets a fresh context window focused on one unit — prevents context degradation across many tasks. Requires plan-unit metadata (Goal, Files, Approach, Test scenarios) |
|
||||
| **Parallel subagents** | 3+ tasks that pass the Parallel Safety Check (below). Dispatch independent units simultaneously, run dependent units after their prerequisites complete. Requires plan-unit metadata |
|
||||
|
||||
**Parallel Safety Check** — required before choosing parallel dispatch:
|
||||
|
||||
1. Build a file-to-unit mapping from every candidate unit's `Files:` section (Create, Modify, and Test paths)
|
||||
2. Check for intersection — any file path appearing in 2+ units means overlap
|
||||
3. **If overlap is found AND worktree isolation is unavailable**: downgrade to serial subagents. Log the reason (e.g., "Units 2 and 4 share `config/routes.rb` — using serial dispatch"). Serial subagents still provide context-window isolation without shared-directory write races.
|
||||
4. **If overlap is found AND worktree isolation is available**: parallel dispatch is still safe — subagents work in isolation, and the overlap surfaces as a predictable merge conflict the orchestrator handles via the post-batch flow below. Log the predicted overlap so the post-batch flow knows which merges to expect conflicts on.
|
||||
|
||||
Even with no file overlap, parallel subagents sharing the orchestrator's working directory face git index contention (concurrent staging/committing corrupts the index) and test interference (concurrent test runs pick up each other's in-progress changes). Worktree isolation eliminates both; the shared-directory fallback constraints below mitigate them.
|
||||
|
||||
**Subagent isolation** — give each parallel subagent its own working tree:
|
||||
- **Claude Code (`Agent` tool):** pass `isolation: "worktree"` and `run_in_background: true`. The harness creates a per-subagent worktree under `.claude/worktrees/agent-<id>` on its own branch. Verify `.claude/worktrees/` is gitignored before relying on this.
|
||||
- **Other platforms** without built-in worktree isolation (e.g., Codex `spawn_agent`, Pi `subagent`): subagents share the orchestrator's directory.
|
||||
|
||||
**Subagent dispatch** uses your available subagent or task spawning mechanism. For each unit, give the subagent:
|
||||
- The full plan file path (for overall context)
|
||||
- The specific unit's Goal, Files, Approach, Execution note, Patterns, Test scenarios, and Verification
|
||||
- Any resolved deferred questions relevant to that unit
|
||||
- Instruction to check whether the unit's test scenarios cover all applicable categories (happy paths, edge cases, error paths, integration) and supplement gaps before writing tests
|
||||
|
||||
**Shared-directory fallback constraints** — apply only when worktree isolation is unavailable:
|
||||
- Instruct each subagent: "Do not stage files (`git add`), create commits, or run the project test suite. The orchestrator handles testing, staging, and committing after all parallel units complete."
|
||||
- These constraints prevent git index contention and test interference between concurrent subagents.
|
||||
- With worktree isolation active, omit these constraints — subagents may stage, commit, and run their unit's tests within their own worktree branch.
|
||||
|
||||
**Permission mode:** Omit the `mode` parameter when dispatching subagents so the user's configured permission settings apply. Do not pass `mode: "auto"` — it overrides user-level settings like `bypassPermissions`.
|
||||
|
||||
**After each subagent completes (serial mode):**
|
||||
1. Review the subagent's diff — verify changes match the unit's scope and `Files:` list
|
||||
2. Run the relevant test suite to confirm the tree is healthy
|
||||
3. If tests fail, diagnose and fix before proceeding — do not dispatch dependent units on a broken tree
|
||||
4. Update the task list (do not edit the plan body — progress is carried by the commit)
|
||||
5. Dispatch the next unit
|
||||
|
||||
**After all parallel subagents in a batch complete (worktree-isolated mode):**
|
||||
1. Wait for every subagent in the current parallel batch to finish.
|
||||
2. For each completed subagent, in dependency order: review the worktree's diff against the orchestrator's branch. If the subagent did not commit its own work, stage and commit it inside that worktree.
|
||||
3. Merge each subagent's branch into the orchestrator's branch sequentially in dependency order. **If a merge conflict surfaces, abort the merge (`git merge --abort`) and re-dispatch the conflicting unit serially against the now-merged tree** — hand-resolving silently picks a side and discards one unit's intent. (Predicted overlap from the Parallel Safety Check surfaces here as a conflict, not as silent data loss in shared-directory mode.)
|
||||
4. After each merge, run the relevant test suite. If tests fail, diagnose and fix before merging the next branch.
|
||||
5. Update the task list (progress is carried by the merge commits).
|
||||
6. After merging, remove each subagent's worktree and delete its branch. Use the absolute path and branch name returned in the subagent's result.
|
||||
- Unlock the worktree first — the harness locks per-subagent worktrees: `git worktree unlock <absolute-path>`
|
||||
- Remove the worktree: `git worktree remove <absolute-path>`
|
||||
- Delete the branch: `git branch -d <branch-name>` (the branch outlives the worktree by default and accumulates as orphans if not cleaned up; `-d` lowercase refuses to delete unmerged branches, which is the safety we want — if it fails, investigate before forcing)
|
||||
7. Dispatch the next batch of independent units, or the next dependent unit.
|
||||
|
||||
**After all parallel subagents in a batch complete (shared-directory fallback):**
|
||||
1. Wait for every subagent in the current parallel batch to finish before acting on any of their results
|
||||
2. Cross-check for discovered file collisions: compare the actual files modified by all subagents in the batch (not just their declared `Files:` lists). Subagents may create or modify files not anticipated during planning — this is expected, since plans describe *what* not *how*. A collision only matters when 2+ subagents in the same batch modified the same file. In a shared working directory, only the last writer's version survives — the other unit's changes to that file are lost. If a collision is detected: commit all non-colliding files from all units first, then re-run the affected units serially for the shared file so each builds on the other's committed work
|
||||
3. For each completed unit, in dependency order: review the diff, run the relevant test suite, stage only that unit's files, and commit with a conventional message derived from the unit's Goal
|
||||
4. If tests fail after committing a unit's changes, diagnose and fix before committing the next unit
|
||||
5. Update the task list (do not edit the plan body — progress is carried by the commits just made)
|
||||
6. Dispatch the next batch of independent units, or the next dependent unit
|
||||
|
||||
### Phase 2: Execute
|
||||
|
||||
1. **Task Execution Loop**
|
||||
|
||||
For each task in priority order:
|
||||
|
||||
```
|
||||
while (tasks remain):
|
||||
- Mark task as in-progress
|
||||
- Read any referenced files from the plan or discovered during Phase 0
|
||||
- **If the unit's work is already present and matches the plan's intent** (files exist with the expected capability, or the unit's `Verification` criteria are already satisfied by the current code), the work has likely shipped on a prior branch or session. Verify it matches, mark the task complete, and move on. Do not silently reimplement.
|
||||
- Look for similar patterns in codebase
|
||||
- Find existing test files for implementation files being changed (Test Discovery — see below)
|
||||
- Implement following existing conventions
|
||||
- Add, update, or remove tests to match implementation changes (see Test Discovery below)
|
||||
- Run System-Wide Test Check (see below)
|
||||
- Run tests after changes
|
||||
- Assess testing coverage: did this task change behavior? If yes, were tests written or updated? If no tests were added, is the justification deliberate (e.g., pure config, no behavioral change)?
|
||||
- Mark task as completed
|
||||
- Evaluate for incremental commit (see below)
|
||||
```
|
||||
|
||||
When a unit carries an `Execution note`, honor it. For test-first units, write the failing test before implementation for that unit. For characterization-first units, capture existing behavior before changing it. For units without an `Execution note`, proceed pragmatically.
|
||||
|
||||
Guardrails for execution posture:
|
||||
- Do not write the test and implementation in the same step when working test-first
|
||||
- Do not skip verifying that a new test fails before implementing the fix or feature
|
||||
- Do not over-implement beyond the current behavior slice when working test-first
|
||||
- Skip test-first discipline for trivial renames, pure configuration, and pure styling work
|
||||
|
||||
**Test Discovery** — Before implementing changes to a file, find its existing test files (search for test/spec files that import, reference, or share naming patterns with the implementation file). When a plan specifies test scenarios or test files, start there, then check for additional test coverage the plan may not have enumerated. Changes to implementation files should be accompanied by corresponding test updates — new tests for new behavior, modified tests for changed behavior, removed or updated tests for deleted behavior.
|
||||
|
||||
**Test Scenario Completeness** — Before writing tests for a feature-bearing unit, check whether the plan's `Test scenarios` cover all categories that apply to this unit. If a category is missing or scenarios are vague (e.g., "validates correctly" without naming inputs and expected outcomes), supplement from the unit's own context before writing tests:
|
||||
|
||||
| Category | When it applies | How to derive if missing |
|
||||
|----------|----------------|------------------------|
|
||||
| **Happy path** | Always for feature-bearing units | Read the unit's Goal and Approach for core input/output pairs |
|
||||
| **Edge cases** | When the unit has meaningful boundaries (inputs, state, concurrency) | Identify boundary values, empty/nil inputs, and concurrent access patterns |
|
||||
| **Error/failure paths** | When the unit has failure modes (validation, external calls, permissions) | Enumerate invalid inputs the unit should reject, permission/auth denials it should enforce, and downstream failures it should handle |
|
||||
| **Integration** | When the unit crosses layers (callbacks, middleware, multi-service) | Identify the cross-layer chain and write a scenario that exercises it without mocks |
|
||||
|
||||
**System-Wide Test Check** — Before marking a task done, pause and ask:
|
||||
|
||||
| Question | What to do |
|
||||
|----------|------------|
|
||||
| **What fires when this runs?** Callbacks, middleware, observers, event handlers — trace two levels out from your change. | Read the actual code (not docs) for callbacks on models you touch, middleware in the request chain, `after_*` hooks. |
|
||||
| **Do my tests exercise the real chain?** If every dependency is mocked, the test proves your logic works *in isolation* — it says nothing about the interaction. | Write at least one integration test that uses real objects through the full callback/middleware chain. No mocks for the layers that interact. |
|
||||
| **Can failure leave orphaned state?** If your code persists state (DB row, cache, file) before calling an external service, what happens when the service fails? Does retry create duplicates? | Trace the failure path with real objects. If state is created before the risky call, test that failure cleans up or that retry is idempotent. |
|
||||
| **What other interfaces expose this?** Mixins, DSLs, alternative entry points (Agent vs Chat vs ChatMethods). | Grep for the method/behavior in related classes. If parity is needed, add it now — not as a follow-up. |
|
||||
| **Do error strategies align across layers?** Retry middleware + application fallback + framework error handling — do they conflict or create double execution? | List the specific error classes at each layer. Verify your rescue list matches what the lower layer actually raises. |
|
||||
|
||||
**When to skip:** Leaf-node changes with no callbacks, no state persistence, no parallel interfaces. If the change is purely additive (new helper method, new view partial), the check takes 10 seconds and the answer is "nothing fires, skip."
|
||||
|
||||
**When this matters most:** Any change that touches models with callbacks, error handling with fallback/retry, or functionality exposed through multiple interfaces.
|
||||
|
||||
|
||||
2. **Incremental Commits**
|
||||
|
||||
After completing each task, evaluate whether to create an incremental commit:
|
||||
|
||||
| Commit when... | Don't commit when... |
|
||||
|----------------|---------------------|
|
||||
| Logical unit complete (model, service, component) | Small part of a larger unit |
|
||||
| Tests pass + meaningful progress | Tests failing |
|
||||
| About to switch contexts (backend → frontend) | Purely scaffolding with no behavior |
|
||||
| About to attempt risky/uncertain changes | Would need a "WIP" commit message |
|
||||
|
||||
**Heuristic:** "Can I write a commit message that describes a complete, valuable change? If yes, commit. If the message would be 'WIP' or 'partial X', wait."
|
||||
|
||||
If the plan has Implementation Units, use them as a starting guide for commit boundaries — but adapt based on what you find during implementation. A unit might need multiple commits if it's larger than expected, or small related units might land together. Use each unit's Goal to inform the commit message.
|
||||
|
||||
**Commit workflow:**
|
||||
```bash
|
||||
# 1. Verify tests pass (use project's test command)
|
||||
# Examples: bin/rails test, npm test, pytest, go test, etc.
|
||||
|
||||
# 2. Stage only files related to this logical unit (not `git add .`)
|
||||
git add <files related to this logical unit>
|
||||
|
||||
# 3. Commit with conventional message
|
||||
git commit -m "feat(scope): description of this unit"
|
||||
```
|
||||
|
||||
**Handling merge conflicts:** If conflicts arise during rebasing or merging, resolve them immediately. Incremental commits make conflict resolution easier since each commit is small and focused.
|
||||
|
||||
**Note:** Incremental commits use clean conventional messages without attribution footers. The final Phase 4 commit/PR includes the full attribution.
|
||||
|
||||
**Parallel subagent mode:** Commit ownership is split by isolation mode (see Phase 1 Step 4):
|
||||
- **Worktree-isolated:** subagents may stage and commit inside their own worktree branch; the orchestrator merges those branches in dependency order after the batch.
|
||||
- **Shared-directory fallback:** subagents do not commit; the orchestrator stages and commits each unit after the entire parallel batch completes.
|
||||
|
||||
3. **Follow Existing Patterns**
|
||||
|
||||
- The plan should reference similar code - read those files first
|
||||
- Match naming conventions exactly
|
||||
- Reuse existing components where possible
|
||||
- Follow project coding standards (see AGENTS.md; use CLAUDE.md only if the repo still keeps a compatibility shim)
|
||||
- When in doubt, grep for similar implementations
|
||||
|
||||
4. **Test Continuously**
|
||||
|
||||
- Run relevant tests after each significant change
|
||||
- Don't wait until the end to test
|
||||
- Fix failures immediately
|
||||
- Add new tests for new behavior, update tests for changed behavior, remove tests for deleted behavior
|
||||
- **Unit tests with mocks prove logic in isolation. Integration tests with real objects prove the layers work together.** If your change touches callbacks, middleware, or error handling — you need both.
|
||||
|
||||
5. **Simplify as You Go**
|
||||
|
||||
After completing a cluster of related implementation units (or every 2-3 units), review recently changed files for simplification opportunities — consolidate duplicated patterns, extract shared helpers, and improve code reuse and efficiency. This is especially valuable when using subagents, since each agent works with isolated context and can't see patterns emerging across units.
|
||||
|
||||
Don't simplify after every single unit — early patterns may look duplicated but diverge intentionally in later units. Wait for a natural phase boundary or when you notice accumulated complexity.
|
||||
|
||||
If a `/simplify` skill or equivalent is available, use it. Otherwise, review the changed files yourself for reuse and consolidation opportunities.
|
||||
|
||||
6. **Figma Design Sync** (if applicable)
|
||||
|
||||
For UI work with Figma designs:
|
||||
|
||||
- Implement components following design specs
|
||||
- Use ce-figma-design-sync agent iteratively to compare
|
||||
- Fix visual differences identified
|
||||
- Repeat until implementation matches design
|
||||
|
||||
6. **Track Progress**
|
||||
- Keep the task list updated as you complete tasks
|
||||
- Note any blockers or unexpected discoveries
|
||||
- Create new tasks if scope expands
|
||||
- Keep user informed of major milestones
|
||||
- When the plan defines U-IDs for Implementation Units, or the plan or origin document carries stable R-IDs (and optionally A/F/AE IDs), reference them in blockers, deferred-work notes, task summaries, and final verification — not routine status updates. U-IDs anchor units across plan edits; R/A/F/AE anchor product intent across the brainstorm-plan handoff. Use the IDs the plan supplies and do not invent ones it does not. This preserves traceability without burying signal under noise.
|
||||
|
||||
### Phase 3-4: Quality Check and Finishing Work
|
||||
|
||||
When all Phase 2 tasks are complete and execution transitions to quality check, you must read `references/shipping-workflow.md` for the full shipping workflow.Do not skip this.
|
||||
|
||||
## Key Principles
|
||||
|
||||
### Start Fast, Execute Faster
|
||||
|
||||
- Get clarification once at the start, then execute
|
||||
- Don't wait for perfect understanding - ask questions and move
|
||||
- The goal is to **finish the feature**, not create perfect process
|
||||
|
||||
### The Plan is Your Guide
|
||||
|
||||
- Work documents should reference similar code and patterns
|
||||
- Load those references and follow them
|
||||
- Don't reinvent - match what exists
|
||||
|
||||
### Test As You Go
|
||||
|
||||
- Run tests after each change, not at the end
|
||||
- Fix failures immediately
|
||||
- Continuous testing prevents big surprises
|
||||
|
||||
### Quality is Built In
|
||||
|
||||
- Follow existing patterns
|
||||
- Write tests for new code
|
||||
- Run linting before pushing
|
||||
- Review every change — inline for simple additive work, full review for everything else
|
||||
|
||||
### Ship Complete Features
|
||||
|
||||
- Mark all tasks completed before moving on
|
||||
- Don't leave features 80% done
|
||||
- A finished feature that ships beats a perfect feature that doesn't
|
||||
|
||||
## Common Pitfalls to Avoid
|
||||
|
||||
- **Analysis paralysis** - Don't overthink, read the plan and execute
|
||||
- **Skipping clarifying questions** - Ask now, not after building wrong thing
|
||||
- **Ignoring plan references** - The plan has links for a reason
|
||||
- **Testing at the end** - Test continuously or suffer later
|
||||
- **Forgetting to track progress** - Update task status as you go or lose track of what's done
|
||||
- **80% done syndrome** - Finish the feature, don't move on early
|
||||
- **Skipping review** - Every change gets reviewed; only the depth varies
|
||||
- **Re-scoping the plan into human-time phases** - The plan's Implementation Units define the scope of execution. Do not estimate human-hours per unit, propose multi-day breakdowns, or ask the user to pick a subset of units for "this session". Agents execute at agent speed, and context-window pressure is addressed by subagent dispatch (Phase 1 Step 4), not by phased sessions. If a plan-file input is genuinely too large for a single execution, say so plainly and suggest the user return to `/ce-plan` to reduce scope — don't invent session phases as a workaround. For bare-prompt input, Phase 0's Large routing already handles oversized work
|
||||
@@ -0,0 +1,154 @@
|
||||
# Shipping Workflow
|
||||
|
||||
This file contains the shipping workflow (Phase 3-4). It is loaded when all Phase 2 tasks are complete and execution transitions to quality check.
|
||||
|
||||
## Phase 3: Quality Check
|
||||
|
||||
1. **Run Core Quality Checks**
|
||||
|
||||
Always run before submitting:
|
||||
|
||||
```bash
|
||||
# Run full test suite (use project's test command)
|
||||
# Examples: bin/rails test, npm test, pytest, go test, etc.
|
||||
|
||||
# Run linting (per AGENTS.md)
|
||||
# Use linting-agent before pushing to origin
|
||||
```
|
||||
|
||||
2. **Simplify** (Claude Code only; REQUIRED for >=30 changed lines)
|
||||
|
||||
Before code review, run the `/simplify` skill on the change to consolidate duplicated patterns, remove dead code, and improve reuse. Skip when the diff is purely mechanical (formatting, dependency bumps, lint fixes, generated artifacts) -- simplification has no useful yield on those.
|
||||
|
||||
On other harnesses, proceed directly to code review.
|
||||
|
||||
3. **Code Review** (REQUIRED)
|
||||
|
||||
Every change gets reviewed before shipping. Default to Tier 1 and escalate to Tier 2 only when a concrete signal calls for it. Tier 2 is materially more expensive in time and tokens -- pay that cost when a signal justifies it, not as a default.
|
||||
|
||||
**Tier 1 -- harness-native code review (default).** Run your built-in code review command or skill (e.g., `/review` in Claude Code). Address blocking and suggested findings inline before Final Validation. Skip the Residual Work Gate. If the current harness has no built-in code review command or skill, escalate to Tier 2 -- Tier 1 cannot run, and "Every change gets reviewed" still applies.
|
||||
|
||||
**Tier 2 -- `ce-code-review` (escalation).** Invoke the `ce-code-review` skill with `mode:autofix`, passing `plan:<path>` when known. Then proceed to the Residual Work Gate.
|
||||
|
||||
Escalate to Tier 2 when **any** of the following is true:
|
||||
|
||||
- **Sensitive surface touched.** The diff modifies any of: authentication or authorization, payments or billing, data migrations or backfills, cryptography or secret handling, security-relevant configuration, public API or library contracts, or dependency manifests.
|
||||
- **Large and diffuse change.** The diff exceeds >=400 changed lines **and** spans more than 3 directories or 2 distinct subsystems. Either alone is a soft signal; together they are an escalation trigger.
|
||||
- **Very large change.** The diff exceeds >=1,000 changed lines regardless of diffusion.
|
||||
- **Plan or task explicitly requests it.** The plan, the originating task, or another instruction in scope calls for a full / deep / thorough code review.
|
||||
|
||||
When the change is small, concentrated, and outside the sensitive surface list, Tier 1 is sufficient -- do not escalate "to be safe."
|
||||
|
||||
4. **Residual Work Gate** (REQUIRED when Tier 2 ran)
|
||||
|
||||
After Tier 2 code review completes, inspect the Residual Actionable Work summary it returned (or read the run artifact directly if the summary was not emitted). If one or more residual `downstream-resolver` findings remain, do not proceed to Final Validation until the user decides how to handle them.
|
||||
|
||||
Ask the user using the platform's blocking question tool (`AskUserQuestion` in Claude Code with `ToolSearch select:AskUserQuestion` pre-loaded if needed, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)). Fall back to numbered options in chat only when the harness genuinely lacks a blocking tool. Never silently skip the gate.
|
||||
|
||||
Stem: `Code review found N residual finding(s) the skill did not auto-fix. How should the agent proceed?`
|
||||
|
||||
Options (four or fewer, self-contained labels):
|
||||
- `Apply/fix now` — loop back into review with focused fixes; the agent investigates each finding, applies changes where safe, and re-runs review.
|
||||
- `File tickets via project tracker` — load `references/tracker-defer.md` in Interactive mode; the agent files tickets in the project's detected tracker (or `gh` fallback, or leaves them in the report if no sink exists) and proceeds to Final Validation.
|
||||
- `Accept and proceed` — record the residual findings verbatim in a durable "Known Residuals" sink before shipping. If a PR will be created or updated in Phase 4, include them in the PR description's "Known Residuals" section (the agent owns this when calling `ce-commit-push-pr`). If the user later chooses the no-PR `ce-commit` path, create `docs/residual-review-findings/<branch-or-head-sha>.md`, include the accepted findings and source review-run context, stage it with the implementation commit, and mention the file path in the final summary. The user has acknowledged the risk, but the findings must not live only in the transient session.
|
||||
- `Stop — do not ship` — abort the shipping workflow. The user will handle findings manually before re-invoking.
|
||||
|
||||
Skip this gate entirely when the review reported `Residual actionable work: none.` or when only Tier 1 was used. Do not proceed past this gate on an `Accept and proceed` decision until the agent has recorded whether the durable sink is `PR Known Residuals` or `docs/residual-review-findings/<branch-or-head-sha>.md`.
|
||||
|
||||
5. **Final Validation**
|
||||
- All tasks marked completed
|
||||
- Testing addressed -- tests pass and new/changed behavior has corresponding test coverage (or an explicit justification for why tests are not needed)
|
||||
- Linting passes
|
||||
- Code follows existing patterns
|
||||
- Figma designs match (if applicable)
|
||||
- No console errors or warnings
|
||||
- If the plan has a `Requirements` section (or legacy `Requirements Trace`), verify each requirement is satisfied by the completed work
|
||||
- If any `Deferred to Implementation` questions were noted, confirm they were resolved during execution
|
||||
|
||||
6. **Prepare Operational Validation Plan** (REQUIRED)
|
||||
- Add a `## Post-Deploy Monitoring & Validation` section to the PR description for every change.
|
||||
- Include concrete:
|
||||
- Log queries/search terms
|
||||
- Metrics or dashboards to watch
|
||||
- Expected healthy signals
|
||||
- Failure signals and rollback/mitigation trigger
|
||||
- Validation window and owner
|
||||
- If there is truly no production/runtime impact, still include the section with: `No additional operational monitoring required` and a one-line reason.
|
||||
|
||||
## Phase 4: Ship It
|
||||
|
||||
1. **Prepare Evidence Context**
|
||||
|
||||
Do not invoke `ce-demo-reel` directly in this step. Evidence capture belongs to the PR creation or PR description update flow, where the final PR diff and description context are available.
|
||||
|
||||
Note whether the completed work has observable behavior (UI rendering, CLI output, API/library behavior with a runnable example, generated artifacts, or workflow output). The `ce-commit-push-pr` skill will ask whether to capture evidence only when evidence is possible.
|
||||
|
||||
2. **Update Plan Status**
|
||||
|
||||
Update the plan's `status` field from `active` to `completed`. The
|
||||
mechanic depends on the plan's format:
|
||||
|
||||
- **Markdown plan (`.md`).** YAML frontmatter at the top of the file
|
||||
carries the status. Edit the YAML directly:
|
||||
```
|
||||
status: active -> status: completed
|
||||
```
|
||||
- **HTML plan (`.html`).** Status lives as visible text in the rendered
|
||||
header (typically `<span class="status">active</span>` or similar).
|
||||
Edit the visible element's text content directly. There is no hidden
|
||||
JSON-frontmatter copy to keep in sync — HTML metadata is a single
|
||||
source of truth in visible text per the html-rendering invariants.
|
||||
|
||||
If no status field exists in either format, skip this step — some
|
||||
plans omit frontmatter entirely.
|
||||
|
||||
3. **Commit and Create Pull Request**
|
||||
|
||||
Load the `ce-commit-push-pr` skill to handle committing, pushing, and PR creation. The skill handles convention detection, branch safety, logical commit splitting, adaptive PR descriptions, and attribution badges.
|
||||
|
||||
When providing context for the PR description, include:
|
||||
- The plan's summary and key decisions
|
||||
- Testing notes (tests added/modified, manual testing performed)
|
||||
- Evidence context from step 1, so `ce-commit-push-pr` can decide whether to ask about capturing evidence
|
||||
- Figma design link (if applicable)
|
||||
- The Post-Deploy Monitoring & Validation section (see Phase 3 Step 6)
|
||||
- Any "Known Residuals" accepted in the Phase 3 Residual Work Gate, rendered as a dedicated section in the PR body with severity, file:line, and title per finding
|
||||
|
||||
If the user prefers to commit without creating a PR, load the `ce-commit` skill instead.
|
||||
|
||||
4. **Notify User**
|
||||
- Summarize what was completed
|
||||
- Link to PR (if one was created)
|
||||
- Note any follow-up work needed
|
||||
- Suggest next steps if applicable
|
||||
|
||||
## Quality Checklist
|
||||
|
||||
Before creating PR, verify:
|
||||
|
||||
- [ ] All clarifying questions asked and answered
|
||||
- [ ] All tasks marked completed
|
||||
- [ ] Testing addressed -- tests pass AND new/changed behavior has corresponding test coverage (or an explicit justification for why tests are not needed)
|
||||
- [ ] Linting passes (use linting-agent)
|
||||
- [ ] Code follows existing patterns
|
||||
- [ ] Figma designs match implementation (if applicable)
|
||||
- [ ] Evidence decision handled by `ce-commit-push-pr` when the change has observable behavior
|
||||
- [ ] Commit messages follow conventional format
|
||||
- [ ] PR description includes Post-Deploy Monitoring & Validation section (or explicit no-impact rationale)
|
||||
- [ ] Code review completed (Tier 1 harness-native or Tier 2 `ce-code-review`)
|
||||
- [ ] PR description includes summary, testing notes, and evidence when captured
|
||||
- [ ] PR description includes Compound Engineered badge with accurate model and harness
|
||||
|
||||
## Code Review Tiers
|
||||
|
||||
Every change gets reviewed. Default to Tier 1; escalate to Tier 2 only on a concrete signal. Tier 2 is materially more expensive in time and tokens.
|
||||
|
||||
**Tier 1 -- harness-native code review (default).** Run your built-in code review command or skill (e.g., `/review` in Claude Code). Address blocking and suggested findings inline. If the current harness has no built-in code review command or skill, escalate to Tier 2 -- Tier 1 cannot run.
|
||||
|
||||
**Tier 2 -- `ce-code-review` (escalation).** Invoke `ce-code-review mode:autofix` with `plan:<path>` when available. Safe fixes are applied automatically; residual work routes through the Residual Work Gate.
|
||||
|
||||
Escalate to Tier 2 when any of these holds:
|
||||
- Sensitive surface touched (auth/authz, payments/billing, data migrations or backfills, cryptography or secrets, security-relevant config, public API or library contracts, dependency manifests)
|
||||
- Large and diffuse change (>=400 changed lines AND >3 directories or 2 subsystems)
|
||||
- Very large change (>=1,000 changed lines)
|
||||
- Plan or task explicitly requests a full / deep / thorough code review
|
||||
@@ -0,0 +1,149 @@
|
||||
# Tracker Detection and Defer Execution
|
||||
|
||||
This reference covers how Defer actions file tickets in the project's tracker. It is loaded by `SKILL.md` when Interactive mode's routing question needs to decide whether to offer option C (File tickets), when the walk-through's Defer option executes, and when the bulk-preview of option C is shown. It is also loaded by autonomous callers (e.g., `lfg`) that need to file residual actionable findings without user prompts — see Execution Modes below.
|
||||
|
||||
---
|
||||
|
||||
## Execution Modes
|
||||
|
||||
Tracker-defer has two execution modes. The caller selects one; the detection, fallback chain, and ticket composition are shared.
|
||||
|
||||
### Interactive mode (default)
|
||||
|
||||
Used by `ce-code-review` Interactive mode's routing question, walk-through Defer actions, and bulk-preview option C. All user-facing prompts fire:
|
||||
|
||||
- First Defer of the session with a generic (non-named) label confirms the effective tracker choice.
|
||||
- Execution failures prompt with Retry / Fall back to next sink / Convert to Skip.
|
||||
- Labels in the routing question reflect `named_sink_available` (name the tracker) vs fallback generics.
|
||||
|
||||
### Non-interactive mode
|
||||
|
||||
Used by autonomous callers like `lfg` that must not prompt. All blocking questions are skipped; the fallback chain is executed silently in order. Behavior:
|
||||
|
||||
- No confirmation on the first generic-label Defer; proceed directly.
|
||||
- On execution failure, automatically fall to the next tier without prompting. Record the failure.
|
||||
- On total chain exhaustion (every tier failed or no sink available), return findings in the `no_sink` bucket so the caller can route them to another surface (e.g., inline them in a PR description).
|
||||
- Return a structured result: `{ filed: [{ finding_id, tracker, url }], failed: [{ finding_id, tracker, reason }], no_sink: [{ finding_id, title, severity, file, line }] }`.
|
||||
|
||||
The caller decides how to surface the result to the user. The non-interactive mode treats "no sink available" as a data-producing outcome, not a prompt trigger.
|
||||
|
||||
---
|
||||
|
||||
## Detection
|
||||
|
||||
The agent determines the project's tracker from whatever documentation is obvious. Primary sources: `CLAUDE.md` and `AGENTS.md` at the repo root and in relevant subdirectories. Supplementary signals (when primary documentation is ambiguous): `CONTRIBUTING.md`, `README.md`, PR templates under `.github/`, visible tracker URLs in the repo.
|
||||
|
||||
A tracker can be surfaced via MCP tool (e.g., a Linear MCP server), CLI (e.g., `gh`), or direct API. All are acceptable. The detection output is a tuple with two availability flags — one for the named tracker specifically (drives label confidence in Interactive mode) and one for the full fallback chain (drives whether Defer is offered at all):
|
||||
|
||||
```
|
||||
{ tracker_name, confidence, named_sink_available, any_sink_available }
|
||||
```
|
||||
|
||||
Where:
|
||||
- `tracker_name` — human-readable name ("Linear", "GitHub Issues", "Jira"), or `null` when detection cannot identify a specific tracker
|
||||
- `confidence` — `high` when the tracker is named explicitly in documentation (or via a linked URL to a specific project/workspace) and is unambiguously the project's canonical tracker; `low` when the signal is thin, conflicting, or implied only
|
||||
- `named_sink_available` — `true` only when the agent can actually invoke the detected tracker (MCP tool is loaded, CLI is authenticated, or API credentials are in environment); `false` when the tracker is documented but no tool reaches it, or when no tracker is found at all. Drives label confidence: inline tracker naming requires this to be `true`.
|
||||
- `any_sink_available` — `true` when any tier in the fallback chain (named tracker or GitHub Issues via `gh`) can be invoked this session. Drives whether Defer is offered in Interactive mode, and drives the `no_sink` bucket in Non-interactive mode.
|
||||
|
||||
Detection is reasoning-based. Do not maintain an enumerated checklist of files to read. Read the obvious sources and form a confident conclusion; when the obvious sources don't resolve, the label falls back to generic wording and the agent confirms with the user before executing (Interactive mode only).
|
||||
|
||||
---
|
||||
|
||||
## Probe timing and caching
|
||||
|
||||
Availability probes run **at most once per session** and **only when Defer execution is imminent**. Never speculatively at review start, never per-Defer, never per-walk-through-finding. The cached tuple is reused for every Defer action in the same run.
|
||||
|
||||
Typical probe sequence:
|
||||
|
||||
1. Read `CLAUDE.md` / `AGENTS.md` for tracker references. If nothing found, set `tracker_name = null`, `confidence = low`.
|
||||
2. **Probe the named tracker when one was found.** For GitHub Issues, run `gh auth status` and `gh repo view --json hasIssuesEnabled`. For Linear or other MCP-backed trackers, verify the relevant MCP tool is loaded and responsive. For API-backed trackers, verify credentials in environment. Set `named_sink_available` from the probe result.
|
||||
3. **Probe the GitHub Issues fallback to compute `any_sink_available`.** Even when the named tracker was found and probed, `gh` matters for the `no_sink` bucket decision so that a run with no documented tracker but working `gh` still offers Defer.
|
||||
- If `named_sink_available = true`: `any_sink_available = true` (no further probes needed).
|
||||
- Otherwise, probe GitHub Issues via `gh auth status` + `gh repo view --json hasIssuesEnabled` (skip if already probed in step 2). If it works, `any_sink_available = true`.
|
||||
- Otherwise, `any_sink_available = false`.
|
||||
|
||||
When Interactive mode's routing question is skipped entirely (R2 zero-findings case), no probes run. When the cached tuple is reused across a session, any `named_sink_available = true` from the session's first probe stays cached — do not re-probe per Defer.
|
||||
|
||||
---
|
||||
|
||||
## Label logic (Interactive mode)
|
||||
|
||||
- When `confidence = high` AND `named_sink_available = true`: the routing question's option C and the walk-through's per-finding Defer option both include the tracker name verbatim. Example: `File a Linear ticket per finding`, `Defer — file a Linear ticket`.
|
||||
- When `any_sink_available = true` but either `confidence = low` or `named_sink_available = false` (a fallback tier is working instead): the labels read generically — `File an issue per finding`, `Defer — file a ticket`. Before executing the first Defer of the session, the agent confirms the effective tracker choice with the user using the platform's blocking question tool.
|
||||
- When `any_sink_available = false`: option C is omitted from the routing question, option B (Defer) is omitted from the walk-through per-finding options, and the agent tells the user why in the routing question's stem.
|
||||
|
||||
Non-interactive mode skips label decisions entirely — it acts silently on the detected sink.
|
||||
|
||||
---
|
||||
|
||||
## Fallback chain
|
||||
|
||||
When the named tracker is unavailable or no tracker is named, fall back in this order. Prefer the project's detected tracker; use `gh` only when no named tracker was found or the named one is unreachable.
|
||||
|
||||
1. **Named tracker** (MCP tool, CLI, or API the agent can invoke directly, identified via Detection above)
|
||||
2. **GitHub Issues via `gh`** — when `gh auth status` succeeds and the current repo has issues enabled (`gh repo view --json hasIssuesEnabled` returns `true`)
|
||||
3. **No sink** — findings remain in the review report's residual-work section (Interactive mode) or are returned in the `no_sink` bucket for the caller to route (Non-interactive mode). The agent does not re-display them through a transient surface.
|
||||
|
||||
Previously this chain included a third in-session fallback tier. That tier was removed because in-session tasks do not survive past the session and therefore do not meet the "durable filing" intent of a Defer action. When no durable tracker exists, the correct behavior is to leave findings in the report (Interactive) or return them to the caller (Non-interactive).
|
||||
|
||||
---
|
||||
|
||||
## Ticket composition
|
||||
|
||||
Every Defer action creates a ticket with the following content, adapted to the tracker's capabilities:
|
||||
|
||||
- **Title:** the merged finding's `title` (schema-capped at 10 words).
|
||||
- **Body:**
|
||||
- Plain-English problem statement — reads the persona-produced `why_it_matters` from the contributing reviewer's artifact file at `/tmp/compound-engineering/ce-code-review/<run-id>/{reviewer}.json`, using the same `file + line_bucket(line, +/-3) + normalize(title)` matching headless mode uses (see SKILL.md Stage 6 detail enrichment). Falls back to the merged finding's `title`, `severity`, `file`, and `suggested_fix` (when present) when no artifact match is available — these fields are guaranteed in the merge-tier compact return.
|
||||
- Suggested fix (when present in the finding's `suggested_fix`).
|
||||
- Evidence (direct quotes from the reviewer's artifact).
|
||||
- Metadata block: `Severity: <level>`, `Confidence: <score>`, `Reviewer(s): <list>`, `Finding ID: <fingerprint>`.
|
||||
- **Labels** (when the tracker supports labels): severity tag (`P0`, `P1`, `P2`, `P3`) and, when the tracker convention supports it, a category label sourced from the reviewer name.
|
||||
- **Length cap:** when the composed body would exceed a tracker's body length limit, truncate with `... (continued in ce-code-review run artifact: /tmp/compound-engineering/ce-code-review/<run-id>/)` and include the finding_id in both the truncated body and the metadata block so the artifact is discoverable.
|
||||
|
||||
The finding_id is a stable fingerprint composed as `normalize(file) + line_bucket(line, +/-3) + normalize(title)` — the same fingerprint used by the merge pipeline.
|
||||
|
||||
---
|
||||
|
||||
## Failure path
|
||||
|
||||
When ticket creation fails at execution (API error, auth expiry mid-session, rate limit, malformed body rejected, 4xx/5xx response):
|
||||
|
||||
**Interactive mode:** surface the failure inline and ask the user using the platform's blocking question tool.
|
||||
|
||||
Stem:
|
||||
> Defer failed: <tracker name> returned <error summary>. How should the agent handle this finding?
|
||||
|
||||
Options:
|
||||
- `Retry on <tracker>` — re-attempt the same tracker once more (useful for transient errors)
|
||||
- `Fall back to next sink` — move this finding's Defer to the next tier in the fallback chain (e.g., from Linear to GitHub Issues)
|
||||
- `Convert to Skip — record the failure` — abandon this Defer, note the failure in the completion report's failure section, and continue the walk-through or bulk flow
|
||||
|
||||
**Non-interactive mode:** do not prompt. Automatically fall through to the next tier. If every tier fails, record the finding in the `failed` bucket of the structured return and continue. If the chain exhausts with no sink ever available, the finding ends up in the `no_sink` bucket.
|
||||
|
||||
When a high-confidence named tracker fails at execution, the cached `named_sink_available` is set to `false` for the rest of the session. Subsequent Defer actions fall straight through to the next tier without retrying a confirmed-broken sink. `any_sink_available` is only downgraded to `false` when every tier has been confirmed broken — a failed Linear call that succeeds via `gh` keeps `any_sink_available = true`.
|
||||
|
||||
Only when `ToolSearch` explicitly returns no match or the tool call errors — or on a platform with no blocking question tool — fall back to numbered options and waiting for the user's reply (Interactive mode only).
|
||||
|
||||
---
|
||||
|
||||
## Per-tracker behavior
|
||||
|
||||
Concrete behavior per tracker at execution time. The agent may invoke any of these through the appropriate interface (MCP, CLI, or API) — the choice depends on what is available in the current environment.
|
||||
|
||||
| Tracker | Interface | Invocation sketch | Body format | Labels |
|
||||
|---------|-----------|-------------------|-------------|--------|
|
||||
| Linear | MCP (preferred) or API | Create issue in the project/workspace identified by documentation; assign to the reporter if the MCP tool exposes user context | Markdown | Severity priority field if the MCP exposes it; otherwise include severity in body |
|
||||
| GitHub Issues | `gh issue create` | Repo defaults to the current repo. Use `--label` for severity tag when labels exist; omit `--label` if the repo has no label fixture. Fall back to a label-less issue on first failure. | Markdown | `--label P0` / `--label P1` / etc. when labels exist |
|
||||
| Jira | MCP or API | Create issue in the project identified by documentation; Jira's markdown dialect differs from GitHub's — use plain text in the body when MCP does not handle conversion | Plain text when MCP does not handle markdown | Severity priority field |
|
||||
| No sink available | — | Interactive: Defer option omitted, findings remain in the report's residual-work section. Non-interactive: findings returned in the `no_sink` bucket for caller routing. | — | — |
|
||||
|
||||
When uncertain, prefer "drop with explicit user-facing notice" over "pass through silently and hope." A Defer that produces no durable artifact and no user message is data loss.
|
||||
|
||||
---
|
||||
|
||||
## Cross-platform notes
|
||||
|
||||
The question-tool name varies by platform. In Interactive mode, use the platform's blocking question tool (`AskUserQuestion` in Claude Code, `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension)). In Claude Code the tool should already be loaded from the Interactive-mode pre-load step — if it isn't, call `ToolSearch` with query `select:AskUserQuestion` now. Fall back to numbered options in chat only when the harness genuinely lacks a blocking tool — `ToolSearch` returns no match, the tool call explicitly fails, or the runtime mode does not expose it (e.g., Codex edit modes without `request_user_input`). A pending schema load is not a fallback trigger. Never silently skip the question.
|
||||
|
||||
Non-interactive mode is platform-agnostic: it never prompts, so the platform's question tool is not relevant.
|
||||
Reference in New Issue
Block a user