Fix structured strategy schema

This commit is contained in:
centra 2026-04-04 16:08:27 +09:00
parent a909a99310
commit 0b5f07a7d2
Signed by: centra
GPG key ID: 0C09689D20B25ACA
5 changed files with 34 additions and 9 deletions

View file

@ -12,13 +12,14 @@ The CLI persists state in SQLite plus run artifacts under `.agent-r/`, so a long
```bash
npm install
npm run build
npm run dev -- run "Build a plugin system" --repo /path/to/repo
agent-r run "Build a plugin system" --repo /path/to/repo
agent-r resume <run-id> --repo /path/to/repo
agent-r status <run-id> --repo /path/to/repo
agent-r inspect <run-id> --repo /path/to/repo
agent-r logs <run-id> --repo /path/to/repo --agent strategy
npm run build
./dist/index.js run "Build a plugin system" --repo /path/to/repo
./dist/index.js resume <run-id> --repo /path/to/repo
./dist/index.js status <run-id> --repo /path/to/repo
./dist/index.js inspect <run-id> --repo /path/to/repo
./dist/index.js logs <run-id> --repo /path/to/repo --agent strategy
```
## Config

View file

@ -126,6 +126,7 @@ Decision policy:
- Use \`decision: "continue"\` when there is meaningful work left. Provide the complete next backlog.
- Use \`decision: "done"\` only when no substantial work remains.
- Use \`decision: "blocked"\` only for external blockers or unresolved contradictions.
- Always include \`blockedReason\`. Use \`null\` unless \`decision\` is \`"blocked"\`.
- Keep the backlog concise. Prefer a few substantive tasks over many trivial tasks.
`.trim();
}

View file

@ -23,7 +23,7 @@ export const taskDraftSchema = {
export const strategyPlanSchema = {
type: "object",
additionalProperties: false,
required: ["decision", "summary", "rationale", "goalProgress", "risks", "tasks"],
required: ["decision", "summary", "rationale", "goalProgress", "risks", "tasks", "blockedReason"],
properties: {
decision: {
type: "string",
@ -40,7 +40,9 @@ export const strategyPlanSchema = {
type: "array",
items: taskDraftSchema,
},
blockedReason: { type: "string" },
blockedReason: {
type: ["string", "null"],
},
},
} as const;

View file

@ -142,7 +142,7 @@ export interface StrategyPlanOutput {
goalProgress: string;
risks: string[];
tasks: TaskDraft[];
blockedReason?: string;
blockedReason: string | null;
}
export interface StrategySelfCheckOutput {

View file

@ -5,6 +5,7 @@ import { describe, expect, test } from "vitest";
import { ArtifactManager } from "../src/artifacts.js";
import { loadConfig } from "../src/config.js";
import { AgentROrchestrator } from "../src/orchestrator.js";
import { strategyPlanSchema } from "../src/schema-catalog.js";
import { RunStore } from "../src/store.js";
import type { AgentInvocation, AgentInvocationResult, RawAgentRunner } from "../src/types.js";
@ -63,6 +64,7 @@ describe("AgentROrchestrator", () => {
allowedPaths: ["src", "package.json"],
},
],
blockedReason: null,
}),
JSON.stringify({
taskId: "",
@ -81,6 +83,7 @@ describe("AgentROrchestrator", () => {
goalProgress: "The vertical slice exists.",
risks: [],
tasks: [],
blockedReason: null,
}),
JSON.stringify({
readyForIndependentCheck: true,
@ -135,4 +138,22 @@ describe("AgentROrchestrator", () => {
expect(runner.prompts[0]).toContain("You may inspect the repository directly");
expect(runner.prompts[0]).toContain("Do not edit files");
});
test("strategy plan schema requires blockedReason and accepts null", () => {
const valid = JSON.parse(
JSON.stringify({
decision: "continue",
summary: "Keep going.",
rationale: "Work remains.",
goalProgress: "Partial.",
risks: [],
tasks: [],
blockedReason: null,
}),
) as Record<string, unknown>;
expect(strategyPlanSchema.required).toContain("blockedReason");
expect(strategyPlanSchema.properties.blockedReason.type).toEqual(["string", "null"]);
expect(valid.blockedReason).toBeNull();
});
});