@phamvuhoang/otto-core 0.15.0 → 0.17.0

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.
Files changed (61) hide show
  1. package/dist/cli-help.d.ts +6 -0
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +10 -0
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/eval.d.ts +20 -1
  6. package/dist/eval.d.ts.map +1 -1
  7. package/dist/eval.js +17 -1
  8. package/dist/eval.js.map +1 -1
  9. package/dist/index.d.ts +7 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/inspect.d.ts.map +1 -1
  14. package/dist/inspect.js +13 -4
  15. package/dist/inspect.js.map +1 -1
  16. package/dist/loop.d.ts.map +1 -1
  17. package/dist/loop.js +23 -1
  18. package/dist/loop.js.map +1 -1
  19. package/dist/main.d.ts.map +1 -1
  20. package/dist/main.js +1 -0
  21. package/dist/main.js.map +1 -1
  22. package/dist/plan-checkpoint.d.ts +47 -0
  23. package/dist/plan-checkpoint.d.ts.map +1 -0
  24. package/dist/plan-checkpoint.js +54 -0
  25. package/dist/plan-checkpoint.js.map +1 -0
  26. package/dist/plan-gate.d.ts +41 -0
  27. package/dist/plan-gate.d.ts.map +1 -0
  28. package/dist/plan-gate.js +46 -0
  29. package/dist/plan-gate.js.map +1 -0
  30. package/dist/plan-report-cli.d.ts +42 -0
  31. package/dist/plan-report-cli.d.ts.map +1 -0
  32. package/dist/plan-report-cli.js +75 -0
  33. package/dist/plan-report-cli.js.map +1 -0
  34. package/dist/plan-rubric.d.ts +68 -0
  35. package/dist/plan-rubric.d.ts.map +1 -0
  36. package/dist/plan-rubric.js +119 -0
  37. package/dist/plan-rubric.js.map +1 -0
  38. package/dist/report-explain.d.ts +33 -0
  39. package/dist/report-explain.d.ts.map +1 -0
  40. package/dist/report-explain.js +85 -0
  41. package/dist/report-explain.js.map +1 -0
  42. package/dist/report-rubric.d.ts +68 -0
  43. package/dist/report-rubric.d.ts.map +1 -0
  44. package/dist/report-rubric.js +107 -0
  45. package/dist/report-rubric.js.map +1 -0
  46. package/dist/run-bin.d.ts +2 -0
  47. package/dist/run-bin.d.ts.map +1 -1
  48. package/dist/run-bin.js +39 -17
  49. package/dist/run-bin.js.map +1 -1
  50. package/dist/run-report.d.ts +20 -0
  51. package/dist/run-report.d.ts.map +1 -1
  52. package/dist/run-report.js +41 -0
  53. package/dist/run-report.js.map +1 -1
  54. package/dist/stages.d.ts +5 -0
  55. package/dist/stages.d.ts.map +1 -1
  56. package/dist/stages.js +7 -0
  57. package/dist/stages.js.map +1 -1
  58. package/package.json +1 -1
  59. package/templates/plan.md +95 -0
  60. package/templates/prompt.md +6 -0
  61. package/templates/quality-report.md +43 -7
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Plan-quality rubric (issue #63 P8, slice 1 — "measure plan quality before
3
+ * generating plans").
4
+ *
5
+ * A pure scorer over a spec/plan markdown document: it checks the document
6
+ * against the structural criteria of a world-class plan (the proven
7
+ * `docs/superpowers` + issue-62 shape) and reports per-criterion results, a
8
+ * met-count / max score, a 0..1 completeness ratio, and what is missing. It is
9
+ * the measurement substrate every later P8 slice reads — the `plan` stage,
10
+ * its eval signal ("plan-completeness rubric score ↑ across fixtures"), the
11
+ * `--plan-report` surface, and the human checkpoint.
12
+ *
13
+ * Sibling to `eval.ts`'s `scoreTrajectory` (a pure scorer over recorded data),
14
+ * but scoring a *document* rather than a run trajectory. INERT on the loop —
15
+ * nothing here runs a stage or changes behavior; it only describes text.
16
+ *
17
+ * The detectors are deterministic header/keyword heuristics (no tokenizer, no
18
+ * model call): the rubric judges *structural completeness* (does the plan have
19
+ * the sections a good plan has), the orthogonal question to the *semantic*
20
+ * quality a human/model judges at the checkpoint. Heuristic and labelled as
21
+ * such, mirroring P7's `ceil(chars/4)` estimate discipline.
22
+ */
23
+ /** Count non-overlapping matches of a global regex in the document. */
24
+ function count(doc, re) {
25
+ return (doc.match(re) ?? []).length;
26
+ }
27
+ // Path-like inline code: a backticked token with a path separator or a known
28
+ // source-file extension — the signal a plan names the files it will touch.
29
+ const PATH_TOKEN_RE = /`[^`\n]*(?:\/[^`\n]*|\.(?:ts|tsx|js|jsx|mjs|cjs|md|json|py|go|rs|java|rb|sh))`/gi;
30
+ // Task list items: markdown checkboxes or top-of-line ordered items.
31
+ const CHECKBOX_RE = /(?:^|\n)\s*[-*]\s*\[[ xX]\]/g;
32
+ const ORDERED_ITEM_RE = /(?:^|\n)\s*\d+\.\s+\S/g;
33
+ /**
34
+ * The rubric criteria, in scorecard order. Each detector keys off the proven
35
+ * plan shape: a section header or the characteristic keywords/markup of that
36
+ * section. Order is fixed so the scorecard and the `missing` list are stable.
37
+ */
38
+ export const PLAN_CRITERIA = [
39
+ {
40
+ criterion: "problem",
41
+ label: "Problem statement",
42
+ detect: (d) => /(?:^|\n)#{1,6}\s+[^\n]*\bproblem\b/i.test(d),
43
+ },
44
+ {
45
+ criterion: "decisions",
46
+ label: "Decisions / assumptions",
47
+ detect: (d) => /\b(?:assumptions?|decisions?|rationale)\b/i.test(d),
48
+ },
49
+ {
50
+ criterion: "scopeGuard",
51
+ label: "Scope guard / non-goals",
52
+ detect: (d) => /\b(?:scope guard|non-?goals?|out of scope|not in scope|won'?t (?:do|build|change|ship))\b/i.test(d),
53
+ },
54
+ {
55
+ criterion: "fileMap",
56
+ label: "File / component map",
57
+ detect: (d) => /(?:^|\n)#{1,6}\s+[^\n]*\b(?:file (?:map|structure)|component map|files)\b/i.test(d) || count(d, PATH_TOKEN_RE) >= 2,
58
+ },
59
+ {
60
+ criterion: "taskBreakdown",
61
+ label: "Task breakdown",
62
+ detect: (d) => count(d, CHECKBOX_RE) >= 2 ||
63
+ count(d, ORDERED_ITEM_RE) >= 2 ||
64
+ count(d, /(?:^|\n)#{1,6}\s+task\b/gi) >= 2,
65
+ },
66
+ {
67
+ criterion: "testFirst",
68
+ label: "Failing-test-first (TDD)",
69
+ detect: (d) => /\b(?:failing test|test[- ]first|tdd|red[- ]green|write (?:a |the )?(?:failing )?test|tests? (?:first|before)|pinned by)\b/i.test(d),
70
+ },
71
+ {
72
+ criterion: "verifyCommands",
73
+ label: "Explicit verify commands",
74
+ detect: (d) => /\bverify:/i.test(d) ||
75
+ (/\bverif(?:y|ied|ication)\b/i.test(d) &&
76
+ /\b(?:pnpm|npm run|vitest|tsc|node --test|node --|pytest|go test|cargo test|make)\b/i.test(d)),
77
+ },
78
+ {
79
+ criterion: "successCriteria",
80
+ label: "Testable success criteria",
81
+ detect: (d) => /\b(?:success (?:criteria|metric)|acceptance (?:criteria|checklist|test)|done when|testing notes|testable)\b/i.test(d),
82
+ },
83
+ ];
84
+ /**
85
+ * Score a spec/plan markdown document against {@link PLAN_CRITERIA}. Pure and
86
+ * deterministic — pass the spec text, the plan text, or both concatenated. Each
87
+ * criterion contributes equally; the per-criterion breakdown lets a consumer
88
+ * reweight later without changing the scorer.
89
+ */
90
+ export function scorePlanQuality(doc) {
91
+ const results = PLAN_CRITERIA.map((c) => ({
92
+ criterion: c.criterion,
93
+ label: c.label,
94
+ met: c.detect(doc),
95
+ }));
96
+ const metCount = results.reduce((n, r) => n + (r.met ? 1 : 0), 0);
97
+ const maxScore = results.length;
98
+ return {
99
+ results,
100
+ metCount,
101
+ maxScore,
102
+ ratio: maxScore > 0 ? metCount / maxScore : 0,
103
+ missing: results.filter((r) => !r.met).map((r) => r.label),
104
+ };
105
+ }
106
+ const pct = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
107
+ /**
108
+ * Render a rubric score as a short, human-readable scorecard: a header with the
109
+ * met/total and percentage, a checked/unchecked line per criterion, and a
110
+ * one-line "missing" note when the plan is incomplete.
111
+ */
112
+ export function formatPlanRubric(score) {
113
+ const header = `plan quality: ${score.metCount}/${score.maxScore} ` +
114
+ `(${pct.format(score.ratio * 100)}%)`;
115
+ const lines = score.results.map((r) => ` [${r.met ? "x" : " "}] ${r.label}`);
116
+ const tail = score.missing.length > 0 ? [` missing: ${score.missing.join(", ")}`] : [];
117
+ return [header, ...lines, ...tail].join("\n");
118
+ }
119
+ //# sourceMappingURL=plan-rubric.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-rubric.js","sourceRoot":"","sources":["../src/plan-rubric.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAoBH,uEAAuE;AACvE,SAAS,KAAK,CAAC,GAAW,EAAE,EAAU;IACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;AACtC,CAAC;AAED,6EAA6E;AAC7E,2EAA2E;AAC3E,MAAM,aAAa,GACjB,kFAAkF,CAAC;AACrF,qEAAqE;AACrE,MAAM,WAAW,GAAG,8BAA8B,CAAC;AACnD,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAgC;IACxD;QACE,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,mBAAmB;QAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,qCAAqC,CAAC,IAAI,CAAC,CAAC,CAAC;KAC7D;IACD;QACE,SAAS,EAAE,WAAW;QACtB,KAAK,EAAE,yBAAyB;QAChC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,4CAA4C,CAAC,IAAI,CAAC,CAAC,CAAC;KACpE;IACD;QACE,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,yBAAyB;QAChC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACZ,4FAA4F,CAAC,IAAI,CAC/F,CAAC,CACF;KACJ;IACD;QACE,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,sBAAsB;QAC7B,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACZ,4EAA4E,CAAC,IAAI,CAC/E,CAAC,CACF,IAAI,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC;KACpC;IACD;QACE,SAAS,EAAE,eAAe;QAC1B,KAAK,EAAE,gBAAgB;QACvB,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACZ,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC;YAC1B,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,IAAI,CAAC;YAC9B,KAAK,CAAC,CAAC,EAAE,2BAA2B,CAAC,IAAI,CAAC;KAC7C;IACD;QACE,SAAS,EAAE,WAAW;QACtB,KAAK,EAAE,0BAA0B;QACjC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACZ,4HAA4H,CAAC,IAAI,CAC/H,CAAC,CACF;KACJ;IACD;QACE,SAAS,EAAE,gBAAgB;QAC3B,KAAK,EAAE,0BAA0B;QACjC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACZ,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;YACpB,CAAC,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC;gBACpC,qFAAqF,CAAC,IAAI,CACxF,CAAC,CACF,CAAC;KACP;IACD;QACE,SAAS,EAAE,iBAAiB;QAC5B,KAAK,EAAE,2BAA2B;QAClC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACZ,8GAA8G,CAAC,IAAI,CACjH,CAAC,CACF;KACJ;CACF,CAAC;AAqBF;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,OAAO,GAA0B,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;KACnB,CAAC,CAAC,CAAC;IACJ,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAChC,OAAO;QACL,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;KAC3D,CAAC;AACJ,CAAC;AAED,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,MAAM,MAAM,GACV,iBAAiB,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,GAAG;QACpD,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAC7B,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,EAAE,CAC7C,CAAC;IACF,MAAM,IAAI,GACR,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,OAAO,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { type RunManifest } from "./run-report.js";
2
+ /**
3
+ * Injectable host surface for {@link runExplain} so the reader stays
4
+ * unit-testable without touching the real cwd/env or process stdio. Mirrors
5
+ * {@link import("./inspect.js").InspectDeps}.
6
+ */
7
+ export type ExplainDeps = {
8
+ env: NodeJS.ProcessEnv;
9
+ cwd: string;
10
+ out: (msg: string) => void;
11
+ err: (msg: string) => void;
12
+ };
13
+ /**
14
+ * Render one run for a non-engineer (P9 #64): the plain-language report the run
15
+ * emitted, then a compact "run facts" footer (source, iterations, cost) from the
16
+ * manifest. Pure — takes the already-read manifest and the persisted report text
17
+ * (or `null` when the run emitted none), returns the report string.
18
+ *
19
+ * The persisted report already leads with the layperson prose (What changed /
20
+ * Why / How to verify / What to watch) and keeps engineer detail below its own
21
+ * divider, so this surface is mostly the report plus the run's bottom-line facts.
22
+ * When no report was persisted (an older run, or plan/PRD `afk` mode, which emits
23
+ * none), it falls back to the facts with a one-line note explaining the absence.
24
+ */
25
+ export declare function formatPlainReport(manifest: RunManifest, reportText: string | null): string;
26
+ /**
27
+ * Drive the `otto-explain` command: resolve a run id (an explicit id, or
28
+ * `latest`/no arg → the most recent run under `.otto/runs/`), read its bundle,
29
+ * and print the plain-language report for a non-engineer. Resolves to the
30
+ * process exit code. Mirrors {@link import("./inspect.js").runInspect}.
31
+ */
32
+ export declare function runExplain(argv: string[], deps?: ExplainDeps): Promise<number>;
33
+ //# sourceMappingURL=report-explain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-explain.d.ts","sourceRoot":"","sources":["../src/report-explain.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAEzB;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,CAAC;AAWF;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,CA+BR;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAmCjB"}
@@ -0,0 +1,85 @@
1
+ import { resolve } from "node:path";
2
+ import { listRunIds, readManifest, readRunReport, } from "./run-report.js";
3
+ const defaultDeps = {
4
+ env: process.env,
5
+ cwd: process.cwd(),
6
+ out: (m) => process.stdout.write(`${m}\n`),
7
+ err: (m) => process.stderr.write(`${m}\n`),
8
+ };
9
+ const USAGE = "Usage: otto-explain [<run-id>|latest]";
10
+ /**
11
+ * Render one run for a non-engineer (P9 #64): the plain-language report the run
12
+ * emitted, then a compact "run facts" footer (source, iterations, cost) from the
13
+ * manifest. Pure — takes the already-read manifest and the persisted report text
14
+ * (or `null` when the run emitted none), returns the report string.
15
+ *
16
+ * The persisted report already leads with the layperson prose (What changed /
17
+ * Why / How to verify / What to watch) and keeps engineer detail below its own
18
+ * divider, so this surface is mostly the report plus the run's bottom-line facts.
19
+ * When no report was persisted (an older run, or plan/PRD `afk` mode, which emits
20
+ * none), it falls back to the facts with a one-line note explaining the absence.
21
+ */
22
+ export function formatPlainReport(manifest, reportText) {
23
+ const completed = manifest.completedIterations != null
24
+ ? `${manifest.completedIterations} of ${manifest.iterations}`
25
+ : `${manifest.iterations}`;
26
+ const facts = [];
27
+ facts.push("— Run facts ————————————————————————————");
28
+ facts.push(` What ran: ${manifest.bin} (${manifest.mode} mode)`);
29
+ facts.push(` Asked to do: ${manifest.inputs || "(no inputs)"}`);
30
+ facts.push(` Effort: ${completed} iterations · $${manifest.costUsd.toFixed(2)}`);
31
+ if (manifest.exitReason) {
32
+ facts.push(` Outcome: ${manifest.exitReason}`);
33
+ }
34
+ const lines = [];
35
+ if (reportText) {
36
+ lines.push(reportText.trimEnd());
37
+ lines.push("");
38
+ lines.push(...facts);
39
+ }
40
+ else {
41
+ lines.push(`Run ${manifest.runId}`);
42
+ lines.push("");
43
+ lines.push("This run didn't emit a plain-language report — it's an older run, or a " +
44
+ "plan/PRD (afk) run, which doesn't produce one yet. The run facts:");
45
+ lines.push("");
46
+ lines.push(...facts);
47
+ }
48
+ return lines.join("\n");
49
+ }
50
+ /**
51
+ * Drive the `otto-explain` command: resolve a run id (an explicit id, or
52
+ * `latest`/no arg → the most recent run under `.otto/runs/`), read its bundle,
53
+ * and print the plain-language report for a non-engineer. Resolves to the
54
+ * process exit code. Mirrors {@link import("./inspect.js").runInspect}.
55
+ */
56
+ export async function runExplain(argv, deps = defaultDeps) {
57
+ const arg = argv[0];
58
+ if (arg === "-h" || arg === "--help") {
59
+ deps.out(USAGE);
60
+ return 0;
61
+ }
62
+ const workspaceDir = resolve(deps.env.OTTO_WORKSPACE ?? deps.cwd);
63
+ let runId;
64
+ if (arg && arg !== "latest") {
65
+ runId = arg;
66
+ }
67
+ else {
68
+ const ids = listRunIds(workspaceDir);
69
+ if (ids.length === 0) {
70
+ deps.err(`No runs found under ${workspaceDir}/.otto/runs/. ` +
71
+ "Run Otto first, then explain the bundle it writes.");
72
+ return 1;
73
+ }
74
+ runId = ids[ids.length - 1];
75
+ }
76
+ const manifest = readManifest(workspaceDir, runId);
77
+ if (!manifest) {
78
+ deps.err(`No manifest for run '${runId}' under ${workspaceDir}/.otto/runs/. ` +
79
+ "Check the run id (or pass `latest`).");
80
+ return 1;
81
+ }
82
+ deps.out(formatPlainReport(manifest, readRunReport(workspaceDir, runId)));
83
+ return 0;
84
+ }
85
+ //# sourceMappingURL=report-explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-explain.js","sourceRoot":"","sources":["../src/report-explain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Report-legibility rubric (issue #64 P9, slice 3 — "report-legibility rubric
3
+ * + eval signal").
4
+ *
5
+ * A pure scorer over a quality-report markdown document: it checks the document
6
+ * against the structural criteria of the P9 layperson-first contract shape
7
+ * (quality-report.md) and reports per-criterion results, a met-count / max
8
+ * score, a 0..1 legibility ratio, and what is missing. It is the measurement
9
+ * substrate for the P9 success metric ("% of reports a non-engineer understood
10
+ * without reading code").
11
+ *
12
+ * Sibling to `plan-rubric.ts` (which scores a spec/plan document) and to
13
+ * `eval.ts`'s `scoreTrajectory` (which scores a run trajectory). INERT on the
14
+ * loop — nothing here runs a stage or changes behaviour; it only describes text.
15
+ *
16
+ * The detectors are deterministic header/keyword heuristics (no tokenizer, no
17
+ * model call): the rubric judges *structural legibility* (does the report have
18
+ * the layperson sections the P9 contract requires), the orthogonal question to
19
+ * the *semantic* quality a human judges at review. Heuristic and labelled as
20
+ * such, mirroring plan-rubric.ts's discipline.
21
+ */
22
+ export type ReportCriterion = "verdict" | "whatChanged" | "why" | "howToVerify" | "whatToWatch" | "uncertainty" | "engineerDivider";
23
+ type CriterionDef = {
24
+ criterion: ReportCriterion;
25
+ /** Human-readable label for the scorecard. */
26
+ label: string;
27
+ /** Pure predicate: does the document satisfy this criterion? */
28
+ detect: (doc: string) => boolean;
29
+ };
30
+ /**
31
+ * The rubric criteria, in scorecard order. Each detector keys off the proven
32
+ * P9 quality-report shape: a section header or the characteristic
33
+ * keywords/markup of that section. Order is fixed so the scorecard and the
34
+ * `missing` list are stable.
35
+ */
36
+ export declare const REPORT_CRITERIA: ReadonlyArray<CriterionDef>;
37
+ export type ReportCriterionResult = {
38
+ criterion: ReportCriterion;
39
+ label: string;
40
+ met: boolean;
41
+ };
42
+ export type ReportRubricScore = {
43
+ /** Per-criterion outcome, in fixed scorecard order. */
44
+ results: ReportCriterionResult[];
45
+ /** Criteria met. */
46
+ metCount: number;
47
+ /** Total criteria (the denominator). */
48
+ maxScore: number;
49
+ /** metCount / maxScore in 0..1; 0 when there are no criteria. */
50
+ ratio: number;
51
+ /** Labels of unmet criteria — the "what to improve" surface. */
52
+ missing: string[];
53
+ };
54
+ /**
55
+ * Score a quality-report markdown document against {@link REPORT_CRITERIA}.
56
+ * Pure and deterministic — pass the full report text. Each criterion
57
+ * contributes equally; the per-criterion breakdown lets a consumer reweight
58
+ * later without changing the scorer.
59
+ */
60
+ export declare function scoreReportLegibility(doc: string): ReportRubricScore;
61
+ /**
62
+ * Render a rubric score as a short, human-readable scorecard: a header with
63
+ * the met/total and percentage, a checked/unchecked line per criterion, and a
64
+ * one-line "missing" note when the report is incomplete.
65
+ */
66
+ export declare function formatReportRubric(score: ReportRubricScore): string;
67
+ export {};
68
+ //# sourceMappingURL=report-rubric.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-rubric.d.ts","sourceRoot":"","sources":["../src/report-rubric.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,aAAa,GACb,KAAK,GACL,aAAa,GACb,aAAa,GACb,aAAa,GACb,iBAAiB,CAAC;AAEtB,KAAK,YAAY,GAAG;IAClB,SAAS,EAAE,eAAe,CAAC;IAC3B,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAClC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,YAAY,CA0CvD,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uDAAuD;IACvD,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAepE;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAUnE"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Report-legibility rubric (issue #64 P9, slice 3 — "report-legibility rubric
3
+ * + eval signal").
4
+ *
5
+ * A pure scorer over a quality-report markdown document: it checks the document
6
+ * against the structural criteria of the P9 layperson-first contract shape
7
+ * (quality-report.md) and reports per-criterion results, a met-count / max
8
+ * score, a 0..1 legibility ratio, and what is missing. It is the measurement
9
+ * substrate for the P9 success metric ("% of reports a non-engineer understood
10
+ * without reading code").
11
+ *
12
+ * Sibling to `plan-rubric.ts` (which scores a spec/plan document) and to
13
+ * `eval.ts`'s `scoreTrajectory` (which scores a run trajectory). INERT on the
14
+ * loop — nothing here runs a stage or changes behaviour; it only describes text.
15
+ *
16
+ * The detectors are deterministic header/keyword heuristics (no tokenizer, no
17
+ * model call): the rubric judges *structural legibility* (does the report have
18
+ * the layperson sections the P9 contract requires), the orthogonal question to
19
+ * the *semantic* quality a human judges at review. Heuristic and labelled as
20
+ * such, mirroring plan-rubric.ts's discipline.
21
+ */
22
+ /**
23
+ * The rubric criteria, in scorecard order. Each detector keys off the proven
24
+ * P9 quality-report shape: a section header or the characteristic
25
+ * keywords/markup of that section. Order is fixed so the scorecard and the
26
+ * `missing` list are stable.
27
+ */
28
+ export const REPORT_CRITERIA = [
29
+ {
30
+ criterion: "verdict",
31
+ label: "Verdict section",
32
+ detect: (d) => /(?:^|\n)#{1,6}\s+Verdict\b/i.test(d),
33
+ },
34
+ {
35
+ criterion: "whatChanged",
36
+ label: "What Changed section",
37
+ detect: (d) => /(?:^|\n)#{1,6}\s+What Changed\b/i.test(d),
38
+ },
39
+ {
40
+ criterion: "why",
41
+ label: "Why section",
42
+ detect: (d) => /(?:^|\n)#{1,6}\s+Why\b/i.test(d),
43
+ },
44
+ {
45
+ criterion: "howToVerify",
46
+ label: "How To Verify section with numbered step",
47
+ detect: (d) => {
48
+ // Require BOTH the section heading AND at least one numbered step after it.
49
+ const headingMatch = /(?:^|\n)(#{1,6}\s+How To Verify\b)/i.exec(d);
50
+ if (!headingMatch)
51
+ return false;
52
+ const afterHeading = d.slice(headingMatch.index + headingMatch[0].length);
53
+ return /^\s*\d+\./m.test(afterHeading);
54
+ },
55
+ },
56
+ {
57
+ criterion: "whatToWatch",
58
+ label: "What To Watch section",
59
+ detect: (d) => /(?:^|\n)#{1,6}\s+What To Watch\b/i.test(d),
60
+ },
61
+ {
62
+ criterion: "uncertainty",
63
+ label: "What I Was Unsure About section",
64
+ detect: (d) => /(?:^|\n)#{1,6}\s+What I Was Unsure About\b/i.test(d),
65
+ },
66
+ {
67
+ criterion: "engineerDivider",
68
+ label: "Engineer detail divider",
69
+ detect: (d) => /Engineer detail below/i.test(d),
70
+ },
71
+ ];
72
+ /**
73
+ * Score a quality-report markdown document against {@link REPORT_CRITERIA}.
74
+ * Pure and deterministic — pass the full report text. Each criterion
75
+ * contributes equally; the per-criterion breakdown lets a consumer reweight
76
+ * later without changing the scorer.
77
+ */
78
+ export function scoreReportLegibility(doc) {
79
+ const results = REPORT_CRITERIA.map((c) => ({
80
+ criterion: c.criterion,
81
+ label: c.label,
82
+ met: c.detect(doc),
83
+ }));
84
+ const metCount = results.reduce((n, r) => n + (r.met ? 1 : 0), 0);
85
+ const maxScore = results.length;
86
+ return {
87
+ results,
88
+ metCount,
89
+ maxScore,
90
+ ratio: maxScore > 0 ? metCount / maxScore : 0,
91
+ missing: results.filter((r) => !r.met).map((r) => r.label),
92
+ };
93
+ }
94
+ const pct = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
95
+ /**
96
+ * Render a rubric score as a short, human-readable scorecard: a header with
97
+ * the met/total and percentage, a checked/unchecked line per criterion, and a
98
+ * one-line "missing" note when the report is incomplete.
99
+ */
100
+ export function formatReportRubric(score) {
101
+ const header = `report legibility: ${score.metCount}/${score.maxScore} ` +
102
+ `(${pct.format(score.ratio * 100)}%)`;
103
+ const lines = score.results.map((r) => ` [${r.met ? "x" : " "}] ${r.label}`);
104
+ const tail = score.missing.length > 0 ? [` missing: ${score.missing.join(", ")}`] : [];
105
+ return [header, ...lines, ...tail].join("\n");
106
+ }
107
+ //# sourceMappingURL=report-rubric.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-rubric.js","sourceRoot":"","sources":["../src/report-rubric.ts"],"names":[],"mappings":"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"}
package/dist/run-bin.d.ts CHANGED
@@ -45,6 +45,8 @@ export type RunBinConfig = {
45
45
  issueStage?: Stage;
46
46
  /** Single read-only gate stage used when --verify is set. Only otto-afk sets this. */
47
47
  verifyStage?: Stage;
48
+ /** Single authoring gate stage used when --plan is set (#63 P8). Only otto-afk sets this. */
49
+ planStage?: Stage;
48
50
  /** Gate stage used when --apply-review is set. Only otto-afk sets this. */
49
51
  applyReviewStage?: Stage;
50
52
  /** Run mode identifier threaded into runLoop state (e.g. "afk" / "ghafk"). */
@@ -1 +1 @@
1
- {"version":3,"file":"run-bin.d.ts","sourceRoot":"","sources":["../src/run-bin.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG;IACzB,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,iEAAiE;IACjE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,sFAAsF;IACtF,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;CAC/C,CAAC;AA2BF;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgjB7E"}
1
+ {"version":3,"file":"run-bin.d.ts","sourceRoot":"","sources":["../src/run-bin.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG;IACzB,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,iEAAiE;IACjE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,sFAAsF;IACtF,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,6FAA6F;IAC7F,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;CAC/C,CAAC;AA2BF;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAskB7E"}
package/dist/run-bin.js CHANGED
@@ -65,6 +65,16 @@ export async function runBin(argv, cfg) {
65
65
  process.exit(code);
66
66
  return;
67
67
  }
68
+ // --plan-report (issue #63 P8): a read-only surface scoring the authored plans
69
+ // under .otto/tasks/ with the plan-quality rubric. Like --context-report it
70
+ // prints and exits without running a stage.
71
+ if (flags.planReport) {
72
+ const { runPlanReport } = await import("./plan-report-cli.js");
73
+ const code = await runPlanReport();
74
+ if (code !== 0)
75
+ process.exit(code);
76
+ return;
77
+ }
68
78
  const envMaxWait = process.env.OTTO_MAX_WAIT?.trim();
69
79
  const maxWaitMs = flags.maxWaitMs ?? (envMaxWait ? parseDurationMs(envMaxWait) : undefined);
70
80
  let tokenMode = flags.tokenMode ?? "off";
@@ -157,9 +167,11 @@ export async function runBin(argv, cfg) {
157
167
  // the selected mode. Depends only on flags, not on the guards below.
158
168
  const runMode = flags.verify
159
169
  ? "verify"
160
- : flags.applyReview != null
161
- ? "review"
162
- : cfg.mode;
170
+ : flags.plan
171
+ ? "plan"
172
+ : flags.applyReview != null
173
+ ? "review"
174
+ : cfg.mode;
163
175
  // Single source of truth for the --watch label: the per-mode resolver
164
176
  // (otto-linear-afk → OTTO_LINEAR_LABEL) falling back to OTTO_WATCH_LABEL.
165
177
  // Both --print-config and the runWatch call below read this, so the reported
@@ -362,16 +374,21 @@ export async function runBin(argv, cfg) {
362
374
  }
363
375
  const modeCount = (flags.issue != null ? 1 : 0) +
364
376
  (flags.verify ? 1 : 0) +
377
+ (flags.plan ? 1 : 0) +
365
378
  (flags.applyReview != null ? 1 : 0) +
366
379
  (flags.watch ? 1 : 0);
367
380
  if (modeCount > 1) {
368
- console.error("--issue, --verify, --apply-review, and --watch are mutually exclusive");
381
+ console.error("--issue, --verify, --plan, --apply-review, and --watch are mutually exclusive");
369
382
  process.exit(1);
370
383
  }
371
384
  if (flags.verify && !cfg.verifyStage) {
372
385
  console.error("--verify is only supported by otto-afk");
373
386
  process.exit(1);
374
387
  }
388
+ if (flags.plan && !cfg.planStage) {
389
+ console.error("--plan is only supported by otto-afk");
390
+ process.exit(1);
391
+ }
375
392
  if (flags.applyReview != null && !cfg.applyReviewStage) {
376
393
  console.error("--apply-review is only supported by otto-afk");
377
394
  process.exit(1);
@@ -390,21 +407,24 @@ export async function runBin(argv, cfg) {
390
407
  : cfg.takesInputArg
391
408
  ? flags.rest[1]
392
409
  : flags.rest[0];
393
- if (flags.verify && (!cfg.takesInputArg || !inputs)) {
394
- console.error(`Usage: ${cfg.bin} --verify "<plan-and-prd>"`);
410
+ // --verify and --plan are both one-shot, input-taking gates (no iterations arg).
411
+ const oneShot = flags.verify || flags.plan;
412
+ const oneShotFlag = flags.verify ? "--verify" : "--plan";
413
+ if (oneShot && (!cfg.takesInputArg || !inputs)) {
414
+ console.error(`Usage: ${cfg.bin} ${oneShotFlag} "<plan-and-prd>"`);
395
415
  process.exit(1);
396
416
  }
397
- if (!flags.verify && ((cfg.takesInputArg && !inputs) || !iterationsArg)) {
417
+ if (!oneShot && ((cfg.takesInputArg && !inputs) || !iterationsArg)) {
398
418
  console.error(`Usage: ${cfg.bin} ${cfg.usage}`);
399
419
  console.error(` ${cfg.bin} --help`);
400
420
  process.exit(1);
401
421
  }
402
- // --verify is one-shot regardless of any positional count.
403
- if (flags.verify && iterationsArg) {
404
- console.error("--verify is one-shot; ignoring the iterations argument");
422
+ // One-shot regardless of any positional count.
423
+ if (oneShot && iterationsArg) {
424
+ console.error(`${oneShotFlag} is one-shot; ignoring the iterations argument`);
405
425
  }
406
- const iterations = flags.verify ? 1 : Number.parseInt(iterationsArg, 10);
407
- if (!flags.verify && (!Number.isFinite(iterations) || iterations < 1)) {
426
+ const iterations = oneShot ? 1 : Number.parseInt(iterationsArg, 10);
427
+ if (!oneShot && (!Number.isFinite(iterations) || iterations < 1)) {
408
428
  console.error(`Invalid iterations: ${iterationsArg}`);
409
429
  process.exit(1);
410
430
  }
@@ -421,11 +441,13 @@ export async function runBin(argv, cfg) {
421
441
  }
422
442
  const stages = flags.verify
423
443
  ? [cfg.verifyStage]
424
- : flags.applyReview != null
425
- ? [cfg.applyReviewStage, ...cfg.stages.slice(1)]
426
- : flags.issue != null
427
- ? [cfg.issueStage, ...cfg.stages.slice(1)]
428
- : cfg.stages;
444
+ : flags.plan
445
+ ? [cfg.planStage]
446
+ : flags.applyReview != null
447
+ ? [cfg.applyReviewStage, ...cfg.stages.slice(1)]
448
+ : flags.issue != null
449
+ ? [cfg.issueStage, ...cfg.stages.slice(1)]
450
+ : cfg.stages;
429
451
  if (flags.detach && detachLogPath) {
430
452
  detachAndExit({
431
453
  logPath: detachLogPath,