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 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 before halting. The project must be a git repository: `implement` needs git and has no flag to turn it off.
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. When Flanders can't get a single task past the build, test, and review gates within its five attempts, it doesn't keep flailing away it calls a **hard stop**: the whole `implement` run ends right there and exits with a non-zero status.
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: the worker's output, the build and test output, each reviewer's output, and the `error.log` that briefed the final iteration. It's the whole story of what was tried and where each go-round fell short.
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._workspace.preserveOnDispose();
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
- await this._writeErrorLog(ws, `git add -A failed (exit ${addResult.code})\n--- stdout ---\n${addResult.stdout}\n--- stderr ---\n${addResult.stderr}`);
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
- await this._writeErrorLog(ws, `git commit failed (exit ${commitResult.code})\n--- stdout ---\n${commitResult.stdout}\n--- stderr ---\n${commitResult.stderr}`);
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
- await this._writeErrorLog(ws, `build stage failed (exit ${result.code})\n--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}`);
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
- await this._writeErrorLog(ws, `test stage failed (exit ${result.code})\n--- stdout ---\n${result.stdout}\n--- stderr ---\n${result.stderr}`);
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) {
@@ -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>",
@@ -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
- Use a light Ned-Flanders touch in ${parts.subject}, only while ${parts.languageFraming} is English — deliver any other language plainly. Keep it out of ${voiceExclusionLead}${parts.finalExclusion}.`;
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. 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:
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\`
@@ -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. Multiple-choice questions are preferred when the answer space is bounded.
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. Prefer multiple-choice questions when the answer space is bounded. 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.
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\` exceeds its per-task iteration cap 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.
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 hard-stop temporary folder: its per-iteration worker, build, test, and reviewer output logs, its briefing \`error.log\`, and its consolidated \`spec.md\`, together with 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.
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
- Every message you address to the user during the run is written in the natural language of the user's most recent message in the conversation. When the user switches the language they write in partway through the interaction, every subsequent message you address to the user follows the language of their latest message.
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() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flanders",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Flanders never breaks a rule",
5
5
  "main": "lib/",
6
6
  "types": "lib/types",