feat(runner): point autonomous runs at the findings index
This commit is contained in:
@@ -123,7 +123,7 @@ public sealed class TaskRunner
|
|||||||
var wtCtx = prep.WtCtx;
|
var wtCtx = prep.WtCtx;
|
||||||
var runDir = prep.RunDir!;
|
var runDir = prep.RunDir!;
|
||||||
|
|
||||||
var resolvedConfig = await ResolveConfigAsync(task, listConfig, null, ct);
|
var resolvedConfig = await ResolveConfigAsync(task, list, listConfig, null, ct);
|
||||||
|
|
||||||
// Every run gets a per-run MCP identity so the agent can ask the user a
|
// Every run gets a per-run MCP identity so the agent can ask the user a
|
||||||
// mid-run question via AskUser. Improvement-eligible (standalone top-level)
|
// mid-run question via AskUser. Improvement-eligible (standalone top-level)
|
||||||
@@ -237,7 +237,7 @@ public sealed class TaskRunner
|
|||||||
worktree = await wtRepo.GetByTaskIdAsync(taskId, ct);
|
worktree = await wtRepo.GetByTaskIdAsync(taskId, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolvedConfig = await ResolveConfigAsync(task, listConfig, lastRun.SessionId, ct);
|
var resolvedConfig = await ResolveConfigAsync(task, list, listConfig, lastRun.SessionId, ct);
|
||||||
|
|
||||||
// Determine run directory from existing worktree or sandbox.
|
// Determine run directory from existing worktree or sandbox.
|
||||||
string runDir;
|
string runDir;
|
||||||
@@ -556,7 +556,7 @@ public sealed class TaskRunner
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ClaudeRunConfig> ResolveConfigAsync(
|
private async Task<ClaudeRunConfig> ResolveConfigAsync(
|
||||||
TaskEntity task, ListConfigEntity? listConfig, string? resumeSessionId, CancellationToken ct)
|
TaskEntity task, ListEntity list, ListConfigEntity? listConfig, string? resumeSessionId, CancellationToken ct)
|
||||||
{
|
{
|
||||||
AppSettingsEntity global;
|
AppSettingsEntity global;
|
||||||
using (var ctx = _dbFactory.CreateDbContext())
|
using (var ctx = _dbFactory.CreateDbContext())
|
||||||
@@ -572,8 +572,11 @@ public sealed class TaskRunner
|
|||||||
var isImprovementChild = task.ParentTaskId is not null && task.CreatedBy == task.ParentTaskId;
|
var isImprovementChild = task.ParentTaskId is not null && task.CreatedBy == task.ParentTaskId;
|
||||||
var improvementPrompt = isImprovementChild ? PromptFiles.ReadOrDefault(PromptKind.ImprovementChild) : null;
|
var improvementPrompt = isImprovementChild ? PromptFiles.ReadOrDefault(PromptKind.ImprovementChild) : null;
|
||||||
|
|
||||||
|
var findingsPointer = BuildFindingsPointer(list.WorkingDir);
|
||||||
|
|
||||||
var instructions = MergeInstructions(
|
var instructions = MergeInstructions(
|
||||||
systemFile, improvementPrompt, global.DefaultClaudeInstructions, listConfig?.SystemPrompt, task.SystemPrompt);
|
systemFile, improvementPrompt, global.DefaultClaudeInstructions, findingsPointer,
|
||||||
|
listConfig?.SystemPrompt, task.SystemPrompt);
|
||||||
|
|
||||||
var requestedSkills = UnionSkillNames(task.SessionSkills, listConfig?.SessionSkills, global.SessionSkills);
|
var requestedSkills = UnionSkillNames(task.SessionSkills, listConfig?.SessionSkills, global.SessionSkills);
|
||||||
var skillNames = await FilterToInstalledSkillsAsync(task.Id, requestedSkills, ct);
|
var skillNames = await FilterToInstalledSkillsAsync(task.Id, requestedSkills, ct);
|
||||||
@@ -661,6 +664,20 @@ public sealed class TaskRunner
|
|||||||
internal static int? ResolveMaxTurns(int? taskTurns, int? listTurns, int globalDefault, int ceiling)
|
internal static int? ResolveMaxTurns(int? taskTurns, int? listTurns, int globalDefault, int ceiling)
|
||||||
=> Math.Min(taskTurns ?? listTurns ?? globalDefault, ceiling);
|
=> Math.Min(taskTurns ?? listTurns ?? globalDefault, ceiling);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Points the run at the project's findings index. Kept to one sentence on purpose: it enters the
|
||||||
|
/// prefix of every message in the run, so its cost is multiplied by the turn count.
|
||||||
|
/// </summary>
|
||||||
|
internal static string? BuildFindingsPointer(string? workingDir)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(workingDir)) return null;
|
||||||
|
var index = Path.Combine(workingDir, ".claudedo", "INDEX.md");
|
||||||
|
if (!File.Exists(index)) return null;
|
||||||
|
return $"Known traps for this project are indexed at {index}. Read that index before exploring "
|
||||||
|
+ "the code, and open only the findings that touch your task. When you hit a lasting, "
|
||||||
|
+ "non-obvious trap yourself, record it with the save_finding tool.";
|
||||||
|
}
|
||||||
|
|
||||||
public static string MergeInstructions(params string?[] parts)
|
public static string MergeInstructions(params string?[] parts)
|
||||||
{
|
{
|
||||||
var trimmed = parts
|
var trimmed = parts
|
||||||
|
|||||||
@@ -244,4 +244,34 @@ public sealed class MergeInstructionsTests
|
|||||||
var s = ClaudeDo.Worker.Runner.TaskRunner.MergeInstructions(" ", "L", "\t\n");
|
var s = ClaudeDo.Worker.Runner.TaskRunner.MergeInstructions(" ", "L", "\t\n");
|
||||||
Assert.Equal("L", s);
|
Assert.Equal("L", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildFindingsPointer_ReturnsNullWhenNoIndexExists()
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(Path.GetTempPath(), "cdo-ptr-" + Guid.NewGuid().ToString("N"));
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Null(ClaudeDo.Worker.Runner.TaskRunner.BuildFindingsPointer(dir));
|
||||||
|
Assert.Null(ClaudeDo.Worker.Runner.TaskRunner.BuildFindingsPointer(null));
|
||||||
|
Assert.Null(ClaudeDo.Worker.Runner.TaskRunner.BuildFindingsPointer(" "));
|
||||||
|
}
|
||||||
|
finally { Directory.Delete(dir, recursive: true); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildFindingsPointer_MentionsTheIndexPathWhenItExists()
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(Path.GetTempPath(), "cdo-ptr-" + Guid.NewGuid().ToString("N"));
|
||||||
|
Directory.CreateDirectory(Path.Combine(dir, ".claudedo"));
|
||||||
|
File.WriteAllText(Path.Combine(dir, ".claudedo", "INDEX.md"), "# Findings\n");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pointer = ClaudeDo.Worker.Runner.TaskRunner.BuildFindingsPointer(dir);
|
||||||
|
Assert.NotNull(pointer);
|
||||||
|
Assert.Contains("INDEX.md", pointer);
|
||||||
|
Assert.Contains("save_finding", pointer);
|
||||||
|
}
|
||||||
|
finally { Directory.Delete(dir, recursive: true); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user