flanders 0.3.0 → 0.5.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.
- package/lib/ai/AiRunner.d.ts +0 -1
- package/lib/ai/AiRunner.js +1 -4
- package/lib/ai/AiSession.d.ts +0 -1
- package/lib/ai/AiSession.js +0 -1
- package/lib/ai/ClaudeAdapter.js +0 -3
- package/lib/ai/CodexAdapter.js +7 -11
- package/lib/ai/ToolAdapter.d.ts +1 -7
- package/lib/commands/Implement.d.ts +5 -5
- package/lib/commands/Implement.js +144 -135
- package/lib/commands/Install.d.ts +2 -0
- package/lib/commands/Install.js +151 -1
- package/lib/commands/InstallModelProbe.js +39 -41
- package/lib/plan/PlanFile.d.ts +2 -0
- package/lib/plan/PlanFile.js +32 -18
- package/lib/prompts/prompts.d.ts +1 -3
- package/lib/prompts/prompts.js +18 -68
- package/lib/prompts/skills.d.ts +1 -1
- package/lib/prompts/skills.js +39 -30
- package/lib/ui/BottomBlock.d.ts +1 -3
- package/lib/ui/BottomBlock.js +3 -5
- package/lib/ui/formatters.d.ts +0 -1
- package/lib/ui/formatters.js +1 -5
- package/lib/workspace/FlandersConfig.d.ts +5 -1
- package/lib/workspace/FlandersConfig.js +9 -1
- package/lib/workspace/SpecDiscovery.js +4 -4
- package/package.json +1 -1
package/lib/commands/Install.js
CHANGED
|
@@ -112,6 +112,34 @@ const CLAUDE_CROSS_FAMILY_ALIASES = [
|
|
|
112
112
|
];
|
|
113
113
|
const CLAUDE_BACK_LABEL = "← back";
|
|
114
114
|
const REVIEWER_INDEXED_RE = /^--reviewer-(\d+)-(tool|model|effort)=/;
|
|
115
|
+
const REVIEWER_OPTIONAL_INDEXED_RE = /^--reviewer-(\d+)-optional$/;
|
|
116
|
+
function validateWeightedFlagsForReviewerCount(reviewerMinimum, optionalReviewerIndices, reviewerCount) {
|
|
117
|
+
if (reviewerCount === 1) {
|
|
118
|
+
if (reviewerMinimum !== undefined) {
|
|
119
|
+
return "Invalid flag for a single-reviewer configuration: --reviewer-minimum. Weighted-review flags require two or more reviewers.\n";
|
|
120
|
+
}
|
|
121
|
+
if (optionalReviewerIndices !== undefined) {
|
|
122
|
+
const idx = optionalReviewerIndices[0];
|
|
123
|
+
const flag = idx === 1 ? "--reviewer-optional" : `--reviewer-${idx}-optional`;
|
|
124
|
+
return `Invalid flag for a single-reviewer configuration: ${flag}. Weighted-review flags require two or more reviewers.\n`;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
if (reviewerMinimum !== undefined && (reviewerMinimum < 1 || reviewerMinimum > reviewerCount)) {
|
|
129
|
+
return `Invalid value for --reviewer-minimum: "${reviewerMinimum}". Must be an integer between 1 and ${reviewerCount}.\n`;
|
|
130
|
+
}
|
|
131
|
+
if (optionalReviewerIndices !== undefined) {
|
|
132
|
+
for (const idx of optionalReviewerIndices) {
|
|
133
|
+
if (idx > reviewerCount) {
|
|
134
|
+
return `Invalid reviewer flag: --reviewer-${idx}-optional references reviewer ${idx}, beyond the configured reviewer list of ${reviewerCount}.\n`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (reviewerMinimum === reviewerCount && optionalReviewerIndices !== undefined) {
|
|
139
|
+
return `Invalid flag combination: --reviewer-minimum equal to the reviewer count (${reviewerCount}) leaves no reviewer that can be optional, so it cannot be combined with --reviewer[-N]-optional.\n`;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
115
143
|
function parseInstallFlags(rawArgs) {
|
|
116
144
|
var _a;
|
|
117
145
|
const hasGlobal = rawArgs.includes("--global");
|
|
@@ -226,6 +254,38 @@ function parseInstallFlags(rawArgs) {
|
|
|
226
254
|
}
|
|
227
255
|
answers.reviewers = list;
|
|
228
256
|
}
|
|
257
|
+
const optionalIndices = new Set();
|
|
258
|
+
for (const arg of rawArgs) {
|
|
259
|
+
if (arg === "--reviewer-optional") {
|
|
260
|
+
optionalIndices.add(1);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const match = arg.match(REVIEWER_OPTIONAL_INDEXED_RE);
|
|
264
|
+
if (match === null) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const idx = Number(match[1]);
|
|
268
|
+
if (idx < 1) {
|
|
269
|
+
return { ok: false, diagnostic: `Invalid reviewer flag: "${arg}". --reviewer-N-optional requires N >= 1.\n` };
|
|
270
|
+
}
|
|
271
|
+
optionalIndices.add(idx);
|
|
272
|
+
}
|
|
273
|
+
if (optionalIndices.size > 0) {
|
|
274
|
+
answers.optionalReviewerIndices = [...optionalIndices].sort((a, b) => a - b);
|
|
275
|
+
}
|
|
276
|
+
const minimumRaw = extractFlagValue(rawArgs, "--reviewer-minimum");
|
|
277
|
+
if (minimumRaw !== undefined) {
|
|
278
|
+
if (!/^\d+$/.test(minimumRaw)) {
|
|
279
|
+
return { ok: false, diagnostic: `Invalid value for --reviewer-minimum: "${minimumRaw}". Expected a non-negative integer.\n` };
|
|
280
|
+
}
|
|
281
|
+
answers.reviewerMinimum = Number(minimumRaw);
|
|
282
|
+
}
|
|
283
|
+
if (answers.reviewers !== undefined) {
|
|
284
|
+
const diagnostic = validateWeightedFlagsForReviewerCount(answers.reviewerMinimum, answers.optionalReviewerIndices, answers.reviewers.length);
|
|
285
|
+
if (diagnostic !== null) {
|
|
286
|
+
return { ok: false, diagnostic };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
229
289
|
return { ok: true, answers };
|
|
230
290
|
}
|
|
231
291
|
class Install {
|
|
@@ -590,6 +650,95 @@ class Install {
|
|
|
590
650
|
idx++;
|
|
591
651
|
}
|
|
592
652
|
}
|
|
653
|
+
if (suppliedReviewers === undefined) {
|
|
654
|
+
const diagnostic = validateWeightedFlagsForReviewerCount(answers.reviewerMinimum, answers.optionalReviewerIndices, reviewers.length);
|
|
655
|
+
if (diagnostic !== null) {
|
|
656
|
+
contexts.output.writeError(diagnostic);
|
|
657
|
+
return 1;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
let minimumReviews;
|
|
661
|
+
const reviewerConfigs = [];
|
|
662
|
+
if (reviewers.length === 1) {
|
|
663
|
+
minimumReviews = 1;
|
|
664
|
+
reviewerConfigs.push({ ...reviewers[0], optional: false });
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
const reviewerCount = reviewers.length;
|
|
668
|
+
if (answers.reviewerMinimum !== undefined) {
|
|
669
|
+
minimumReviews = answers.reviewerMinimum;
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
if (this._disposed) {
|
|
673
|
+
return 1;
|
|
674
|
+
}
|
|
675
|
+
let chosen = null;
|
|
676
|
+
while (chosen === null) {
|
|
677
|
+
const entry = await promptText(contexts.ask, {
|
|
678
|
+
question: "Minimum reviewers that must run to a verdict in each review round",
|
|
679
|
+
placeholder: `1-${reviewerCount}, empty for ${reviewerCount}`
|
|
680
|
+
});
|
|
681
|
+
if (entry === null) {
|
|
682
|
+
return 1;
|
|
683
|
+
}
|
|
684
|
+
if (this._disposed) {
|
|
685
|
+
return 1;
|
|
686
|
+
}
|
|
687
|
+
const trimmed = entry.trim();
|
|
688
|
+
if (trimmed === "") {
|
|
689
|
+
chosen = reviewerCount;
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
const parsed = Number(trimmed);
|
|
693
|
+
if (/^\d+$/.test(trimmed) && parsed >= 1 && parsed <= reviewerCount) {
|
|
694
|
+
chosen = parsed;
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
contexts.output.write(`Enter an integer between 1 and ${reviewerCount}, or leave empty for ${reviewerCount}.\n`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
minimumReviews = chosen;
|
|
702
|
+
}
|
|
703
|
+
if (minimumReviews < reviewerCount) {
|
|
704
|
+
if (answers.optionalReviewerIndices !== undefined) {
|
|
705
|
+
const optionalSet = new Set(answers.optionalReviewerIndices);
|
|
706
|
+
for (let i = 0; i < reviewerCount; i++) {
|
|
707
|
+
reviewerConfigs.push({ ...reviewers[i], optional: optionalSet.has(i + 1) });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
for (let i = 0; i < reviewerCount; i++) {
|
|
712
|
+
if (this._disposed) {
|
|
713
|
+
return 1;
|
|
714
|
+
}
|
|
715
|
+
const reviewer = reviewers[i];
|
|
716
|
+
const modelLabel = reviewer.model === "" ? "default configured model" : reviewer.model;
|
|
717
|
+
const effortLabel = reviewer.effort === "" ? "default configured effort" : reviewer.effort;
|
|
718
|
+
const optionalOption = await promptChoice(contexts.ask, {
|
|
719
|
+
header: `Reviewer ${i + 1} optional`,
|
|
720
|
+
question: `Is reviewer ${i + 1} (${reviewer.tool} · ${modelLabel} · ${effortLabel}) optional?`,
|
|
721
|
+
options: [
|
|
722
|
+
{ label: "no", description: "Required — always waits out its rate-limit waits; the round never completes without its verdict" },
|
|
723
|
+
{ label: "yes", description: "Optional — reviews exactly like a required reviewer; the only difference is the round abandons it while it is in a rate-limit wait, once every required reviewer is in and the minimum is met" }
|
|
724
|
+
]
|
|
725
|
+
});
|
|
726
|
+
if (!optionalOption) {
|
|
727
|
+
return 1;
|
|
728
|
+
}
|
|
729
|
+
if (this._disposed) {
|
|
730
|
+
return 1;
|
|
731
|
+
}
|
|
732
|
+
reviewerConfigs.push({ ...reviewer, optional: optionalOption.label === "yes" });
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
for (let i = 0; i < reviewerCount; i++) {
|
|
738
|
+
reviewerConfigs.push({ ...reviewers[i], optional: false });
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
593
742
|
const scopeRoot = mode === "global"
|
|
594
743
|
? contexts.platform.homedir()
|
|
595
744
|
: options.projectRoot;
|
|
@@ -656,7 +805,8 @@ class Install {
|
|
|
656
805
|
}
|
|
657
806
|
const config = {
|
|
658
807
|
worker: { tool: workerTool, model: workerModel, effort: workerEffort },
|
|
659
|
-
reviewers
|
|
808
|
+
reviewers: reviewerConfigs,
|
|
809
|
+
minimumReviews
|
|
660
810
|
};
|
|
661
811
|
const configWrittenPath = await (0, FlandersConfig_1.write)(contexts.fs, {
|
|
662
812
|
scope: mode,
|
|
@@ -9,6 +9,39 @@ function isCommandNotFound(combinedOutput) {
|
|
|
9
9
|
const lower = combinedOutput.toLowerCase();
|
|
10
10
|
return COMMAND_NOT_FOUND_MARKERS.some(marker => lower.includes(marker));
|
|
11
11
|
}
|
|
12
|
+
function parseModelCatalog(stdout) {
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(stdout);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const models = parsed.models;
|
|
24
|
+
if (!Array.isArray(models)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const slugs = [];
|
|
28
|
+
for (const entry of models) {
|
|
29
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const record = entry;
|
|
33
|
+
if (typeof record.slug !== "string" || typeof record.visibility !== "string") {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (record.visibility === "list") {
|
|
37
|
+
slugs.push(record.slug);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (slugs.length === 0) {
|
|
41
|
+
return { kind: "no-list" };
|
|
42
|
+
}
|
|
43
|
+
return { kind: "list", models: slugs };
|
|
44
|
+
}
|
|
12
45
|
function probeModelList(script) {
|
|
13
46
|
return new Promise(resolve => {
|
|
14
47
|
const stdoutChunks = [];
|
|
@@ -48,52 +81,17 @@ function probeModelList(script) {
|
|
|
48
81
|
});
|
|
49
82
|
proc.on("exit", (code) => {
|
|
50
83
|
const stdout = stdoutChunks.join("");
|
|
84
|
+
const catalog = parseModelCatalog(stdout);
|
|
85
|
+
if (catalog) {
|
|
86
|
+
settle(catalog);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
51
89
|
const stderr = stderrChunks.join("");
|
|
52
90
|
if (code === 127 || isCommandNotFound(stderr + stdout)) {
|
|
53
91
|
notStarted(null);
|
|
54
92
|
return;
|
|
55
93
|
}
|
|
56
|
-
|
|
57
|
-
settle({ kind: "no-list" });
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
let parsed;
|
|
61
|
-
try {
|
|
62
|
-
parsed = JSON.parse(stdout);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
settle({ kind: "no-list" });
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
69
|
-
settle({ kind: "no-list" });
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const models = parsed.models;
|
|
73
|
-
if (!Array.isArray(models)) {
|
|
74
|
-
settle({ kind: "no-list" });
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const slugs = [];
|
|
78
|
-
for (const entry of models) {
|
|
79
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
80
|
-
settle({ kind: "no-list" });
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const record = entry;
|
|
84
|
-
if (typeof record.slug !== "string" || typeof record.visibility !== "string") {
|
|
85
|
-
settle({ kind: "no-list" });
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (record.visibility === "list") {
|
|
89
|
-
slugs.push(record.slug);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (slugs.length === 0) {
|
|
93
|
-
settle({ kind: "no-list" });
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
settle({ kind: "list", models: slugs });
|
|
94
|
+
settle({ kind: "no-list" });
|
|
97
95
|
});
|
|
98
96
|
});
|
|
99
97
|
}
|
package/lib/plan/PlanFile.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export type PlanParseResult = Readonly<{
|
|
|
27
27
|
}>;
|
|
28
28
|
export declare const TASK_LINE: RegExp;
|
|
29
29
|
export declare function parsePlan(content: string): PlanParseResult;
|
|
30
|
+
export declare function extractFullTaskText(content: string, taskLineNumber: number): string;
|
|
30
31
|
export declare function parseLinkedPaths(content: string, taskLineNumber: number): TaskLinkedPaths;
|
|
31
32
|
export declare class PlanFile {
|
|
32
33
|
readonly path: string;
|
|
@@ -40,6 +41,7 @@ export declare class PlanFile {
|
|
|
40
41
|
markDone(lineNumber: number, metrics: TaskMetrics): Promise<void>;
|
|
41
42
|
markOpen(lineNumber: number, metrics: TaskMetrics): Promise<void>;
|
|
42
43
|
linkedPaths(task: PlanTask): TaskLinkedPaths;
|
|
44
|
+
fullTaskText(task: PlanTask): string;
|
|
43
45
|
planTotals(): TaskMetrics;
|
|
44
46
|
private _rewriteTaskLine;
|
|
45
47
|
}
|
package/lib/plan/PlanFile.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PlanFile = exports.TASK_LINE = void 0;
|
|
4
4
|
exports.parsePlan = parsePlan;
|
|
5
|
+
exports.extractFullTaskText = extractFullTaskText;
|
|
5
6
|
exports.parseLinkedPaths = parseLinkedPaths;
|
|
6
7
|
exports.TASK_LINE = /^(\s*[-*+]\s+)\[([ xX])\](\{[^}]*\})(\s.*)?$/;
|
|
7
8
|
const MALFORMED_TASK_LINE = /^\s*[-*+]\s+\[[^\]]*\]\{/;
|
|
@@ -88,14 +89,12 @@ function parsePlan(content) {
|
|
|
88
89
|
size: content.length
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
return paths;
|
|
92
|
+
const MARKDOWN_LINK = /\[[^\]]*\]\(([^)]+)\)/g;
|
|
93
|
+
const CONTRACTS_SEGMENT = ".spec/contracts/";
|
|
94
|
+
const RULES_SEGMENT = ".spec/rules/";
|
|
95
|
+
function detectNewline(content) {
|
|
96
|
+
const newlineMatch = /\r\n|\n/.exec(content);
|
|
97
|
+
return newlineMatch ? newlineMatch[0] : "\n";
|
|
99
98
|
}
|
|
100
99
|
function taskBodyLines(content, taskLineNumber) {
|
|
101
100
|
const lines = content.split(/\r?\n/);
|
|
@@ -108,17 +107,30 @@ function taskBodyLines(content, taskLineNumber) {
|
|
|
108
107
|
}
|
|
109
108
|
return body;
|
|
110
109
|
}
|
|
110
|
+
function extractFullTaskText(content, taskLineNumber) {
|
|
111
|
+
const lines = content.split(/\r?\n/);
|
|
112
|
+
const taskLine = lines[taskLineNumber - 1];
|
|
113
|
+
const body = taskBodyLines(content, taskLineNumber);
|
|
114
|
+
return [taskLine, ...body].join(detectNewline(content));
|
|
115
|
+
}
|
|
111
116
|
function parseLinkedPaths(content, taskLineNumber) {
|
|
112
117
|
const body = taskBodyLines(content, taskLineNumber);
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
const contracts = [];
|
|
119
|
+
const rules = [];
|
|
115
120
|
for (const line of body) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
let m;
|
|
122
|
+
while ((m = MARKDOWN_LINK.exec(line)) !== null) {
|
|
123
|
+
const path = m[1].split("#")[0].replace(/^\//, "");
|
|
124
|
+
if (path.includes(CONTRACTS_SEGMENT)) {
|
|
125
|
+
if (!contracts.includes(path)) {
|
|
126
|
+
contracts.push(path);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (path.includes(RULES_SEGMENT)) {
|
|
130
|
+
if (!rules.includes(path)) {
|
|
131
|
+
rules.push(path);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
return { contracts, rules };
|
|
@@ -156,6 +168,9 @@ class PlanFile {
|
|
|
156
168
|
linkedPaths(task) {
|
|
157
169
|
return parseLinkedPaths(this._content, task.line);
|
|
158
170
|
}
|
|
171
|
+
fullTaskText(task) {
|
|
172
|
+
return extractFullTaskText(this._content, task.line);
|
|
173
|
+
}
|
|
159
174
|
planTotals() {
|
|
160
175
|
const { tasks } = parsePlan(this._content);
|
|
161
176
|
let it = 0, ot = 0, t = 0;
|
|
@@ -168,8 +183,7 @@ class PlanFile {
|
|
|
168
183
|
}
|
|
169
184
|
async _rewriteTaskLine(lineNumber, metrics, flip) {
|
|
170
185
|
validateMetricsInput(metrics);
|
|
171
|
-
const
|
|
172
|
-
const newline = newlineMatch ? newlineMatch[0] : "\n";
|
|
186
|
+
const newline = detectNewline(this._content);
|
|
173
187
|
const lines = this._content.split(/\r?\n/);
|
|
174
188
|
const idx = lineNumber - 1;
|
|
175
189
|
const raw = lines[idx];
|
package/lib/prompts/prompts.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export declare const enum Placeholders {
|
|
2
2
|
PLAN_PATH = "<PLAN_PATH>",
|
|
3
|
-
|
|
4
|
-
TASK_TITLE = "<TASK_TITLE>",
|
|
3
|
+
TASK_TEXT = "<TASK_TEXT>",
|
|
5
4
|
BUILD_SCRIPT_PATH = "<BUILD_SCRIPT_PATH>",
|
|
6
5
|
TEST_SCRIPT_PATH = "<TEST_SCRIPT_PATH>",
|
|
7
6
|
ERROR_LOG_PATH = "<ERROR_LOG_PATH>",
|
|
@@ -43,6 +42,5 @@ export declare const prompts: {
|
|
|
43
42
|
detectBuildAndTest: string;
|
|
44
43
|
worker: string;
|
|
45
44
|
reviewer: string;
|
|
46
|
-
prep: string;
|
|
47
45
|
previousIterationBriefing: string;
|
|
48
46
|
};
|
package/lib/prompts/prompts.js
CHANGED
|
@@ -35,7 +35,7 @@ ${s.readOnlyParagraph}`;
|
|
|
35
35
|
2. A contract referenced by ${s.specRef} is not honored.
|
|
36
36
|
3. A rule referenced by ${s.specRef} is not applied in the changes — you have the positive obligation to verify that every referenced rule is actively applied; a referenced rule that is not applied is FAIL.
|
|
37
37
|
4. A contract or rule from the global lists above that you determine should have been applied but was not, even if not referenced by ${s.specRef}, is FAIL.
|
|
38
|
-
5. A behavior rule from the behavior-rule list above whose \`.
|
|
38
|
+
5. A behavior rule from the behavior-rule list above whose \`.spec/flanders\` scope encloses the files the working-tree changes touch is not honored by the changes, even if ${s.specRef} did not reference it, is FAIL.
|
|
39
39
|
|
|
40
40
|
Exhaustiveness: do not stop at the first violation. Run every verification you are required to run and every additional check your judgment deems applicable, even after one of them has already produced a FAIL. The five conditions above and the ${s.critProtocolName} are executed in full on every invocation; encountering a violation in one of them does not exempt you from completing the rest. The goal is that a single review produces the complete list of fixes ${s.nextWorker} needs to apply.
|
|
41
41
|
|
|
@@ -149,21 +149,22 @@ If you cannot confidently determine how to build the project, leave the build sc
|
|
|
149
149
|
|
|
150
150
|
## Available rules
|
|
151
151
|
|
|
152
|
-
Each path below is the rule's namespace. Before deciding the build or test commands, scan this list and open every rule whose scope governs how the project is built or how its tests are run — for example, any rule under a \`testing/\` or \`build/\` subfolder of a \`.
|
|
152
|
+
Each path below is the rule's namespace. Before deciding the build or test commands, scan this list and open every rule whose scope governs how the project is built or how its tests are run — for example, any rule under a \`testing/\` or \`build/\` subfolder of a \`.spec/rules\` folder, or any rule that prescribes a specific runner, invocation form, required flag, or toolchain convention. Reading is not optional for rules whose scope matches build/test invocation. The commands you write must honor those rules: if a rule pins the test runner to a specific invocation form or required flag, the script you write must use that exact invocation.
|
|
153
153
|
|
|
154
154
|
${"<RULE_LIST>"}
|
|
155
155
|
|
|
156
156
|
Git boundary: you must not execute any git command that modifies repository state. Read-only git commands (\`git status\`, \`git log\`, \`git show\`, \`git diff\`, \`git blame\`, \`git ls-files\`) are allowed if they help you understand the project; commits, staging, branches, tags, stashes, resets, restores, merges, rebases, edits under \`.git/\`, and any remote git operation are forbidden. See rules/ai/agents/no-git-writes.md for the full obligation.
|
|
157
157
|
|
|
158
|
-
Spec-folder write boundary: you must not create, modify, delete, or rename any file inside any \`.
|
|
158
|
+
Spec-folder write boundary: you must not create, modify, delete, or rename any file inside any \`.spec/contracts\` folder, any \`.spec/rules\` folder, or the \`plans/\` folder. These folders are governed by dedicated skills and the implement command's bounded checkpoint updates; no other agent may write to them. See shared/spec-folder-write-authority.md for the full obligation.
|
|
159
159
|
|
|
160
160
|
${foregroundBoundary}`,
|
|
161
161
|
worker: `You are the worker agent for the Flanders implement iteration loop.
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
The plan file is at ${"<PLAN_PATH>"}; you may open it for broader context.
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
## Your task
|
|
166
|
+
|
|
167
|
+
${"<TASK_TEXT>"}
|
|
167
168
|
|
|
168
169
|
## Adversarial review awaits
|
|
169
170
|
|
|
@@ -173,12 +174,12 @@ Your output will be inspected by an adversarial reviewer immediately after you f
|
|
|
173
174
|
2. A contract referenced by the task is not honored.
|
|
174
175
|
3. A rule referenced by the task is not actively applied — acknowledging a rule is not enough; the changes must demonstrate compliance.
|
|
175
176
|
4. A contract or rule from the global lists below that the reviewer determines should have been applied but was not — even if the task did not reference it.
|
|
176
|
-
5. A behavior rule from the behavior-rule list below whose \`.
|
|
177
|
+
5. A behavior rule from the behavior-rule list below whose \`.spec/flanders\` scope encloses the files your changes touch is not honored by the changes — in-scope behavior rules are mandatory whether or not the task links them.
|
|
177
178
|
|
|
178
179
|
Condition 4 causes most rejections in practice. Rules whose scope matches your changes (testing rules when you touch tests, disposable rules when you touch async resources, UI rules when you change terminal output, etc.) are mandatory whether the task links them or not. Treat the global contract and rule lists below as part of your specification, not as optional reading. The reviewer will also enumerate every occurrence of a pattern violation, not just the first one, so partial compliance within a file is itself a FAIL.
|
|
179
180
|
|
|
180
181
|
Procedure:
|
|
181
|
-
1.
|
|
182
|
+
1. Read the task shown above and respect the obligations of every contract and rule it references exactly. You may consult those files, or the plan file for broader context, at your discretion.
|
|
182
183
|
2. Implement the task. Update or extend tests so the new behavior is covered.
|
|
183
184
|
3. If your implementation changes how the project builds or how its tests run, also update the build and test scripts at:
|
|
184
185
|
- Build script: ${"<BUILD_SCRIPT_PATH>"}
|
|
@@ -209,7 +210,7 @@ Do not flip the task's checkbox in the plan file. Flanders flips the checkbox it
|
|
|
209
210
|
|
|
210
211
|
Git boundary: you must not execute any git command that modifies repository state — no \`git add\`, \`git commit\`, \`git stash\`, \`git reset\`, \`git restore\`, \`git checkout -b\`, \`git branch\`, \`git tag\`, \`git rebase\`, \`git merge\`, \`git cherry-pick\`, no edits under \`.git/\`, and no remote git operations (\`fetch\`, \`pull\`, \`push\`). Read-only git commands (\`git status\`, \`git diff\`, \`git log\`, \`git show\`, \`git blame\`, \`git ls-files\`) are allowed when you need to inspect the repo. Leave your implementation as a dirty working tree — Flanders performs the commit itself once your changes pass build, test, and review. If your task seems to require a git write, stop and explain it in your final message instead of doing it. The full obligation lives in rules/ai/agents/no-git-writes.md.
|
|
211
212
|
|
|
212
|
-
Spec-folder write boundary: you must not create, modify, delete, or rename any file inside any \`.
|
|
213
|
+
Spec-folder write boundary: you must not create, modify, delete, or rename any file inside any \`.spec/contracts\` folder, any \`.spec/rules\` folder, or the \`plans/\` folder. These folders are governed by dedicated skills and the implement command's bounded checkpoint updates; no other agent may write to them. See shared/spec-folder-write-authority.md for the full obligation.
|
|
213
214
|
|
|
214
215
|
${foregroundBoundary}
|
|
215
216
|
|
|
@@ -227,17 +228,18 @@ ${"<RULE_LIST>"}
|
|
|
227
228
|
|
|
228
229
|
## Available behavior rules
|
|
229
230
|
|
|
230
|
-
Each path below is a behavior rule's namespace. A behavior rule governs how the files and changes you author are named, placed, and organized within the part of the project tree that the rule's \`.
|
|
231
|
+
Each path below is a behavior rule's namespace. A behavior rule governs how the files and changes you author are named, placed, and organized within the part of the project tree that the rule's \`.spec/flanders\` folder scopes. You must honor every behavior rule whose \`.spec/flanders\` scope encloses the files your changes touch. Like the global contract and rule lists above, in-scope behavior rules are mandatory whether or not the task links them; the reviewer FAILS for any in-scope behavior rule the changes do not honor.
|
|
231
232
|
|
|
232
233
|
${"<BEHAVIOR_RULE_LIST>"}`,
|
|
233
234
|
reviewer: `You are the adversarial reviewer agent for the Flanders implement iteration loop.
|
|
234
235
|
|
|
235
|
-
|
|
236
|
+
The plan file is at ${"<PLAN_PATH>"}; you may open it for broader context, but you do not need to in order to find the task — the full task is provided in this prompt.
|
|
237
|
+
|
|
238
|
+
## The task under review
|
|
236
239
|
|
|
237
|
-
|
|
238
|
-
${"<TASK_TITLE>"}
|
|
240
|
+
${"<TASK_TEXT>"}
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
The task's full description, its acceptance criteria, and the full content of every contract and rule it references are provided to you directly — the referenced contracts and rules are injected inline at the end of this prompt (under "Linked reference content"). Inspect the working-tree changes that the worker just produced.
|
|
241
243
|
|
|
242
244
|
## Determining the worker's change set
|
|
243
245
|
|
|
@@ -257,7 +259,7 @@ ${"<RULE_LIST>"}
|
|
|
257
259
|
|
|
258
260
|
## Available behavior rules
|
|
259
261
|
|
|
260
|
-
Each path below is a behavior rule's namespace. A behavior rule governs how the files and changes the worker authored are named, placed, and organized within the part of the project tree that the rule's \`.
|
|
262
|
+
Each path below is a behavior rule's namespace. A behavior rule governs how the files and changes the worker authored are named, placed, and organized within the part of the project tree that the rule's \`.spec/flanders\` folder scopes. You must verify that the working-tree changes honor every behavior rule whose \`.spec/flanders\` scope encloses the files they touch. Like the global contract and rule lists above, in-scope behavior rules are mandatory whether or not the task links them.
|
|
261
263
|
|
|
262
264
|
${"<BEHAVIOR_RULE_LIST>"}
|
|
263
265
|
|
|
@@ -265,61 +267,9 @@ ${implementReviewerMethodology.audit}
|
|
|
265
267
|
|
|
266
268
|
Git boundary: you are an inspection-only agent. You must not execute any git command that modifies repository state — no \`git add\`, \`git commit\`, \`git stash\`, \`git reset\`, \`git restore\`, \`git checkout -b\`, \`git branch\`, \`git tag\`, no edits under \`.git/\`, and no remote git operations. Read-only git commands (\`git status\`, \`git diff\`, \`git log\`, \`git show\`, \`git blame\`, \`git ls-files\`) are allowed and are how you should inspect the worker's changes. The full obligation lives in rules/ai/agents/no-git-writes.md.
|
|
267
269
|
|
|
268
|
-
Spec-folder write boundary: you must not create, modify, delete, or rename any file inside any \`.
|
|
270
|
+
Spec-folder write boundary: you must not create, modify, delete, or rename any file inside any \`.spec/contracts\` folder, any \`.spec/rules\` folder, or the \`plans/\` folder. These folders are governed by dedicated skills and the implement command's bounded checkpoint updates; no other agent may write to them. See shared/spec-folder-write-authority.md for the full obligation.
|
|
269
271
|
|
|
270
272
|
${foregroundBoundary}`,
|
|
271
|
-
prep: `You are the prep agent for the Flanders implement iteration loop.
|
|
272
|
-
|
|
273
|
-
Plan file path: ${"<PLAN_PATH>"}
|
|
274
|
-
|
|
275
|
-
The current task is on line ${"<TASK_LINE>"} of that plan file. Its title, verbatim, is:
|
|
276
|
-
${"<TASK_TITLE>"}
|
|
277
|
-
|
|
278
|
-
## Your job
|
|
279
|
-
|
|
280
|
-
Read the task and its reference material so the session is ready to be forked by the worker and reviewer agents. You do not implement anything — you are a read-only context-loading agent.
|
|
281
|
-
|
|
282
|
-
Procedure:
|
|
283
|
-
1. Open the plan file and find the task at the line indicated above. Read its full description, acceptance criteria, and every contract and rule file the task references.
|
|
284
|
-
2. From the global lists below, read the full content of every additional contract or rule you judge relevant to the task, even if the task does not explicitly reference it. Err on the side of loading material that might be needed rather than skipping it.
|
|
285
|
-
|
|
286
|
-
## Read-only obligation
|
|
287
|
-
|
|
288
|
-
You must not implement, modify, or write anything in the project. Do not use Edit, Write, or any Bash command that mutates project state. Your only job is to read and load context.
|
|
289
|
-
|
|
290
|
-
## Spec-folder write boundary
|
|
291
|
-
|
|
292
|
-
You must not write to any \`.docs/contracts\` folder, any \`.docs/rules\` folder, or the \`plans/\` folder. These folders are governed by dedicated skills and the implement command's bounded checkpoint updates; no other agent may create, modify, delete, or rename files in them. See shared/spec-folder-write-authority.md for the full obligation.
|
|
293
|
-
|
|
294
|
-
## Git boundary
|
|
295
|
-
|
|
296
|
-
You must not execute any git command that modifies repository state — no \`git add\`, \`git commit\`, \`git stash\`, \`git reset\`, \`git restore\`, \`git checkout -b\`, \`git branch\`, \`git tag\`, \`git rebase\`, \`git merge\`, \`git cherry-pick\`, no edits under \`.git/\`, and no remote git operations (\`fetch\`, \`pull\`, \`push\`). Read-only git commands (\`git status\`, \`git diff\`, \`git log\`, \`git show\`, \`git blame\`, \`git ls-files\`) are allowed when you need to inspect the repo. The full obligation lives in rules/ai/agents/no-git-writes.md.
|
|
297
|
-
|
|
298
|
-
${foregroundBoundary}
|
|
299
|
-
|
|
300
|
-
## Available contracts
|
|
301
|
-
|
|
302
|
-
Each path below is the contract's namespace. Scan this list and open every contract whose public surface intersects the work in this task.
|
|
303
|
-
|
|
304
|
-
${"<CONTRACT_LIST>"}
|
|
305
|
-
|
|
306
|
-
## Available rules
|
|
307
|
-
|
|
308
|
-
Each path below is the rule's namespace. Scan this list and open every rule whose scope matches the work in this task.
|
|
309
|
-
|
|
310
|
-
${"<RULE_LIST>"}
|
|
311
|
-
|
|
312
|
-
## Available behavior rules
|
|
313
|
-
|
|
314
|
-
Each path below is a behavior rule's namespace. A behavior rule governs how the files and changes Flanders authors are named, placed, and organized within the part of the project tree that the rule's \`.docs/flanders\` folder scopes; every behavior rule whose \`.docs/flanders\` scope encloses the files this task's work touches must be honored. Read all of those in-scope behavior rules now, so the worker and reviewer forked from this session honor them. Like the global contract and rule lists above, in-scope behavior rules are mandatory whether or not the task links them.
|
|
315
|
-
|
|
316
|
-
${"<BEHAVIOR_RULE_LIST>"}
|
|
317
|
-
|
|
318
|
-
## Ending discipline
|
|
319
|
-
|
|
320
|
-
When you have finished reading all relevant material, end your reply with the word READY on its own line and no pending tool calls. The session must be in a forkable state.
|
|
321
|
-
|
|
322
|
-
READY`,
|
|
323
273
|
previousIterationBriefing: `This is iteration ${"<ITERATION>"} for this task. The previous iteration produced a problem to review before retrying. Read the full context written into the error log file at:
|
|
324
274
|
|
|
325
275
|
${"<ERROR_LOG_PATH>"}
|