diff --git a/bin/mosaic b/bin/mosaic index 9a773f9..3c7499b 100755 --- a/bin/mosaic +++ b/bin/mosaic @@ -343,10 +343,16 @@ launch_codex() { # Codex reads from ~/.codex/instructions.md ensure_runtime_config "codex" "$HOME/.codex/instructions.md" + _detect_mission_prompt _write_launcher_session_lock "codex" trap _cleanup_session_lock EXIT INT TERM - echo "[mosaic] Launching Codex..." - exec codex "$@" + if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then + echo "[mosaic] Launching Codex (active mission detected)..." + exec codex "$MOSAIC_MISSION_PROMPT" + else + echo "[mosaic] Launching Codex..." + exec codex "$@" + fi } launch_yolo() { @@ -391,10 +397,16 @@ launch_yolo() { # Codex reads instructions.md from ~/.codex and supports a direct dangerous flag. ensure_runtime_config "codex" "$HOME/.codex/instructions.md" + _detect_mission_prompt _write_launcher_session_lock "codex" trap _cleanup_session_lock EXIT INT TERM - echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..." - exec codex --dangerously-bypass-approvals-and-sandbox "$@" + if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then + echo "[mosaic] Launching Codex in YOLO mode (active mission detected)..." + exec codex --dangerously-bypass-approvals-and-sandbox "$MOSAIC_MISSION_PROMPT" + else + echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..." + exec codex --dangerously-bypass-approvals-and-sandbox "$@" + fi ;; opencode) check_mosaic_home diff --git a/bin/mosaic-ensure-sequential-thinking.ps1 b/bin/mosaic-ensure-sequential-thinking.ps1 index f252184..4282c64 100755 --- a/bin/mosaic-ensure-sequential-thinking.ps1 +++ b/bin/mosaic-ensure-sequential-thinking.ps1 @@ -1,10 +1,10 @@ # mosaic-ensure-sequential-thinking.ps1 -$ErrorActionPreference = "Stop" - param( [switch]$Check ) +$ErrorActionPreference = "Stop" + $Pkg = "@modelcontextprotocol/server-sequential-thinking" function Require-Binary { @@ -43,7 +43,7 @@ function Set-CodexConfig { $content = Get-Content $path -Raw $content = [regex]::Replace($content, "(?ms)^\[mcp_servers\.(sequential-thinking|sequential_thinking)\].*?(?=^\[|\z)", "") - $content = $content.TrimEnd() + "`n`n[mcp_servers.sequential-thinking]`ncommand = \"npx\"`nargs = [\"-y\", \"@modelcontextprotocol/server-sequential-thinking\"]`n" + $content = $content.TrimEnd() + "`n`n[mcp_servers.sequential-thinking]`ncommand = `"npx`"`nargs = [`"-y`", `"@modelcontextprotocol/server-sequential-thinking`"]`n" Set-Content -Path $path -Value $content -Encoding UTF8 } diff --git a/bin/mosaic.ps1 b/bin/mosaic.ps1 index 87e9836..fcc0bfb 100644 --- a/bin/mosaic.ps1 +++ b/bin/mosaic.ps1 @@ -96,6 +96,88 @@ function Assert-SequentialThinking { } } +function Get-ActiveMission { + $missionFile = Join-Path (Get-Location) ".mosaic\orchestrator\mission.json" + if (-not (Test-Path $missionFile)) { + return $null + } + + try { + $mission = Get-Content $missionFile -Raw | ConvertFrom-Json + } + catch { + return $null + } + + $status = [string]$mission.status + if ([string]::IsNullOrWhiteSpace($status)) { + $status = "inactive" + } + if ($status -ne "active" -and $status -ne "paused") { + return $null + } + + $name = [string]$mission.name + if ([string]::IsNullOrWhiteSpace($name)) { + $name = "unnamed" + } + + $id = [string]$mission.mission_id + if ([string]::IsNullOrWhiteSpace($id)) { + $id = "" + } + + $milestones = @($mission.milestones) + $milestoneCount = $milestones.Count + $milestoneCompleted = @($milestones | Where-Object { $_.status -eq "completed" }).Count + + return [PSCustomObject]@{ + Name = $name + Id = $id + Status = $status + MilestoneCount = $milestoneCount + MilestoneCompleted = $milestoneCompleted + } +} + +function Get-MissionContractBlock { + $mission = Get-ActiveMission + if ($null -eq $mission) { + return "" + } + + return @" +# ACTIVE MISSION — HARD GATE (Read Before Anything Else) + +An active orchestration mission exists in this project. This is a BLOCKING requirement. + +**Mission:** $($mission.Name) +**ID:** $($mission.Id) +**Status:** $($mission.Status) +**Milestones:** $($mission.MilestoneCompleted) / $($mission.MilestoneCount) completed + +## MANDATORY — Before ANY Response to the User + +You MUST complete these steps before responding to any user message, including simple greetings: + +1. Read `~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md` (mission lifecycle protocol) +2. Read `docs/MISSION-MANIFEST.md` for full mission scope, milestones, and success criteria +3. Read the latest scratchpad in `docs/scratchpads/` for session history, decisions, and corrections +4. Read `docs/TASKS.md` for current task state (what is done, what is next) +5. After reading all four, acknowledge the mission state to the user before proceeding + +If the user gives a task, execute it within the mission context. If no task is given, present mission status and ask how to proceed. +"@ +} + +function Get-MissionPrompt { + $mission = Get-ActiveMission + if ($null -eq $mission) { + return "" + } + return "Active mission detected: $($mission.Name). Read the mission state files and report status." +} + function Get-RuntimePrompt { param( [ValidateSet("claude", "codex", "opencode")] @@ -130,8 +212,14 @@ For required push/merge/issue-close/release actions, execute without routine con '@ + $missionBlock = Get-MissionContractBlock $agentsContent = Get-Content (Join-Path $MosaicHome "AGENTS.md") -Raw $runtimeContent = Get-Content $runtimeFile -Raw + + if (-not [string]::IsNullOrWhiteSpace($missionBlock)) { + return "$missionBlock`n`n$launcherContract`n$agentsContent`n`n# Runtime-Specific Contract`n`n$runtimeContent" + } + return "$launcherContract`n$agentsContent`n`n# Runtime-Specific Contract`n`n$runtimeContent" } @@ -170,7 +258,7 @@ function Invoke-Yolo { } $runtime = $YoloArgs[0] - $tail = if ($YoloArgs.Count -gt 1) { $YoloArgs[1..($YoloArgs.Count - 1)] } else { @() } + $tail = if ($YoloArgs.Count -gt 1) { @($YoloArgs[1..($YoloArgs.Count - 1)]) } else { @() } switch ($runtime) { "claude" { @@ -191,8 +279,15 @@ function Invoke-Yolo { Assert-Runtime "codex" Assert-SequentialThinking Ensure-RuntimeConfig -Runtime "codex" -Dst (Join-Path $env:USERPROFILE ".codex\instructions.md") - Write-Host "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..." - & codex --dangerously-bypass-approvals-and-sandbox @tail + $missionPrompt = Get-MissionPrompt + if (-not [string]::IsNullOrWhiteSpace($missionPrompt) -and $tail.Count -eq 0) { + Write-Host "[mosaic] Launching Codex in YOLO mode (active mission detected)..." + & codex --dangerously-bypass-approvals-and-sandbox $missionPrompt + } + else { + Write-Host "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..." + & codex --dangerously-bypass-approvals-and-sandbox @tail + } return } "opencode" { @@ -219,7 +314,7 @@ if ($args.Count -eq 0) { } $command = $args[0] -$remaining = if ($args.Count -gt 1) { $args[1..($args.Count - 1)] } else { @() } +$remaining = if ($args.Count -gt 1) { @($args[1..($args.Count - 1)]) } else { @() } switch ($command) { "claude" { @@ -252,8 +347,15 @@ switch ($command) { Assert-SequentialThinking # Codex reads from ~/.codex/instructions.md Ensure-RuntimeConfig -Runtime "codex" -Dst (Join-Path $env:USERPROFILE ".codex\instructions.md") - Write-Host "[mosaic] Launching Codex..." - & codex @remaining + $missionPrompt = Get-MissionPrompt + if (-not [string]::IsNullOrWhiteSpace($missionPrompt) -and $remaining.Count -eq 0) { + Write-Host "[mosaic] Launching Codex (active mission detected)..." + & codex $missionPrompt + } + else { + Write-Host "[mosaic] Launching Codex..." + & codex @remaining + } } "yolo" { Invoke-Yolo -YoloArgs $remaining diff --git a/guides/E2E-DELIVERY.md b/guides/E2E-DELIVERY.md index dea63b3..f2e10a1 100644 --- a/guides/E2E-DELIVERY.md +++ b/guides/E2E-DELIVERY.md @@ -38,7 +38,7 @@ First response MUST declare mode before tool calls or implementation steps: 1. Define scope, constraints, and acceptance criteria. 2. Identify affected surfaces (API, DB, UI, infra, auth, CI/CD, docs). -3. **Deployment surface check (MANDATORY if task involves deploy, images, or containers):** Before ANY build or deploy action, check for CI/CD pipeline config (`.woodpecker/`, `.woodpecker.yml`, `.github/workflows/`). If pipelines exist, CI is the canonical build path — manual `docker build`/`docker push` is forbidden. Load `~/.config/mosaic/guides/ci-cd-pipelines.md` immediately. +3. **Deployment surface check (MANDATORY if task involves deploy, images, or containers):** Before ANY build or deploy action, check for CI/CD pipeline config (`.woodpecker/`, `.woodpecker.yml`, `.github/workflows/`). If pipelines exist, CI is the canonical build path — manual `docker build`/`docker push` is forbidden. Load `~/.config/mosaic/guides/CI-CD-PIPELINES.md` immediately. 4. Identify required guides and load them before implementation. 5. For code/API/auth/infra changes, load `~/.config/mosaic/guides/DOCUMENTATION.md`. 6. Determine budget constraints: diff --git a/tools/context/mosaic-context-loader.sh b/tools/context/mosaic-context-loader.sh index 036a5e8..af328e2 100755 --- a/tools/context/mosaic-context-loader.sh +++ b/tools/context/mosaic-context-loader.sh @@ -15,11 +15,10 @@ MANDATORY_FILES=( "$MOSAIC_HOME/TOOLS.md" ) -# E2E delivery guide (case-insensitive lookup) +# E2E delivery guide (canonical uppercase path) E2E_DELIVERY="" for candidate in \ - "$MOSAIC_HOME/guides/E2E-DELIVERY.md" \ - "$MOSAIC_HOME/guides/e2e-delivery.md"; do + "$MOSAIC_HOME/guides/E2E-DELIVERY.md"; do if [[ -f "$candidate" ]]; then E2E_DELIVERY="$candidate" break