docs: establish canonical documentation architecture (#1210)
ci/woodpecker/push/publish Pipeline failed
ci/woodpecker/push/publish Pipeline failed
This commit was merged in pull request #1210.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# Monorepo consolidation planning bundle
|
||||
|
||||
> **Status:** Historical planning evidence. The five source records were moved byte-identically from migration quarantine on 2026-08-10.
|
||||
|
||||
This bundle records the decision and proposed work packages for consolidating prior Forge, MACP, and OpenClaw framework work into this monorepo.
|
||||
|
||||
## Records
|
||||
|
||||
- [Consolidation brief](brief.md) — original scope, target layout, constraints, and success criteria.
|
||||
- [Board review](board-review.md) — historical deliberation and conditional approval.
|
||||
- [WP1: Forge package](wp1-forge-package.md) — proposed TypeScript Forge implementation.
|
||||
- [WP2: MACP package](wp2-macp-package.md) — proposed protocol, gate, credential, and event implementation.
|
||||
- [WP3: Mosaic framework plugin](wp3-mosaic-framework-plugin.md) — proposed OpenClaw framework plugin port.
|
||||
|
||||
## Current boundary
|
||||
|
||||
`packages/forge`, `packages/macp`, and `plugins/mosaic-framework` exist in the current checkout. That existence is sufficient to classify this bundle as historical planning, but it does **not** prove that every stated success criterion, integration, coverage target, or behavior remains satisfied.
|
||||
|
||||
Use current package source, manifests, and tests for implementation truth. Do not use this bundle as an active task ledger or as authority to change package behavior.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,231 @@
|
||||
# Brief: Monorepo Consolidation — mosaic/stack → mosaic/mosaic-stack
|
||||
|
||||
## Source
|
||||
|
||||
Architecture consolidation — merge the mosaic/stack repo (Forge pipeline, MACP protocol, framework tools) into mosaic/mosaic-stack (Harness Foundation platform). Two repos doing related work that need to converge.
|
||||
|
||||
## Context
|
||||
|
||||
**mosaic/stack** (OLD) contains:
|
||||
|
||||
- Forge progressive refinement pipeline (stages, agents, personas, rails, debate protocol, brief classification)
|
||||
- MACP protocol (JSON schemas, deterministic Python controller, dispatcher, event system, gate runner)
|
||||
- Credential resolver (Python — OC config, mosaic files, ambient env, JSON5 parser)
|
||||
- OC framework plugin (injects Mosaic rails into all agent sessions)
|
||||
- Profiles (runtime-neutral context packs for tech stacks and domains)
|
||||
- Stage adapter (Forge→MACP bridge)
|
||||
- Board tasks (multi-agent board evaluation)
|
||||
- OpenBrain specialist memory (learning capture/recall)
|
||||
- 17 guides, 5 universal skills
|
||||
|
||||
**mosaic/mosaic-stack** (NEW) contains:
|
||||
|
||||
- Harness Foundation platform (NestJS gateway, Next.js web, Drizzle ORM, Pi SDK runtime)
|
||||
- 5 provider adapters, task classifier, routing rules, model capability matrix
|
||||
- MACP OC plugin (ACP runtime backend with Pi bridge)
|
||||
- TS coord package (mission runner, tasks file manager, status tracker — 1635 lines)
|
||||
- BullMQ job queue, OTEL telemetry, channel plugins (Discord, Telegram)
|
||||
- CLI with TUI, 65/65 tasks done, v0.2.0
|
||||
|
||||
**Decision:** NEW repo is the base. All unique work from OLD gets ported into NEW as packages.
|
||||
|
||||
## Scope
|
||||
|
||||
### Work Package 1: Forge Pipeline Package (`packages/forge`)
|
||||
|
||||
Port the entire Forge progressive refinement pipeline as a TypeScript package.
|
||||
|
||||
**From OLD:**
|
||||
|
||||
- `forge/pipeline/stages/*.md` — 11 stage definitions
|
||||
- `forge/pipeline/agents/{board,generalists,specialists,cross-cutting}/*.md` — all persona definitions
|
||||
- `forge/pipeline/rails/*.md` — debate protocol, dynamic composition, worker rails
|
||||
- `forge/pipeline/gates/` — gate reviewer definitions
|
||||
- `forge/pipeline/orchestrator/run-structure.md` — file-based observability spec
|
||||
- `forge/templates/` — brief and PRD templates
|
||||
- `forge/pipeline/orchestrator/board_tasks.py` → rewrite in TS
|
||||
- `forge/pipeline/orchestrator/stage_adapter.py` → rewrite in TS
|
||||
- `forge/pipeline/orchestrator/pipeline_runner.py` → rewrite in TS
|
||||
- `forge/forge` CLI (Python) → rewrite in TS, integrate with `packages/cli`
|
||||
|
||||
**Package structure:**
|
||||
|
||||
```
|
||||
packages/forge/
|
||||
├── src/
|
||||
│ ├── index.ts # Public API
|
||||
│ ├── pipeline-runner.ts # Orchestrates full pipeline run
|
||||
│ ├── stage-adapter.ts # Maps stages to MACP/coord tasks
|
||||
│ ├── board-tasks.ts # Multi-agent board evaluation task generator
|
||||
│ ├── brief-classifier.ts # strategic/technical/hotfix classification
|
||||
│ ├── types.ts # Stage specs, run manifest, gate results
|
||||
│ └── constants.ts # Stage sequence, timeouts, labels
|
||||
├── pipeline/
|
||||
│ ├── stages/ # .md stage definitions (copied)
|
||||
│ ├── agents/ # .md persona definitions (copied)
|
||||
│ │ ├── board/
|
||||
│ │ ├── cross-cutting/
|
||||
│ │ ├── generalists/
|
||||
│ │ └── specialists/
|
||||
│ │ ├── language/
|
||||
│ │ └── domain/
|
||||
│ ├── rails/ # .md rails (copied)
|
||||
│ ├── gates/ # .md gate definitions (copied)
|
||||
│ └── templates/ # brief + PRD templates (copied)
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Key design decisions:**
|
||||
|
||||
- Pipeline markdown assets are runtime data, not compiled — ship as-is in the package
|
||||
- `pipeline-runner.ts` calls into `packages/coord` for task execution (not a separate controller)
|
||||
- Stage adapter generates coord-compatible tasks, not MACP JSON directly
|
||||
- Board tasks use `depends_on_policy: "all_terminal"` for synthesis
|
||||
- Per-stage timeouts from `STAGE_TIMEOUTS` map
|
||||
- Brief classifier supports CLI flag, YAML frontmatter, and keyword auto-detection
|
||||
- Run output goes to project-scoped `.forge/runs/{run-id}/` (not inside the Forge package)
|
||||
|
||||
**Persona override system (new):**
|
||||
|
||||
- Base personas ship with the package (read-only)
|
||||
- Project-level overrides in `.forge/personas/{role}.md` extend (not replace) base personas
|
||||
- Board composition configurable via `.forge/config.yaml`:
|
||||
```yaml
|
||||
board:
|
||||
additional_members:
|
||||
- compliance-officer.md
|
||||
skip_members: []
|
||||
specialists:
|
||||
always_include:
|
||||
- proxmox-expert
|
||||
```
|
||||
- OpenBrain integration for cross-run specialist memory (when enabled)
|
||||
|
||||
### Work Package 2: MACP Protocol Package (`packages/macp`)
|
||||
|
||||
Port the MACP protocol layer, event system, and gate runner as a TypeScript package.
|
||||
|
||||
**From OLD:**
|
||||
|
||||
- `tools/macp/protocol/task.schema.json` — task JSON schema
|
||||
- `tools/macp/protocol/` — event schemas
|
||||
- `tools/macp/controller/gate_runner.py` → rewrite in TS as `gate-runner.ts`
|
||||
- `tools/macp/events/` — event watcher, webhook adapter, Discord formatter → rewrite in TS
|
||||
- `tools/macp/dispatcher/credential_resolver.py` → rewrite in TS as `credential-resolver.ts`
|
||||
- `tools/macp/memory/learning_capture.py` + `learning_recall.py` → rewrite in TS
|
||||
|
||||
**Package structure:**
|
||||
|
||||
```
|
||||
packages/macp/
|
||||
├── src/
|
||||
│ ├── index.ts # Public API
|
||||
│ ├── types.ts # Task, event, result, gate types
|
||||
│ ├── schemas/ # JSON schemas (copied)
|
||||
│ ├── gate-runner.ts # Mechanical + AI review quality gates
|
||||
│ ├── credential-resolver.ts # Provider credential resolution (mosaic files, OC config, ambient)
|
||||
│ ├── event-emitter.ts # Append events to ndjson, structured event types
|
||||
│ ├── event-watcher.ts # Poll events.ndjson with cursor persistence
|
||||
│ ├── webhook-adapter.ts # POST events to configurable URL
|
||||
│ ├── discord-formatter.ts # Human-readable event messages
|
||||
│ └── learning.ts # OpenBrain capture + recall
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Integration with existing packages:**
|
||||
|
||||
- `packages/coord` uses `packages/macp` for event emission, gate running, and credential resolution
|
||||
- `plugins/macp` uses `packages/macp` for protocol types and credential resolution
|
||||
- `packages/forge` uses `packages/macp` gate types for stage gates
|
||||
|
||||
### Work Package 3: OC Framework Plugin (`plugins/mosaic-framework`)
|
||||
|
||||
Port the OC framework plugin that injects Mosaic rails into all agent sessions.
|
||||
|
||||
**From OLD:**
|
||||
|
||||
- `oc-plugins/mosaic-framework/index.ts` — `before_agent_start` + `subagent_spawning` hooks
|
||||
- `oc-plugins/mosaic-framework/openclaw.plugin.json`
|
||||
|
||||
**Structure:**
|
||||
|
||||
```
|
||||
plugins/mosaic-framework/
|
||||
├── src/
|
||||
│ └── index.ts # Plugin hooks
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**This is separate from `plugins/macp`:**
|
||||
|
||||
- `mosaic-framework` = injects Mosaic rails/contracts into every OC session (passive enforcement)
|
||||
- `macp` = provides an ACP runtime backend for MACP task execution (active runtime)
|
||||
|
||||
### Work Package 4: Profiles + Guides + Skills
|
||||
|
||||
Port reference content as a documentation/config package or top-level directories.
|
||||
|
||||
**From OLD:**
|
||||
|
||||
- `profiles/domains/*.json` — HIPAA, fintech, crypto context packs
|
||||
- `profiles/tech-stacks/*.json` — NestJS, Next.js, FastAPI, React conventions
|
||||
- `profiles/workflows/*.json` — API development, frontend component, testing workflows
|
||||
- `guides/*.md` — 17 guides (auth, backend, QA, orchestrator, PRD, etc.)
|
||||
- `skills-universal/` — jarvis, macp, mosaic-standards, prd, setup-cicd skills
|
||||
|
||||
**Destination:**
|
||||
|
||||
```
|
||||
profiles/ # Top-level (same as OLD)
|
||||
guides/ # Top-level (same as OLD)
|
||||
skills/ # Top-level (renamed from skills-universal)
|
||||
```
|
||||
|
||||
These are runtime-neutral assets consumed by any agent or profile loader — they don't belong in a compiled package.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Rewriting the NestJS orchestrator app from OLD (`apps/orchestrator/`) — its functionality is subsumed by `packages/coord` + `apps/gateway`
|
||||
- Porting the FastAPI coordinator from OLD (`apps/coordinator/`) — its functionality (webhook receiver, issue parser, quality orchestrator) is handled by `packages/coord` + `apps/gateway` in the new architecture
|
||||
- Porting the Prisma schema or OLD's `apps/api` — Drizzle migration is complete
|
||||
- Old Docker Compose configs (Traefik, Matrix, OpenBao) — NEW has its own infra setup
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. `packages/forge` exists with all 11 stage definitions, all persona markdowns, all rails, and TS implementations of pipeline-runner, stage-adapter, board-tasks, and brief-classifier
|
||||
2. `packages/macp` exists with gate-runner, credential-resolver, event system, and learning capture/recall — all in TypeScript
|
||||
3. `plugins/mosaic-framework` exists and registers OC hooks for rails injection
|
||||
4. Profiles, guides, and skills are present at top-level
|
||||
5. `packages/forge` integrates with `packages/coord` for task execution
|
||||
6. `packages/macp` credential-resolver is used by `plugins/macp` Pi bridge
|
||||
7. All existing tests pass (no regressions)
|
||||
8. New packages have test coverage ≥85%
|
||||
9. `pnpm lint && pnpm typecheck && pnpm build` passes
|
||||
10. `.forge/runs/` project-scoped output directory works for at least one test run
|
||||
|
||||
## Technical Constraints
|
||||
|
||||
- All new code is ESM with NodeNext module resolution
|
||||
- No Python in the new repo — everything rewrites to TypeScript
|
||||
- Pipeline markdown assets (stages, personas, rails) are shipped as package data, not compiled
|
||||
- Credential resolver must support: mosaic credential files, OC config (JSON5), ambient environment — same resolution order as the Python version
|
||||
- Must preserve `depends_on_policy` semantics (all, any, all_terminal)
|
||||
- Per-stage timeouts must be preserved
|
||||
- JSON5 stripping must use the placeholder-extraction approach (not naive regex on string content)
|
||||
|
||||
## Estimated Complexity
|
||||
|
||||
High — crosses 4 work packages with protocol porting, TS rewrites, and integration wiring. Each work package is independently shippable.
|
||||
|
||||
**Suggested execution order:**
|
||||
|
||||
1. WP4 (profiles/guides/skills) — pure copy, no code, fast win
|
||||
2. WP2 (packages/macp) — protocol foundation, needed by WP1 and WP3
|
||||
3. WP1 (packages/forge) — the big one, depends on WP2
|
||||
4. WP3 (plugins/mosaic-framework) — OC integration, can parallel with WP1
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `packages/coord` must be stable (it is — WP1 integrates with it)
|
||||
- `plugins/macp` must be stable (it is — WP2 provides types/credentials to it)
|
||||
- Pi SDK (`@mariozechner/pi-agent-core`) already in the dependency tree
|
||||
@@ -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:**
|
||||
|
||||
- `@mosaicstack/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": "@mosaicstack/forge",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mosaicstack/macp": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "workspace:*",
|
||||
"typescript": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only dependency: @mosaicstack/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.
|
||||
@@ -0,0 +1,150 @@
|
||||
# WP2: packages/macp — MACP Protocol Package
|
||||
|
||||
## Context
|
||||
|
||||
Port the MACP protocol layer from Python (in ~/src/mosaic-stack/tools/macp/) to TypeScript as `packages/macp` in this monorepo. This package provides the foundational protocol types, quality gate execution, credential resolution, and event system that `packages/coord` and `plugins/macp` depend on.
|
||||
|
||||
**Board decisions that constrain this work:**
|
||||
|
||||
- No Python in the new repo — everything rewrites to TypeScript
|
||||
- OpenBrain learning capture/recall is OUT OF SCOPE (deferred to future brief)
|
||||
- 85% test coverage on TS implementation files
|
||||
- Credential resolver behavior must be captured as test fixtures BEFORE rewrite
|
||||
- Clean index.ts exports, no internal path leakage
|
||||
|
||||
## Source Files (Python → TypeScript)
|
||||
|
||||
### 1. credential-resolver.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/tools/macp/dispatcher/credential_resolver.py`
|
||||
|
||||
Resolution order (MUST preserve exactly):
|
||||
|
||||
1. Mosaic credential files (`~/.config/mosaic/credentials/{provider}.env`)
|
||||
2. OpenClaw config (`~/.openclaw/openclaw.json`) — env block + models.providers.{provider}.apiKey
|
||||
3. Ambient environment variables
|
||||
4. CredentialError (failure)
|
||||
|
||||
Key behaviors to preserve:
|
||||
|
||||
- Provider registry: anthropic, openai, zai → env var names + credential file paths + OC config paths
|
||||
- Dotenv parser: handles single/double quotes, comments, blank lines
|
||||
- JSON5 stripping: placeholder-extraction approach (NOT naive regex) — protects URLs and timestamps inside string values
|
||||
- OC config permission check: warn on world-readable, skip if wrong owner
|
||||
- Redacted marker detection: `__OPENCLAW_REDACTED__` values skipped
|
||||
- Task-level override via `credentials.provider_key_env`
|
||||
|
||||
### 2. gate-runner.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/tools/macp/controller/gate_runner.py`
|
||||
|
||||
Three gate types:
|
||||
|
||||
- `mechanical`: shell command, pass = exit code 0
|
||||
- `ai-review`: shell command producing JSON, parse findings, fail on blockers
|
||||
- `ci-pipeline`: placeholder (always passes for now)
|
||||
|
||||
Key behaviors:
|
||||
|
||||
- `normalize_gate()`: accepts string or dict, normalizes to gate entry
|
||||
- `run_gate()`: executes single gate, returns result with pass/fail
|
||||
- `run_gates()`: executes all gates, emits events, returns (all_passed, results)
|
||||
- AI review parsing: `_count_ai_findings()` reads stats.blockers or findings[].severity
|
||||
- `fail_on` modes: "blocker" (default) or "any"
|
||||
|
||||
### 3. event-emitter.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/tools/macp/controller/gate_runner.py` (emit_event, append_event functions) + `~/src/mosaic-stack/tools/macp/events/`
|
||||
|
||||
- Append structured events to ndjson file
|
||||
- Event types: task.assigned, task.started, task.completed, task.failed, task.escalated, task.gated, task.retry.scheduled, rail.check.started, rail.check.passed, rail.check.failed
|
||||
- Each event: event_id (uuid), event_type, task_id, status, timestamp, source, message, metadata
|
||||
|
||||
### 4. types.ts
|
||||
|
||||
**Source:** `~/src/mosaic-stack/tools/macp/protocol/task.schema.json`
|
||||
|
||||
TypeScript types for:
|
||||
|
||||
- Task (id, title, status, dispatch, runtime, depends_on, depends_on_policy, quality_gates, timeout_seconds, metadata, etc.)
|
||||
- Event (event_id, event_type, task_id, status, timestamp, source, message, metadata)
|
||||
- GateResult (command, exit_code, type, passed, output, findings, blockers)
|
||||
- TaskResult (task_id, status, completed_at, exit_code, gate_results, files_changed, etc.)
|
||||
- CredentialError, ProviderRegistry
|
||||
|
||||
### 5. schemas/ (copy)
|
||||
|
||||
Copy `~/src/mosaic-stack/tools/macp/protocol/task.schema.json` as-is.
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
packages/macp/
|
||||
├── src/
|
||||
│ ├── index.ts
|
||||
│ ├── types.ts
|
||||
│ ├── credential-resolver.ts
|
||||
│ ├── gate-runner.ts
|
||||
│ ├── event-emitter.ts
|
||||
│ └── schemas/
|
||||
│ └── task.schema.json
|
||||
├── __tests__/
|
||||
│ ├── credential-resolver.test.ts
|
||||
│ ├── gate-runner.test.ts
|
||||
│ └── event-emitter.test.ts
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── vitest.config.ts
|
||||
```
|
||||
|
||||
## Package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@mosaicstack/macp",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"vitest": "workspace:*",
|
||||
"typescript": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Zero external dependencies. Uses node:fs, node:path, node:child_process, node:crypto only.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
Port ALL existing Python tests as TypeScript equivalents:
|
||||
|
||||
- `test_resolve_from_file` → credential file resolution
|
||||
- `test_resolve_from_ambient` → ambient env resolution
|
||||
- `test_resolve_from_oc_config_env_block` → OC config env block
|
||||
- `test_resolve_from_oc_config_provider_apikey` → OC config provider
|
||||
- `test_oc_config_precedence` → mosaic file wins over OC config
|
||||
- `test_oc_config_missing_file` → graceful fallback
|
||||
- `test_json5_strip` → structural transforms
|
||||
- `test_json5_strip_urls_and_timestamps` → URLs/timestamps survive
|
||||
- `test_redacted_values_skipped` → redacted marker detection
|
||||
- `test_oc_config_permission_warning` → file permission check
|
||||
- `test_resolve_missing_raises` → CredentialError thrown
|
||||
- Gate runner: mechanical pass/fail, AI review parsing, ci-pipeline placeholder
|
||||
- Event emitter: append to ndjson, event structure validation
|
||||
|
||||
## ESM Requirements
|
||||
|
||||
- `"type": "module"` in package.json
|
||||
- NodeNext module resolution in tsconfig
|
||||
- `.js` extensions in all imports
|
||||
- No CommonJS (`require`, `module.exports`)
|
||||
|
||||
## Integration Points
|
||||
|
||||
After this package is built:
|
||||
|
||||
- `packages/coord` should import `@mosaicstack/macp` for event emission and gate types
|
||||
- `plugins/macp` should import `@mosaicstack/macp` for credential resolution and protocol types
|
||||
@@ -0,0 +1,63 @@
|
||||
# WP3: plugins/mosaic-framework — OC Rails Injection Plugin
|
||||
|
||||
## Context
|
||||
|
||||
Port the OpenClaw framework plugin from ~/src/mosaic-stack/oc-plugins/mosaic-framework/ to `plugins/mosaic-framework` in this monorepo. This plugin injects Mosaic framework contracts (rails, completion gates, worktree requirements) into every OpenClaw agent session.
|
||||
|
||||
**This is SEPARATE from plugins/macp:**
|
||||
|
||||
- `mosaic-framework` = passive enforcement — injects rails into all OC sessions
|
||||
- `macp` = active runtime — provides ACP backend for MACP task execution
|
||||
|
||||
## Source Files
|
||||
|
||||
**Source:** `~/src/mosaic-stack/oc-plugins/mosaic-framework/`
|
||||
|
||||
- `index.ts` — plugin hooks (before_agent_start, subagent_spawning)
|
||||
- `openclaw.plugin.json` — plugin manifest
|
||||
- `package.json`
|
||||
|
||||
## What It Does
|
||||
|
||||
### For OC native agents (before_agent_start hook):
|
||||
|
||||
- Injects Mosaic global hard rules via `appendSystemContext`
|
||||
- Completion gates: code review ✓ | security review ✓ | tests GREEN ✓ | CI green ✓
|
||||
- Worker completion protocol: open PR → fire system event → EXIT — never merge
|
||||
- Worktree requirement: `~/src/{repo}-worktrees/{task-slug}`, never `/tmp`
|
||||
- Injects dynamic mission state via `prependContext` (reads from project's `.mosaic/orchestrator/mission.json`)
|
||||
|
||||
### For ACP coding workers (subagent_spawning hook):
|
||||
|
||||
- Writes `~/.codex/instructions.md` or `~/.claude/CLAUDE.md` BEFORE the process starts
|
||||
- Full runtime contract: mandatory load order, hard gates, mode declaration
|
||||
- Global framework rules + worktree + completion gate requirements
|
||||
|
||||
## Implementation
|
||||
|
||||
Port the TypeScript source, updating hardcoded paths to be configurable. The OC plugin SDK imports should reference the installed OpenClaw location dynamically (not hardcoded `/home/jarvis/` paths like the OLD version).
|
||||
|
||||
**Structure:**
|
||||
|
||||
```
|
||||
plugins/mosaic-framework/
|
||||
├── src/
|
||||
│ └── index.ts
|
||||
├── openclaw.plugin.json
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
## Key Constraint
|
||||
|
||||
The plugin SDK imports in the OLD version use absolute paths:
|
||||
|
||||
```typescript
|
||||
import type { OpenClawPluginApi } from '/home/jarvis/.npm-global/lib/node_modules/openclaw/dist/plugin-sdk/index.js';
|
||||
```
|
||||
|
||||
This must be resolved dynamically or via a peer dependency. Check how `plugins/macp` handles this in the new repo and follow the same pattern.
|
||||
|
||||
## Tests
|
||||
|
||||
Minimal — plugin hooks are integration-tested against OC runtime. Unit test the context string builders and config resolution.
|
||||
Reference in New Issue
Block a user