diff --git a/src/core/agents/types.ts b/src/core/agents/types.ts index 602b169..38d2c26 100644 --- a/src/core/agents/types.ts +++ b/src/core/agents/types.ts @@ -7,6 +7,7 @@ export interface AgentOutput { export const AGENT_OUTPUT_SCHEMA = { type: "object", + additionalProperties: false, properties: { success: { type: "boolean" }, summary: { type: "string" }, diff --git a/src/core/run.test.ts b/src/core/run.test.ts index 041a294..961495a 100644 --- a/src/core/run.test.ts +++ b/src/core/run.test.ts @@ -95,6 +95,7 @@ describe("setupRun", () => { expect(schemaCall).toBeDefined(); const schema = JSON.parse(schemaCall![1] as string); expect(schema.type).toBe("object"); + expect(schema.additionalProperties).toBe(false); expect(schema.required).toContain("success"); expect(schema.required).toContain("summary"); expect(schema.required).toContain("key_changes_made"); @@ -147,6 +148,26 @@ describe("resumeRun", () => { vi.clearAllMocks(); }); + it("refreshes output-schema.json to the current JSON schema", () => { + mockExistsSync.mockImplementation( + (path) => path === "/project/.gnhf/runs/run-abc", + ); + + resumeRun("run-abc", "/project"); + + expect(mockWriteFileSync).toHaveBeenCalledWith( + "/project/.gnhf/runs/run-abc/output-schema.json", + expect.any(String), + "utf-8", + ); + const schemaCall = mockWriteFileSync.mock.calls.find( + (call) => + typeof call[0] === "string" && call[0].endsWith("output-schema.json"), + ); + const schema = JSON.parse(schemaCall![1] as string); + expect(schema.additionalProperties).toBe(false); + }); + it("reads the stored base commit when present", () => { mockExistsSync.mockImplementation( (path) => diff --git a/src/core/run.ts b/src/core/run.ts index d780745..37d9afa 100644 --- a/src/core/run.ts +++ b/src/core/run.ts @@ -21,6 +21,14 @@ export interface RunInfo { baseCommitPath: string; } +function writeSchemaFile(schemaPath: string): void { + writeFileSync( + schemaPath, + JSON.stringify(AGENT_OUTPUT_SCHEMA, null, 2), + "utf-8", + ); +} + function ensureRunMetadataIgnored(cwd: string): void { const excludePath = execFileSync( "git", @@ -67,11 +75,7 @@ export function setupRun( ); const schemaPath = join(runDir, "output-schema.json"); - writeFileSync( - schemaPath, - JSON.stringify(AGENT_OUTPUT_SCHEMA, null, 2), - "utf-8", - ); + writeSchemaFile(schemaPath); const baseCommitPath = join(runDir, "base-commit"); const hasStoredBaseCommit = existsSync(baseCommitPath); @@ -102,6 +106,7 @@ export function resumeRun(runId: string, cwd: string): RunInfo { const promptPath = join(runDir, "prompt.md"); const notesPath = join(runDir, "notes.md"); const schemaPath = join(runDir, "output-schema.json"); + writeSchemaFile(schemaPath); const baseCommitPath = join(runDir, "base-commit"); const baseCommit = existsSync(baseCommitPath) ? readFileSync(baseCommitPath, "utf-8").trim()