codeharness 0.27.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JMYDBV6O.js → chunk-AK47LBEG.js} +66 -55
- package/dist/{docker-5LUADX2H.js → docker-IXLAFPH2.js} +1 -1
- package/dist/index.js +14 -19
- package/package.json +1 -1
- package/templates/agents/retro.yaml +63 -0
- package/templates/agents/reviewer.yaml +76 -0
- package/templates/workflows/default.yaml +19 -1
|
@@ -1957,8 +1957,10 @@ var DEPENDENCY_REGISTRY = [
|
|
|
1957
1957
|
name: "showboat",
|
|
1958
1958
|
displayName: "Showboat",
|
|
1959
1959
|
installCommands: [
|
|
1960
|
-
{ cmd: "
|
|
1961
|
-
{ cmd: "
|
|
1960
|
+
{ cmd: "npx", args: ["showboat", "--version"] },
|
|
1961
|
+
{ cmd: "uvx", args: ["install", "showboat"] },
|
|
1962
|
+
{ cmd: "pipx", args: ["install", "showboat"] },
|
|
1963
|
+
{ cmd: "brew", args: ["install", "showboat"] }
|
|
1962
1964
|
],
|
|
1963
1965
|
checkCommand: { cmd: "showboat", args: ["--version"] },
|
|
1964
1966
|
critical: false
|
|
@@ -1970,24 +1972,16 @@ var DEPENDENCY_REGISTRY = [
|
|
|
1970
1972
|
{ cmd: "npm", args: ["install", "-g", "@anthropic/agent-browser"] }
|
|
1971
1973
|
],
|
|
1972
1974
|
checkCommand: { cmd: "agent-browser", args: ["--version"] },
|
|
1973
|
-
critical: false
|
|
1974
|
-
|
|
1975
|
-
{
|
|
1976
|
-
name: "beads",
|
|
1977
|
-
displayName: "beads",
|
|
1978
|
-
installCommands: [
|
|
1979
|
-
{ cmd: "pip", args: ["install", "beads"] },
|
|
1980
|
-
{ cmd: "pipx", args: ["install", "beads"] }
|
|
1981
|
-
],
|
|
1982
|
-
checkCommand: { cmd: "bd", args: ["--version"] },
|
|
1983
|
-
critical: false
|
|
1975
|
+
critical: false,
|
|
1976
|
+
stacks: ["nodejs", "python"]
|
|
1984
1977
|
},
|
|
1985
1978
|
{
|
|
1986
1979
|
name: "semgrep",
|
|
1987
1980
|
displayName: "Semgrep",
|
|
1988
1981
|
installCommands: [
|
|
1989
1982
|
{ cmd: "pipx", args: ["install", "semgrep"] },
|
|
1990
|
-
{ cmd: "
|
|
1983
|
+
{ cmd: "uvx", args: ["install", "semgrep"] },
|
|
1984
|
+
{ cmd: "brew", args: ["install", "semgrep"] }
|
|
1991
1985
|
],
|
|
1992
1986
|
checkCommand: { cmd: "semgrep", args: ["--version"] },
|
|
1993
1987
|
critical: false
|
|
@@ -2007,7 +2001,8 @@ var DEPENDENCY_REGISTRY = [
|
|
|
2007
2001
|
displayName: "cargo-tarpaulin",
|
|
2008
2002
|
installCommands: [{ cmd: "cargo", args: ["install", "cargo-tarpaulin"] }],
|
|
2009
2003
|
checkCommand: { cmd: "cargo", args: ["tarpaulin", "--version"] },
|
|
2010
|
-
critical: false
|
|
2004
|
+
critical: false,
|
|
2005
|
+
stacks: ["rust"]
|
|
2011
2006
|
}
|
|
2012
2007
|
];
|
|
2013
2008
|
function checkInstalled(spec) {
|
|
@@ -2023,16 +2018,55 @@ function parseVersion(output) {
|
|
|
2023
2018
|
const match = /(\d+\.\d+[\w.-]*)/.exec(output);
|
|
2024
2019
|
return match ? match[1] : null;
|
|
2025
2020
|
}
|
|
2026
|
-
function
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
return
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2021
|
+
function filterDepsForStacks(stacks) {
|
|
2022
|
+
return DEPENDENCY_REGISTRY.filter((spec) => {
|
|
2023
|
+
if (!spec.stacks) return true;
|
|
2024
|
+
return spec.stacks.some((s) => stacks.includes(s));
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
function installAllDependencies(opts) {
|
|
2028
|
+
const specs = opts.stacks ? filterDepsForStacks(opts.stacks) : [...DEPENDENCY_REGISTRY];
|
|
2029
|
+
const results = [];
|
|
2030
|
+
const checks = specs.map((spec) => ({ spec, ...checkInstalled(spec) }));
|
|
2031
|
+
const missing = [];
|
|
2032
|
+
for (const { spec, installed, version } of checks) {
|
|
2033
|
+
if (installed) {
|
|
2034
|
+
const result = {
|
|
2035
|
+
name: spec.name,
|
|
2036
|
+
displayName: spec.displayName,
|
|
2037
|
+
status: "already-installed",
|
|
2038
|
+
version
|
|
2039
|
+
};
|
|
2040
|
+
results.push(result);
|
|
2041
|
+
if (!opts.json) {
|
|
2042
|
+
const versionStr = version ? ` (v${version})` : "";
|
|
2043
|
+
ok(`${spec.displayName}: already installed${versionStr}`);
|
|
2044
|
+
}
|
|
2045
|
+
} else {
|
|
2046
|
+
missing.push(spec);
|
|
2047
|
+
}
|
|
2035
2048
|
}
|
|
2049
|
+
for (const spec of missing) {
|
|
2050
|
+
const result = installMissing(spec);
|
|
2051
|
+
results.push(result);
|
|
2052
|
+
if (!opts.json) {
|
|
2053
|
+
if (result.status === "installed") {
|
|
2054
|
+
const versionStr = result.version ? ` (v${result.version})` : "";
|
|
2055
|
+
ok(`${spec.displayName}: installed${versionStr}`);
|
|
2056
|
+
} else if (result.status === "failed") {
|
|
2057
|
+
fail(`${spec.displayName}: install failed. ${result.error ?? ""}`);
|
|
2058
|
+
if (!spec.critical) {
|
|
2059
|
+
info(`${spec.displayName} is optional \u2014 continuing without it`);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (result.status === "failed" && spec.critical) {
|
|
2064
|
+
throw new CriticalDependencyError(spec.displayName, result.error ?? "Install failed");
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
return results;
|
|
2068
|
+
}
|
|
2069
|
+
function installMissing(spec) {
|
|
2036
2070
|
for (const installCmd of spec.installCommands) {
|
|
2037
2071
|
try {
|
|
2038
2072
|
execFileSync8(installCmd.cmd, installCmd.args, { stdio: "pipe", timeout: 3e5 });
|
|
@@ -2058,31 +2092,6 @@ function installDependency(spec) {
|
|
|
2058
2092
|
error: `Install failed. Try: ${remedy}`
|
|
2059
2093
|
};
|
|
2060
2094
|
}
|
|
2061
|
-
function installAllDependencies(opts) {
|
|
2062
|
-
const results = [];
|
|
2063
|
-
for (const spec of DEPENDENCY_REGISTRY) {
|
|
2064
|
-
const result = installDependency(spec);
|
|
2065
|
-
results.push(result);
|
|
2066
|
-
if (!opts.json) {
|
|
2067
|
-
if (result.status === "installed") {
|
|
2068
|
-
const versionStr = result.version ? ` (v${result.version})` : "";
|
|
2069
|
-
ok(`${spec.displayName}: installed${versionStr}`);
|
|
2070
|
-
} else if (result.status === "already-installed") {
|
|
2071
|
-
const versionStr = result.version ? ` (v${result.version})` : "";
|
|
2072
|
-
ok(`${spec.displayName}: already installed${versionStr}`);
|
|
2073
|
-
} else if (result.status === "failed") {
|
|
2074
|
-
fail(`${spec.displayName}: install failed. ${result.error ?? ""}`);
|
|
2075
|
-
if (!spec.critical) {
|
|
2076
|
-
info(`${spec.displayName} is optional \u2014 continuing without it`);
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
if (result.status === "failed" && spec.critical) {
|
|
2081
|
-
throw new CriticalDependencyError(spec.displayName, result.error ?? "Install failed");
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
return results;
|
|
2085
|
-
}
|
|
2086
2095
|
var CriticalDependencyError = class extends Error {
|
|
2087
2096
|
constructor(dependencyName, reason) {
|
|
2088
2097
|
super(`Critical dependency '${dependencyName}' failed to install: ${reason}`);
|
|
@@ -2095,7 +2104,7 @@ var CriticalDependencyError = class extends Error {
|
|
|
2095
2104
|
// src/modules/infra/deps-install.ts
|
|
2096
2105
|
function installDeps(opts) {
|
|
2097
2106
|
try {
|
|
2098
|
-
const depResults = installAllDependencies({ json: opts.isJson });
|
|
2107
|
+
const depResults = installAllDependencies({ json: opts.isJson, stacks: opts.stacks });
|
|
2099
2108
|
return ok2(depResults);
|
|
2100
2109
|
} catch (err) {
|
|
2101
2110
|
if (err instanceof CriticalDependencyError) {
|
|
@@ -2105,9 +2114,10 @@ function installDeps(opts) {
|
|
|
2105
2114
|
return fail2(`Dependency install error: ${message}`);
|
|
2106
2115
|
}
|
|
2107
2116
|
}
|
|
2108
|
-
function verifyDeps(isJson) {
|
|
2117
|
+
function verifyDeps(isJson, stacks) {
|
|
2118
|
+
const specs = stacks ? filterDepsForStacks(stacks) : [...DEPENDENCY_REGISTRY];
|
|
2109
2119
|
const depResults = [];
|
|
2110
|
-
for (const spec of
|
|
2120
|
+
for (const spec of specs) {
|
|
2111
2121
|
const check = checkInstalled(spec);
|
|
2112
2122
|
const depResult = {
|
|
2113
2123
|
name: spec.name,
|
|
@@ -2886,7 +2896,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
2886
2896
|
}
|
|
2887
2897
|
|
|
2888
2898
|
// src/modules/infra/init-project.ts
|
|
2889
|
-
var HARNESS_VERSION = true ? "0.
|
|
2899
|
+
var HARNESS_VERSION = true ? "0.28.1" : "0.0.0-dev";
|
|
2890
2900
|
function failResult(opts, error) {
|
|
2891
2901
|
return {
|
|
2892
2902
|
status: "fail",
|
|
@@ -2988,7 +2998,7 @@ async function initProjectInner(opts) {
|
|
|
2988
2998
|
process.exitCode = 1;
|
|
2989
2999
|
return ok2(result);
|
|
2990
3000
|
}
|
|
2991
|
-
const depResult = installDeps({ isJson });
|
|
3001
|
+
const depResult = installDeps({ isJson, stacks: result.stacks });
|
|
2992
3002
|
if (!isOk(depResult)) {
|
|
2993
3003
|
result.status = "fail";
|
|
2994
3004
|
result.error = depResult.error;
|
|
@@ -3114,7 +3124,7 @@ function handleRerun(opts, result) {
|
|
|
3114
3124
|
result.workflow = { status: "created", path: workflowRelPath };
|
|
3115
3125
|
if (!isJson) ok(`Workflow: ${workflowRelPath} created`);
|
|
3116
3126
|
}
|
|
3117
|
-
result.dependencies = verifyDeps(isJson);
|
|
3127
|
+
result.dependencies = verifyDeps(isJson, result.stacks);
|
|
3118
3128
|
result.docker = existingState.docker ? { compose_file: existingState.docker.compose_file, stack_running: existingState.docker.stack_running, services: [], ports: existingState.docker.ports } : null;
|
|
3119
3129
|
const bmadResult = verifyBmadOnRerun(projectDir, isJson);
|
|
3120
3130
|
if (bmadResult) result.bmad = bmadResult;
|
|
@@ -3360,6 +3370,7 @@ export {
|
|
|
3360
3370
|
getStackProvider,
|
|
3361
3371
|
detectStacks,
|
|
3362
3372
|
detectStack,
|
|
3373
|
+
getPackageRoot,
|
|
3363
3374
|
getStatePath,
|
|
3364
3375
|
writeState,
|
|
3365
3376
|
readState,
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getComposeFilePath,
|
|
13
13
|
getElkComposeFilePath,
|
|
14
14
|
getNestedValue,
|
|
15
|
+
getPackageRoot,
|
|
15
16
|
getStackDir,
|
|
16
17
|
getStackHealth,
|
|
17
18
|
getStackProvider,
|
|
@@ -39,7 +40,7 @@ import {
|
|
|
39
40
|
validateDockerfile,
|
|
40
41
|
warn,
|
|
41
42
|
writeState
|
|
42
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-AK47LBEG.js";
|
|
43
44
|
|
|
44
45
|
// src/index.ts
|
|
45
46
|
import { Command } from "commander";
|
|
@@ -1557,8 +1558,7 @@ function formatElapsed(ms) {
|
|
|
1557
1558
|
|
|
1558
1559
|
// src/lib/workflow-parser.ts
|
|
1559
1560
|
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
1560
|
-
import { join as join7, resolve as resolve3
|
|
1561
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1561
|
+
import { join as join7, resolve as resolve3 } from "path";
|
|
1562
1562
|
import os2 from "os";
|
|
1563
1563
|
import { parse as parse3 } from "yaml";
|
|
1564
1564
|
|
|
@@ -1907,12 +1907,9 @@ function suggestCheaperDriver(driverName, requiredCaps) {
|
|
|
1907
1907
|
|
|
1908
1908
|
// src/lib/agent-resolver.ts
|
|
1909
1909
|
import { readFileSync as readFileSync8, existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
|
|
1910
|
-
import { resolve as resolve2, join as join6
|
|
1911
|
-
import { fileURLToPath } from "url";
|
|
1910
|
+
import { resolve as resolve2, join as join6 } from "path";
|
|
1912
1911
|
import os from "os";
|
|
1913
1912
|
import { parse as parse2 } from "yaml";
|
|
1914
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
1915
|
-
var __dirname = dirname3(__filename);
|
|
1916
1913
|
var AgentResolveError = class extends Error {
|
|
1917
1914
|
filePath;
|
|
1918
1915
|
errors;
|
|
@@ -1923,8 +1920,8 @@ var AgentResolveError = class extends Error {
|
|
|
1923
1920
|
this.errors = errors ?? [];
|
|
1924
1921
|
}
|
|
1925
1922
|
};
|
|
1926
|
-
var TEMPLATES_DIR = resolve2(
|
|
1927
|
-
var DEFAULT_MODEL = "claude-sonnet-4-20250514";
|
|
1923
|
+
var TEMPLATES_DIR = resolve2(getPackageRoot(), "templates/agents");
|
|
1924
|
+
var DEFAULT_MODEL = "claude-sonnet-4-6-20250514";
|
|
1928
1925
|
var SAFE_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
1929
1926
|
function validateName(name) {
|
|
1930
1927
|
if (!name || !SAFE_NAME_RE.test(name)) {
|
|
@@ -2215,9 +2212,7 @@ var HierarchicalFlowError = class extends Error {
|
|
|
2215
2212
|
};
|
|
2216
2213
|
|
|
2217
2214
|
// src/lib/workflow-parser.ts
|
|
2218
|
-
var
|
|
2219
|
-
var __dirname2 = dirname4(__filename2);
|
|
2220
|
-
var TEMPLATES_DIR2 = resolve3(__dirname2, "../../templates/workflows");
|
|
2215
|
+
var TEMPLATES_DIR2 = resolve3(getPackageRoot(), "templates/workflows");
|
|
2221
2216
|
var WorkflowParseError = class extends Error {
|
|
2222
2217
|
errors;
|
|
2223
2218
|
constructor(message, errors) {
|
|
@@ -7259,7 +7254,7 @@ function getACById(id) {
|
|
|
7259
7254
|
// src/modules/verify/validation-runner.ts
|
|
7260
7255
|
import { execSync as execSync5 } from "child_process";
|
|
7261
7256
|
import { writeFileSync as writeFileSync12, mkdirSync as mkdirSync9 } from "fs";
|
|
7262
|
-
import { join as join25, dirname as
|
|
7257
|
+
import { join as join25, dirname as dirname3 } from "path";
|
|
7263
7258
|
var MAX_VALIDATION_ATTEMPTS = 10;
|
|
7264
7259
|
var AC_COMMAND_TIMEOUT_MS = 3e4;
|
|
7265
7260
|
var VAL_KEY_PREFIX = "val-";
|
|
@@ -7411,7 +7406,7 @@ function createFixStory(ac, error) {
|
|
|
7411
7406
|
"Fix the root cause so the validation command passes.",
|
|
7412
7407
|
""
|
|
7413
7408
|
].join("\n");
|
|
7414
|
-
mkdirSync9(
|
|
7409
|
+
mkdirSync9(dirname3(storyPath), { recursive: true });
|
|
7415
7410
|
writeFileSync12(storyPath, markdown, "utf-8");
|
|
7416
7411
|
return ok2(storyKey);
|
|
7417
7412
|
} catch (err) {
|
|
@@ -9634,7 +9629,7 @@ function formatAuditJson(result) {
|
|
|
9634
9629
|
|
|
9635
9630
|
// src/modules/audit/fix-generator.ts
|
|
9636
9631
|
import { existsSync as existsSync34, writeFileSync as writeFileSync14, mkdirSync as mkdirSync11 } from "fs";
|
|
9637
|
-
import { join as join33, dirname as
|
|
9632
|
+
import { join as join33, dirname as dirname5 } from "path";
|
|
9638
9633
|
function buildStoryKey(gap2, index) {
|
|
9639
9634
|
const safeDimension = gap2.dimension.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
9640
9635
|
return `audit-fix-${safeDimension}-${index}`;
|
|
@@ -9688,7 +9683,7 @@ function generateFixStories(auditResult) {
|
|
|
9688
9683
|
continue;
|
|
9689
9684
|
}
|
|
9690
9685
|
const markdown = buildStoryMarkdown(gap2, key);
|
|
9691
|
-
mkdirSync11(
|
|
9686
|
+
mkdirSync11(dirname5(filePath), { recursive: true });
|
|
9692
9687
|
writeFileSync14(filePath, markdown, "utf-8");
|
|
9693
9688
|
stories.push({ key, filePath, gap: gap2, skipped: false });
|
|
9694
9689
|
created++;
|
|
@@ -9912,7 +9907,7 @@ function registerTeardownCommand(program) {
|
|
|
9912
9907
|
} else if (otlpMode === "remote-routed") {
|
|
9913
9908
|
if (!options.keepDocker) {
|
|
9914
9909
|
try {
|
|
9915
|
-
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-
|
|
9910
|
+
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-IXLAFPH2.js");
|
|
9916
9911
|
stopCollectorOnly2();
|
|
9917
9912
|
result.docker.stopped = true;
|
|
9918
9913
|
if (!isJson) {
|
|
@@ -9944,7 +9939,7 @@ function registerTeardownCommand(program) {
|
|
|
9944
9939
|
info("Shared stack: kept running (other projects may use it)");
|
|
9945
9940
|
}
|
|
9946
9941
|
} else if (isLegacyStack) {
|
|
9947
|
-
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-
|
|
9942
|
+
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-IXLAFPH2.js");
|
|
9948
9943
|
let stackRunning = false;
|
|
9949
9944
|
try {
|
|
9950
9945
|
stackRunning = isStackRunning2(composeFile);
|
|
@@ -12822,7 +12817,7 @@ function registerDriversCommand(program) {
|
|
|
12822
12817
|
}
|
|
12823
12818
|
|
|
12824
12819
|
// src/index.ts
|
|
12825
|
-
var VERSION = true ? "0.
|
|
12820
|
+
var VERSION = true ? "0.28.1" : "0.0.0-dev";
|
|
12826
12821
|
function createProgram() {
|
|
12827
12822
|
const program = new Command();
|
|
12828
12823
|
program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
|
package/package.json
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: retro
|
|
2
|
+
role:
|
|
3
|
+
title: Retrospective Agent
|
|
4
|
+
purpose: Extract actionable lessons from completed epic execution to improve future epics
|
|
5
|
+
persona:
|
|
6
|
+
identity: |
|
|
7
|
+
Experienced scrum master who facilitates blameless retrospectives.
|
|
8
|
+
Analyzes patterns across story implementations — what worked, what failed, what was retried.
|
|
9
|
+
Focuses on systemic improvements, not individual failures.
|
|
10
|
+
communication_style: "Analytical, structured, forward-looking. Backs every insight with data from the sprint. No filler, no blame."
|
|
11
|
+
principles:
|
|
12
|
+
- Psychological safety is paramount — focus on systems and processes, not blame
|
|
13
|
+
- Every lesson must be backed by specific evidence from the epic execution
|
|
14
|
+
- Action items must be concrete and achievable — no vague aspirations
|
|
15
|
+
- Compare against previous retrospectives to track whether lessons were actually applied
|
|
16
|
+
- Distinguish between one-off incidents and recurring patterns
|
|
17
|
+
disallowedTools:
|
|
18
|
+
- Edit
|
|
19
|
+
- Write
|
|
20
|
+
prompt_template: |
|
|
21
|
+
## Role
|
|
22
|
+
|
|
23
|
+
You are conducting a retrospective for a completed epic. Analyze what happened and extract lessons that will improve the next epic.
|
|
24
|
+
|
|
25
|
+
## Input
|
|
26
|
+
|
|
27
|
+
1. Read the sprint state and progress files to understand what was executed
|
|
28
|
+
2. Read story files for the completed epic to understand scope
|
|
29
|
+
3. Read any previous retrospective files for pattern comparison
|
|
30
|
+
4. Check git log for the epic's commits — look for retry patterns, reverts, fixups
|
|
31
|
+
|
|
32
|
+
## Analysis Framework
|
|
33
|
+
|
|
34
|
+
### 1. Epic Summary
|
|
35
|
+
- Stories completed, failed, retried
|
|
36
|
+
- Total cost (tokens/dollars if available)
|
|
37
|
+
- Time from first implement to final verify
|
|
38
|
+
|
|
39
|
+
### 2. What Worked
|
|
40
|
+
- Stories that passed on first attempt — what made them clean?
|
|
41
|
+
- Patterns worth repeating
|
|
42
|
+
|
|
43
|
+
### 3. What Failed
|
|
44
|
+
- Stories that required retries — root cause for each
|
|
45
|
+
- Review/verify failures — were they legitimate catches or false positives?
|
|
46
|
+
- Common failure modes across stories
|
|
47
|
+
|
|
48
|
+
### 4. Patterns & Trends
|
|
49
|
+
- Compare with previous retros — are past lessons being applied?
|
|
50
|
+
- Recurring issues that need systemic fixes
|
|
51
|
+
- Test quality trends — are tests catching real issues?
|
|
52
|
+
|
|
53
|
+
### 5. Action Items for Next Epic
|
|
54
|
+
- Concrete, specific changes to make
|
|
55
|
+
- Each item must reference the evidence that motivates it
|
|
56
|
+
|
|
57
|
+
## Output Format
|
|
58
|
+
|
|
59
|
+
Output a structured markdown document with the sections above.
|
|
60
|
+
|
|
61
|
+
## Output Location
|
|
62
|
+
|
|
63
|
+
Write retrospective to ./retro/epic-{epic_number}-retro.md
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: reviewer
|
|
2
|
+
role:
|
|
3
|
+
title: Code Reviewer
|
|
4
|
+
purpose: Adversarial code review that finds real issues before runtime verification
|
|
5
|
+
persona:
|
|
6
|
+
identity: Senior engineer who reviews code for correctness, security, architecture violations, and adherence to story requirements. Does not fix — only reports.
|
|
7
|
+
communication_style: "Terse, evidence-based. Cites file:line for every finding. No praise, no filler."
|
|
8
|
+
principles:
|
|
9
|
+
- Every finding must cite a specific file and line number
|
|
10
|
+
- Distinguish blocking issues from suggestions — only block on real problems
|
|
11
|
+
- Check that ALL acceptance criteria are addressed in the implementation
|
|
12
|
+
- Flag security issues, missing error handling at system boundaries, and dead code
|
|
13
|
+
- Do not suggest stylistic changes or cosmetic improvements
|
|
14
|
+
- Compare implementation against story spec — catch scope creep and missed requirements
|
|
15
|
+
disallowedTools:
|
|
16
|
+
- Edit
|
|
17
|
+
- Write
|
|
18
|
+
prompt_template: |
|
|
19
|
+
## Role
|
|
20
|
+
|
|
21
|
+
You are performing adversarial code review on a story implementation. Your job is to find real issues — not nitpick style.
|
|
22
|
+
|
|
23
|
+
## Input
|
|
24
|
+
|
|
25
|
+
Read the story spec from ./story-files/ to understand what was supposed to be built.
|
|
26
|
+
Then review all changed files (use `git diff` against the branch base).
|
|
27
|
+
|
|
28
|
+
## Review Checklist
|
|
29
|
+
|
|
30
|
+
1. **Acceptance Criteria Coverage** — is every AC actually implemented? Map each AC to the code that satisfies it.
|
|
31
|
+
2. **Correctness** — logic errors, off-by-one, race conditions, unhandled edge cases at system boundaries.
|
|
32
|
+
3. **Security** — injection, XSS, secrets in code, unsafe deserialization, missing auth checks.
|
|
33
|
+
4. **Architecture** — does it follow existing patterns? New abstractions justified?
|
|
34
|
+
5. **Tests** — do tests actually test the behavior, or just assert mocks?
|
|
35
|
+
6. **Dead Code** — unused imports, unreachable branches, commented-out code.
|
|
36
|
+
|
|
37
|
+
## Anti-Leniency Rules
|
|
38
|
+
|
|
39
|
+
- Do not give benefit of the doubt. If something looks wrong, flag it.
|
|
40
|
+
- Do not suggest improvements. Only flag things that are broken, insecure, or missing.
|
|
41
|
+
- "It probably works" is not acceptable — if you can't verify, flag as UNKNOWN.
|
|
42
|
+
|
|
43
|
+
## Output Format
|
|
44
|
+
|
|
45
|
+
Output a single JSON object:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"verdict": "pass" | "fail",
|
|
50
|
+
"blocking": [
|
|
51
|
+
{
|
|
52
|
+
"file": "<path>",
|
|
53
|
+
"line": <number>,
|
|
54
|
+
"severity": "error" | "security",
|
|
55
|
+
"description": "<what's wrong>",
|
|
56
|
+
"ac": <number or null>
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"warnings": [
|
|
60
|
+
{
|
|
61
|
+
"file": "<path>",
|
|
62
|
+
"line": <number>,
|
|
63
|
+
"description": "<concern>"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"ac_coverage": {
|
|
67
|
+
"<ac_id>": "covered" | "missing" | "partial"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Verdict is "pass" only if `blocking` is empty and all ACs are "covered".
|
|
73
|
+
|
|
74
|
+
## Output Location
|
|
75
|
+
|
|
76
|
+
Write your review JSON to ./verdict/review.json
|
|
@@ -4,20 +4,38 @@ tasks:
|
|
|
4
4
|
scope: per-story
|
|
5
5
|
session: fresh
|
|
6
6
|
source_access: true
|
|
7
|
+
model: claude-sonnet-4-6-20250514
|
|
8
|
+
review:
|
|
9
|
+
agent: reviewer
|
|
10
|
+
scope: per-story
|
|
11
|
+
session: fresh
|
|
12
|
+
source_access: true
|
|
13
|
+
driver: codex
|
|
7
14
|
verify:
|
|
8
15
|
agent: evaluator
|
|
9
|
-
scope: per-
|
|
16
|
+
scope: per-story
|
|
10
17
|
session: fresh
|
|
11
18
|
source_access: false
|
|
19
|
+
driver: codex
|
|
12
20
|
retry:
|
|
13
21
|
agent: dev
|
|
14
22
|
scope: per-story
|
|
15
23
|
session: fresh
|
|
16
24
|
source_access: true
|
|
25
|
+
model: claude-sonnet-4-6-20250514
|
|
26
|
+
retro:
|
|
27
|
+
agent: retro
|
|
28
|
+
scope: per-epic
|
|
29
|
+
session: fresh
|
|
30
|
+
source_access: true
|
|
31
|
+
model: claude-opus-4-6-20250514
|
|
17
32
|
|
|
18
33
|
flow:
|
|
19
34
|
- implement
|
|
35
|
+
- review
|
|
20
36
|
- verify
|
|
21
37
|
- loop:
|
|
22
38
|
- retry
|
|
39
|
+
- review
|
|
23
40
|
- verify
|
|
41
|
+
- retro
|