worktreeのバグフィックス
This commit is contained in:
parent
73c2d6b381
commit
ee0d846c5b
@ -77,7 +77,7 @@ describe('instruction-builder', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('report_dir replacement', () => {
|
describe('report_dir replacement', () => {
|
||||||
it('should replace .takt/reports/{report_dir} with full absolute path', () => {
|
it('should replace {report_dir} in paths keeping them relative', () => {
|
||||||
const step = createMinimalStep(
|
const step = createMinimalStep(
|
||||||
'- Report Directory: .takt/reports/{report_dir}/'
|
'- Report Directory: .takt/reports/{report_dir}/'
|
||||||
);
|
);
|
||||||
@ -89,31 +89,31 @@ describe('instruction-builder', () => {
|
|||||||
const result = buildInstruction(step, context);
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
expect(result).toContain(
|
expect(result).toContain(
|
||||||
'- Report Directory: /project/.takt/reports/20260128-test-report/'
|
'- Report Directory: .takt/reports/20260128-test-report/'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use projectCwd for report path when cwd is a worktree', () => {
|
it('should not leak projectCwd absolute path into instruction', () => {
|
||||||
const step = createMinimalStep(
|
const step = createMinimalStep(
|
||||||
'- Report: .takt/reports/{report_dir}/00-plan.md'
|
'- Report: .takt/reports/{report_dir}/00-plan.md'
|
||||||
);
|
);
|
||||||
const context = createMinimalContext({
|
const context = createMinimalContext({
|
||||||
cwd: '/project/.takt/worktrees/my-task',
|
cwd: '/clone/my-task',
|
||||||
projectCwd: '/project',
|
projectCwd: '/project',
|
||||||
reportDir: '20260128-worktree-report',
|
reportDir: '20260128-worktree-report',
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = buildInstruction(step, context);
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
|
// Path should be relative, not absolute with projectCwd
|
||||||
expect(result).toContain(
|
expect(result).toContain(
|
||||||
'- Report: /project/.takt/reports/20260128-worktree-report/00-plan.md'
|
'- Report: .takt/reports/20260128-worktree-report/00-plan.md'
|
||||||
);
|
);
|
||||||
expect(result).toContain('Working Directory: /project/.takt/worktrees/my-task');
|
expect(result).not.toContain('/project/.takt/reports/');
|
||||||
// Project Root should NOT be included in metadata (to avoid agent confusion)
|
expect(result).toContain('Working Directory: /clone/my-task');
|
||||||
expect(result).not.toContain('Project Root');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should replace multiple .takt/reports/{report_dir} occurrences', () => {
|
it('should replace multiple {report_dir} occurrences', () => {
|
||||||
const step = createMinimalStep(
|
const step = createMinimalStep(
|
||||||
'- Scope: .takt/reports/{report_dir}/01-scope.md\n- Decisions: .takt/reports/{report_dir}/02-decisions.md'
|
'- Scope: .takt/reports/{report_dir}/01-scope.md\n- Decisions: .takt/reports/{report_dir}/02-decisions.md'
|
||||||
);
|
);
|
||||||
@ -125,8 +125,9 @@ describe('instruction-builder', () => {
|
|||||||
|
|
||||||
const result = buildInstruction(step, context);
|
const result = buildInstruction(step, context);
|
||||||
|
|
||||||
expect(result).toContain('/project/.takt/reports/20260128-multi/01-scope.md');
|
expect(result).toContain('.takt/reports/20260128-multi/01-scope.md');
|
||||||
expect(result).toContain('/project/.takt/reports/20260128-multi/02-decisions.md');
|
expect(result).toContain('.takt/reports/20260128-multi/02-decisions.md');
|
||||||
|
expect(result).not.toContain('/project/.takt/reports/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should replace standalone {report_dir} with directory name only', () => {
|
it('should replace standalone {report_dir} with directory name only', () => {
|
||||||
@ -141,22 +142,6 @@ describe('instruction-builder', () => {
|
|||||||
|
|
||||||
expect(result).toContain('Report dir name: 20260128-standalone');
|
expect(result).toContain('Report dir name: 20260128-standalone');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fall back to cwd when projectCwd is not provided', () => {
|
|
||||||
const step = createMinimalStep(
|
|
||||||
'- Dir: .takt/reports/{report_dir}/'
|
|
||||||
);
|
|
||||||
const context = createMinimalContext({
|
|
||||||
cwd: '/fallback-project',
|
|
||||||
reportDir: '20260128-fallback',
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = buildInstruction(step, context);
|
|
||||||
|
|
||||||
expect(result).toContain(
|
|
||||||
'- Dir: /fallback-project/.takt/reports/20260128-fallback/'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildExecutionMetadata', () => {
|
describe('buildExecutionMetadata', () => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from 'node:events';
|
import { EventEmitter } from 'node:events';
|
||||||
import { mkdirSync, existsSync } from 'node:fs';
|
import { mkdirSync, existsSync, symlinkSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import type {
|
import type {
|
||||||
WorkflowConfig,
|
WorkflowConfig,
|
||||||
@ -79,6 +79,18 @@ export class WorkflowEngine extends EventEmitter {
|
|||||||
if (!existsSync(reportDirPath)) {
|
if (!existsSync(reportDirPath)) {
|
||||||
mkdirSync(reportDirPath, { recursive: true });
|
mkdirSync(reportDirPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Worktree mode: create symlink so agents can access reports via relative path
|
||||||
|
if (this.cwd !== this.projectCwd) {
|
||||||
|
const cwdReportsDir = join(this.cwd, '.takt', 'reports');
|
||||||
|
if (!existsSync(cwdReportsDir)) {
|
||||||
|
mkdirSync(join(this.cwd, '.takt'), { recursive: true });
|
||||||
|
symlinkSync(
|
||||||
|
join(this.projectCwd, '.takt', 'reports'),
|
||||||
|
cwdReportsDir,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Validate workflow configuration at construction time */
|
/** Validate workflow configuration at construction time */
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
* template placeholders with actual values.
|
* template placeholders with actual values.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { join } from 'node:path';
|
|
||||||
import type { WorkflowStep, AgentResponse, Language } from '../models/types.js';
|
import type { WorkflowStep, AgentResponse, Language } from '../models/types.js';
|
||||||
import { getGitDiff } from '../agents/runner.js';
|
import { getGitDiff } from '../agents/runner.js';
|
||||||
|
|
||||||
@ -180,14 +179,11 @@ export function buildInstruction(
|
|||||||
escapeTemplateChars(userInputsStr)
|
escapeTemplateChars(userInputsStr)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Replace .takt/reports/{report_dir} with absolute path first,
|
// Replace {report_dir} with the directory name, keeping paths relative.
|
||||||
// then replace standalone {report_dir} with the directory name.
|
// In worktree mode, a symlink from cwd/.takt/reports → projectCwd/.takt/reports
|
||||||
// This ensures agents always use the correct project root for reports,
|
// ensures the relative path resolves correctly without embedding absolute paths
|
||||||
// even when their cwd is a clone.
|
// that could cause agents to operate on the wrong repository.
|
||||||
if (context.reportDir) {
|
if (context.reportDir) {
|
||||||
const projectRoot = context.projectCwd ?? context.cwd;
|
|
||||||
const reportDirFullPath = join(projectRoot, '.takt', 'reports', context.reportDir);
|
|
||||||
instruction = instruction.replace(/\.takt\/reports\/\{report_dir\}/g, reportDirFullPath);
|
|
||||||
instruction = instruction.replace(/\{report_dir\}/g, context.reportDir);
|
instruction = instruction.replace(/\{report_dir\}/g, context.reportDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user