feat: monorepo consolidation — forge pipeline, MACP protocol, framework plugin, profiles/guides/skills
Work packages completed: - WP1: packages/forge — pipeline runner, stage adapter, board tasks, brief classifier, persona loader with project-level overrides. 89 tests, 95.62% coverage. - WP2: packages/macp — credential resolver, gate runner, event emitter, protocol types. 65 tests, 96.24% coverage. Full Python-to-TS port preserving all behavior. - WP3: plugins/mosaic-framework — OC rails injection plugin (before_agent_start + subagent_spawning hooks for Mosaic contract enforcement). - WP4: profiles/ (domains, tech-stacks, workflows), guides/ (17 docs), skills/ (5 universal skills), forge pipeline assets (48 markdown files). Board deliberation: docs/reviews/consolidation-board-memo.md Brief: briefs/monorepo-consolidation.md Consolidates mosaic/stack (forge, MACP, bootstrap framework) into mosaic/mosaic-stack. 154 new tests total. Zero Python — all TypeScript/ESM.
This commit is contained in:
265
docs/tasks/WP1-forge-package.md
Normal file
265
docs/tasks/WP1-forge-package.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# WP1: packages/forge — Forge Pipeline Package
|
||||
|
||||
## Context
|
||||
|
||||
Port the Forge progressive refinement pipeline from Python (~/src/mosaic-stack/forge/) to TypeScript as `packages/forge` in this monorepo. The pipeline markdown assets (stages, agents, personas, rails, gates, templates) are already copied to `packages/forge/pipeline/`. This task is the TypeScript implementation layer.
|
||||
|
||||
**Board decisions that constrain this work:**
|
||||
|
||||
- Abstract TaskExecutor interface — packages/forge must NOT hard-import packages/coord. Define an abstract interface; coord satisfies it.
|
||||
- Clean index.ts exports, no internal path leakage, no hardcoded paths
|
||||
- 85% test coverage on TS implementation files (markdown assets excluded)
|
||||
- Test strategy for non-deterministic AI orchestration: fixture-based integration tests
|
||||
- OpenBrain is OUT OF SCOPE
|
||||
- ESM only, zero Python
|
||||
|
||||
**Dependencies available:**
|
||||
|
||||
- `@mosaic/macp` (packages/macp) is built and provides: GateEntry, GateResult, Task types, credential resolution, gate running, event emission
|
||||
|
||||
## Source Files (Python → TypeScript)
|
||||
|
||||
### 1. types.ts
|
||||
|
||||
Define all Forge-specific types:
|
||||
|
||||
```typescript
|
||||
// Stage specification
|
||||
interface StageSpec {
|
||||
number: string;
|
||||
title: string;
|
||||
dispatch: 'exec' | 'yolo' | 'pi';
|
||||
type: 'research' | 'review' | 'coding' | 'deploy';
|
||||
gate: string;
|
||||
promptFile: string;
|
||||
qualityGates: (string | GateEntry)[];
|
||||
}
|
||||
|
||||
// Brief classification
|
||||
type BriefClass = 'strategic' | 'technical' | 'hotfix';
|
||||
type ClassSource = 'cli' | 'frontmatter' | 'auto';
|
||||
|
||||
// Run manifest (persisted to disk)
|
||||
interface RunManifest {
|
||||
runId: string;
|
||||
brief: string;
|
||||
codebase: string;
|
||||
briefClass: BriefClass;
|
||||
classSource: ClassSource;
|
||||
forceBoard: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
currentStage: string;
|
||||
status: 'in_progress' | 'completed' | 'failed' | 'interrupted' | 'rejected';
|
||||
stages: Record<string, StageStatus>;
|
||||
}
|
||||
|
||||
// Abstract task executor (decouples from packages/coord)
|
||||
interface TaskExecutor {
|
||||
submitTask(task: ForgeTask): Promise<void>;
|
||||
waitForCompletion(taskId: string, timeoutMs: number): Promise<TaskResult>;
|
||||
}
|
||||
|
||||
// Persona override config
|
||||
interface ForgeConfig {
|
||||
board?: {
|
||||
additionalMembers?: string[];
|
||||
skipMembers?: string[];
|
||||
};
|
||||
specialists?: {
|
||||
alwaysInclude?: string[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. constants.ts
|
||||
|
||||
**Source:** Top of `~/src/mosaic-stack/forge/lib` (ALL_STAGES, LABELS, STAGE_SPECS equivalent) + `~/src/mosaic-stack/forge/pipeline/orchestrator/stage_adapter.py` (STAGE_TIMEOUTS)
|
||||
|
||||
```typescript
|
||||
export const STAGE_SEQUENCE = [
|
||||
'00-intake',
|
||||
'00b-discovery',
|
||||
'01-board',
|
||||
'01b-brief-analyzer',
|
||||
'02-planning-1',
|
||||
'03-planning-2',
|
||||
'04-planning-3',
|
||||
'05-coding',
|
||||
'06-review',
|
||||
'07-remediate',
|
||||
'08-test',
|
||||
'09-deploy',
|
||||
];
|
||||
|
||||
export const STAGE_TIMEOUTS: Record<string, number> = {
|
||||
'00-intake': 120,
|
||||
'00b-discovery': 300,
|
||||
'01-board': 120,
|
||||
'02-planning-1': 600,
|
||||
// ... etc
|
||||
};
|
||||
|
||||
export const STAGE_LABELS: Record<string, string> = {
|
||||
'00-intake': 'INTAKE',
|
||||
// ... etc
|
||||
};
|
||||
```
|
||||
|
||||
Also: STRATEGIC_KEYWORDS, TECHNICAL_KEYWORDS for brief classification.
|
||||
|
||||
### 3. brief-classifier.ts
|
||||
|
||||
**Source:** `classify_brief()`, `parse_brief_frontmatter()`, `stages_for_class()` from `~/src/mosaic-stack/forge/lib`
|
||||
|
||||
- Auto-classify brief by keyword analysis (strategic vs technical)
|
||||
- Parse YAML frontmatter for explicit `class:` field
|
||||
- CLI flag override
|
||||
- Return stage list based on classification (strategic = full pipeline, technical = skip board, hotfix = skip board + brief analyzer)
|
||||
|
||||
### 4. stage-adapter.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/forge/pipeline/orchestrator/stage_adapter.py`
|
||||
|
||||
- `mapStageToTask()`: Convert a Forge stage into a task compatible with TaskExecutor
|
||||
- Stage briefs written to `{runDir}/{stageName}/brief.md`
|
||||
- Result paths at `{runDir}/{stageName}/result.json`
|
||||
- Previous results read from disk at runtime (not baked into brief)
|
||||
- Per-stage timeouts from STAGE_TIMEOUTS
|
||||
- depends_on chain built from stage sequence
|
||||
|
||||
### 5. board-tasks.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/forge/pipeline/orchestrator/board_tasks.py`
|
||||
|
||||
- `loadBoardPersonas()`: Read all .md files from `pipeline/agents/board/`
|
||||
- `generateBoardTasks()`: One task per persona + synthesis task
|
||||
- Synthesis depends on all persona tasks with `depends_on_policy: 'all_terminal'`
|
||||
- Persona briefs include role description + brief under review
|
||||
- Synthesis script merges independent reviews into board memo
|
||||
|
||||
### 6. pipeline-runner.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/forge/pipeline/orchestrator/pipeline_runner.py` + `~/src/mosaic-stack/forge/lib` (cmd_run, cmd_resume, cmd_status)
|
||||
|
||||
- `runPipeline(briefPath, projectRoot, options)`: Full pipeline execution
|
||||
- Creates run directory at `{projectRoot}/.forge/runs/{runId}/`
|
||||
- Generates tasks for all stages, submits to TaskExecutor
|
||||
- Tracks manifest.json with stage statuses
|
||||
- `resumePipeline(runDir)`: Pick up from last incomplete stage
|
||||
- `getPipelineStatus(runDir)`: Read manifest and report
|
||||
|
||||
**Key difference from Python:** Run output goes to PROJECT-scoped `.forge/runs/`, not inside the Forge package.
|
||||
|
||||
### 7. Persona Override System (NEW — not in Python)
|
||||
|
||||
- Base personas read from `packages/forge/pipeline/agents/`
|
||||
- Project overrides read from `{projectRoot}/.forge/personas/{role}.md`
|
||||
- Merge strategy: project persona content APPENDED to base persona (not replaced)
|
||||
- Board composition configurable via `{projectRoot}/.forge/config.yaml`
|
||||
- If no project config exists, use defaults (all base personas, no overrides)
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
packages/forge/
|
||||
├── src/
|
||||
│ ├── index.ts
|
||||
│ ├── types.ts
|
||||
│ ├── constants.ts
|
||||
│ ├── brief-classifier.ts
|
||||
│ ├── stage-adapter.ts
|
||||
│ ├── board-tasks.ts
|
||||
│ ├── pipeline-runner.ts
|
||||
│ └── persona-loader.ts
|
||||
├── pipeline/ # Already copied (WP4) — markdown assets
|
||||
│ ├── stages/
|
||||
│ ├── agents/
|
||||
│ ├── rails/
|
||||
│ ├── gates/
|
||||
│ └── templates/
|
||||
├── __tests__/
|
||||
│ ├── brief-classifier.test.ts
|
||||
│ ├── stage-adapter.test.ts
|
||||
│ ├── board-tasks.test.ts
|
||||
│ ├── pipeline-runner.test.ts
|
||||
│ └── persona-loader.test.ts
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── vitest.config.ts
|
||||
```
|
||||
|
||||
## Package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@mosaic/forge",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mosaic/macp": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "workspace:*",
|
||||
"typescript": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only dependency: @mosaic/macp (for gate types, event emission).
|
||||
|
||||
## Test Strategy (Board requirement)
|
||||
|
||||
**Deterministic code (brief-classifier, stage-adapter, board-tasks, persona-loader, constants):**
|
||||
|
||||
- Standard unit tests with known inputs/outputs
|
||||
- 100% of classification logic, stage mapping, persona loading covered
|
||||
|
||||
**Non-deterministic code (pipeline-runner):**
|
||||
|
||||
- Fixture-based integration tests using a mock TaskExecutor
|
||||
- Mock executor returns pre-recorded results for each stage
|
||||
- Tests verify: manifest progression, stage ordering, dependency enforcement, resume behavior, error handling
|
||||
- NO real AI calls in tests
|
||||
|
||||
**Markdown assets:** Excluded from coverage measurement (configure vitest to exclude `pipeline/` directory).
|
||||
|
||||
## ESM Requirements
|
||||
|
||||
- `"type": "module"` in package.json
|
||||
- NodeNext module resolution in tsconfig
|
||||
- `.js` extensions in all imports
|
||||
- No CommonJS
|
||||
|
||||
## Key Design: Abstract TaskExecutor
|
||||
|
||||
```typescript
|
||||
// In packages/forge/src/types.ts
|
||||
export interface TaskExecutor {
|
||||
submitTask(task: ForgeTask): Promise<void>;
|
||||
waitForCompletion(taskId: string, timeoutMs: number): Promise<TaskResult>;
|
||||
getTaskStatus(taskId: string): Promise<TaskStatus>;
|
||||
}
|
||||
|
||||
// In packages/coord (or wherever the concrete impl lives)
|
||||
export class CoordTaskExecutor implements TaskExecutor {
|
||||
// ... uses packages/coord runner
|
||||
}
|
||||
```
|
||||
|
||||
This means packages/forge can be tested with a mock executor and deployed with any backend.
|
||||
|
||||
## Asset Resolution
|
||||
|
||||
Pipeline markdown assets (stages, personas, rails) must be resolved relative to the package installation, NOT hardcoded paths:
|
||||
|
||||
```typescript
|
||||
// Use import.meta.url to find package root
|
||||
const PACKAGE_ROOT = new URL('..', import.meta.url).pathname;
|
||||
const PIPELINE_DIR = path.join(PACKAGE_ROOT, 'pipeline');
|
||||
```
|
||||
|
||||
Project-level overrides resolved relative to projectRoot parameter.
|
||||
Reference in New Issue
Block a user