diff --git a/guides/ORCHESTRATOR.md b/guides/ORCHESTRATOR.md index bfd9792..256ec8b 100644 --- a/guides/ORCHESTRATOR.md +++ b/guides/ORCHESTRATOR.md @@ -73,6 +73,27 @@ Spawn a worker instead. No exceptions. No "quick fixes." - Wait for at least one worker to complete before spawning more - This optimizes token usage and reduces context pressure +## File Ownership & Partitioning (Hard Rule for Parallel Workers) + +When dispatching parallel workers, the orchestrator MUST assign **non-overlapping file scopes** to each worker. File collisions between parallel workers cause merge conflicts, lost edits, and wasted tokens. + +**Rules:** + +1. **Exclusive file ownership.** Each file may be assigned to at most one active worker. The orchestrator records ownership in the worker dispatch (prompt or task definition). +2. **Partition by directory or module.** Prefer assigning entire directories/modules to one worker rather than splitting files within a directory across workers. +3. **Shared files are serialized.** If two tasks must modify the same file (e.g., a shared types file, a barrel export), they MUST run sequentially — never in parallel. Mark the second task with `depends_on` pointing to the first. +4. **Test files follow source ownership.** If Worker A owns `src/auth/login.ts`, Worker A also owns `src/auth/__tests__/login.test.ts`. Do not split source and test across workers. +5. **Config files are orchestrator-reserved.** Files like `package.json`, `tsconfig.json`, and CI config are owned by the orchestrator and modified only between worker cycles, never during parallel execution. +6. **Document ownership in dispatch.** When spawning a worker, include an explicit `Files:` section listing owned paths/globs. Example: + +``` +Files (exclusive — do not touch files outside this scope): + - apps/web/src/components/auth/** + - apps/web/src/lib/auth.ts +``` + +7. **Conflict recovery.** If a worker edits a file outside its scope, the orchestrator MUST flag the violation, assess the diff, and either revert the out-of-scope change or re-run the affected worker with the corrected file. + ## Delegation Mode Selection Choose one delegation mode at session start: diff --git a/packages/mosaic/framework/defaults/AGENTS.md b/packages/mosaic/framework/defaults/AGENTS.md index 978fca6..7e0c542 100755 --- a/packages/mosaic/framework/defaults/AGENTS.md +++ b/packages/mosaic/framework/defaults/AGENTS.md @@ -151,11 +151,68 @@ When delegating work to subagents, you MUST select the cheapest model capable of **Runtime-specific syntax**: See the runtime reference for how to specify model tier when spawning subagents (e.g., Claude Code Task tool `model` parameter). +## Superpowers Enforcement (Hard Rule) + +Mosaic provides capabilities beyond basic code editing: **skills**, **hooks**, **MCP tools**, and **plugins**. These are not optional extras — they are force multipliers that agents MUST actively use when applicable. Under-utilization of superpowers is a framework violation. + +### Skills + +Skills are domain-specific instruction sets in `~/.config/mosaic/skills/` that encode best practices, patterns, and guardrails. They are loaded into agents via the runtime's skill mechanism (e.g., Claude Code slash commands, Pi `--skill` flag). + +**Rules:** + +1. Before starting implementation, scan available skills (`ls ~/.config/mosaic/skills/`) and load any that match the task domain. +2. When a skill exists for the technology being used (e.g., `nestjs-best-practices` for NestJS work), you MUST load it. +3. When spawning workers, include skill loading in the kickstart prompt. +4. If you complete a task without loading a relevant available skill, that is a quality gap. + +### Hooks + +Hooks provide automated quality gates (lint, format, typecheck) that fire on file edits. They are configured in the runtime settings and run automatically. + +**Rules:** + +1. Do NOT bypass or suppress hook output. If a hook reports errors, fix them before proceeding. +2. Hook failures are immediate feedback — treat them like failing tests. +3. If a hook is consistently failing on valid code, report it as a framework issue rather than working around it. + +### MCP Tools + +MCP servers extend agent capabilities with external integrations (sequential-thinking, web search, memory, browser automation, etc.). Available MCP tools are listed at session start. + +**Rules:** + +1. **sequential-thinking** is REQUIRED for planning, architecture, and multi-step reasoning. Use it — do not skip structured thinking for complex decisions. +2. **OpenBrain** (`capture`, `search`, `recent`) is the cross-agent memory layer. Capture discoveries and search for prior context at session start. +3. When a task involves web research, browser testing, or external data, use the available MCP tools (web-search, chrome-devtools, web-reader) rather than asking the user to look things up. +4. Check available MCP tools at session start and use them proactively throughout the session. + +### Plugins (Runtime-Specific) + +Runtime plugins (e.g., Claude Code's `feature-dev`, `pr-review-toolkit`, `code-review`) provide specialized agent capabilities like code review, architecture analysis, and test coverage analysis. + +**Rules:** + +1. After completing a significant code change, use code review plugins proactively — do not wait for the user to ask. +2. Before creating a PR, use PR review plugins to catch issues early. +3. When designing architecture, use planning/architecture plugins for structured analysis. + +### Self-Evolution + +The Mosaic framework should improve over time based on usage patterns: + +1. When you discover a recurring pattern that should be codified, capture it to OpenBrain with `type: "framework-improvement"`. +2. When a hook, skill, or tool is missing for a common task, capture the gap to OpenBrain with `type: "tooling-gap"`. +3. When a framework rule causes friction without adding value, capture the observation to OpenBrain with `type: "framework-friction"`. + +These captures feed the framework's continuous improvement cycle. + ## Skills Policy -- Use only the minimum required skills for the active task. +- Load skills that match the active task domain before starting implementation. - Do not load unrelated skills. - Follow skill trigger rules from the active runtime instruction layer. +- Actively check `~/.config/mosaic/skills/` for applicable skills rather than passively waiting for them to be mentioned. ## Session Closure Requirement diff --git a/packages/mosaic/framework/runtime/claude/RUNTIME.md b/packages/mosaic/framework/runtime/claude/RUNTIME.md index 690aca0..7e69061 100644 --- a/packages/mosaic/framework/runtime/claude/RUNTIME.md +++ b/packages/mosaic/framework/runtime/claude/RUNTIME.md @@ -102,3 +102,30 @@ claude mcp add --scope user -- npx -y `--scope local` = default, local-only (not committed). Do NOT add `mcpServers` to `~/.claude/settings.json` — that key is ignored for MCP loading. + +## Required Claude Code Settings (Enforced by Launcher) + +The `mosaic claude` launcher validates that `~/.claude/settings.json` contains the required Mosaic configuration. Missing or outdated settings trigger a warning at launch. + +**Required hooks:** + +| Event | Matcher | Script | Purpose | +| ----------- | ------------------------ | ------------------------- | ---------------------------------------------- | +| PreToolUse | `Write\|Edit\|MultiEdit` | `prevent-memory-write.sh` | Block writes to `~/.claude/projects/*/memory/` | +| PostToolUse | `Edit\|MultiEdit\|Write` | `qa-hook-stdin.sh` | QA report generation after code edits | +| PostToolUse | `Edit\|MultiEdit\|Write` | `typecheck-hook.sh` | Inline TypeScript type checking | + +**Required plugins:** + +| Plugin | Purpose | +| ------------------- | -------------------------------------------------------------------------------------------------------- | +| `feature-dev` | Subagent architecture: code-reviewer, code-architect, code-explorer | +| `pr-review-toolkit` | PR review: code-simplifier, comment-analyzer, test-analyzer, silent-failure-hunter, type-design-analyzer | +| `code-review` | Standalone code review capabilities | + +**Required settings:** + +- `enableAllMcpTools: true` — Allow all configured MCP tools without per-tool approval +- `model: "opus"` — Default to opus for orchestrator-level sessions (workers use tiered models via Task tool) + +If `mosaic claude` detects missing hooks or plugins, it will print a warning with the exact settings to add. The session will still launch — enforcement is advisory, not blocking — but agents operating without these settings are running degraded. diff --git a/packages/mosaic/framework/runtime/claude/settings.json b/packages/mosaic/framework/runtime/claude/settings.json index 4978675..557fcbc 100644 --- a/packages/mosaic/framework/runtime/claude/settings.json +++ b/packages/mosaic/framework/runtime/claude/settings.json @@ -23,6 +23,16 @@ "timeout": 60 } ] + }, + { + "matcher": "Edit|MultiEdit|Write", + "hooks": [ + { + "type": "command", + "command": "~/.config/mosaic/tools/qa/typecheck-hook.sh", + "timeout": 30 + } + ] } ] }, diff --git a/packages/mosaic/framework/tools/qa/typecheck-hook.sh b/packages/mosaic/framework/tools/qa/typecheck-hook.sh new file mode 100755 index 0000000..c463ad8 --- /dev/null +++ b/packages/mosaic/framework/tools/qa/typecheck-hook.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Lightweight PostToolUse typecheck hook for TypeScript files. +# Runs tsc --noEmit on the nearest tsconfig after TS/TSX edits. +# Returns non-zero with diagnostic output so the agent sees type errors immediately. +# Location: ~/.config/mosaic/tools/qa/typecheck-hook.sh + +set -eo pipefail + +# Read JSON from stdin (Claude Code PostToolUse payload) +JSON_INPUT=$(cat) + +# Extract file path +if command -v jq &>/dev/null; then + FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // .tool_response.filePath // .file_path // empty' 2>/dev/null || echo "") +else + FILE_PATH=$(echo "$JSON_INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)"$/\1/' | head -1) +fi + +# Only check TypeScript files +if ! [[ "$FILE_PATH" =~ \.(ts|tsx)$ ]]; then + exit 0 +fi + +# Must be a real file +if [ ! -f "$FILE_PATH" ]; then + exit 0 +fi + +# Find nearest tsconfig.json by walking up from the file +DIR=$(dirname "$FILE_PATH") +TSCONFIG="" +while [ "$DIR" != "/" ] && [ "$DIR" != "." ]; do + if [ -f "$DIR/tsconfig.json" ]; then + TSCONFIG="$DIR/tsconfig.json" + break + fi + DIR=$(dirname "$DIR") +done + +if [ -z "$TSCONFIG" ]; then + # No tsconfig found — skip silently + exit 0 +fi + +# Run tsc --noEmit from the tsconfig directory +# Use --pretty for readable output, limit to 10 errors to keep output short +TSCONFIG_DIR=$(dirname "$TSCONFIG") +cd "$TSCONFIG_DIR" + +# Run typecheck — capture output and exit code +OUTPUT=$(npx tsc --noEmit --pretty --maxNodeModuleJsDepth 0 2>&1) || STATUS=$? + +if [ "${STATUS:-0}" -ne 0 ]; then + # Filter output to only show errors related to the edited file (if possible) + BASENAME=$(basename "$FILE_PATH") + RELEVANT=$(echo "$OUTPUT" | grep -A2 "$BASENAME" 2>/dev/null || echo "$OUTPUT" | head -20) + + echo "TypeScript type errors detected after editing $FILE_PATH:" + echo "$RELEVANT" + exit 1 +fi + +exit 0 diff --git a/packages/mosaic/src/commands/launch.ts b/packages/mosaic/src/commands/launch.ts index 0ea9dd4..fe7c57d 100644 --- a/packages/mosaic/src/commands/launch.ts +++ b/packages/mosaic/src/commands/launch.ts @@ -78,6 +78,82 @@ function checkSoul(): void { } } +// ─── Claude settings validation ───────────────────────────────────────────── + +interface SettingsAudit { + warnings: string[]; +} + +function auditClaudeSettings(): SettingsAudit { + const warnings: string[] = []; + const settingsPath = join(homedir(), '.claude', 'settings.json'); + const settings = readJson(settingsPath); + + if (!settings) { + warnings.push('~/.claude/settings.json not found — hooks and plugins will be missing'); + return { warnings }; + } + + // Check required hooks + const hooks = settings['hooks'] as Record | undefined; + + const requiredPreToolUse = ['prevent-memory-write.sh']; + const requiredPostToolUse = ['qa-hook-stdin.sh', 'typecheck-hook.sh']; + + const preHooks = (hooks?.['PreToolUse'] ?? []) as Array>; + const postHooks = (hooks?.['PostToolUse'] ?? []) as Array>; + + const preCommands = preHooks.flatMap((h) => { + const inner = (h['hooks'] ?? []) as Array>; + return inner.map((ih) => String(ih['command'] ?? '')); + }); + const postCommands = postHooks.flatMap((h) => { + const inner = (h['hooks'] ?? []) as Array>; + return inner.map((ih) => String(ih['command'] ?? '')); + }); + + for (const script of requiredPreToolUse) { + if (!preCommands.some((c) => c.includes(script))) { + warnings.push(`Missing PreToolUse hook: ${script}`); + } + } + for (const script of requiredPostToolUse) { + if (!postCommands.some((c) => c.includes(script))) { + warnings.push(`Missing PostToolUse hook: ${script}`); + } + } + + // Check required plugins + const plugins = (settings['enabledPlugins'] ?? {}) as Record; + const requiredPlugins = ['feature-dev', 'pr-review-toolkit', 'code-review']; + + for (const plugin of requiredPlugins) { + const found = Object.keys(plugins).some((k) => k.startsWith(plugin) && plugins[k]); + if (!found) { + warnings.push(`Missing plugin: ${plugin}`); + } + } + + // Check enableAllMcpTools + if (!settings['enableAllMcpTools']) { + warnings.push('enableAllMcpTools is not true — MCP tools may require per-tool approval'); + } + + return { warnings }; +} + +function printSettingsWarnings(audit: SettingsAudit): void { + if (audit.warnings.length === 0) return; + + console.log('\n[mosaic] Claude Code settings audit:'); + for (const w of audit.warnings) { + console.log(` ⚠ ${w}`); + } + console.log( + '[mosaic] Run: mosaic doctor — or see ~/.config/mosaic/runtime/claude/RUNTIME.md for required settings.\n', + ); +} + function checkSequentialThinking(runtime: string): void { const checker = fwScript('mosaic-ensure-sequential-thinking'); if (!existsSync(checker)) return; // Skip if checker doesn't exist @@ -407,6 +483,10 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev switch (runtime) { case 'claude': { + // Audit Claude Code settings and warn about missing hooks/plugins + const settingsAudit = auditClaudeSettings(); + printSettingsWarnings(settingsAudit); + const prompt = buildRuntimePrompt('claude'); const cliArgs = yolo ? ['--dangerously-skip-permissions'] : []; cliArgs.push('--append-system-prompt', prompt);