fix: validate override piece via resolver including ensemble scope

This commit is contained in:
nrslib 2026-02-22 02:47:11 +09:00
parent 102f31447a
commit 9e3fb5cf16
2 changed files with 14 additions and 5 deletions

View File

@ -33,6 +33,7 @@ vi.mock('../infra/config/index.js', () => ({
resolvePieceConfigValue: (...args: unknown[]) => mockResolvePieceConfigValue(...args), resolvePieceConfigValue: (...args: unknown[]) => mockResolvePieceConfigValue(...args),
listPieces: vi.fn(() => ['default']), listPieces: vi.fn(() => ['default']),
listPieceEntries: vi.fn(() => []), listPieceEntries: vi.fn(() => []),
loadPieceByIdentifier: vi.fn((identifier: string) => (identifier === 'default' ? { name: 'default' } : null)),
isPiecePath: vi.fn(() => false), isPiecePath: vi.fn(() => false),
})); }));
@ -86,11 +87,13 @@ vi.mock('../features/pieceSelection/index.js', () => ({
})); }));
import { confirm } from '../shared/prompt/index.js'; import { confirm } from '../shared/prompt/index.js';
import { loadPieceByIdentifier } from '../infra/config/index.js';
import { createSharedClone, autoCommitAndPush, summarizeTaskName } from '../infra/task/index.js'; import { createSharedClone, autoCommitAndPush, summarizeTaskName } from '../infra/task/index.js';
import { selectPiece } from '../features/pieceSelection/index.js'; import { selectPiece } from '../features/pieceSelection/index.js';
import { selectAndExecuteTask, determinePiece } from '../features/tasks/execute/selectAndExecute.js'; import { selectAndExecuteTask, determinePiece } from '../features/tasks/execute/selectAndExecute.js';
const mockConfirm = vi.mocked(confirm); const mockConfirm = vi.mocked(confirm);
const mockLoadPieceByIdentifier = vi.mocked(loadPieceByIdentifier);
const mockCreateSharedClone = vi.mocked(createSharedClone); const mockCreateSharedClone = vi.mocked(createSharedClone);
const mockAutoCommitAndPush = vi.mocked(autoCommitAndPush); const mockAutoCommitAndPush = vi.mocked(autoCommitAndPush);
const mockSummarizeTaskName = vi.mocked(summarizeTaskName); const mockSummarizeTaskName = vi.mocked(summarizeTaskName);
@ -180,6 +183,14 @@ describe('resolveAutoPr default in selectAndExecuteTask', () => {
expect(mockSelectPiece).toHaveBeenCalledWith('/project'); expect(mockSelectPiece).toHaveBeenCalledWith('/project');
}); });
it('should accept ensemble scoped piece override when it exists', async () => {
mockLoadPieceByIdentifier.mockReturnValueOnce({ name: '@nrslib/takt-packages/critical-thinking' } as never);
const selected = await determinePiece('/project', '@nrslib/takt-packages/critical-thinking');
expect(selected).toBe('@nrslib/takt-packages/critical-thinking');
});
it('should fail task record when executeTask throws', async () => { it('should fail task record when executeTask throws', async () => {
mockConfirm.mockResolvedValue(true); mockConfirm.mockResolvedValue(true);
mockSummarizeTaskName.mockResolvedValue('test-task'); mockSummarizeTaskName.mockResolvedValue('test-task');

View File

@ -7,12 +7,11 @@
*/ */
import { import {
listPieces, loadPieceByIdentifier,
isPiecePath, isPiecePath,
} from '../../../infra/config/index.js'; } from '../../../infra/config/index.js';
import { confirm } from '../../../shared/prompt/index.js'; import { confirm } from '../../../shared/prompt/index.js';
import { createSharedClone, summarizeTaskName, getCurrentBranch, TaskRunner } from '../../../infra/task/index.js'; import { createSharedClone, summarizeTaskName, getCurrentBranch, TaskRunner } from '../../../infra/task/index.js';
import { DEFAULT_PIECE_NAME } from '../../../shared/constants.js';
import { info, error, withProgress } from '../../../shared/ui/index.js'; import { info, error, withProgress } from '../../../shared/ui/index.js';
import { createLogger } from '../../../shared/utils/index.js'; import { createLogger } from '../../../shared/utils/index.js';
import { executeTask } from './taskExecution.js'; import { executeTask } from './taskExecution.js';
@ -30,9 +29,8 @@ export async function determinePiece(cwd: string, override?: string): Promise<st
if (isPiecePath(override)) { if (isPiecePath(override)) {
return override; return override;
} }
const availablePieces = listPieces(cwd); const resolvedPiece = loadPieceByIdentifier(override, cwd);
const knownPieces = availablePieces.length === 0 ? [DEFAULT_PIECE_NAME] : availablePieces; if (!resolvedPiece) {
if (!knownPieces.includes(override)) {
error(`Piece not found: ${override}`); error(`Piece not found: ${override}`);
return null; return null;
} }