feat: Task decomposition for Tailwind/Radix/shadcn foundation setup

Decomposed "Foundation Setup - Install Dependencies & Configure Tailwind"
phase into 6 executable tasks:

1. Install Tailwind CSS, PostCSS & Autoprefixer
2. Map MUI theme to Tailwind design tokens
3. Setup CSS variables for dynamic theming
4. Install Radix UI primitives
5. Initialize shadcn/ui and setup component directory
6. Move MUI to devDependencies and verify setup

Tasks follow logical dependency chain with final human verification
checkpoint before proceeding with component migration.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Lukas May
2026-02-10 09:48:51 +01:00
parent da4152264c
commit 342b490fe7
83 changed files with 3200 additions and 913 deletions

View File

@@ -19,8 +19,10 @@ import type { AgentManager } from '../agent/types.js';
import type { TaskRepository } from '../db/repositories/task-repository.js';
import type { MessageRepository } from '../db/repositories/message-repository.js';
import type { InitiativeRepository } from '../db/repositories/initiative-repository.js';
import type { PhaseRepository } from '../db/repositories/phase-repository.js';
import type { Task } from '../db/schema.js';
import type { DispatchManager, QueuedTask, DispatchResult } from './types.js';
import { initiativeBranchName, phaseBranchName, taskBranchName } from '../git/branch-naming.js';
import { createModuleLogger } from '../logger/index.js';
const log = createModuleLogger('dispatch');
@@ -59,7 +61,8 @@ export class DefaultDispatchManager implements DispatchManager {
private messageRepository: MessageRepository,
private agentManager: AgentManager,
private eventBus: EventBus,
private initiativeRepository?: InitiativeRepository
private initiativeRepository?: InitiativeRepository,
private phaseRepository?: PhaseRepository,
) {}
/**
@@ -320,10 +323,39 @@ export class DefaultDispatchManager implements DispatchManager {
};
}
// Compute branch info for branch-aware spawning
let baseBranch: string | undefined;
let branchName: string | undefined;
if (task.phaseId && task.initiativeId && this.initiativeRepository && this.phaseRepository) {
try {
const initiative = await this.initiativeRepository.findById(task.initiativeId);
const phase = await this.phaseRepository.findById(task.phaseId);
if (initiative?.mergeTarget && phase) {
const initBranch = initiativeBranchName(initiative.mergeTarget);
if (task.category === 'merge') {
// Merge tasks work directly on the phase branch
baseBranch = initBranch;
branchName = phaseBranchName(initBranch, phase.name);
} else {
baseBranch = phaseBranchName(initBranch, phase.name);
branchName = taskBranchName(initBranch, task.id);
}
}
} catch {
// Non-fatal: fall back to default branching
}
}
// Spawn agent with task (alias auto-generated by agent manager)
const agent = await this.agentManager.spawn({
taskId: nextTask.taskId,
initiativeId: task.initiativeId ?? undefined,
phaseId: task.phaseId ?? undefined,
prompt: task.description || task.name,
baseBranch,
branchName,
});
log.info({ taskId: nextTask.taskId, agentId: agent.id }, 'task dispatched');

View File

@@ -16,7 +16,15 @@ import type {
} from '../events/index.js';
import type { PhaseRepository } from '../db/repositories/phase-repository.js';
import type { TaskRepository } from '../db/repositories/task-repository.js';
import type { InitiativeRepository } from '../db/repositories/initiative-repository.js';
import type { ProjectRepository } from '../db/repositories/project-repository.js';
import type { BranchManager } from '../git/branch-manager.js';
import type { PhaseDispatchManager, DispatchManager, QueuedPhase, PhaseDispatchResult } from './types.js';
import { initiativeBranchName, phaseBranchName } from '../git/branch-naming.js';
import { ensureProjectClone } from '../git/project-clones.js';
import { createModuleLogger } from '../logger/index.js';
const log = createModuleLogger('phase-dispatch');
// =============================================================================
// Internal Types
@@ -51,7 +59,11 @@ export class DefaultPhaseDispatchManager implements PhaseDispatchManager {
private phaseRepository: PhaseRepository,
private taskRepository: TaskRepository,
private dispatchManager: DispatchManager,
private eventBus: EventBus
private eventBus: EventBus,
private initiativeRepository?: InitiativeRepository,
private projectRepository?: ProjectRepository,
private branchManager?: BranchManager,
private workspaceRoot?: string,
) {}
/**
@@ -156,6 +168,26 @@ export class DefaultPhaseDispatchManager implements PhaseDispatchManager {
// Update phase status to 'in_progress'
await this.phaseRepository.update(nextPhase.phaseId, { status: 'in_progress' });
// Create phase branch in all linked project clones
if (this.initiativeRepository && this.projectRepository && this.branchManager && this.workspaceRoot) {
try {
const initiative = await this.initiativeRepository.findById(phase.initiativeId);
if (initiative?.mergeTarget) {
const initBranch = initiativeBranchName(initiative.mergeTarget);
const phBranch = phaseBranchName(initBranch, phase.name);
const projects = await this.projectRepository.findProjectsByInitiativeId(phase.initiativeId);
for (const project of projects) {
const clonePath = await ensureProjectClone(project, this.workspaceRoot);
await this.branchManager.ensureBranch(clonePath, initBranch, 'main');
await this.branchManager.ensureBranch(clonePath, phBranch, initBranch);
}
log.info({ phaseId: nextPhase.phaseId, phBranch, initBranch }, 'phase branch created');
}
} catch (err) {
log.error({ phaseId: nextPhase.phaseId, err: err instanceof Error ? err.message : String(err) }, 'failed to create phase branch');
}
}
// Remove from queue (now being worked on)
this.phaseQueue.delete(nextPhase.phaseId);