fix: address PR review issues for auto-mode refactor

- agent-executor: move executeQuery into try block for proper heartbeat cleanup,
  re-parse tasks when edited plan is approved
- auto-loop-coordinator: handle feature execution failures with proper logging
  and failure tracking, support backward-compatible method signatures
- facade: delegate getActiveAutoLoopProjects/Worktrees to coordinator,
  always create own AutoLoopCoordinator (not shared), pass projectPath
  to approval methods and branchName to failure tracking
- global-service: document shared autoLoopCoordinator is for monitoring only
- execution-types: fix ExecuteFeatureFn type to match implementation
- feature-state-manager: use readJsonWithRecovery for loadFeature
- pipeline-orchestrator: add defensive null check and try/catch for
  merge response parsing
- plan-approval-service: use project-scoped keys to prevent cross-project
  collisions, maintain backward compatibility for featureId-only lookups

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Shirone
2026-01-31 13:59:24 +01:00
parent bf82f92132
commit 5a5c56a4cf
9 changed files with 214 additions and 91 deletions

View File

@@ -71,20 +71,21 @@ describe('FeatureStateManager', () => {
describe('loadFeature', () => {
it('should load feature from disk', async () => {
(secureFs.readFile as Mock).mockResolvedValue(JSON.stringify(mockFeature));
(readJsonWithRecovery as Mock).mockResolvedValue({ data: mockFeature, recovered: false });
const feature = await manager.loadFeature('/project', 'feature-123');
expect(feature).toEqual(mockFeature);
expect(getFeatureDir).toHaveBeenCalledWith('/project', 'feature-123');
expect(secureFs.readFile).toHaveBeenCalledWith(
expect(readJsonWithRecovery).toHaveBeenCalledWith(
'/project/.automaker/features/feature-123/feature.json',
'utf-8'
null,
expect.objectContaining({ autoRestore: true })
);
});
it('should return null if feature does not exist', async () => {
(secureFs.readFile as Mock).mockRejectedValue(new Error('ENOENT'));
(readJsonWithRecovery as Mock).mockRejectedValue(new Error('ENOENT'));
const feature = await manager.loadFeature('/project', 'non-existent');
@@ -92,7 +93,8 @@ describe('FeatureStateManager', () => {
});
it('should return null if feature JSON is invalid', async () => {
(secureFs.readFile as Mock).mockResolvedValue('not valid json');
// readJsonWithRecovery returns null as the default value when JSON is invalid
(readJsonWithRecovery as Mock).mockResolvedValue({ data: null, recovered: false });
const feature = await manager.loadFeature('/project', 'feature-123');