docs: implementation plan for bundled-prompts overhaul
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
972
docs/superpowers/plans/2026-06-04-bundled-prompts-overhaul.md
Normal file
972
docs/superpowers/plans/2026-06-04-bundled-prompts-overhaul.md
Normal file
@@ -0,0 +1,972 @@
|
||||
# Bundled Prompts Overhaul Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Externalize every bundled prose prompt into editable files with strong defaults, collapse system+agent, and add an inline `CLAUDEDO_BLOCKED:` roadblock protocol surfaced at review.
|
||||
|
||||
**Architecture:** `PromptFiles` becomes the single source of prompt defaults + a pure token renderer. Each consumer (TaskRunner, PlanningSessionManager, DailyPrepPrompt, WeekReportPromptBuilder) reads its prompt via `PromptFiles`. `StreamAnalyzer` collects roadblock markers from streamed assistant text; the runner folds them into the review result.
|
||||
|
||||
**Tech Stack:** .NET 8, xUnit, EF Core (no schema change in this plan).
|
||||
|
||||
Spec: `docs/superpowers/specs/2026-06-04-bundled-prompts-overhaul-design.md`
|
||||
|
||||
---
|
||||
|
||||
## File structure
|
||||
|
||||
- `src/ClaudeDo.Data/PromptFiles.cs` — new `PromptKind` members, new defaults, `RenderTemplate` + `ReadOrDefault` + `Render`.
|
||||
- `src/ClaudeDo.Worker/Runner/StreamAnalyzer.cs` — collect `Blocks` from assistant text.
|
||||
- `src/ClaudeDo.Worker/Runner/RunResult.cs` — carry `Blocks`.
|
||||
- `src/ClaudeDo.Worker/Runner/ClaudeProcess.cs` — pass `Blocks`; expose no-result prefix const.
|
||||
- `src/ClaudeDo.Worker/Runner/TaskRunner.cs` — drop agent file; retry via `retry.md`; fold blocks into review result.
|
||||
- `src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs` — read planning prompts via `PromptFiles`.
|
||||
- `src/ClaudeDo.Worker/Prime/DailyPrepPrompt.cs` — read `daily-prep.md`.
|
||||
- `src/ClaudeDo.Worker/Report/WeekReportPromptBuilder.cs` — read `weekly-report.md`.
|
||||
- `src/ClaudeDo.Ui/ViewModels/Modals/Settings/FilesSettingsTabViewModel.cs` + its view — expose new prompt files, drop agent.
|
||||
- Tests in `tests/ClaudeDo.Data.Tests` and `tests/ClaudeDo.Worker.Tests`.
|
||||
|
||||
Build commands (this repo is on .NET 8 — build per project, not the .slnx):
|
||||
```bash
|
||||
dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release
|
||||
dotnet test tests/ClaudeDo.Data.Tests/ClaudeDo.Data.Tests.csproj -c Release
|
||||
dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 1: PromptFiles — kinds, defaults, pure renderer
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Data/PromptFiles.cs`
|
||||
- Test: `tests/ClaudeDo.Data.Tests/PromptFilesTests.cs` (create)
|
||||
|
||||
- [ ] **Step 1: Write failing tests for the pure renderer**
|
||||
|
||||
Create `tests/ClaudeDo.Data.Tests/PromptFilesTests.cs`:
|
||||
|
||||
```csharp
|
||||
using ClaudeDo.Data;
|
||||
|
||||
namespace ClaudeDo.Data.Tests;
|
||||
|
||||
public class PromptFilesTests
|
||||
{
|
||||
[Fact]
|
||||
public void RenderTemplate_replaces_known_tokens()
|
||||
{
|
||||
var outp = PromptFiles.RenderTemplate(
|
||||
"Plan for {date}, cap {maxTasks}.",
|
||||
new Dictionary<string, string> { ["date"] = "2026-06-04", ["maxTasks"] = "5" });
|
||||
Assert.Equal("Plan for 2026-06-04, cap 5.", outp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RenderTemplate_leaves_unknown_braces_intact()
|
||||
{
|
||||
var outp = PromptFiles.RenderTemplate(
|
||||
"## {Wochentag}, {dd.MM.yyyy} — {start}",
|
||||
new Dictionary<string, string> { ["start"] = "01.06.2026" });
|
||||
Assert.Equal("## {Wochentag}, {dd.MM.yyyy} — 01.06.2026", outp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultFor_system_mentions_blocked_marker_and_scope()
|
||||
{
|
||||
var d = PromptFiles.DefaultFor(PromptKind.System);
|
||||
Assert.Contains("CLAUDEDO_BLOCKED:", d);
|
||||
Assert.Contains("unattended", d, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultFor_planning_initial_has_title_and_description_tokens()
|
||||
{
|
||||
var d = PromptFiles.DefaultFor(PromptKind.PlanningInitial);
|
||||
Assert.Contains("{title}", d);
|
||||
Assert.Contains("{description}", d);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PathFor_planning_is_planning_system_file()
|
||||
{
|
||||
Assert.EndsWith("planning-system.md", PromptFiles.PathFor(PromptKind.Planning));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Data.Tests/ClaudeDo.Data.Tests.csproj -c Release`
|
||||
Expected: FAIL — `RenderTemplate`/`DefaultFor` don't exist, `PromptKind.PlanningInitial` undefined.
|
||||
|
||||
- [ ] **Step 3: Rewrite PromptFiles.cs**
|
||||
|
||||
Replace the entire contents of `src/ClaudeDo.Data/PromptFiles.cs` with:
|
||||
|
||||
```csharp
|
||||
using System.Text;
|
||||
|
||||
namespace ClaudeDo.Data;
|
||||
|
||||
public enum PromptKind { System, Planning, PlanningInitial, Retry, DailyPrep, WeeklyReport }
|
||||
|
||||
public static class PromptFiles
|
||||
{
|
||||
public static string Root => Path.Combine(Paths.AppDataRoot(), "prompts");
|
||||
|
||||
public static string PathFor(PromptKind kind) => kind switch
|
||||
{
|
||||
PromptKind.System => Path.Combine(Root, "system.md"),
|
||||
PromptKind.Planning => Path.Combine(Root, "planning-system.md"),
|
||||
PromptKind.PlanningInitial => Path.Combine(Root, "planning-initial.md"),
|
||||
PromptKind.Retry => Path.Combine(Root, "retry.md"),
|
||||
PromptKind.DailyPrep => Path.Combine(Root, "daily-prep.md"),
|
||||
PromptKind.WeeklyReport => Path.Combine(Root, "weekly-report.md"),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind))
|
||||
};
|
||||
|
||||
public static void EnsureExists(PromptKind kind)
|
||||
{
|
||||
Directory.CreateDirectory(Root);
|
||||
var path = PathFor(kind);
|
||||
if (File.Exists(path)) return;
|
||||
File.WriteAllText(path, DefaultFor(kind));
|
||||
}
|
||||
|
||||
public static string? ReadOrNull(PromptKind kind)
|
||||
{
|
||||
var path = PathFor(kind);
|
||||
if (!File.Exists(path)) return null;
|
||||
var content = File.ReadAllText(path).Trim();
|
||||
return string.IsNullOrEmpty(content) ? null : content;
|
||||
}
|
||||
|
||||
/// <summary>File content if present and non-empty, otherwise the bundled default.</summary>
|
||||
public static string ReadOrDefault(PromptKind kind) => ReadOrNull(kind) ?? DefaultFor(kind);
|
||||
|
||||
/// <summary>Render a prompt: read file-or-default, then substitute named tokens.</summary>
|
||||
public static string Render(PromptKind kind, IReadOnlyDictionary<string, string> values)
|
||||
=> RenderTemplate(ReadOrDefault(kind), values);
|
||||
|
||||
/// <summary>Replace only the given {name} tokens; any other braces pass through untouched.</summary>
|
||||
public static string RenderTemplate(string template, IReadOnlyDictionary<string, string> values)
|
||||
{
|
||||
var sb = new StringBuilder(template);
|
||||
foreach (var (key, val) in values)
|
||||
sb.Replace("{" + key + "}", val);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string DefaultFor(PromptKind kind) => kind switch
|
||||
{
|
||||
PromptKind.System => SystemDefault,
|
||||
PromptKind.Planning => PlanningSystemDefault,
|
||||
PromptKind.PlanningInitial => PlanningInitialDefault,
|
||||
PromptKind.Retry => RetryDefault,
|
||||
PromptKind.DailyPrep => DailyPrepDefault,
|
||||
PromptKind.WeeklyReport => WeeklyReportDefault,
|
||||
_ => ""
|
||||
};
|
||||
|
||||
private const string SystemDefault = """
|
||||
# Working Agreement
|
||||
|
||||
You are completing one well-defined task autonomously in a git repository.
|
||||
|
||||
## Scope
|
||||
- Do exactly what the task asks — no unrequested refactors, renames, dependency
|
||||
changes, or "while I'm here" cleanup.
|
||||
- If intent is ambiguous, state the assumption you're making and proceed with the
|
||||
most reasonable reading. Stop only if you genuinely cannot move forward.
|
||||
- Prefer three similar lines over a premature abstraction. Don't build for
|
||||
hypothetical future needs.
|
||||
|
||||
## Working in the repo
|
||||
- Read a file before editing it. Match the conventions already in this codebase —
|
||||
they override generic defaults.
|
||||
- Prefer editing existing files to creating new ones. Don't write comments that
|
||||
just restate the code.
|
||||
- Validate only at real boundaries (user input, external APIs).
|
||||
|
||||
## Finishing
|
||||
- Before claiming done, verify: run the build and relevant tests, confirm they
|
||||
pass, and report what you ran. If you couldn't verify something, say so plainly.
|
||||
- Make focused commits using the repository's existing commit-message convention.
|
||||
|
||||
## Safety
|
||||
- Never force-push, hard-reset, or delete branches/files beyond the task's scope
|
||||
without being asked.
|
||||
- Don't introduce injection/XSS/secret-leak issues. Never commit credentials.
|
||||
|
||||
## You are running unattended
|
||||
You run autonomously with no human watching. There is no one to answer mid-task
|
||||
questions, so never stop to ask — make the most reasonable decision, note the
|
||||
assumption, and continue.
|
||||
|
||||
## When you are blocked
|
||||
If something genuinely prevents you from completing part of the task (missing
|
||||
credentials, contradictory requirements, a destructive action you won't take
|
||||
unasked), do NOT silently give up. Write this marker on its own line, then keep
|
||||
working on whatever else you can:
|
||||
|
||||
CLAUDEDO_BLOCKED: <one short sentence describing what blocked you>
|
||||
|
||||
Emit it as many times as needed — once per distinct blocker. Use it only for true
|
||||
blockers, not for routine decisions you can make yourself.
|
||||
""";
|
||||
|
||||
private const string PlanningSystemDefault = """
|
||||
You are the planning assistant for ClaudeDo. Your job is to break a task into
|
||||
smaller, independently executable subtasks — the session ends by creating those
|
||||
subtasks.
|
||||
|
||||
Start every session by invoking the `superpowers:brainstorming` skill (Skill
|
||||
tool) and follow it end to end: clarifying questions one at a time, then 2–3
|
||||
approaches with a recommendation, then a short design. Do not create any subtasks
|
||||
until the user has approved the design.
|
||||
|
||||
You can ONLY shape this task's plan — you cannot edit files or touch other tasks.
|
||||
The tools available to you are: CreateChildTask, ListChildTasks, UpdateChildTask,
|
||||
DeleteChildTask, UpdatePlanningTask, and Finalize. Use nothing else.
|
||||
|
||||
Once the design is approved, create the child tasks with CreateChildTask, then
|
||||
call Finalize. Keep each subtask concrete and self-contained with a clear
|
||||
done-state, ordered so dependencies come first.
|
||||
""";
|
||||
|
||||
private const string PlanningInitialDefault = """
|
||||
# Task to plan: {title}
|
||||
|
||||
{description}
|
||||
""";
|
||||
|
||||
private const string RetryDefault = """
|
||||
The task did not complete on the previous attempt — you may have run out of
|
||||
turns, hit an error, or stopped before finishing.
|
||||
|
||||
Review the work already done in this session and the current state of the
|
||||
repository, identify what is still incomplete or broken, and finish the task.
|
||||
Don't restart from scratch or repeat a failed approach. Verify the result
|
||||
(build + tests) before you stop.
|
||||
""";
|
||||
|
||||
private const string DailyPrepDefault = """
|
||||
You are preparing my workday for {date}.
|
||||
|
||||
1. Call mcp__claudedo__get_daily_prep_candidates.
|
||||
2. Keep tasks already marked MyDay (currentMyDay) — never remove them.
|
||||
3. Fill MyDay to at most {maxTasks} open tasks TOTAL (currentMyDay counts). Never exceed it.
|
||||
4. Estimate each candidate's effort and pick a feasible mix — not only big items.
|
||||
Prioritize isStarred, due (scheduledFor), and older tasks.
|
||||
5. Place related tasks next to each other using consecutive sortOrder values.
|
||||
6. Apply via mcp__claudedo__set_my_day(taskId, true, sortOrder). Never mark anything
|
||||
outside the candidate list.
|
||||
|
||||
If there are no candidates, do nothing.
|
||||
""";
|
||||
|
||||
private const string WeeklyReportDefault = """
|
||||
You are generating a concise weekly standup report for a software developer,
|
||||
covering {start} to {end}.
|
||||
|
||||
Rules:
|
||||
- Write the ENTIRE report in German.
|
||||
- Group by day. One "## {Wochentag}, {dd.MM.yyyy}" section per day that has
|
||||
activity (German weekday names). Omit days with no activity.
|
||||
- Within each day: 3–5 first-person, past-tense bullets ("- Habe X umgesetzt",
|
||||
"- Y behoben"). Merge related small work into one bullet.
|
||||
- Drop trivia: typo fixes, pure exploration, false starts, tooling/log noise.
|
||||
- Blend the developer's own notes and the derived activity into ONE deduplicated
|
||||
bullet list per day. The notes are authoritative — never omit or contradict them.
|
||||
- Name the project/repo when it adds clarity.
|
||||
- Output ONLY the dated sections. No preamble, no intro, no closing remarks.
|
||||
|
||||
Two sections follow below: an activity log derived from Claude session history,
|
||||
and the developer's own notes. Base the report on both; the notes are
|
||||
authoritative where they conflict with the derived activity.
|
||||
""";
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Data.Tests/ClaudeDo.Data.Tests.csproj -c Release`
|
||||
Expected: PASS (5 new tests).
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Data/PromptFiles.cs tests/ClaudeDo.Data.Tests/PromptFilesTests.cs
|
||||
git commit -m "feat(prompts): externalize prompt kinds with defaults and token renderer"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: TaskRunner — drop agent file from system prompt merge
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/TaskRunner.cs:382-386`
|
||||
|
||||
- [ ] **Step 1: Remove the agent-file read and merge**
|
||||
|
||||
In `ResolveConfigAsync`, replace:
|
||||
|
||||
```csharp
|
||||
var systemFile = PromptFiles.ReadOrNull(PromptKind.System);
|
||||
var agentFile = PromptFiles.ReadOrNull(PromptKind.Agent);
|
||||
|
||||
var instructions = MergeInstructions(
|
||||
systemFile, global.DefaultClaudeInstructions, listConfig?.SystemPrompt, task.SystemPrompt, agentFile);
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```csharp
|
||||
var systemFile = PromptFiles.ReadOrNull(PromptKind.System);
|
||||
|
||||
var instructions = MergeInstructions(
|
||||
systemFile, global.DefaultClaudeInstructions, listConfig?.SystemPrompt, task.SystemPrompt);
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Build to verify it compiles**
|
||||
|
||||
Run: `dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release`
|
||||
Expected: PASS (no reference to `PromptKind.Agent` remains).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Runner/TaskRunner.cs
|
||||
git commit -m "refactor(prompts): collapse agent prompt into system prompt"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Retry prompt from file + conditional stderr append
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/ClaudeProcess.cs:101-103` (expose prefix const)
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/TaskRunner.cs` (add `BuildRetryPrompt`, use it at ~L107)
|
||||
- Test: `tests/ClaudeDo.Worker.Tests/Runner/RetryPromptTests.cs` (create)
|
||||
|
||||
- [ ] **Step 1: Write failing tests for the retry-prompt helper**
|
||||
|
||||
Create `tests/ClaudeDo.Worker.Tests/Runner/RetryPromptTests.cs`:
|
||||
|
||||
```csharp
|
||||
using ClaudeDo.Worker.Runner;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Runner;
|
||||
|
||||
public class RetryPromptTests
|
||||
{
|
||||
[Fact]
|
||||
public void Generic_no_result_error_is_not_appended()
|
||||
{
|
||||
var prompt = TaskRunner.BuildRetryPrompt($"{ClaudeProcess.NoResultPrefix} 1 and no result.");
|
||||
Assert.DoesNotContain("Captured error", prompt);
|
||||
Assert.Contains("did not complete", prompt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Real_error_is_appended()
|
||||
{
|
||||
var prompt = TaskRunner.BuildRetryPrompt("error CS1002: ; expected");
|
||||
Assert.Contains("Captured error", prompt);
|
||||
Assert.Contains("CS1002", prompt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_error_yields_bare_prompt()
|
||||
{
|
||||
var prompt = TaskRunner.BuildRetryPrompt(null);
|
||||
Assert.DoesNotContain("Captured error", prompt);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter RetryPromptTests`
|
||||
Expected: FAIL — `BuildRetryPrompt` / `NoResultPrefix` don't exist.
|
||||
|
||||
- [ ] **Step 3: Expose the no-result prefix in ClaudeProcess**
|
||||
|
||||
In `src/ClaudeDo.Worker/Runner/ClaudeProcess.cs`, add the const near the top of the class and use it in the error fallback. Replace:
|
||||
|
||||
```csharp
|
||||
var error = lastStderr.Length > 0
|
||||
? lastStderr.ToString().Trim()
|
||||
: $"Claude exited with code {exitCode} and no result.";
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```csharp
|
||||
var error = lastStderr.Length > 0
|
||||
? lastStderr.ToString().Trim()
|
||||
: $"{NoResultPrefix} {exitCode} and no result.";
|
||||
```
|
||||
|
||||
and add inside the class (e.g. just below the fields):
|
||||
|
||||
```csharp
|
||||
public const string NoResultPrefix = "Claude exited with code";
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Add BuildRetryPrompt to TaskRunner and use it**
|
||||
|
||||
In `src/ClaudeDo.Worker/Runner/TaskRunner.cs`, add this static method (next to `MergeInstructions`):
|
||||
|
||||
```csharp
|
||||
public static string BuildRetryPrompt(string? capturedError)
|
||||
{
|
||||
var basePrompt = PromptFiles.ReadOrDefault(PromptKind.Retry);
|
||||
var isReal = !string.IsNullOrWhiteSpace(capturedError)
|
||||
&& !capturedError!.StartsWith(ClaudeProcess.NoResultPrefix, StringComparison.Ordinal);
|
||||
return isReal
|
||||
? $"{basePrompt}\n\nCaptured error from the failed run:\n\n{capturedError!.Trim()}"
|
||||
: basePrompt;
|
||||
}
|
||||
```
|
||||
|
||||
Then replace the inline retry prompt at ~L107:
|
||||
|
||||
```csharp
|
||||
var retryPrompt = $"The previous attempt failed with:\n\n{result.ErrorMarkdown}\n\nTry again and fix the issues.";
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```csharp
|
||||
var retryPrompt = BuildRetryPrompt(result.ErrorMarkdown);
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run tests to verify they pass**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter RetryPromptTests`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Runner/ClaudeProcess.cs src/ClaudeDo.Worker/Runner/TaskRunner.cs tests/ClaudeDo.Worker.Tests/Runner/RetryPromptTests.cs
|
||||
git commit -m "feat(prompts): retry prompt from file, append only real captured errors"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: PlanningSessionManager reads planning prompts from files
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs` (`BuildSystemPrompt` ~L366, `BuildInitialPrompt` ~L392)
|
||||
|
||||
- [ ] **Step 1: Replace BuildSystemPrompt body**
|
||||
|
||||
Replace the whole method body of `BuildSystemPrompt()` with:
|
||||
|
||||
```csharp
|
||||
private static string BuildSystemPrompt() => PromptFiles.ReadOrDefault(PromptKind.Planning);
|
||||
```
|
||||
|
||||
(Delete the inline fallback string literal that followed.)
|
||||
|
||||
- [ ] **Step 2: Replace BuildInitialPrompt body**
|
||||
|
||||
Replace the whole method body of `BuildInitialPrompt(TaskEntity task)` with:
|
||||
|
||||
```csharp
|
||||
private static string BuildInitialPrompt(TaskEntity task) =>
|
||||
PromptFiles.Render(PromptKind.PlanningInitial, new Dictionary<string, string>
|
||||
{
|
||||
["title"] = task.Title,
|
||||
["description"] = task.Description ?? "",
|
||||
});
|
||||
```
|
||||
|
||||
Ensure `using ClaudeDo.Data;` is present (it is — `PromptFiles` lived there already via `ReadOrNull`).
|
||||
|
||||
- [ ] **Step 3: Build to verify it compiles**
|
||||
|
||||
Run: `dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs
|
||||
git commit -m "refactor(prompts): planning prompts read from editable files"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: DailyPrepPrompt reads from file
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Prime/DailyPrepPrompt.cs`
|
||||
- Modify: `tests/ClaudeDo.Worker.Tests/Prime/DailyPrepPromptTests.cs`
|
||||
|
||||
- [ ] **Step 1: Update DailyPrepPromptTests to assert the English default render**
|
||||
|
||||
Replace the `Build_prompt_contains_cap_and_date` test body with:
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public void Build_prompt_contains_cap_and_date()
|
||||
{
|
||||
var prompt = DailyPrepPrompt.BuildPrompt(maxTasks: 5, today: new DateOnly(2026, 6, 3));
|
||||
Assert.Contains("5", prompt);
|
||||
Assert.Contains("2026-06-03", prompt);
|
||||
Assert.Contains("get_daily_prep_candidates", prompt);
|
||||
Assert.Contains("set_my_day", prompt);
|
||||
Assert.Contains("preparing my workday", prompt);
|
||||
}
|
||||
```
|
||||
|
||||
(The new assertion pins the English default; the file-read path is exercised by the same default when no `daily-prep.md` exists.)
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter DailyPrepPromptTests`
|
||||
Expected: FAIL — current German prompt has no "preparing my workday".
|
||||
|
||||
- [ ] **Step 3: Rewrite BuildPrompt to read the file**
|
||||
|
||||
In `src/ClaudeDo.Worker/Prime/DailyPrepPrompt.cs`, replace the `BuildPrompt` method with:
|
||||
|
||||
```csharp
|
||||
public static string BuildPrompt(int maxTasks, DateOnly today) =>
|
||||
ClaudeDo.Data.PromptFiles.Render(
|
||||
ClaudeDo.Data.PromptKind.DailyPrep,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["date"] = today.ToString("yyyy-MM-dd"),
|
||||
["maxTasks"] = maxTasks.ToString(),
|
||||
});
|
||||
```
|
||||
|
||||
Leave `BuildArgs`, `LogPath`, and the tool-name consts unchanged.
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter DailyPrepPromptTests`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Prime/DailyPrepPrompt.cs tests/ClaudeDo.Worker.Tests/Prime/DailyPrepPromptTests.cs
|
||||
git commit -m "feat(prompts): daily-prep prompt from file, English default"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: WeekReportPromptBuilder reads instructions from file
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Report/WeekReportPromptBuilder.cs`
|
||||
- Check: `tests/ClaudeDo.Worker.Tests/Report/WeekReportPromptBuilderTests.cs`
|
||||
|
||||
- [ ] **Step 1: Replace the inline Instructions with a file read**
|
||||
|
||||
In `WeekReportPromptBuilder.Build`, replace:
|
||||
|
||||
```csharp
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, Instructions,
|
||||
start.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture),
|
||||
end.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture)));
|
||||
sb.AppendLine();
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```csharp
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(ClaudeDo.Data.PromptFiles.Render(
|
||||
ClaudeDo.Data.PromptKind.WeeklyReport,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["start"] = start.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture),
|
||||
["end"] = end.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture),
|
||||
}));
|
||||
sb.AppendLine();
|
||||
```
|
||||
|
||||
Then delete the now-unused `private const string Instructions = ...` block. (The `{Wochentag}`/`{dd.MM.yyyy}` literals inside the default survive because `RenderTemplate` only replaces `{start}`/`{end}`.)
|
||||
|
||||
- [ ] **Step 2: Verify the existing builder test still passes**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter WeekReportPromptBuilderTests`
|
||||
Expected: PASS. If a test asserted exact old wording, update it to assert the date appears and that activity/notes sections render (the new default keeps German output rules).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Report/WeekReportPromptBuilder.cs tests/ClaudeDo.Worker.Tests/Report/WeekReportPromptBuilderTests.cs
|
||||
git commit -m "feat(prompts): weekly-report instructions from file, point at data sections"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: StreamAnalyzer collects roadblock markers
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/StreamAnalyzer.cs`
|
||||
- Test: `tests/ClaudeDo.Worker.Tests/Runner/StreamAnalyzerTests.cs`
|
||||
|
||||
- [ ] **Step 1: Write failing tests**
|
||||
|
||||
Append to `StreamAnalyzerTests`:
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public void Collects_Blocked_Markers_From_Assistant_Text()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"assistant","message":{"content":[{"type":"text","text":"working\nCLAUDEDO_BLOCKED: missing API key\nmoving on"}]}}""");
|
||||
analyzer.ProcessLine("""{"type":"assistant","message":{"content":[{"type":"text","text":"CLAUDEDO_BLOCKED: cannot reach db"}]}}""");
|
||||
analyzer.ProcessLine("""{"type":"result","result":"done","session_id":"s1"}""");
|
||||
var result = analyzer.GetResult();
|
||||
Assert.Equal(2, result.Blocks.Count);
|
||||
Assert.Equal("missing API key", result.Blocks[0]);
|
||||
Assert.Equal("cannot reach db", result.Blocks[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Strips_Blocked_Markers_From_Result_Text()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"result","result":"All set.\nCLAUDEDO_BLOCKED: no creds\nDone.","session_id":"s1"}""");
|
||||
var result = analyzer.GetResult();
|
||||
Assert.DoesNotContain("CLAUDEDO_BLOCKED", result.ResultMarkdown);
|
||||
Assert.Single(result.Blocks);
|
||||
Assert.Equal("no creds", result.Blocks[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void No_Markers_Means_Empty_Blocks()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"result","result":"done","session_id":"s1"}""");
|
||||
Assert.Empty(analyzer.GetResult().Blocks);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter StreamAnalyzerTests`
|
||||
Expected: FAIL — `Blocks` doesn't exist.
|
||||
|
||||
- [ ] **Step 3: Implement marker collection in StreamAnalyzer**
|
||||
|
||||
In `src/ClaudeDo.Worker/Runner/StreamAnalyzer.cs`:
|
||||
|
||||
Add to `StreamResult`:
|
||||
|
||||
```csharp
|
||||
public IReadOnlyList<string> Blocks { get; set; } = Array.Empty<string>();
|
||||
```
|
||||
|
||||
Add a field and a constant to `StreamAnalyzer`:
|
||||
|
||||
```csharp
|
||||
private readonly List<string> _blocks = new();
|
||||
private const string BlockedPrefix = "CLAUDEDO_BLOCKED:";
|
||||
```
|
||||
|
||||
In the `case "result":` branch, after `_resultMarkdown` is assigned, scan and strip:
|
||||
|
||||
```csharp
|
||||
if (root.TryGetProperty("result", out var resultProp))
|
||||
_resultMarkdown = StripAndCollect(resultProp.GetString());
|
||||
```
|
||||
|
||||
In the `case "assistant":` branch, collect from text content (keep `_turnCount++`):
|
||||
|
||||
```csharp
|
||||
case "assistant":
|
||||
_turnCount++;
|
||||
CollectFromAssistant(root);
|
||||
break;
|
||||
```
|
||||
|
||||
Add these helpers to the class:
|
||||
|
||||
```csharp
|
||||
private void CollectFromAssistant(JsonElement root)
|
||||
{
|
||||
if (!root.TryGetProperty("message", out var msg)) return;
|
||||
if (!msg.TryGetProperty("content", out var content) || content.ValueKind != JsonValueKind.Array) return;
|
||||
foreach (var block in content.EnumerateArray())
|
||||
if (block.TryGetProperty("type", out var t) && t.GetString() == "text"
|
||||
&& block.TryGetProperty("text", out var txt))
|
||||
ScanForBlocks(txt.GetString());
|
||||
}
|
||||
|
||||
private void ScanForBlocks(string? text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return;
|
||||
foreach (var line in text.Split('\n'))
|
||||
{
|
||||
var trimmed = line.Trim();
|
||||
if (trimmed.StartsWith(BlockedPrefix, StringComparison.Ordinal))
|
||||
_blocks.Add(trimmed[BlockedPrefix.Length..].Trim());
|
||||
}
|
||||
}
|
||||
|
||||
private string? StripAndCollect(string? text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
ScanForBlocks(text);
|
||||
var kept = text.Split('\n')
|
||||
.Where(l => !l.Trim().StartsWith(BlockedPrefix, StringComparison.Ordinal));
|
||||
return string.Join('\n', kept).Trim();
|
||||
}
|
||||
```
|
||||
|
||||
Add `Blocks = _blocks` to the `GetResult()` initializer:
|
||||
|
||||
```csharp
|
||||
public StreamResult GetResult() => new()
|
||||
{
|
||||
ResultMarkdown = FallbackResult(),
|
||||
StructuredOutputJson = _structuredOutputJson,
|
||||
SessionId = _sessionId,
|
||||
TurnCount = _turnCount,
|
||||
TokensIn = _tokensIn,
|
||||
TokensOut = _tokensOut,
|
||||
ApiRetryCount = _apiRetryCount,
|
||||
Blocks = _blocks,
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter StreamAnalyzerTests`
|
||||
Expected: PASS (all old + 3 new).
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Runner/StreamAnalyzer.cs tests/ClaudeDo.Worker.Tests/Runner/StreamAnalyzerTests.cs
|
||||
git commit -m "feat(roadblock): collect and strip CLAUDEDO_BLOCKED markers in StreamAnalyzer"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: RunResult + ClaudeProcess carry Blocks
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/RunResult.cs`
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/ClaudeProcess.cs:89-113`
|
||||
|
||||
- [ ] **Step 1: Add Blocks to RunResult**
|
||||
|
||||
In `src/ClaudeDo.Worker/Runner/RunResult.cs`, add inside the class:
|
||||
|
||||
```csharp
|
||||
public IReadOnlyList<string> Blocks { get; init; } = Array.Empty<string>();
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Populate Blocks in both RunResult returns**
|
||||
|
||||
In `ClaudeProcess.RunAsync`, add `Blocks = streamResult.Blocks,` to **both** the success `RunResult { ... }` (after `TokensOut`) and the error `RunResult { ... }` initializer.
|
||||
|
||||
- [ ] **Step 3: Build to verify it compiles**
|
||||
|
||||
Run: `dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Runner/RunResult.cs src/ClaudeDo.Worker/Runner/ClaudeProcess.cs
|
||||
git commit -m "feat(roadblock): carry blocks through RunResult"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: Fold roadblocks into the review result
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Worker/Runner/TaskRunner.cs` (`HandleSuccess` ~L319-352; add `ComposeReviewResult`)
|
||||
- Test: `tests/ClaudeDo.Worker.Tests/Runner/ReviewResultTests.cs` (create)
|
||||
|
||||
- [ ] **Step 1: Write failing tests for the compose helper**
|
||||
|
||||
Create `tests/ClaudeDo.Worker.Tests/Runner/ReviewResultTests.cs`:
|
||||
|
||||
```csharp
|
||||
using ClaudeDo.Worker.Runner;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Runner;
|
||||
|
||||
public class ReviewResultTests
|
||||
{
|
||||
[Fact]
|
||||
public void No_blocks_returns_result_unchanged()
|
||||
{
|
||||
Assert.Equal("done", TaskRunner.ComposeReviewResult("done", Array.Empty<string>()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Blocks_are_appended_as_a_section()
|
||||
{
|
||||
var outp = TaskRunner.ComposeReviewResult("done", new[] { "no creds", "db down" });
|
||||
Assert.Contains("⚠ Roadblocks", outp);
|
||||
Assert.Contains("- no creds", outp);
|
||||
Assert.Contains("- db down", outp);
|
||||
Assert.Contains("done", outp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_result_with_blocks_still_lists_them()
|
||||
{
|
||||
var outp = TaskRunner.ComposeReviewResult(null, new[] { "x" });
|
||||
Assert.Contains("⚠ Roadblocks", outp);
|
||||
Assert.Contains("- x", outp);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter ReviewResultTests`
|
||||
Expected: FAIL — `ComposeReviewResult` doesn't exist.
|
||||
|
||||
- [ ] **Step 3: Add ComposeReviewResult and use it in HandleSuccess**
|
||||
|
||||
In `TaskRunner`, add:
|
||||
|
||||
```csharp
|
||||
public static string? ComposeReviewResult(string? result, IReadOnlyList<string> blocks)
|
||||
{
|
||||
if (blocks.Count == 0) return result;
|
||||
var section = "⚠ Roadblocks reported during the run:\n"
|
||||
+ string.Join('\n', blocks.Select(b => $"- {b}"));
|
||||
return string.IsNullOrWhiteSpace(result) ? section : $"{result}\n\n{section}";
|
||||
}
|
||||
```
|
||||
|
||||
In `HandleSuccess`, compute the composed result once and pass it to both terminal writes:
|
||||
|
||||
```csharp
|
||||
var finishedAt = DateTime.UtcNow;
|
||||
var reviewResult = ComposeReviewResult(result.ResultMarkdown, result.Blocks);
|
||||
if (task.ParentTaskId is null && task.PlanningPhase == PlanningPhase.None)
|
||||
{
|
||||
await _state.SubmitForReviewAsync(task.Id, finishedAt, reviewResult, CancellationToken.None);
|
||||
await _broadcaster.WorkerLog($"Finished \"{task.Title}\" (waiting for review)", WorkerLogLevel.Success, DateTime.UtcNow);
|
||||
await _broadcaster.TaskFinished(slot, task.Id, "waiting_for_review", finishedAt);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _state.CompleteAsync(task.Id, finishedAt, reviewResult, CancellationToken.None);
|
||||
await _broadcaster.WorkerLog($"Finished \"{task.Title}\" (done)", WorkerLogLevel.Success, DateTime.UtcNow);
|
||||
await _broadcaster.TaskFinished(slot, task.Id, "done", finishedAt);
|
||||
}
|
||||
```
|
||||
|
||||
(Make sure `using System.Linq;` is available — it is, via implicit usings.)
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run: `dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release --filter ReviewResultTests`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Worker/Runner/TaskRunner.cs tests/ClaudeDo.Worker.Tests/Runner/ReviewResultTests.cs
|
||||
git commit -m "feat(roadblock): surface reported roadblocks in the review result"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 10: Files-settings UI exposes the new prompt files
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ClaudeDo.Ui/ViewModels/Modals/Settings/FilesSettingsTabViewModel.cs`
|
||||
- Modify: the Files settings view (find with: `Grep "SystemPromptPath" src/ClaudeDo.Ui` → the `.axaml` binding to `OpenPromptCommand`)
|
||||
|
||||
- [ ] **Step 1: Replace the prompt-path properties**
|
||||
|
||||
In `FilesSettingsTabViewModel`, replace the three path properties with the new set (drop Agent, add the rest):
|
||||
|
||||
```csharp
|
||||
public string SystemPromptPath { get; } = PromptFiles.PathFor(PromptKind.System);
|
||||
public string PlanningPromptPath { get; } = PromptFiles.PathFor(PromptKind.Planning);
|
||||
public string PlanningInitialPromptPath { get; } = PromptFiles.PathFor(PromptKind.PlanningInitial);
|
||||
public string RetryPromptPath { get; } = PromptFiles.PathFor(PromptKind.Retry);
|
||||
public string DailyPrepPromptPath { get; } = PromptFiles.PathFor(PromptKind.DailyPrep);
|
||||
public string WeeklyReportPromptPath { get; } = PromptFiles.PathFor(PromptKind.WeeklyReport);
|
||||
```
|
||||
|
||||
(`OpenPromptCommand` already parses the `PromptKind` name from its parameter, so no command change is needed.)
|
||||
|
||||
- [ ] **Step 2: Update the view**
|
||||
|
||||
Open the Files settings `.axaml`. For the existing System/Planning/Agent rows: keep System, keep Planning, **remove the Agent row**, and add four rows mirroring the System row's markup — each binding its label/path to the new property and passing the matching `PromptKind` name as the `OpenPromptCommand` parameter:
|
||||
|
||||
- `Planning` (system) → "Planning system prompt", `PlanningPromptPath`, parameter `Planning`
|
||||
- `PlanningInitial` → "Planning kickoff prompt", `PlanningInitialPromptPath`, parameter `PlanningInitial`
|
||||
- `Retry` → "Retry prompt", `RetryPromptPath`, parameter `Retry`
|
||||
- `DailyPrep` → "Daily-prep prompt", `DailyPrepPromptPath`, parameter `DailyPrep`
|
||||
- `WeeklyReport` → "Weekly-report prompt", `WeeklyReportPromptPath`, parameter `WeeklyReport`
|
||||
|
||||
Use the exact same control template as the existing System row (same button + `CommandParameter` shape); only the bound property, label text, and parameter string differ.
|
||||
|
||||
- [ ] **Step 3: Build the UI project**
|
||||
|
||||
Run: `dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Visual check (manual — flag for user)**
|
||||
|
||||
Start the app, open Settings → Files tab. Confirm six "Open" prompt buttons appear (System, Planning system, Planning kickoff, Retry, Daily-prep, Weekly-report), no Agent row, and each opens/seeds the right file under `~/.todo-app/prompts/`. **This step cannot be verified by the agent — ask the user to confirm visually.**
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ClaudeDo.Ui/ViewModels/Modals/Settings/FilesSettingsTabViewModel.cs src/ClaudeDo.Ui/Views/**/*Files*.axaml
|
||||
git commit -m "feat(ui): expose all editable prompt files, drop agent prompt"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 11: Full build + test sweep
|
||||
|
||||
- [ ] **Step 1: Build worker + app**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj -c Release
|
||||
dotnet build src/ClaudeDo.App/ClaudeDo.App.csproj -c Release
|
||||
```
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 2: Run all affected test projects**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
dotnet test tests/ClaudeDo.Data.Tests/ClaudeDo.Data.Tests.csproj -c Release
|
||||
dotnet test tests/ClaudeDo.Worker.Tests/ClaudeDo.Worker.Tests.csproj -c Release
|
||||
```
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Update docs**
|
||||
|
||||
Update `docs/prompts-inventory.md` to note the externalized files and that `agent.md`/`planning.md` are retired in favor of `system.md`/`planning-system.md`. Note `CLAUDEDO_BLOCKED:` in the inventory.
|
||||
|
||||
```bash
|
||||
git add docs/prompts-inventory.md
|
||||
git commit -m "docs: refresh prompt inventory for externalized prompts + roadblock marker"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Self-review notes
|
||||
|
||||
- **Spec coverage:** system.md collapse (T2), planning prompts (T4), retry (T3), daily-prep English (T5), weekly-report + data pointer (T6), templating/`Render` (T1), roadblock detect/strip/route (T7–T9), file layout + migration via `EnsureExists`/new `PathFor` (T1), UI surface (T10). The "Out-of-scope improvements" system.md section is intentionally **deferred to the child-tasks plan** (it depends on the `SuggestImprovement` tool).
|
||||
- **Migration:** old `planning.md`/`agent.md` go inert automatically — `TaskRunner` no longer reads agent (T2), planning now reads `planning-system.md` (T1 PathFor). No code deletes the old files; harmless.
|
||||
- **Determinism:** content tests target `DefaultFor`/`RenderTemplate` (pure, no disk). Consumers fall back to the same default when no user file exists.
|
||||
Reference in New Issue
Block a user