flanders 0.11.0 → 0.13.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/README.md +4 -4
- package/lib/commands/Implement.d.ts +3 -0
- package/lib/commands/Implement.js +49 -12
- package/lib/prompts/prompts.d.ts +1 -0
- package/lib/prompts/prompts.js +3 -2
- package/lib/prompts/skills.js +31 -14
- package/lib/workspace/Workspace.d.ts +5 -0
- package/lib/workspace/Workspace.js +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -184,7 +184,7 @@ For `/flanders-hard-stop-review`, `<data>` is the path of the preserved hard-sto
|
|
|
184
184
|
flanders implement [plan]
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
`implement` takes a plan from your `plans/` folder and carries it through from start to finish. Leave `[plan]` off and Flanders runs the single plan in `plans/` for you automatically. From there it works through each open task with the worker AI, gating every result through build, test, and adversarial review before marking that task complete in the plan — and it commits once per accepted task, so each step lands as its own neat little commit. The whole run is non-interactive: once started, it never stops to ask you anything, and it caps each task at five attempts
|
|
187
|
+
`implement` takes a plan from your `plans/` folder and carries it through from start to finish. Leave `[plan]` off and Flanders runs the single plan in `plans/` for you automatically. From there it works through each open task with the worker AI, gating every result through build, test, and adversarial review before marking that task complete in the plan — and it commits once per accepted task, so each step lands as its own neat little commit. The whole run is non-interactive: once started, it never stops to ask you anything, and it caps each task at five attempts, calling a hard stop if a task overruns those attempts or the worker declares it structurally impossible (see [Hard stop](#hard-stop)). The project must be a git repository: `implement` needs git and has no flag to turn it off.
|
|
188
188
|
|
|
189
189
|
When the plan implementation is finished, you can squash all the plan commits. They are ordered by task for easier identification.
|
|
190
190
|
|
|
@@ -254,10 +254,10 @@ If you really want the font to be kept at that size, just save the spec, and no
|
|
|
254
254
|
|
|
255
255
|
## Hard stop
|
|
256
256
|
|
|
257
|
-
Even the most neighborly run can run out of road.
|
|
257
|
+
Even the most neighborly run can run out of road, and there are two ways it can happen. The first is when Flanders can't get a single task built, tested, reviewed, and committed within its five attempts — it doesn't keep flailing away. The second is when the worker itself decides the task simply can't be reached by any implementation the task allows, and rather than burn attempts on the impossible it writes a `hard-stop.log` into that run's temporary folder, declaring the structural cause, the evidence, and the change to the plan or the spec that would unblock the task. Either trigger calls a **hard stop**: the whole `implement` run ends right there and exits with a non-zero status.
|
|
258
258
|
|
|
259
|
-
It won't leave you guessing, though. Flanders prints an error that names the task that got stuck — its line number in the plan and its title — and points you at that run's temporary folder. Every other time Flanders exits it tidies that folder away, but on a hard stop it leaves it right where it is, on purpose, so you can have a look.
|
|
259
|
+
It won't leave you guessing, though. Flanders prints an error that names the task that got stuck — its line number in the plan and its title — and points you at that run's temporary folder. When the stop was the worker's own declaration, that error also reads back the contents of the `hard-stop.log` — the cause, the evidence, and the unblocking change. Every other time Flanders exits it tidies that folder away, but on a hard stop it leaves it right where it is, on purpose, so you can have a look.
|
|
260
260
|
|
|
261
|
-
Inside you'll find the sessions from every attempt on the task
|
|
261
|
+
Inside you'll find the sessions from every attempt on the task — the worker's output, the build and test output, and each reviewer's output. Alongside them, each iteration that fell short leaves its error set down by the stage that tripped it: a build, test, or commit log for the iteration whose build, test, or commit stage failed, and — when it was the review that failed — one log per reviewer that recorded a violation. So the folder spells out plainly which stage failed in each iteration: it's the whole story of what was tried and where each go-round fell short. And when the stop was the worker's declaration, the `hard-stop.log` it wrote is sitting right there in the folder too.
|
|
262
262
|
|
|
263
263
|
And here's the neighborly part — you don't have to untangle it all yourself. Invoke **`/flanders-hard-stop-review`** in your AI coding tool and hand it that folder's path, and it reads back through the sessions, tells you why the run failed, and recommends how to relaunch `implement` so the stuck task finishes this time — mending the spec or the plan, or simply running it again.
|
|
@@ -38,6 +38,7 @@ export declare class Implement {
|
|
|
38
38
|
private _cancelledReviewers;
|
|
39
39
|
private _currentIndexLabel;
|
|
40
40
|
private _currentIteration;
|
|
41
|
+
private _retainedMaterializations;
|
|
41
42
|
private _currentTask;
|
|
42
43
|
private _taskStartedAt;
|
|
43
44
|
private _taskExcludedMs;
|
|
@@ -80,6 +81,8 @@ export declare class Implement {
|
|
|
80
81
|
private _runScript;
|
|
81
82
|
private _writeLog;
|
|
82
83
|
private _writeErrorLog;
|
|
84
|
+
private _hardStop;
|
|
85
|
+
private _materializeHardStopErrorLogs;
|
|
83
86
|
private _stringifyError;
|
|
84
87
|
private _finalizeBlock;
|
|
85
88
|
dispose(): Promise<void>;
|
|
@@ -83,6 +83,7 @@ class Implement {
|
|
|
83
83
|
this._cancelledReviewers = new Set();
|
|
84
84
|
this._currentIndexLabel = "";
|
|
85
85
|
this._currentIteration = 0;
|
|
86
|
+
this._retainedMaterializations = [];
|
|
86
87
|
this._currentTask = null;
|
|
87
88
|
this._taskStartedAt = 0;
|
|
88
89
|
this._taskExcludedMs = 0;
|
|
@@ -311,6 +312,7 @@ class Implement {
|
|
|
311
312
|
this._taskRateLimitStartedAt = null;
|
|
312
313
|
this._reviewPauseStartedAt = null;
|
|
313
314
|
this._taskTokens = { it: 0, ot: 0 };
|
|
315
|
+
this._retainedMaterializations = [];
|
|
314
316
|
this._otherTasksSeconds = plan.planTotals().t - task.metrics.t;
|
|
315
317
|
this._updateMetrics(plan);
|
|
316
318
|
let iteration = 0;
|
|
@@ -318,8 +320,7 @@ class Implement {
|
|
|
318
320
|
iteration++;
|
|
319
321
|
this._currentIteration = iteration;
|
|
320
322
|
if (iteration > MAX_ITER) {
|
|
321
|
-
this.
|
|
322
|
-
this._buffered.writeError(`Hard stop: task at line ${task.line} ("${task.title}") exceeded ${MAX_ITER} iterations. Inspect logs at ${ws.root}.\n`);
|
|
323
|
+
await this._hardStop(`Hard stop: task at line ${task.line} ("${task.title}") exceeded ${MAX_ITER} iterations. Inspect logs at ${ws.root}.\n`);
|
|
323
324
|
return false;
|
|
324
325
|
}
|
|
325
326
|
if (this._disposed) {
|
|
@@ -333,6 +334,11 @@ class Implement {
|
|
|
333
334
|
if (!workerOk) {
|
|
334
335
|
continue;
|
|
335
336
|
}
|
|
337
|
+
if (await this._contexts.fs.exists(ws.hardStopLog)) {
|
|
338
|
+
const declared = await this._contexts.fs.readFile(ws.hardStopLog);
|
|
339
|
+
await this._hardStop(`Hard stop: task at line ${task.line} ("${task.title}") declared structurally impossible.\n--- hard-stop.log ---\n${declared}\n--- end hard-stop.log ---\nInspect logs at ${ws.root}.\n`);
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
336
342
|
const workerAddResult = await (0, Git_1.addAll)(this._contexts.script, this._contexts.time, this._gitOutputContext(), this._options.projectRoot);
|
|
337
343
|
if (workerAddResult.code !== 0) {
|
|
338
344
|
await this._writeErrorLog(ws, `git add -A failed (exit ${workerAddResult.code})\n--- stdout ---\n${workerAddResult.stdout}\n--- stderr ---\n${workerAddResult.stderr}`);
|
|
@@ -382,13 +388,17 @@ class Implement {
|
|
|
382
388
|
const commitMessage = task.taskNumber ? `${task.taskNumber} ${task.title}` : task.title;
|
|
383
389
|
const addResult = await (0, Git_1.addAll)(this._contexts.script, this._contexts.time, this._gitOutputContext(), this._options.projectRoot);
|
|
384
390
|
if (addResult.code !== 0) {
|
|
385
|
-
|
|
391
|
+
const briefing = `git add -A failed (exit ${addResult.code})\n--- stdout ---\n${addResult.stdout}\n--- stderr ---\n${addResult.stderr}`;
|
|
392
|
+
this._retainedMaterializations.push({ path: ws.commitErrorLog(iteration), content: briefing });
|
|
393
|
+
await this._writeErrorLog(ws, briefing);
|
|
386
394
|
await revertCompletion();
|
|
387
395
|
continue;
|
|
388
396
|
}
|
|
389
397
|
const commitResult = await (0, Git_1.commit)(this._contexts.script, this._contexts.time, this._gitOutputContext(), this._options.projectRoot, commitMessage);
|
|
390
398
|
if (commitResult.code !== 0) {
|
|
391
|
-
|
|
399
|
+
const briefing = `git commit failed (exit ${commitResult.code})\n--- stdout ---\n${commitResult.stdout}\n--- stderr ---\n${commitResult.stderr}`;
|
|
400
|
+
this._retainedMaterializations.push({ path: ws.commitErrorLog(iteration), content: briefing });
|
|
401
|
+
await this._writeErrorLog(ws, briefing);
|
|
392
402
|
await revertCompletion();
|
|
393
403
|
continue;
|
|
394
404
|
}
|
|
@@ -415,6 +425,7 @@ class Implement {
|
|
|
415
425
|
.split("<TASK_TEXT>").join(taskText)
|
|
416
426
|
.split("<BUILD_SCRIPT_PATH>").join(ws.buildScript)
|
|
417
427
|
.split("<TEST_SCRIPT_PATH>").join(ws.testScript)
|
|
428
|
+
.split("<HARD_STOP_LOG_PATH>").join(ws.hardStopLog)
|
|
418
429
|
.split("<CONTRACT_LIST>").join(this._formatPathList(this._contractList))
|
|
419
430
|
.split("<RULE_LIST>").join(this._formatPathList(this._ruleList))
|
|
420
431
|
.split("<BEHAVIOR_RULE_LIST>").join(this._formatPathList(this._behaviorRuleList));
|
|
@@ -478,7 +489,9 @@ class Implement {
|
|
|
478
489
|
this._updateMetrics(plan);
|
|
479
490
|
await this._writeLog(ws.buildLog(iteration), `--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}`);
|
|
480
491
|
if (result.code !== 0) {
|
|
481
|
-
|
|
492
|
+
const briefing = `build stage failed (exit ${result.code})\n--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}`;
|
|
493
|
+
this._retainedMaterializations.push({ path: ws.buildErrorLog(iteration), content: briefing });
|
|
494
|
+
await this._writeErrorLog(ws, briefing);
|
|
482
495
|
return false;
|
|
483
496
|
}
|
|
484
497
|
return true;
|
|
@@ -495,7 +508,9 @@ class Implement {
|
|
|
495
508
|
this._updateMetrics(plan);
|
|
496
509
|
await this._writeLog(ws.testLog(iteration), `--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}`);
|
|
497
510
|
if (result.code !== 0) {
|
|
498
|
-
|
|
511
|
+
const briefing = `test stage failed (exit ${result.code})\n--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}`;
|
|
512
|
+
this._retainedMaterializations.push({ path: ws.testErrorLog(iteration), content: briefing });
|
|
513
|
+
await this._writeErrorLog(ws, briefing);
|
|
499
514
|
return false;
|
|
500
515
|
}
|
|
501
516
|
return true;
|
|
@@ -591,18 +606,22 @@ class Implement {
|
|
|
591
606
|
if (this._disposed) {
|
|
592
607
|
return false;
|
|
593
608
|
}
|
|
609
|
+
const perFile = [];
|
|
610
|
+
for (let i = 0; i < reviewers.length; i++) {
|
|
611
|
+
if (outcomes[i] === "verdict") {
|
|
612
|
+
const content = await this._workspace.readReviewerErrorLog(i + 1);
|
|
613
|
+
perFile.push(content);
|
|
614
|
+
if (content.trim().length > 0) {
|
|
615
|
+
this._retainedMaterializations.push({ path: ws.reviewerErrorLogFor(iteration, i + 1), content });
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
594
619
|
if (failureCaught !== null) {
|
|
595
620
|
await this._persistMetrics(plan, task.line);
|
|
596
621
|
this._updateMetrics(plan);
|
|
597
622
|
await this._writeErrorLog(ws, `reviewer stage failed: ${this._stringifyError(failureCaught)}`);
|
|
598
623
|
return false;
|
|
599
624
|
}
|
|
600
|
-
const perFile = [];
|
|
601
|
-
for (let i = 0; i < reviewers.length; i++) {
|
|
602
|
-
if (outcomes[i] === "verdict") {
|
|
603
|
-
perFile.push(await this._workspace.readReviewerErrorLog(i + 1));
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
625
|
const aggregate = perFile.join("\n").replace(/^\s+|\s+$/g, "");
|
|
607
626
|
if (aggregate.length === 0) {
|
|
608
627
|
return true;
|
|
@@ -831,6 +850,24 @@ class Implement {
|
|
|
831
850
|
async _writeErrorLog(ws, content) {
|
|
832
851
|
await this._writeLog(ws.errorLog, content);
|
|
833
852
|
}
|
|
853
|
+
async _hardStop(diagnostic) {
|
|
854
|
+
try {
|
|
855
|
+
await this._materializeHardStopErrorLogs();
|
|
856
|
+
}
|
|
857
|
+
catch (e) {
|
|
858
|
+
this._buffered.writeError(`Hard stop: could not materialize per-iteration error logs: ${this._stringifyError(e)}\n`);
|
|
859
|
+
}
|
|
860
|
+
finally {
|
|
861
|
+
this._workspace.preserveOnDispose();
|
|
862
|
+
}
|
|
863
|
+
this._buffered.writeError(diagnostic);
|
|
864
|
+
}
|
|
865
|
+
async _materializeHardStopErrorLogs() {
|
|
866
|
+
for (const { path, content } of this._retainedMaterializations) {
|
|
867
|
+
await this._contexts.fs.writeFile(path, content);
|
|
868
|
+
}
|
|
869
|
+
await this._workspace.clearErrorLog();
|
|
870
|
+
}
|
|
834
871
|
_stringifyError(e) {
|
|
835
872
|
var _a;
|
|
836
873
|
if (e instanceof Error) {
|
package/lib/prompts/prompts.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare const enum Placeholders {
|
|
|
3
3
|
TASK_TEXT = "<TASK_TEXT>",
|
|
4
4
|
BUILD_SCRIPT_PATH = "<BUILD_SCRIPT_PATH>",
|
|
5
5
|
TEST_SCRIPT_PATH = "<TEST_SCRIPT_PATH>",
|
|
6
|
+
HARD_STOP_LOG_PATH = "<HARD_STOP_LOG_PATH>",
|
|
6
7
|
ERROR_LOG_PATH = "<ERROR_LOG_PATH>",
|
|
7
8
|
ITERATION = "<ITERATION>",
|
|
8
9
|
CONTRACT_LIST = "<CONTRACT_LIST>",
|
package/lib/prompts/prompts.js
CHANGED
|
@@ -148,7 +148,7 @@ const voiceExclusionLead = "code, file paths, command lines, diagnostics, machin
|
|
|
148
148
|
function buildFlandersVoiceSection(parts) {
|
|
149
149
|
return `## Voice
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
When ${parts.languageFraming} is English, use a light Ned-Flanders touch in ${parts.subject}; deliver any other language plainly. Keep it out of ${voiceExclusionLead}${parts.finalExclusion}.`;
|
|
152
152
|
}
|
|
153
153
|
function flandersToneInstruction(reviewer) {
|
|
154
154
|
return buildFlandersVoiceSection({
|
|
@@ -210,7 +210,8 @@ Procedure:
|
|
|
210
210
|
3. If your implementation changes how the project builds or how its tests run, also update the build and test scripts at:
|
|
211
211
|
- Build script: ${"<BUILD_SCRIPT_PATH>"}
|
|
212
212
|
- Test script: ${"<TEST_SCRIPT_PATH>"}
|
|
213
|
-
4.
|
|
213
|
+
4. If you establish the task cannot reach a clean iteration through any implementation it authorizes — its acceptance criteria cannot be satisfied while honoring a contract or rule the task references or the design the plan prescribes, or closing the recorded review findings requires design decisions or work outside the task's scope — write a \`hard-stop.log\` file at ${"<HARD_STOP_LOG_PATH>"} stating the structural cause, the evidence (the criterion and the obligation or design statement in conflict), and the plan or spec change that would unblock the task, then end your turn without further implementation work. Ordinary difficulty, a failing gate, or findings you can still address within the task's scope never qualify.
|
|
214
|
+
5. Before declaring the task complete, write an Evidence Report as the final part of your output. This is a lightweight self-audit scoped to your diff and the task's links; the reviewer audits the full working tree in a separate, heavier pass. The report has three sections, in order. Consult the following rule files for the full framework:
|
|
214
215
|
- \`rules/ai/agents/evidence-report.md\`
|
|
215
216
|
- \`rules/ai/agents/evidence/claim-evidence-classification.md\`
|
|
216
217
|
- \`rules/ai/agents/evidence/enumerated-claim-coverage.md\`
|
package/lib/prompts/skills.js
CHANGED
|
@@ -10,6 +10,13 @@ function skillVoiceSection(authoredArtifactExclusion) {
|
|
|
10
10
|
finalExclusion: `, and ${authoredArtifactExclusion}`
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
+
const userAnalysisDoesNotWaive = `the user having supplied their own analysis of the same matter does not waive it — state your own finding, where it confirms their account and where it diverges, before asking`;
|
|
14
|
+
function reportBeforeQuestionInstruction(presentation, question) {
|
|
15
|
+
return `Print ${presentation} as its own chat message before ${question}, whether that question goes through a facility your AI tool provides for asking questions or is asked as plain chat text. The question decides only the choice it asks: content embedded in the question interaction — its text, its option labels, or its option descriptions — is not the presentation, and ${userAnalysisDoesNotWaive}.`;
|
|
16
|
+
}
|
|
17
|
+
function launchQuestionInstruction(report, question) {
|
|
18
|
+
return `End the same chat message that carries ${report} with ${question} asked as plain chat text, never through a facility your AI tool provides for asking questions, so the report and its question arrive together in one message; ${userAnalysisDoesNotWaive}.`;
|
|
19
|
+
}
|
|
13
20
|
exports.planSkillBody = `---
|
|
14
21
|
description: Produce a contract-aware work plan inside the project's plans/ folder.
|
|
15
22
|
---
|
|
@@ -30,7 +37,7 @@ The user invokes you as: /flanders-plan [<data>]
|
|
|
30
37
|
2. Discover every directory named \`.spec\` across the whole project tree at every depth, excluding every path the project's git ignore rules exclude (for example by enumerating with \`git ls-files --cached --others --exclude-standard\` — which lists tracked files plus untracked-but-not-ignored files — and dropping any candidate that sits under a git-ignored path, for example via \`git check-ignore\`); the files under each \`.spec/contracts\` subfolder form the canonical contracts listing and the files under each \`.spec/rules\` subfolder form the canonical rules listing; the files under each \`.spec/flanders\` subfolder form the behavior-rule listing, treating every file inside a \`.spec/flanders\` folder at any depth as a behavior rule; each file is identified by its namespace — its path relative to the project root, which for nested \`.spec\` folders includes the directories above the \`.spec\` folder, so files sharing a leaf filename in different \`.spec\` folders stay distinct.
|
|
31
38
|
|
|
32
39
|
**Behavior rules.** Before persisting the plan file, read every behavior rule whose \`.spec/flanders\` scope encloses the plan file you are about to write — the project-root \`.spec\` folder and any other \`.spec\` folder whose scope encloses the \`plans/\` target — and honor all of them. Behavior rules govern how you name and organize the plan file you author; an in-scope behavior rule is binding on that work, not advisory, and applies whether or not the request mentions it. This adds no new task-line link obligation: the plan-file format and its contract and rule links are unchanged.
|
|
33
|
-
3. **Clarification phase.** Ask the user clarifying questions only when the question targets one of three things: an implementation choice that shapes a task's observable outcome and that the request does not specify, or a task-scope ambiguity you cannot reasonably infer from the request or from the canonical contracts and rules, or a load-bearing runtime-behavior premise the plan would otherwise have to assert without backing (see the plan content rules below). A choice that affects only how a task's work is carried out internally, with no effect on any observable outcome its acceptance criteria pin, is not asked about: it is left for the implementer to resolve against the real code. Any other doubt is resolved silently: pick the most reasonable default and proceed, documenting the choice in the relevant task's description when it is plan-local and load-bearing. Before you start asking, accumulate every permitted question whose content does not depend on another question's answer and ask that whole set together in a single interaction rather than one question at a time; a question is held back only when it genuinely depends on an earlier answer, and is then asked in a later round that again batches the questions that have become independent. When your AI tool provides a facility for asking several questions at once, present the accumulated batch through that facility in a single interaction; when it provides no such facility, ask one question per turn.
|
|
40
|
+
3. **Clarification phase.** Ask the user clarifying questions only when the question targets one of three things: an implementation choice that shapes a task's observable outcome and that the request does not specify, or a task-scope ambiguity you cannot reasonably infer from the request or from the canonical contracts and rules, or a load-bearing runtime-behavior premise the plan would otherwise have to assert without backing (see the plan content rules below). A choice that affects only how a task's work is carried out internally, with no effect on any observable outcome its acceptance criteria pin, is not asked about: it is left for the implementer to resolve against the real code. Any other doubt is resolved silently: pick the most reasonable default and proceed, documenting the choice in the relevant task's description when it is plan-local and load-bearing. Before you start asking, accumulate every permitted question whose content does not depend on another question's answer and ask that whole set together in a single interaction rather than one question at a time; a question is held back only when it genuinely depends on an earlier answer, and is then asked in a later round that again batches the questions that have become independent. When your AI tool provides a facility for asking several questions at once, present the accumulated batch through that facility in a single interaction; when it provides no such facility, ask one question per turn. Phrase every question whose answer space is bounded as multiple-choice, through the facility and in chat alike.
|
|
34
41
|
|
|
35
42
|
When the doubt is about how the code should be implemented, resolve it through one of two outcomes:
|
|
36
43
|
- **Cross-cutting convention** — the answer would apply to all future code of the same kind in the project and belongs in a \`.spec/rules\` folder. Surface the gap to the user and recommend creating the rule via /flanders-spec before the plan is drafted, instead of silently baking the decision into the plan. The user may explicitly elect to treat the decision as plan-local for this run; in that case it follows the plan-local outcome below.
|
|
@@ -91,9 +98,10 @@ Tasks are written in the order they must be implemented, accounting for dependen
|
|
|
91
98
|
|
|
92
99
|
### Plan content rules
|
|
93
100
|
|
|
94
|
-
- The persisted plan is free of placeholders, contradictions with existing contracts or rules, acceptance criteria that leave a leaf task's observable outcome ambiguous, missing acceptance criteria on leaf tasks, and missing contract or rule links on leaf tasks.
|
|
101
|
+
- The persisted plan is free of placeholders, contradictions with existing contracts or rules, acceptance criteria that leave a leaf task's observable outcome ambiguous, unsatisfiable acceptance criteria, missing acceptance criteria on leaf tasks, and missing contract or rule links on leaf tasks.
|
|
95
102
|
- The persisted plan is internally self-consistent: its narrative — context, rationale, and any explanatory prose — does not contradict the obligations, verification approach, or any other statement made in its task bodies, and no task contradicts that narrative. Where the prose describes how something is tested or built, it matches what the tasks prescribe.
|
|
96
|
-
- Each leaf task's acceptance criteria pin the task's observable outcome — the behavior the result must exhibit through the surface a reader or a test can inspect — so that any two implementations satisfying them are observably equivalent. The plan does not dictate a task's internal mechanism beyond what an observable acceptance criterion or an explicitly required architectural property demands: a choice that affects only how the work is carried out internally — which mechanism a task uses, and how its code and tests are organized across files and modules — changing no observable outcome the acceptance criteria pin, is left for the implementer to resolve against the real code rather than fixed by the planner. When the planner needs a structural property for an architectural reason — a single source for some logic, the absence of duplication, a module boundary — it states that property as a required outcome the acceptance criteria assert rather than fixing a specific internal mechanism, whether a code element to reuse or leave untouched or the files and modules its code and tests are placed in.
|
|
103
|
+
- Each leaf task's acceptance criteria pin the task's observable outcome — the behavior the result must exhibit through the surface a reader or a test can inspect — so that any two implementations satisfying them are observably equivalent. The plan does not dictate a task's internal mechanism beyond what an observable acceptance criterion or an explicitly required architectural property demands: a choice that affects only how the work is carried out internally — which mechanism a task uses, and how its code and tests are organized across files and modules — changing no observable outcome the acceptance criteria pin, is left for the implementer to resolve against the real code rather than fixed by the planner. When the planner needs a structural property for an architectural reason — a single source for some logic, the absence of duplication, a module boundary — it states that property as a required outcome the acceptance criteria assert rather than fixing a specific internal mechanism, whether a code element to reuse or leave untouched or the files and modules its code and tests are placed in. How an outcome is evidenced is such an internal choice too: a criterion states the observable fact to verify, and fixes a test instrument — a test double, a recording fake, a specific harness — only when the interaction that instrument records is itself the observable outcome, exercised through a collaboration the plan's design provides.
|
|
104
|
+
- Every leaf task's acceptance criteria are satisfiable together: at least one implementation satisfies all of them while honoring every contract and rule the plan links and the design the plan itself prescribes. A criterion that prescribes an evidence mechanism whose required structure that design or a canonical rule forbids — for example, asserting the absence of an interaction through a test double on a component the design bars from holding the doubled dependency — is unsatisfiable and is never persisted: the planner restates it as the observable fact the design's own surface can verify, or escalates the conflict during the clarification phase.
|
|
97
105
|
- Write each paragraph of prose in the plan as a single continuous line, however long; insert a line break only where markdown structure requires it — a blank line between paragraphs, a list item, a heading, a table row, or inside a fenced code block (whose contents you reproduce verbatim). Never break a paragraph across multiple lines to keep it within a maximum column width; the reader's editor wraps long lines for display.
|
|
98
106
|
- Use the fewest words that state each task, obligation, and explanation unambiguously, and write content — a sentence, a section, a cross-reference — only when it carries something not already carried by another task, an earlier sentence, or the reader's ordinary competence. Reach for more words only when fewer would leave an observable outcome ambiguous; task granularity itself follows the granularity rule below.
|
|
99
107
|
- Every task that creates, modifies, or removes code is grounded in the real state of the code it builds on — the current source, plus the changes any earlier task it depends on prescribes. Before writing the task, establish that state: read the current source for code that already exists, and consult the producing earlier task for code an earlier task in the plan creates or changes. Changing what the code does is the task's purpose and is allowed; what a task may not do is misstate the code it builds on — naming structure or behavior that code does not and will not have, or removing or rewriting code on a mistaken account of what it is for.
|
|
@@ -161,13 +169,14 @@ Five categories, all mandatory; failure in any one is a FAIL. Each category is a
|
|
|
161
169
|
- Free of placeholders. No \`<TBD>\` or analogous task markers, no template-style blanks, no parenthetical "(to be decided)" deferrals.
|
|
162
170
|
- Free of contradictions with existing contracts or rules. No task pins behavior the canonical listings forbid.
|
|
163
171
|
- Internally self-consistent — no contradiction between the plan's narrative and its tasks. The plan's context, rationale, and explanatory prose do not contradict the obligations, verification approach, or any other statement in its task bodies, and no task contradicts that narrative. Where the prose describes how something is tested or built, it matches what the tasks prescribe.
|
|
164
|
-
- Acceptance criteria pin the observable outcome; the internal mechanism may be left to the implementer. Each leaf task's acceptance criteria pin the task's observable outcome precisely, such that two implementations satisfying them are observably equivalent. Acceptance criteria that leave the observable outcome open — satisfiable by implementations that differ in observable behavior — or hedge wording that defers the outcome are FAIL. This includes, non-exhaustively, hedge phrases such as: \`(or class)\`, \`(or function)\`, \`(or refactor in place if preferred)\`, \`pick the lower-friction option\`, \`pick the X that minimizes Y\`, \`suggested location\`, \`or — alternatively —\`, \`or — equivalently —\`, \`or equivalent\`, \`at the time of implementation\`, \`if the X exists, do Y; otherwise Z\`, \`either A or B — pick one\`, \`A or B (or some hybrid)\`, \`or, more strongly\`, \`or X if Y\`. An outcome-affecting choice that the request did not specify must be either closed to a single observable commitment in the task's acceptance criteria, or escalated by the skill to the user before the plan was drafted. A choice that affects only the task's internal mechanism — which helper to reuse, which internal structure, how its code and tests are organized across files and modules, which implementation approach — and that changes no observable outcome the acceptance criteria pin is NOT a violation when left for the implementer to resolve; conversely, a task that freezes an internal mechanism that no observable acceptance criterion and no explicitly required architectural property needs is FAIL.
|
|
172
|
+
- Acceptance criteria pin the observable outcome; the internal mechanism may be left to the implementer. Each leaf task's acceptance criteria pin the task's observable outcome precisely, such that two implementations satisfying them are observably equivalent. Acceptance criteria that leave the observable outcome open — satisfiable by implementations that differ in observable behavior — or hedge wording that defers the outcome are FAIL. This includes, non-exhaustively, hedge phrases such as: \`(or class)\`, \`(or function)\`, \`(or refactor in place if preferred)\`, \`pick the lower-friction option\`, \`pick the X that minimizes Y\`, \`suggested location\`, \`or — alternatively —\`, \`or — equivalently —\`, \`or equivalent\`, \`at the time of implementation\`, \`if the X exists, do Y; otherwise Z\`, \`either A or B — pick one\`, \`A or B (or some hybrid)\`, \`or, more strongly\`, \`or X if Y\`. An outcome-affecting choice that the request did not specify must be either closed to a single observable commitment in the task's acceptance criteria, or escalated by the skill to the user before the plan was drafted. A choice that affects only the task's internal mechanism — which helper to reuse, which internal structure, how its code and tests are organized across files and modules, how an outcome is evidenced (the test instrument or double that demonstrates it), which implementation approach — and that changes no observable outcome the acceptance criteria pin is NOT a violation when left for the implementer to resolve; conversely, a task that freezes an internal mechanism that no observable acceptance criterion and no explicitly required architectural property needs is FAIL.
|
|
173
|
+
- Acceptance criteria are satisfiable under the plan's own design. For each criterion, and in particular each criterion that prescribes how its outcome is evidenced, confirm at least one implementation can satisfy it while honoring every contract and rule the plan links and the design the plan prescribes — the structure the prescribed evidence requires exists, or is permitted to exist, under that design. A criterion whose evidence mechanism requires a structure the plan's design or a canonical rule forbids — non-exhaustively, an assertion of absent interaction observed through a test double on a component the design forbids from holding the doubled dependency — is FAIL. A call-recording double is legitimate evidence only where the design provides the collaboration it records. Evidence-prescribing criteria are adjudicated one by one, never in aggregate: enumerate every acceptance criterion that prescribes an evidence instrument — a test double, fake, mock, spy, stub, or specific harness — or that asserts the absence of an interaction, each as its own numbered item, and produce for each item, before its verdict: (1) the observed component — the code element the prescribed instrument attaches to or observes; (2) the design disposition, quoted verbatim — the statement, from the plan (the same task's body, another task, or the plan's narrative) or from a linked rule, that provides the observed component with the doubled collaboration or that denies it; when neither the plan nor the linked rules state the disposition, establish it by reading the observed component's on-disk source before adjudicating — a disposition is never assumed; (3) a single-branch verdict — satisfiable or FAIL, decided on the disposition established in record 2. An adjudication conditioned on an unresolved branch — "satisfiable whether or not the component holds the dependency", "in either case", or any wording that leaves the branch unresolved — is not a verdict: resolve which branch the established disposition prescribes and judge that branch alone; when the plan genuinely leaves the disposition open, that openness is itself a FAIL of this category, never a ground for passing the item. An item missing any of the three records is unaudited, and this category is not reported as passed while any enumerated item is unaudited; a summary clause that disposes of several such criteria at once leaves every criterion it covers unaudited. The satisfiability check in category 4 reaches beyond evidence instruments: a task's acceptance criteria must be satisfiable while honoring every contract and rule the task links, and that audit is never rendered in aggregate. For each leaf task, for each contract and rule the task links, the validator produces, before the pair's verdict: (1) the constraining obligation, quoted verbatim — the obligation of that reference that constrains the task's acceptance criteria or the design the task prescribes; when no obligation of the reference constrains them, the record states that explicitly instead; (2) a single-branch verdict — satisfiable or FAIL, deciding whether at least one implementation can satisfy the task's acceptance criteria while honoring the quoted obligation and the design the plan prescribes. An adjudication conditioned on an unresolved question — "satisfiable under either model", "in either case", or any wording that leaves the question unresolved — is not a verdict: the validator resolves what the reference and the plan's design prescribe and judges that alone. A task-reference pair missing its record is unaudited, and the validator does not report category 4 as passed while any pair is unaudited; a summary clause that disposes of several pairs at once leaves every pair it covers unaudited.
|
|
165
174
|
- Every leaf task carries an explicit acceptance-criteria section.
|
|
166
175
|
- Every leaf task carries the relevant contract link(s) as markdown links in project-root-relative namespace form — the link text is the file's listed namespace with no leading slash, and the target is that same namespace prefixed with a single leading slash.
|
|
167
176
|
- Every leaf task carries the relevant rule link(s) as markdown links in project-root-relative namespace form — the link text is the file's listed namespace with no leading slash, and the target is that same namespace prefixed with a single leading slash. When a rule's enforcement is bound to a specific scope, that scope is referenced alongside the file path.
|
|
168
177
|
- The plan only references contracts and rules that exist in the canonical state captured at invocation.
|
|
169
178
|
- Tasks are numbered hierarchically per the Plan file format section above.
|
|
170
|
-
- Task granularity is sane: a leaf task is not so broad it would need to be split nor so narrow it is artificial.
|
|
179
|
+
- Task granularity is sane: a leaf task is not so broad it would need to be split nor so narrow it is artificial. Granularity is rendered task by task, never in aggregate: for every leaf task the validator produces one verdict line — sane, too broad, or too narrow — with the reason that grounds it. A too-broad verdict names the distinct kinds of work the task bundles that would each need their own AI invocation; a too-narrow verdict names the artificial fragmentation the split created; a sane verdict states that the task fits a single AI invocation without artificial fragmentation. A leaf task without its verdict line leaves category 4 incomplete, and a summary clause that disposes of several tasks at once leaves every task it covers unaudited.
|
|
171
180
|
- Each code-touching task's claims about the code it builds on are accurate to its baseline — the current on-disk source, plus the changes any earlier task in the plan it depends on prescribes. A task that names a function, type, field, file, or behavior that neither the source nor any earlier task in the plan provides, or that removes or rewrites code on a mistaken account of what it does, is FAIL. Do NOT FAIL a task merely for describing code the current on-disk source lacks when an earlier task in the plan introduces it — confirm instead that the depended-on task is ordered first. Changing the code's behavior is the task's purpose and is not itself a violation — only a false claim about the code the task builds on is.
|
|
172
181
|
- Runtime-behavior premises are backed or escalated. A task whose approach depends on a runtime- or observable-behavior claim not confirmable from the source — and that no contract, rule, existing test, or preceding task in the plan backs, and that was not escalated to the user — is FAIL. This explicitly includes a task that removes, weakens, or replaces existing code on the strength of such an unbacked claim.
|
|
173
182
|
|
|
@@ -271,7 +280,7 @@ For every obligation in the request, the skill decides whether it is a contract,
|
|
|
271
280
|
**Behavior rules.** Before persisting any file, read every behavior rule whose \`.spec/flanders\` scope encloses each file you are about to write — the \`.spec\` folder you write the file into and every parent \`.spec\` folder up to the project root — and honor all of them. Behavior rules govern how you name, place, and organize the files you author; an in-scope behavior rule is binding on that work, not advisory, and applies whether or not the request mentions it.
|
|
272
281
|
|
|
273
282
|
**Rename sweep.** When the run renames, relocates, or removes a term that can recur across the corpus beyond the files it is editing — a folder name, a path segment, a flag, an identifier, a fixed string, or a namespace convention — establish the full set of files to touch by searching the whole corpus (every contract and every rule) for the old term and inspecting every occurrence the search returns. The search is exhaustive over the corpus; it is not narrowed to the files you already planned to edit. Triage each occurrence individually into exactly one of two dispositions: an occurrence the rename must update, which the run edits; or an occurrence that is an intentional reference the rename leaves alone (for example a cross-reference to an unrelated file, or a deliberately unchanged example). An occurrence is never left unexamined on the grounds that its file looked irrelevant. Coverage is driven by the token, not by a judgment of which files are relevant: the set of files the run edits is the union of the occurrences the sweep shows must be updated, and a file the sweep surfaces that you had not planned to touch is added to the run.
|
|
274
|
-
4. **Clarification phase.** Whenever the request leaves an obligation ambiguous, leaves a UI or logic decision unspecified, leaves a rule or its scope of enforcement unspecified, or admits multiple valid interpretations, ask the user clarifying questions in batches: before you start asking, accumulate every question whose content does not depend on another question's answer and ask that whole set together in a single interaction rather than one question at a time, holding a question back only when it genuinely depends on an earlier answer and asking it in a later round that again batches the questions that have become independent. When your AI tool provides a facility for asking several questions at once, present the accumulated batch through that facility in a single interaction; when it provides no such facility, ask one question per turn.
|
|
283
|
+
4. **Clarification phase.** Whenever the request leaves an obligation ambiguous, leaves a UI or logic decision unspecified, leaves a rule or its scope of enforcement unspecified, or admits multiple valid interpretations, ask the user clarifying questions in batches: before you start asking, accumulate every question whose content does not depend on another question's answer and ask that whole set together in a single interaction rather than one question at a time, holding a question back only when it genuinely depends on an earlier answer and asking it in a later round that again batches the questions that have become independent. When your AI tool provides a facility for asking several questions at once, present the accumulated batch through that facility in a single interaction; when it provides no such facility, ask one question per turn. Phrase every question whose answer space is bounded as multiple-choice, through the facility and in chat alike. Use open-ended questions only when multiple-choice would force a false dichotomy. When two or three substantially different approaches would all satisfy the request, present those approaches with a short trade-off summary for each and ask the user to pick or redirect, instead of silently choosing one. The clarification phase ends only when you have enough information to draft files that contain no placeholders, no contradictions, and no scope ambiguity.
|
|
275
284
|
5. **Drafting phase.** Before persisting any file:
|
|
276
285
|
- Present the planned file layout — which files will exist, which \`.spec\` folder each falls in, which are contracts and which are rules (the classification and placement made visible), and the key obligations of each file — as a structured summary, and wait for user approval or redirection.
|
|
277
286
|
- Once the layout is approved, persist every resulting file in a single batch without any further per-file or per-section confirmation step.
|
|
@@ -310,7 +319,7 @@ Pass the validator:
|
|
|
310
319
|
- The canonical contracts listing captured in step 2 of the procedure.
|
|
311
320
|
- The canonical rules listing captured in step 2 of the procedure.
|
|
312
321
|
- When this run renamed, relocated, or removed a term that can recur across the corpus (per the Rename sweep obligation in the procedure above), the explicit list of those old term(s). The list is empty when the run changed no such term.
|
|
313
|
-
- The verbatim text of the check categories below. The host MUST inline these categories in the validator's prompt — it does not just point the validator at a file by path, and it does not rely on the validator discovering them by transitive reading.
|
|
322
|
+
- The verbatim text of the check categories below, together with the per-item adjudication protocol stated alongside them. The host MUST inline these categories and that protocol in the validator's prompt — it does not just point the validator at a file by path, and it does not rely on the validator discovering them by transitive reading.
|
|
314
323
|
|
|
315
324
|
The validator reads the file(s) in full, plus any contract or rule from the listings it judges relevant to forming its verdict.
|
|
316
325
|
|
|
@@ -318,6 +327,8 @@ The validator reads the file(s) in full, plus any contract or rule from the list
|
|
|
318
327
|
|
|
319
328
|
Three categories, all mandatory; failure in any one is a FAIL. Each category is audited independently and violations are enumerated exhaustively. The category set is selected by the folder each file landed in: category A applies to each file that landed in a \`.spec/contracts\` folder; category B applies to each file that landed in a \`.spec/rules\` folder; category C applies to every file written or updated in the run. A file that landed in a \`.spec/flanders\` folder is audited by the non-contradiction category C only; categories A and B audit files in \`.spec/contracts\` and \`.spec/rules\` folders respectively.
|
|
320
329
|
|
|
330
|
+
Every applicable check item is adjudicated per file individually, never in aggregate: for each file under audit, render every applicable check item — each format-and-shape item, each content item, and the non-contradiction category — as its own verdict line, PASS or FAIL, produced from the record the item's kind requires. Presence checks — a check satisfied by an element the file must carry, such as a descriptive filename, an explicit scope-of-enforcement section, atomic rule sections, or cross-references written as markdown links — name or quote the satisfying element, and a FAIL names the missing or malformed element with its file:line. Absence checks — a check violated by content the file must not carry, such as placeholders, hedge phrasing, historical or migration content, implementation detail in a contract, or an obligation duplicated across files — quote the offending passage with its file:line on FAIL, and on PASS commit that a full read of the file surfaced no occurrence. The non-contradiction verdict names the corpus files read and compared to reach it — a non-contradiction verdict that names no consulted corpus file is not an adjudication — and a flagged contradiction quotes both sides with their file:line. A verdict conditioned on an unresolved reading — "compatible under either reading", "fine either way", or any wording that leaves the reading unresolved — is not a verdict: resolve which reading the corpus text sustains and judge that reading alone; when the audited text genuinely admits both readings, that openness is itself an ambiguous-wording FAIL, never a ground for passing the item. An item missing the record its kind requires is unaudited, and a category is not reported as passed while any of its items is unaudited; a summary clause that disposes of several items or several files at once leaves everything it covers unaudited.
|
|
331
|
+
|
|
321
332
|
**A. Contract artifacts (each file written or updated under a \`.spec/contracts\` folder)**
|
|
322
333
|
|
|
323
334
|
A1. Format and shape. Every contract file written or updated lives inside a \`.spec/contracts\` folder, is non-empty, is markdown, has a filename descriptive of its content, and is organized as described in step 7 of the procedure.
|
|
@@ -376,10 +387,14 @@ When the loop ends with FAIL after five passes, do not declare complete: surface
|
|
|
376
387
|
|
|
377
388
|
Once you have declared the spec complete — the spec files persisted and the final validator returned PASS — offer to continue into the next step in the same session. Make this offer only on successful completion: when the bounded triage-then-fix loop exhausts without a PASS, surface the last FAIL report and stop, and make no such offer.
|
|
378
389
|
|
|
379
|
-
Ask the user which skill to launch next: /flanders-plan, /flanders-work, or neither. Recommend one of them based on the implementation effort the spec you just wrote implies — recommend /flanders-work when the spec describes a single, small, self-contained change, and recommend /flanders-plan when the spec describes larger work that spans multiple obligations or scopes or needs an ordered, multi-step implementation. The user accepts the recommendation, chooses the other skill, or declines.
|
|
390
|
+
Ask the user which skill to launch next: /flanders-plan, /flanders-work, or neither. ${launchQuestionInstruction("your completion declaration", "that launch question")} Recommend one of them based on the implementation effort the spec you just wrote implies — recommend /flanders-work when the spec describes a single, small, self-contained change, and recommend /flanders-plan when the spec describes larger work that spans multiple obligations or scopes or needs an ordered, multi-step implementation. The user accepts the recommendation, chooses the other skill, or declines.
|
|
380
391
|
|
|
381
392
|
When the user chooses /flanders-plan or /flanders-work, launch it by invoking it in the same session with no <data> argument, so the launched skill takes its input from the conversation — the original request together with the spec you just wrote. The run then proceeds under that skill; launching it leaves your own deliverable and write boundary unchanged, so you write only this run's spec files and never code or a plan file. When the user declines, end the run.
|
|
382
393
|
|
|
394
|
+
## Chat presentations precede questions
|
|
395
|
+
|
|
396
|
+
${reportBeforeQuestionInstruction("every presentation a step of this skill owes the user in chat — the approach trade-off summaries of the clarification phase, the drafting-phase layout summary —", "the question that follows it")}
|
|
397
|
+
|
|
383
398
|
## Output language
|
|
384
399
|
|
|
385
400
|
Resolve the natural language to write each spec file in by this priority order:
|
|
@@ -504,7 +519,7 @@ exports.hardStopReviewSkillBody = `---
|
|
|
504
519
|
description: Diagnose a hard stop of the implement command and recommend how to relaunch it so the same task completes.
|
|
505
520
|
---
|
|
506
521
|
|
|
507
|
-
You are the /flanders-hard-stop-review skill. When \`flanders implement\`
|
|
522
|
+
You are the /flanders-hard-stop-review skill. When \`flanders implement\` hard-stops — exceeding its per-task iteration cap, or acting on the worker's own declaration that the task is structurally impossible — it ends the run, preserves its temporary folder on disk, and points the user at that folder. You diagnose why the hard-stopped task never reached a clean iteration and recommend the concrete action that lets \`implement\` be relaunched so the task completes instead of stopping again.
|
|
508
523
|
|
|
509
524
|
## Input resolution
|
|
510
525
|
|
|
@@ -516,7 +531,7 @@ The user invokes you as: /flanders-hard-stop-review [<data>]
|
|
|
516
531
|
|
|
517
532
|
Your work is read-only, drawing only on the preserved hard-stop temporary folder, the plan file, and the project's spec corpus — not the AI tools' own session transcripts.
|
|
518
533
|
|
|
519
|
-
1. **Read the preserved evidence.** Read the preserved
|
|
534
|
+
1. **Read the preserved evidence.** Read the preserved folder's per-iteration worker, build, test, and reviewer output logs; the per-stage error logs the hard stop materializes — \`build.<iteration>.error.log\`, \`test.<iteration>.error.log\`, \`reviewer.<iteration>.<position>.error.log\`, and \`commit.<iteration>.error.log\` — making explicit which stage failed in each iteration and by which reviewer (the single briefing \`error.log\` has been removed at the hard stop); the worker-declared \`hard-stop.log\`, when the stop was the worker's own declaration; its consolidated \`spec.md\`; and each per-reviewer folder's \`error.log\`. From that evidence identify the task that hard-stopped — its plan-file line number and title — and the plan file the run was implementing.
|
|
520
535
|
|
|
521
536
|
2. **Ground the analysis in the project's specs.** Read the identified plan file and the contracts and rules the hard-stopped task references, consulting the wider spec corpus as far as the diagnosis needs.
|
|
522
537
|
|
|
@@ -524,25 +539,27 @@ Your work is read-only, drawing only on the preserved hard-stop temporary folder
|
|
|
524
539
|
- The task made real progress across iterations, so the hard stop reflects a task larger than the iteration cap can finish or a transient failure, and a fresh run or a smaller task would carry it through.
|
|
525
540
|
- The iterations circled the same unresolved failure with no net progress — a loop — driven by a cause the next run must remove first: a contradictory or ambiguous contract or rule, an acceptance criterion no implementation can satisfy as written, a task premise about runtime behavior the code does not bear out, a task scoped too large or ordered ahead of a dependency it needs, or a review that keeps re-failing the change for the same reason.
|
|
526
541
|
|
|
542
|
+
When the preserved folder carries a worker-declared \`hard-stop.log\`, its declared cause is evidence, not a conclusion: verify the declaration against the iteration history, the plan, and the specs, and classify the stop by what that verification sustains.
|
|
543
|
+
|
|
527
544
|
4. **Map the cause to the action that removes it:**
|
|
528
545
|
- Re-run \`flanders implement\` unchanged, when the failure was transient or the task was progressing and needs only a fresh iteration budget. The per-task iteration cap is a fixed five and is not configurable, so the remedy for a task that needs more attempts is a fresh run — which resets the per-task iteration counter to zero — or a task split into smaller tasks, and never a raised cap.
|
|
529
546
|
- Revise the plan through \`/flanders-plan\`: split the hard-stopped task into smaller tasks, correct acceptance criteria or a task premise the iterations proved wrong, or reorder the task against the dependency it needs.
|
|
530
547
|
- Fix the spec through \`/flanders-spec\`: resolve the contradictory or ambiguous contract or rule that left the task unsatisfiable.
|
|
531
548
|
- A combination of the above, when the evidence shows more than one cause.
|
|
532
549
|
|
|
533
|
-
5. **Present your root-cause finding and recommendation in chat.**
|
|
550
|
+
5. **Present your root-cause finding and recommendation in chat.** ${launchQuestionInstruction("that diagnosis", "the launch question of the next section")}
|
|
534
551
|
|
|
535
552
|
## Recommending and launching the next step
|
|
536
553
|
|
|
537
|
-
After presenting the diagnosis, ask the user which skill to launch to carry out the recommendation: \`/flanders-spec\`, \`/flanders-plan\`, or neither. Recommend the skill the action you selected in step 4 points to. When the user chooses one, launch it in the same session with no \`<data>\` argument. It takes the diagnosis from the conversation and operates under its own write boundary; yours remains read-only. When the recommended fix is to re-run \`implement\` unchanged, state the \`flanders implement\` command for the user to run and launch nothing. When the user declines, end the run.
|
|
554
|
+
After presenting the diagnosis, ask the user which skill to launch to carry out the recommendation: \`/flanders-spec\`, \`/flanders-plan\`, or neither. That question is plain chat text at the end of the diagnosis message, per step 5. Recommend the skill the action you selected in step 4 points to. When the user chooses one, launch it in the same session with no \`<data>\` argument. It takes the diagnosis from the conversation and operates under its own write boundary; yours remains read-only. When the recommended fix is to re-run \`implement\` unchanged, state the \`flanders implement\` command for the user to run and launch nothing. When the user declines, end the run.
|
|
538
555
|
|
|
539
556
|
## Write boundary
|
|
540
557
|
|
|
541
558
|
You create, modify, delete, and rename no file of your own: not code, not a plan file, and no file inside any \`.spec/contracts\`, \`.spec/rules\`, or \`.spec/flanders\` folder. Every file change happens only through a skill you launch, under that skill's own write authority.
|
|
542
559
|
|
|
543
|
-
## Interaction language
|
|
560
|
+
## Interaction and reasoning language
|
|
544
561
|
|
|
545
|
-
|
|
562
|
+
Use one resolved language for both your reasoning and every message you address to the user, throughout the run. Resolve it, in order, from the natural language of the user's most recent message when that message carries a determinable natural language; otherwise from the plan file you identify, then the spec corpus you consult; otherwise the general most-recent-message resolution. Follow any mid-conversation language switch the user makes.
|
|
546
563
|
|
|
547
564
|
${(0, prompts_1.buildFlandersVoiceSection)({
|
|
548
565
|
subject: "the messages you address to the user",
|
|
@@ -5,6 +5,7 @@ export type WorkspacePaths = Readonly<{
|
|
|
5
5
|
testScript: string;
|
|
6
6
|
errorLog: string;
|
|
7
7
|
specFile: string;
|
|
8
|
+
hardStopLog: string;
|
|
8
9
|
prepLog(taskIndex: number): string;
|
|
9
10
|
workerLog(iter: number): string;
|
|
10
11
|
buildLog(iter: number): string;
|
|
@@ -13,6 +14,10 @@ export type WorkspacePaths = Readonly<{
|
|
|
13
14
|
reviewerOutputLog(iter: number, reviewerIndex: number): string;
|
|
14
15
|
reviewerErrorLog(reviewerIndex: number): string;
|
|
15
16
|
reviewerSpecFile(reviewerIndex: number): string;
|
|
17
|
+
buildErrorLog(iter: number): string;
|
|
18
|
+
testErrorLog(iter: number): string;
|
|
19
|
+
commitErrorLog(iter: number): string;
|
|
20
|
+
reviewerErrorLogFor(iter: number, position: number): string;
|
|
16
21
|
}>;
|
|
17
22
|
export interface PlatformContext {
|
|
18
23
|
isWindows(): boolean;
|
|
@@ -45,6 +45,7 @@ class Workspace {
|
|
|
45
45
|
testScript: (0, fsUtils_1.joinPath)(root, isWindows ? "test.bat" : "test.sh"),
|
|
46
46
|
errorLog: (0, fsUtils_1.joinPath)(root, "error.log"),
|
|
47
47
|
specFile: (0, fsUtils_1.joinPath)(root, "spec.md"),
|
|
48
|
+
hardStopLog: (0, fsUtils_1.joinPath)(root, "hard-stop.log"),
|
|
48
49
|
prepLog(taskIndex) { return (0, fsUtils_1.joinPath)(root, `prep.${taskIndex}.log`); },
|
|
49
50
|
workerLog(iter) { return (0, fsUtils_1.joinPath)(root, `worker.${iter}.log`); },
|
|
50
51
|
buildLog(iter) { return (0, fsUtils_1.joinPath)(root, `build.${iter}.log`); },
|
|
@@ -52,7 +53,11 @@ class Workspace {
|
|
|
52
53
|
reviewerLog(iter) { return (0, fsUtils_1.joinPath)(root, `reviewer.${iter}.log`); },
|
|
53
54
|
reviewerOutputLog(iter, reviewerIndex) { return (0, fsUtils_1.joinPath)(root, `reviewer.${iter}.${reviewerIndex}.log`); },
|
|
54
55
|
reviewerErrorLog(reviewerIndex) { return (0, fsUtils_1.joinPath)(reviewerRoots[reviewerIndex - 1], "error.log"); },
|
|
55
|
-
reviewerSpecFile(reviewerIndex) { return (0, fsUtils_1.joinPath)(reviewerRoots[reviewerIndex - 1], "spec.md"); }
|
|
56
|
+
reviewerSpecFile(reviewerIndex) { return (0, fsUtils_1.joinPath)(reviewerRoots[reviewerIndex - 1], "spec.md"); },
|
|
57
|
+
buildErrorLog(iter) { return (0, fsUtils_1.joinPath)(root, `build.${iter}.error.log`); },
|
|
58
|
+
testErrorLog(iter) { return (0, fsUtils_1.joinPath)(root, `test.${iter}.error.log`); },
|
|
59
|
+
commitErrorLog(iter) { return (0, fsUtils_1.joinPath)(root, `commit.${iter}.error.log`); },
|
|
60
|
+
reviewerErrorLogFor(iter, position) { return (0, fsUtils_1.joinPath)(root, `reviewer.${iter}.${position}.error.log`); }
|
|
56
61
|
};
|
|
57
62
|
}
|
|
58
63
|
async errorLogExists() {
|