@gonrocca/zero-pi 0.1.48 → 0.1.50
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/README.md +70 -2
- package/extensions/gh-runner.ts +79 -0
- package/extensions/git-runner.ts +44 -0
- package/extensions/pr-body.ts +152 -0
- package/extensions/scan-guard-extension.ts +104 -0
- package/extensions/scan-guard.ts +190 -0
- package/extensions/sdd-config.ts +38 -0
- package/extensions/sdd-links.ts +48 -0
- package/extensions/spec-merge-extension.ts +4 -0
- package/extensions/spec-merge.ts +120 -217
- package/extensions/zero-archive-extension.ts +44 -0
- package/extensions/zero-branch-extension.ts +64 -0
- package/extensions/zero-diff-extension.ts +59 -0
- package/extensions/zero-git-validate-extension.ts +56 -0
- package/extensions/zero-issue-extension.ts +51 -0
- package/extensions/zero-pr-extension.ts +98 -0
- package/extensions/zero-status-extension.ts +59 -0
- package/extensions/zero-status.ts +65 -0
- package/extensions/zero-validate-extension.ts +67 -0
- package/extensions/zero-validate.ts +162 -0
- package/package.json +28 -2
- package/prompts/orchestrator.md +15 -6
- package/prompts/phases/explore.md +6 -0
- package/prompts/phases/plan.md +37 -0
- package/prompts/phases/veredicto.md +39 -11
- package/skills/sdd-routing/SKILL.md +9 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { checkGuardrails, parseDelta, type MergeError, type RenameBlock, type RequirementBlock } from "./spec-merge.ts";
|
|
5
|
+
|
|
6
|
+
export interface ValidationDefect {
|
|
7
|
+
kind: string;
|
|
8
|
+
message: string;
|
|
9
|
+
task?: string;
|
|
10
|
+
path?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
declaredTotal?: number;
|
|
13
|
+
computedTotal?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface TaskRecord {
|
|
17
|
+
id: string;
|
|
18
|
+
title: string;
|
|
19
|
+
files: { path: string; isNew: boolean }[];
|
|
20
|
+
evidence: string | null;
|
|
21
|
+
review: number | null;
|
|
22
|
+
reviewRaw: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface WorkloadSection {
|
|
26
|
+
estimates: Map<string, number>;
|
|
27
|
+
declaredTotal: number | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseTasks(text: string): { tasks: TaskRecord[]; workload: WorkloadSection | null; defects: ValidationDefect[] } {
|
|
31
|
+
const tasks: TaskRecord[] = [];
|
|
32
|
+
const defects: ValidationDefect[] = [];
|
|
33
|
+
const lines = typeof text === "string" ? text.split(/\r?\n/) : [];
|
|
34
|
+
let current: TaskRecord | null = null;
|
|
35
|
+
let collectingFiles = false;
|
|
36
|
+
|
|
37
|
+
const pushFile = (line: string): void => {
|
|
38
|
+
if (!current) return;
|
|
39
|
+
const matches = [...line.matchAll(/`([^`]+)`\s*(\(new\))?/g)];
|
|
40
|
+
for (const m of matches) current.files.push({ path: m[1], isNew: Boolean(m[2]) });
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
const task = line.match(/^\s*- \[[ xX]\]\s+\*\*(T\d+)\.\s+([^*]+)\*\*/) ?? line.match(/^###\s+(T\d+)\s+[—-]\s+(.+)$/) ?? line.match(/^##\s+\[[ xX]\]\s+(T\d+)\s*(?:[—-]\s*(.+))?$/);
|
|
45
|
+
if (task) {
|
|
46
|
+
current = { id: task[1], title: (task[2] ?? "").trim(), files: [], evidence: null, review: null, reviewRaw: null };
|
|
47
|
+
tasks.push(current);
|
|
48
|
+
collectingFiles = false;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!current) continue;
|
|
52
|
+
if (/^\s*-\s+files:/.test(line)) {
|
|
53
|
+
collectingFiles = true;
|
|
54
|
+
pushFile(line);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (/^\s*-\s+evidence:/.test(line)) {
|
|
58
|
+
collectingFiles = false;
|
|
59
|
+
current.evidence = line.replace(/^\s*-\s+evidence:\s*/, "").trim();
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (/^\s*-\s+review:/.test(line)) {
|
|
63
|
+
collectingFiles = false;
|
|
64
|
+
const raw = line.replace(/^\s*-\s+review:\s*/, "").trim();
|
|
65
|
+
current.reviewRaw = raw;
|
|
66
|
+
const m = raw.match(/~?(\d+)\s+changed lines/i);
|
|
67
|
+
current.review = m ? Number(m[1]) : null;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (collectingFiles) {
|
|
71
|
+
if (/^\s*`[^`]+`/.test(line) || /^\s+`[^`]+`/.test(line) || /^\s*-\s+`[^`]+`/.test(line)) pushFile(line);
|
|
72
|
+
else if (/^\s*-\s+/.test(line)) collectingFiles = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const task of tasks) {
|
|
77
|
+
if (task.files.length === 0) defects.push({ kind: "missing-files", task: task.id, message: `${task.id} is missing files list` });
|
|
78
|
+
if (task.evidence === null || task.evidence === "") defects.push({ kind: "missing-evidence", task: task.id, message: `${task.id} is missing evidence` });
|
|
79
|
+
if (task.reviewRaw === null) defects.push({ kind: "missing-review", task: task.id, message: `${task.id} is missing review estimate` });
|
|
80
|
+
else if (task.review === null) defects.push({ kind: "non-integer-review", task: task.id, message: `${task.id} review estimate is not an integer` });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const workload = parseWorkload(lines);
|
|
84
|
+
return { tasks, workload, defects };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseWorkload(lines: string[]): WorkloadSection | null {
|
|
88
|
+
const start = lines.findIndex((l) => /^##\s+Review Workload\s*$/.test(l));
|
|
89
|
+
if (start < 0) return null;
|
|
90
|
+
const estimates = new Map<string, number>();
|
|
91
|
+
let declaredTotal: number | null = null;
|
|
92
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
if (/^##\s+/.test(line)) break;
|
|
95
|
+
let m = line.match(/^\|\s*(T\d+)\s*\|\s*~?(\d+)\s*\|/);
|
|
96
|
+
if (m) estimates.set(m[1], Number(m[2]));
|
|
97
|
+
m = line.match(/^\s*-\s*(T\d+)\s*[:|-]\s*~?(\d+)/);
|
|
98
|
+
if (m) estimates.set(m[1], Number(m[2]));
|
|
99
|
+
m = line.match(/\*\*Total:\s*~?(\d+)\s+changed lines\*\*/i);
|
|
100
|
+
if (m) declaredTotal = Number(m[1]);
|
|
101
|
+
}
|
|
102
|
+
return { estimates, declaredTotal };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function validateTasksFile(text: string): ValidationDefect[] {
|
|
106
|
+
const parsed = parseTasks(text);
|
|
107
|
+
const defects = [...parsed.defects];
|
|
108
|
+
if (parsed.workload === null) {
|
|
109
|
+
defects.push({ kind: "missing-review-workload", message: "tasks.md is missing ## Review Workload" });
|
|
110
|
+
} else {
|
|
111
|
+
const computedTotal = parsed.tasks.reduce((sum, task) => sum + (task.review ?? 0), 0);
|
|
112
|
+
if (parsed.workload.declaredTotal === null) defects.push({ kind: "missing-workload-total", message: "Review Workload is missing total" });
|
|
113
|
+
else if (parsed.workload.declaredTotal !== computedTotal) defects.push({ kind: "total-mismatch", declaredTotal: parsed.workload.declaredTotal, computedTotal, message: `Review Workload total ${parsed.workload.declaredTotal} does not match computed ${computedTotal}` });
|
|
114
|
+
for (const task of parsed.tasks) {
|
|
115
|
+
const est = parsed.workload.estimates.get(task.id);
|
|
116
|
+
if (est !== undefined && task.review !== null && est !== task.review) defects.push({ kind: "workload-task-mismatch", task: task.id, declaredTotal: est, computedTotal: task.review, message: `${task.id} workload estimate does not match task review` });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return defects;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function allBlocks(delta: ReturnType<typeof parseDelta>): Array<RequirementBlock | RenameBlock> {
|
|
123
|
+
return [...delta.added, ...delta.modified, ...delta.removed, ...delta.renamed];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function validateSpecDelta(text: string): ValidationDefect[] {
|
|
127
|
+
const delta = parseDelta(text);
|
|
128
|
+
const defects: ValidationDefect[] = checkGuardrails([], delta).map((e: MergeError) => ({ kind: e.kind, name: e.name, message: e.message }));
|
|
129
|
+
for (const block of allBlocks(delta)) {
|
|
130
|
+
const name = "name" in block ? block.name : block.to;
|
|
131
|
+
if (block.body.trim() === "") defects.push({ kind: "empty-body", name, message: `Requirement ${name} has an empty body` });
|
|
132
|
+
if (!block.body.includes("Acceptance criteria")) defects.push({ kind: "missing-acceptance-criteria", name, message: `Requirement ${name} is missing Acceptance criteria` });
|
|
133
|
+
}
|
|
134
|
+
return defects;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function discoverSpecFiles(runDir: string): string[] {
|
|
138
|
+
const files: string[] = [];
|
|
139
|
+
if (existsSync(join(runDir, "spec.md"))) files.push(join(runDir, "spec.md"));
|
|
140
|
+
const specsDir = join(runDir, "specs");
|
|
141
|
+
try {
|
|
142
|
+
for (const e of readdirSync(specsDir, { withFileTypes: true })) if (e.isDirectory()) {
|
|
143
|
+
const file = join(specsDir, e.name, "spec.md");
|
|
144
|
+
if (existsSync(file)) files.push(file);
|
|
145
|
+
}
|
|
146
|
+
} catch {}
|
|
147
|
+
return files;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function validateSpecInputs(runDir: string): ValidationDefect[] {
|
|
151
|
+
const files = discoverSpecFiles(runDir);
|
|
152
|
+
if (files.length === 0) return [{ kind: "missing-spec", path: "spec.md", message: "spec.md or specs/<domain>/spec.md is missing" }];
|
|
153
|
+
return files.flatMap((file) => validateSpecDelta(readFileSync(file, "utf8")).map((d) => ({ ...d, path: file })));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function validateArtifactSet(present: { proposal: boolean; spec: boolean; design: boolean; tasks: boolean }): ValidationDefect[] {
|
|
157
|
+
const defects: ValidationDefect[] = [];
|
|
158
|
+
for (const key of ["proposal", "spec", "design", "tasks"] as const) {
|
|
159
|
+
if (!present[key]) defects.push({ kind: `missing-${key}`, path: `${key}.md`, message: `${key}.md is missing` });
|
|
160
|
+
}
|
|
161
|
+
return defects;
|
|
162
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/zero-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
4
4
|
"description": "zero-pi — an installable layer for pi (pi.dev): the zero spec-driven development workflow (explore → plan → build → veredicto) with per-phase model autotune and token-efficient batched builds. Adds capability to pi without modifying pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -33,7 +33,16 @@
|
|
|
33
33
|
"./extensions/zero-models.ts",
|
|
34
34
|
"./extensions/autotune-extension.ts",
|
|
35
35
|
"./extensions/spec-merge-extension.ts",
|
|
36
|
-
"./extensions/
|
|
36
|
+
"./extensions/zero-validate-extension.ts",
|
|
37
|
+
"./extensions/zero-status-extension.ts",
|
|
38
|
+
"./extensions/zero-pr-extension.ts",
|
|
39
|
+
"./extensions/zero-branch-extension.ts",
|
|
40
|
+
"./extensions/zero-git-validate-extension.ts",
|
|
41
|
+
"./extensions/zero-archive-extension.ts",
|
|
42
|
+
"./extensions/zero-issue-extension.ts",
|
|
43
|
+
"./extensions/zero-diff-extension.ts",
|
|
44
|
+
"./extensions/provider-guard-extension.ts",
|
|
45
|
+
"./extensions/scan-guard-extension.ts"
|
|
37
46
|
]
|
|
38
47
|
},
|
|
39
48
|
"files": [
|
|
@@ -53,8 +62,25 @@
|
|
|
53
62
|
"extensions/autotune-extension.ts",
|
|
54
63
|
"extensions/spec-merge.ts",
|
|
55
64
|
"extensions/spec-merge-extension.ts",
|
|
65
|
+
"extensions/zero-validate.ts",
|
|
66
|
+
"extensions/zero-validate-extension.ts",
|
|
67
|
+
"extensions/zero-status.ts",
|
|
68
|
+
"extensions/zero-status-extension.ts",
|
|
69
|
+
"extensions/pr-body.ts",
|
|
70
|
+
"extensions/sdd-links.ts",
|
|
71
|
+
"extensions/sdd-config.ts",
|
|
72
|
+
"extensions/git-runner.ts",
|
|
73
|
+
"extensions/gh-runner.ts",
|
|
74
|
+
"extensions/zero-pr-extension.ts",
|
|
75
|
+
"extensions/zero-branch-extension.ts",
|
|
76
|
+
"extensions/zero-git-validate-extension.ts",
|
|
77
|
+
"extensions/zero-archive-extension.ts",
|
|
78
|
+
"extensions/zero-issue-extension.ts",
|
|
79
|
+
"extensions/zero-diff-extension.ts",
|
|
56
80
|
"extensions/provider-guard.ts",
|
|
57
81
|
"extensions/provider-guard-extension.ts",
|
|
82
|
+
"extensions/scan-guard.ts",
|
|
83
|
+
"extensions/scan-guard-extension.ts",
|
|
58
84
|
"README.md",
|
|
59
85
|
"LICENSE"
|
|
60
86
|
],
|
package/prompts/orchestrator.md
CHANGED
|
@@ -177,7 +177,7 @@ A zero SDD run has two language surfaces — keep them apart.
|
|
|
177
177
|
- **Fixed identifiers — verbatim.** Never translate identifiers. Keep verdict
|
|
178
178
|
values (`pasa`, `corregir`, `replantear`, `cap-reached`), feature slugs,
|
|
179
179
|
file and directory paths, model ids, and command names (`/forge`,
|
|
180
|
-
`/zero-sync`) exactly as they are, even inside Spanish text.
|
|
180
|
+
`/zero-sync`, `/zero-branch`, `/zero-git-validate`, `/zero-pr`, `/zero-archive`) exactly as they are, even inside Spanish text.
|
|
181
181
|
|
|
182
182
|
## Output Contract
|
|
183
183
|
|
|
@@ -361,6 +361,14 @@ verdict" above). If the push fails for any reason, or zero runs with `--no-mcp`
|
|
|
361
361
|
or Cortex is down — emit a non-blocking warning and continue, never block the
|
|
362
362
|
run. The local line already stands.
|
|
363
363
|
|
|
364
|
+
## Git/PR/archive commands
|
|
365
|
+
|
|
366
|
+
Recommended command order for an audit-ready SDD change is: `/zero-branch <slug>` → `/zero-issue <slug>` → `/forge <slug>` (plan/build/veredicto) → `/zero-git-validate <slug> --for=pr` → `/zero-pr <slug>` → `/zero-archive <slug>` after `pasa`.
|
|
367
|
+
|
|
368
|
+
- `/zero-branch <slug>` creates/reuses `sdd/<slug>` (configurable) and records `branch`/`baseBranch` in `links.json`.
|
|
369
|
+
- `/zero-git-validate <slug>` checks worktree, branch, remote, `gh auth`, and verdict gating without mutating.
|
|
370
|
+
- `/zero-archive <slug>` merges approved deltas into `.sdd/specs/` and moves the run to `.sdd/archive/YYYY-MM-DD-<slug>/`.
|
|
371
|
+
|
|
364
372
|
## Spec sync & archive
|
|
365
373
|
|
|
366
374
|
The project keeps a **canonical spec store** at `.sdd/specs/requirements.md` —
|
|
@@ -369,12 +377,13 @@ emits a delta `spec.md` against that store; once the run reaches a `pasa`
|
|
|
369
377
|
verdict the delta is folded back into the store.
|
|
370
378
|
|
|
371
379
|
**After a `pasa` verdict — and only then.** Alongside the Cortex save and the
|
|
372
|
-
`zero-runs.jsonl` append, invoke the **`/zero-
|
|
373
|
-
the run's feature slug explicitly. `/zero-
|
|
380
|
+
`zero-runs.jsonl` append, invoke the **`/zero-archive <slug>`** command, passing
|
|
381
|
+
the run's feature slug explicitly. `/zero-archive` is a real pi command — a
|
|
374
382
|
deterministic, unit-tested merge, not a prompt instruction — that reads
|
|
375
|
-
`.sdd/specs/requirements.md`
|
|
376
|
-
|
|
377
|
-
|
|
383
|
+
`.sdd/specs/requirements.md` or per-domain `.sdd/specs/<domain>/requirements.md`
|
|
384
|
+
and `.sdd/<slug>/spec.md` or `.sdd/<slug>/specs/<domain>/spec.md`, folds the
|
|
385
|
+
delta into the store, writes atomically, moves the run to `.sdd/archive/`, and
|
|
386
|
+
records `archivePath` in `links.json`. You only call it; you never edit the store yourself.
|
|
378
387
|
|
|
379
388
|
**Never sync on a non-`pasa` outcome.** Do **not** invoke `/zero-sync` for a
|
|
380
389
|
`corregir` or `replantear` verdict, or when the iteration cap was reached
|
|
@@ -24,6 +24,12 @@ constraints — reading past that point burns tokens without improving the plan.
|
|
|
24
24
|
As a rough gauge, a localized change rarely needs more than ~20 tool calls; if
|
|
25
25
|
you blow past that on a small request, you are over-exploring.
|
|
26
26
|
|
|
27
|
+
**Scope every search to the project, never the filesystem root.** Search inside
|
|
28
|
+
the repo/code directory you are working in. A `find`, `grep -r`, or `rg` rooted
|
|
29
|
+
at `/`, a bare drive (`/c`, `C:\`), or `~`/`$HOME` is forbidden — on Windows it
|
|
30
|
+
hangs forever forcing OneDrive to hydrate every cloud placeholder it walks, and
|
|
31
|
+
zero blocks such commands at the tool boundary anyway.
|
|
32
|
+
|
|
27
33
|
Produce a concise findings report the **plan** phase can build on: what exists,
|
|
28
34
|
what is relevant to the request, and what to watch out for.
|
|
29
35
|
|
package/prompts/phases/plan.md
CHANGED
|
@@ -62,6 +62,38 @@ Write all four into `.sdd/<slug>/`:
|
|
|
62
62
|
absolute (or code-root-relative) paths it creates or edits, marking new files
|
|
63
63
|
`(new)`, so the build phase edits them directly instead of rediscovering them.
|
|
64
64
|
|
|
65
|
+
## Task schema
|
|
66
|
+
|
|
67
|
+
Use this exact checklist shape so build/validate/review can parse it without rediscovery:
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
### T001 — Implement focused capability
|
|
71
|
+
|
|
72
|
+
- files:
|
|
73
|
+
- `<root>/src/example.ts`
|
|
74
|
+
- `<root>/src/example.test.ts` (new)
|
|
75
|
+
- detail: concrete implementation notes and boundaries.
|
|
76
|
+
- evidence: `npm test -- example` passes, or the exact manual check expected.
|
|
77
|
+
- review: ~120 changed lines
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Rules:
|
|
81
|
+
- Task ids are monotonic `T###`; keep existing completed ids stable on resume.
|
|
82
|
+
- Add `[P]` only for tasks that are truly parallel-safe, and `[Story:S1]`/`[Story:S2]` when the plan has independently testable stories.
|
|
83
|
+
- `files:` is mandatory and uses exact paths; append `(new)` for created files.
|
|
84
|
+
- `evidence:` is mandatory and names a concrete verification command or artifact.
|
|
85
|
+
- Keep `## Review Workload` present and synchronized with the task list.
|
|
86
|
+
|
|
87
|
+
## Constitution / Steering check
|
|
88
|
+
|
|
89
|
+
Before finalizing tasks, check project steering/constitution files when present (`.sdd/constitution.md`, `.sdd/steering.md`, `.kiro/steering/*`, or project equivalents). If absent, mark `n/a`; absence is not a blocker. Include this table in `design.md` or `tasks.md`:
|
|
90
|
+
|
|
91
|
+
| rule | status | waiver |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| Steering/constitution present | n/a | No local steering file found |
|
|
94
|
+
| Scope matches product/tech constraints | pass | — |
|
|
95
|
+
| No forbidden dependency or workflow change | pass | — |
|
|
96
|
+
|
|
65
97
|
Honour the prior-run lessons carried in the explore findings: when a past run
|
|
66
98
|
was sent back with `replantear`, do not repeat the plan mistake it recorded.
|
|
67
99
|
|
|
@@ -85,6 +117,11 @@ means the same number on every run.
|
|
|
85
117
|
per-task estimates, and the over-budget exceptions list — state "none" when
|
|
86
118
|
there are no exceptions.
|
|
87
119
|
|
|
120
|
+
When a run is likely to land as a chained PR series, make that explicit in the
|
|
121
|
+
forecast: group tasks into reviewable PR-sized batches, name any ordering
|
|
122
|
+
constraints between batches, and keep each batch small enough that `/zero-pr`
|
|
123
|
+
can produce a focused pull request after `veredicto` returns `pasa`.
|
|
124
|
+
|
|
88
125
|
**Return contract.** Return a concise result envelope to the orchestrator: your
|
|
89
126
|
phase's outcome (findings, plan, build result, or verdict with its concrete
|
|
90
127
|
reasoning) and the `.sdd/<slug>/` artifact path(s) you touched. No step-by-step
|
|
@@ -13,28 +13,56 @@ orchestrator's existing run-trace machinery — the Cortex `zero-run/<slug>` sav
|
|
|
13
13
|
and the `~/.pi/zero-runs.jsonl` append. Do not write a separate verdict file;
|
|
14
14
|
`.sdd/` artifacts stay plan state only.
|
|
15
15
|
|
|
16
|
-
**Locating the code — read, do not search.**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
**Locating the code — read, do not search.** Get the code root from the plan:
|
|
17
|
+
the `## Code roots` section in `design.md`, or — for delta/forge runs that have
|
|
18
|
+
no `design.md` — the `Code root:` line in `tasks.md` or the absolute path in
|
|
19
|
+
your task input. Read the build result for the files it changed and go straight
|
|
20
|
+
to those paths to inspect the changes and run the tests.
|
|
21
|
+
|
|
22
|
+
**NEVER run a filesystem-wide scan to rediscover where the code lives.** A
|
|
23
|
+
`find`, `grep -r`, or `rg` rooted at `/`, a bare drive (`/c`, `C:\`), or `~`/
|
|
24
|
+
`$HOME` is forbidden — on Windows it does not merely run slow, it **hangs
|
|
25
|
+
forever** forcing OneDrive to hydrate every cloud placeholder it walks (a real
|
|
26
|
+
run wedged on `find / -maxdepth 12 …` for 6+ hours). zero blocks such commands
|
|
27
|
+
at the tool boundary, so the call will fail anyway. Always scope a search to the
|
|
28
|
+
code-root absolute path (`find <code-root> -name …`). If you cannot find the
|
|
29
|
+
code root in the plan, say so in the verdict — do not go hunting for it.
|
|
30
|
+
|
|
31
|
+
Also do **not** re-read a file you have already read this review unless it
|
|
32
|
+
changed since; repeated re-reading is the main avoidable token cost on the
|
|
33
|
+
review's model, which is often the most expensive in the pipeline. A single
|
|
34
|
+
targeted search *inside the code root* to fill a genuine gap is fine; a
|
|
35
|
+
full-tree scan is not. This bounds cost only — it never lowers the bar for the
|
|
36
|
+
verdict.
|
|
26
37
|
|
|
27
38
|
Review the build adversarially, with a fresh perspective. Check it against the
|
|
28
39
|
plan's requirements, run the tests yourself, and look for gaps, regressions,
|
|
29
40
|
and unmet acceptance criteria.
|
|
30
41
|
|
|
42
|
+
## Constitution / Steering gate
|
|
43
|
+
|
|
44
|
+
Re-check the plan's Constitution/Steering table before choosing a verdict. If a
|
|
45
|
+
present steering or constitution rule is violated and the plan did not record an
|
|
46
|
+
explicit waiver, return `corregir` even if the code compiles. If steering files
|
|
47
|
+
are absent, treat the row as `n/a`, not as a failure.
|
|
48
|
+
|
|
49
|
+
Example gate table to cite in reasoning when relevant:
|
|
50
|
+
|
|
51
|
+
| rule | status | waiver |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| Steering/constitution present | n/a | No local steering file found |
|
|
54
|
+
| Scope matches product/tech constraints | pass | — |
|
|
55
|
+
| No forbidden dependency or workflow change | fail | corregir: added undeclared dependency |
|
|
56
|
+
|
|
31
57
|
Record exactly one verdict:
|
|
32
58
|
|
|
33
59
|
- `pasa` — the build meets the plan; the run finishes successfully.
|
|
34
60
|
- `corregir` — fixable defects remain; the build phase must re-run.
|
|
35
61
|
- `replantear` — the plan itself is wrong; the plan phase must re-run.
|
|
36
62
|
|
|
37
|
-
Never return `pasa` unless the evidence supports it.
|
|
63
|
+
Never return `pasa` unless the evidence supports it. `/zero-pr` is only allowed
|
|
64
|
+
for runs whose latest recorded `veredicto` is `pasa`; if the verdict is
|
|
65
|
+
`corregir` or `replantear`, the PR must wait for the next successful review.
|
|
38
66
|
|
|
39
67
|
State the verdict's reasoning concretely — the specific defects for `corregir`,
|
|
40
68
|
the specific plan flaw for `replantear`. The orchestrator persists that
|
|
@@ -54,3 +54,12 @@ invoke `/forge` explicitly. A missed natural-language route is harmless — the
|
|
|
54
54
|
user can still type `/forge`. A wrong route forces routine work through the
|
|
55
55
|
heavy pipeline, which is not. Triggering only on a clear signal phrase is the
|
|
56
56
|
rule; `/forge` remains the explicit primary entry point.
|
|
57
|
+
|
|
58
|
+
## Related zero commands
|
|
59
|
+
|
|
60
|
+
- `/zero-branch <slug>` — create/reuse the configured Git branch for the SDD run and persist it in `.sdd/<slug>/links.json`.
|
|
61
|
+
- `/zero-git-validate <slug>` — validate worktree, branch, remote, GitHub CLI auth, and verdict gating before PR/archive.
|
|
62
|
+
- `/zero-pr <slug>` — create an audit-ready GitHub PR from SDD artifacts after `veredicto` returns `pasa`.
|
|
63
|
+
- `/zero-archive <slug>` — merge approved deltas into `.sdd/specs/` and move the run to `.sdd/archive/YYYY-MM-DD-<slug>/`.
|
|
64
|
+
|
|
65
|
+
Recommended flow: `/zero-branch` → `/zero-issue` → `/forge` → `/zero-git-validate --for=pr` → `/zero-pr` → `/zero-archive`.
|