ExecutionMetadataからProject Rootを削除

worktreeモードでタスク実行時、エージェントがmainディレクトリで
作業してしまうバグの修正。metadataでProject Rootを見せていたため
Claudeがそちらで作業していた可能性がある。

- ExecutionMetadataインターフェースからprojectRootを削除
- buildExecutionMetadataからprojectRoot設定を削除
- METADATA_STRINGSからprojectRootとmodeを削除
- renderExecutionMetadataからProject Root表示を削除
- 関連テストを更新
This commit is contained in:
nrslib 2026-01-29 00:49:34 +09:00
parent 588d157daa
commit 277d490eeb
2 changed files with 8 additions and 94 deletions

View File

@ -47,7 +47,7 @@ describe('instruction-builder', () => {
expect(result).toContain('Do some work');
});
it('should include Project Root and Mode when cwd !== projectCwd', () => {
it('should NOT include Project Root even when cwd !== projectCwd', () => {
const step = createMinimalStep('Do some work');
const context = createMinimalContext({
cwd: '/worktree-path',
@ -58,36 +58,11 @@ describe('instruction-builder', () => {
expect(result).toContain('## Execution Context');
expect(result).toContain('Working Directory: /worktree-path');
expect(result).toContain('Project Root: /project-path');
expect(result).toContain('Mode: worktree');
expect(result).not.toContain('Project Root');
expect(result).not.toContain('Mode:');
expect(result).toContain('Do some work');
});
it('should NOT include Project Root or Mode when cwd === projectCwd', () => {
const step = createMinimalStep('Do some work');
const context = createMinimalContext({
cwd: '/project',
projectCwd: '/project',
});
const result = buildInstruction(step, context);
expect(result).toContain('Working Directory: /project');
expect(result).not.toContain('Project Root');
expect(result).not.toContain('Mode:');
});
it('should NOT include Project Root or Mode when projectCwd is not set', () => {
const step = createMinimalStep('Do some work');
const context = createMinimalContext({ cwd: '/project' });
const result = buildInstruction(step, context);
expect(result).toContain('Working Directory: /project');
expect(result).not.toContain('Project Root');
expect(result).not.toContain('Mode:');
});
it('should prepend metadata before the instruction body', () => {
const step = createMinimalStep('Do some work');
const context = createMinimalContext({ cwd: '/project' });
@ -133,7 +108,8 @@ describe('instruction-builder', () => {
'- Report: /project/.takt/reports/20260128-worktree-report/00-plan.md'
);
expect(result).toContain('Working Directory: /project/.takt/worktrees/my-task');
expect(result).toContain('Project Root: /project');
// Project Root should NOT be included in metadata (to avoid agent confusion)
expect(result).not.toContain('Project Root');
});
it('should replace multiple .takt/reports/{report_dir} occurrences', () => {
@ -183,15 +159,14 @@ describe('instruction-builder', () => {
});
describe('buildExecutionMetadata', () => {
it('should set workingDirectory and omit projectRoot in normal mode', () => {
it('should set workingDirectory', () => {
const context = createMinimalContext({ cwd: '/project' });
const metadata = buildExecutionMetadata(context);
expect(metadata.workingDirectory).toBe('/project');
expect(metadata.projectRoot).toBeUndefined();
});
it('should set projectRoot in worktree mode', () => {
it('should use cwd as workingDirectory even in worktree mode', () => {
const context = createMinimalContext({
cwd: '/worktree-path',
projectCwd: '/project-path',
@ -199,27 +174,6 @@ describe('instruction-builder', () => {
const metadata = buildExecutionMetadata(context);
expect(metadata.workingDirectory).toBe('/worktree-path');
expect(metadata.projectRoot).toBe('/project-path');
});
it('should omit projectRoot when projectCwd is not set', () => {
const context = createMinimalContext({ cwd: '/project' });
// projectCwd is undefined by default
const metadata = buildExecutionMetadata(context);
expect(metadata.workingDirectory).toBe('/project');
expect(metadata.projectRoot).toBeUndefined();
});
it('should omit projectRoot when cwd equals projectCwd', () => {
const context = createMinimalContext({
cwd: '/same-path',
projectCwd: '/same-path',
});
const metadata = buildExecutionMetadata(context);
expect(metadata.workingDirectory).toBe('/same-path');
expect(metadata.projectRoot).toBeUndefined();
});
it('should default language to en when not specified', () => {
@ -238,7 +192,7 @@ describe('instruction-builder', () => {
});
describe('renderExecutionMetadata', () => {
it('should render normal mode without Project Root or Mode', () => {
it('should render Working Directory only', () => {
const rendered = renderExecutionMetadata({ workingDirectory: '/project', language: 'en' });
expect(rendered).toContain('## Execution Context');
@ -247,19 +201,6 @@ describe('instruction-builder', () => {
expect(rendered).not.toContain('Mode:');
});
it('should render worktree mode with Project Root and Mode', () => {
const rendered = renderExecutionMetadata({
workingDirectory: '/worktree',
projectRoot: '/project',
language: 'en',
});
expect(rendered).toContain('## Execution Context');
expect(rendered).toContain('- Working Directory: /worktree');
expect(rendered).toContain('- Project Root: /project');
expect(rendered).toContain('- Mode: worktree');
});
it('should end with a trailing empty line', () => {
const rendered = renderExecutionMetadata({ workingDirectory: '/project', language: 'en' });
@ -275,17 +216,6 @@ describe('instruction-builder', () => {
expect(rendered).not.toContain('Working Directory');
});
it('should render worktree mode in Japanese', () => {
const rendered = renderExecutionMetadata({
workingDirectory: '/worktree',
projectRoot: '/project',
language: 'ja',
});
expect(rendered).toContain('- プロジェクトルート: /project');
expect(rendered).toContain('モード: worktree');
});
it('should include English note only for en, not for ja', () => {
const enRendered = renderExecutionMetadata({ workingDirectory: '/project', language: 'en' });
const jaRendered = renderExecutionMetadata({ workingDirectory: '/project', language: 'ja' });

View File

@ -39,8 +39,6 @@ export interface InstructionContext {
export interface ExecutionMetadata {
/** The agent's working directory (may be a worktree) */
readonly workingDirectory: string;
/** Project root where .takt/ lives. Present only in worktree mode. */
readonly projectRoot?: string;
/** Language for metadata rendering */
readonly language: Language;
}
@ -49,15 +47,10 @@ export interface ExecutionMetadata {
* Build execution metadata from instruction context.
*
* Pure function: InstructionContext ExecutionMetadata.
* Sets `projectRoot` only when cwd differs from projectCwd (worktree mode).
*/
export function buildExecutionMetadata(context: InstructionContext): ExecutionMetadata {
const projectRoot = context.projectCwd ?? context.cwd;
const isWorktree = context.cwd !== projectRoot;
return {
workingDirectory: context.cwd,
...(isWorktree ? { projectRoot } : {}),
language: context.language ?? 'en',
};
}
@ -67,15 +60,11 @@ const METADATA_STRINGS = {
en: {
heading: '## Execution Context',
workingDirectory: 'Working Directory',
projectRoot: 'Project Root',
mode: 'Mode: worktree (source edits in Working Directory, reports in Project Root)',
note: 'Note: This section is metadata. Follow the language used in the rest of the prompt.',
},
ja: {
heading: '## 実行コンテキスト',
workingDirectory: '作業ディレクトリ',
projectRoot: 'プロジェクトルート',
mode: 'モード: worktreeソース編集は作業ディレクトリ、レポートはプロジェクトルート',
note: '',
},
} as const;
@ -85,7 +74,6 @@ const METADATA_STRINGS = {
*
* Pure function: ExecutionMetadata string.
* Always includes heading + Working Directory.
* Adds Project Root and Mode only in worktree mode (when projectRoot is present).
* Language determines the output language; 'en' includes a note about language consistency.
*/
export function renderExecutionMetadata(metadata: ExecutionMetadata): string {
@ -94,10 +82,6 @@ export function renderExecutionMetadata(metadata: ExecutionMetadata): string {
strings.heading,
`- ${strings.workingDirectory}: ${metadata.workingDirectory}`,
];
if (metadata.projectRoot !== undefined) {
lines.push(`- ${strings.projectRoot}: ${metadata.projectRoot}`);
lines.push(`- ${strings.mode}`);
}
if (strings.note) {
lines.push('');
lines.push(strings.note);