Skip to content

[Bug] Spec Regeneration Produces Incorrect Output and Creates Duplicate Files #149

@cabana8471-arch

Description

@cabana8471-arch

Bug: Spec Regeneration Produces Incorrect Output and Creates Duplicate Files

Summary

When using the "Regenerate" button in the Spec Editor, two issues occur:

  1. The .automaker/app_spec.txt file contains only Claude's conversational summary (~37 lines) instead of the comprehensive XML specification (~593 lines)
  2. A second file app_spec.txt is created in the project root (not in .automaker/) with the correct XML content

Steps to Reproduce

  1. Open a project that already has an app_spec.txt file
  2. Navigate to the Spec Editor
  3. Click the "Regenerate" button in the header
  4. Fill in the project definition and click "Regenerate Spec"
  5. Wait for generation to complete
  6. Check both locations:
    • .automaker/app_spec.txt - Contains summary (wrong)
    • {project}/app_spec.txt - Contains XML (correct but wrong location)

Expected Behavior

  • Only ONE file should be created: .automaker/app_spec.txt
  • That file should contain the comprehensive XML specification

Actual Behavior

  • .automaker/app_spec.txt contains Claude's conversational summary:
    Perfect! I have successfully created a comprehensive project specification...
    
  • {project}/app_spec.txt (root) contains the actual XML specification

Root Cause Analysis

Bug #1: XML Content Overwritten by Summary Message

File: apps/server/src/routes/app-spec/generate-spec.ts
Lines: 164-176

The stream processing code correctly accumulates the XML specification from Claude's text blocks into responseText. However, when the SDK emits a final result message with subtype: "success", the code overwrites the accumulated XML:

// Lines 164-176 - BUG #1
} else if (msg.type === "result" && (msg as any).subtype === "success") {
  // Only use result if it has content, otherwise keep accumulated text
  if ((msg as any).result && (msg as any).result.length > 0) {
    logger.info("Using result value as responseText");
    responseText = (msg as any).result;  // <-- OVERWRITES the good XML!
  }
}

What happens:

  1. Claude streams the XML spec as multiple text blocks (accumulated correctly)
  2. At the end, SDK emits a result message with Claude's conversational summary
  3. Line 173 overwrites the XML with this summary
  4. The summary is saved to .automaker/app_spec.txt (line 220)

Bug #2: SDK Permission Mode Creates File in Project Root

File: apps/server/src/lib/sdk-options.ts
Lines: 122-126

function getBaseOptions(): Partial<Options> {
  return {
    permissionMode: "acceptEdits",  // <-- BUG #2: Auto-accepts file writes!
  };
}

This permissionMode: "acceptEdits" is inherited by createSpecGenerationOptions(), causing the Claude Agent SDK to auto-accept file write operations.

Why this creates a file in project root:

  1. Spec generation calls SDK with cwd: projectPath (the project root)
  2. permissionMode: "acceptEdits" auto-approves file operations
  3. Even though allowedTools only includes ["Read", "Glob", "Grep"], the SDK may execute write operations based on Claude's response content
  4. Claude's response mentions saving to app_spec.txt (without .automaker/ path)
  5. SDK writes the file to cwd (project root) based on Claude's implicit instruction

The contradiction: Spec generation should be read-only (TOOL_PRESETS.specGeneration is ["Read", "Glob", "Grep"]), but permissionMode: "acceptEdits" overrides this restriction.


Suggested Fixes

Fix #1: Don't Overwrite Accumulated XML Content

In apps/server/src/routes/app-spec/generate-spec.ts, replace lines 164-176:

Option A: Validate XML Before Using Result (Recommended)

} else if (msg.type === "result" && (msg as any).subtype === "success") {
  const result = (msg as any).result;
  // Only use result if responseText is empty AND result contains valid XML
  if ((!responseText || responseText.trim().length === 0) &&
      result && result.trim().startsWith("<project_specification>")) {
    responseText = result;
  }
  // Otherwise keep the accumulated responseText which has the XML
}

Option B: Never Overwrite Accumulated Content

} else if (msg.type === "result" && (msg as any).subtype === "success") {
  logger.info("Received success result");
  // Don't overwrite - responseText already has the accumulated XML
  logger.info(`Final responseText length: ${responseText.length}`);
}

Fix #2: Override Permission Mode for Spec Generation

In apps/server/src/lib/sdk-options.ts, modify createSpecGenerationOptions():

export function createSpecGenerationOptions(
  config: CreateSdkOptionsConfig
): Options {
  return {
    ...getBaseOptions(),
    model: getModelForUseCase("spec", config.model),
    maxTurns: MAX_TURNS.maximum,
    cwd: config.cwd,
    allowedTools: [...TOOL_PRESETS.specGeneration],
    permissionMode: "default",  // <-- FIX: Override to prevent auto-writes
    ...(config.systemPrompt && { systemPrompt: config.systemPrompt }),
    ...(config.abortController && { abortController: config.abortController }),
  };
}

Or alternatively, use permissionMode: "bypassPermissions" if the SDK supports it for read-only operations.


Files to Modify

File Bug Change
apps/server/src/routes/app-spec/generate-spec.ts #1 Fix lines 164-176 to preserve accumulated XML
apps/server/src/lib/sdk-options.ts #2 Override permissionMode in createSpecGenerationOptions()

Testing Checklist

  • Test "Create" flow for new projects
  • Test "Regenerate" flow for existing projects
  • Verify only ONE file is created: .automaker/app_spec.txt
  • Verify the file contains valid XML starting with <project_specification>
  • Verify NO file is created in project root
  • Check server logs to confirm correct content is being saved

Priority

High - This bug makes the Regenerate feature non-functional and creates unexpected files in user projects.

Labels

bug, spec-editor, high-priority, claude-sdk, file-permissions

Metadata

Metadata

Assignees

Labels

BugSomething isn't workingNew IssueA label for reporting issues that have not been reported before after searching for the issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions