feat: monorepo consolidation — forge pipeline, MACP protocol, framework plugin, profiles/guides/skills
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/pr/ci Pipeline failed

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:
Mos (Agent)
2026-03-30 19:43:24 +00:00
parent 40c068fcbc
commit 10689a30d2
123 changed files with 18166 additions and 11 deletions

View File

@@ -0,0 +1,182 @@
import fs from 'node:fs';
import path from 'node:path';
import type { BoardPersona, BoardSynthesis, ForgeTask, PersonaReview } from './types.js';
/**
* Build the brief content for a persona's board evaluation.
*/
export function buildPersonaBrief(brief: string, persona: BoardPersona): string {
return [
`# Board Evaluation: ${persona.name}`,
'',
'## Your Role',
persona.description,
'',
'## Brief Under Review',
brief.trim(),
'',
'## Instructions',
'Evaluate this brief from your perspective. Output a JSON object:',
'{',
` "persona": "${persona.name}",`,
' "verdict": "approve|reject|conditional",',
' "confidence": 0.0-1.0,',
' "concerns": ["..."],',
' "recommendations": ["..."],',
' "key_risks": ["..."]',
'}',
'',
].join('\n');
}
/**
* Write a persona brief to the run directory and return the path.
*/
export function writePersonaBrief(
runDir: string,
baseTaskId: string,
persona: BoardPersona,
brief: string,
): string {
const briefDir = path.join(runDir, '01-board', 'briefs');
fs.mkdirSync(briefDir, { recursive: true });
const briefPath = path.join(briefDir, `${baseTaskId}-${persona.slug}.md`);
fs.writeFileSync(briefPath, buildPersonaBrief(brief, persona), 'utf-8');
return briefPath;
}
/**
* Get the result path for a persona's board review.
*/
export function personaResultPath(runDir: string, taskId: string): string {
return path.join(runDir, '01-board', 'results', `${taskId}.board.json`);
}
/**
* Get the result path for the board synthesis.
*/
export function synthesisResultPath(runDir: string, taskId: string): string {
return path.join(runDir, '01-board', 'results', `${taskId}.board.json`);
}
/**
* Generate one ForgeTask per board persona plus one synthesis task.
*
* Persona tasks run independently (no depends_on).
* The synthesis task depends on all persona tasks with 'all_terminal' policy.
*/
export function generateBoardTasks(
brief: string,
personas: BoardPersona[],
runDir: string,
baseTaskId = 'BOARD',
): ForgeTask[] {
const tasks: ForgeTask[] = [];
const personaTaskIds: string[] = [];
const personaResultPaths: string[] = [];
for (const persona of personas) {
const taskId = `${baseTaskId}-${persona.slug}`;
personaTaskIds.push(taskId);
const briefPath = writePersonaBrief(runDir, baseTaskId, persona, brief);
const resultRelPath = personaResultPath(runDir, taskId);
personaResultPaths.push(resultRelPath);
tasks.push({
id: taskId,
title: `Board review: ${persona.name}`,
description: `Independent board evaluation for ${persona.name}.`,
type: 'review',
dispatch: 'exec',
status: 'pending',
briefPath,
resultPath: resultRelPath,
timeoutSeconds: 120,
qualityGates: ['true'],
metadata: {
personaName: persona.name,
personaSlug: persona.slug,
personaPath: persona.path,
resultOutputPath: resultRelPath,
},
});
}
// Synthesis task — merges all persona reviews
const synthesisId = `${baseTaskId}-SYNTHESIS`;
const synthesisResult = synthesisResultPath(runDir, synthesisId);
tasks.push({
id: synthesisId,
title: 'Board synthesis',
description: 'Merge independent board reviews into a single recommendation.',
type: 'review',
dispatch: 'exec',
status: 'pending',
briefPath: '',
resultPath: synthesisResult,
timeoutSeconds: 120,
dependsOn: personaTaskIds,
dependsOnPolicy: 'all_terminal',
qualityGates: ['true'],
metadata: {
resultOutputPath: synthesisResult,
inputResultPaths: personaResultPaths,
},
});
return tasks;
}
/**
* Merge multiple persona reviews into a board synthesis.
*/
export function synthesizeReviews(reviews: PersonaReview[]): BoardSynthesis {
const verdicts = reviews.map((r) => r.verdict);
let mergedVerdict: PersonaReview['verdict'];
if (verdicts.includes('reject')) {
mergedVerdict = 'reject';
} else if (verdicts.includes('conditional')) {
mergedVerdict = 'conditional';
} else {
mergedVerdict = 'approve';
}
const confidenceValues = reviews.map((r) => r.confidence);
const avgConfidence =
confidenceValues.length > 0
? Math.round((confidenceValues.reduce((a, b) => a + b, 0) / confidenceValues.length) * 1000) /
1000
: 0;
const concerns = unique(reviews.flatMap((r) => r.concerns));
const recommendations = unique(reviews.flatMap((r) => r.recommendations));
const keyRisks = unique(reviews.flatMap((r) => r.keyRisks));
return {
persona: 'Board Synthesis',
verdict: mergedVerdict,
confidence: avgConfidence,
concerns,
recommendations,
keyRisks,
reviews,
};
}
/** Deduplicate while preserving order. */
function unique(items: string[]): string[] {
const seen = new Set<string>();
const result: string[] = [];
for (const item of items) {
if (!seen.has(item)) {
seen.add(item);
result.push(item);
}
}
return result;
}