Entry points:
$maestro-ralph-beta "intent"— New session: infer → decompose → build → tick$maestro-ralph-beta continue— Resume: locate session → tick$maestro-ralph-beta status— Display session progress
Tick kinds:
- Execute step (
step.decision == null):maestro ralph next→ inline →maestro ralph complete→ self-invoke - decision step (
step.decision != null): inline evaluate → apply verdict → self-invoke
Session: .workflow/.maestro/ralph-{YYYYMMDD-HHmmss}/status.json
Parse:
-y flag → auto_confirm = true
.md/.txt path → input_doc (supplementary context only, NEVER substitutes lifecycle stages)
"status" → status mode
"continue" → resume mode
Remaining → intent (new session)
State files:
.workflow/state.json— artifact registry, milestones, phases.workflow/roadmap.md— milestone/phase structure.workflow/.maestro/ralph-*/status.json— ralph session state
Each time entering skill, first execute router, match in order, first hit wins. Terms:
- active session =
.workflow/.maestro/ralph-*/status.jsoninstatus ∈ {running, paused}'s latest session - live session = active session and
status == "running" - active_step =
session.steps[session.active_step_index]
1. intent == "status" → S_STATUS → END
2. intent == "continue" AND active session exists → A_RESUME_SESSION → S_TICK_LOCATE
3. intent non-empty AND intent ∉ {"continue","status"} AND active session exists
→ S_FALLBACK → END
display "已有 active session {id};先 $maestro-ralph-beta continue 续跑或显式 abandon"
4. live session AND active_step.status == "running" AND active_step.decision != null
→ S_TICK_LOCATE → S_TICK_DECISION
5. live session AND has pending step → S_TICK_LOCATE
6. live session AND all completion_confirmed → S_COMPLETE → END
7. active session AND session.status == "paused" AND no intent → S_FALLBACK → END
display "Session {id} paused;输入 $maestro-ralph-beta continue 显式恢复"
8. no active session AND intent non-empty → S_BUILD_PHASE
9. no active session AND no intent → S_FALLBACK → END
<state_machine>
Tick phase (per step)S_TICK_LOCATE — locate session + find active step PERSIST: — S_TICK — dispatch execute step or decision step PERSIST: step.status, active_step_index S_TICK_EXEC — execute step: ralph next → inline → ralph complete PERSIST: via CLI S_TICK_DECISION — decision step: inline evaluation + apply verdict PERSIST: session.steps[], passed_gates, decisions.ndjson S_HANDLE_FAIL — handle execution failure PERSIST: step.status, session.status
TerminalS_STATUS — display session progress PERSIST: — S_COMPLETE — wrap up PERSIST: session.status = "completed" S_FALLBACK — guide user input or exit PERSIST: —
=== Build phase ===S_BUILD_PHASE (entry): → S_RESOLVE_PHASE
S_RESOLVE_PHASE: → S_INFER WHEN: phase resolved or null DO: A_RESOLVE_PHASE → S_FALLBACK WHEN: ambiguous GUARD: auto_confirm does NOT skip phase ambiguity
S_INFER: → S_RESOLVE_SCOPE WHEN: position resolved DO: A_INFER_POSITION → S_FALLBACK WHEN: cannot infer
S_RESOLVE_SCOPE: → S_QUALITY_MODE DO: A_RESOLVE_SCOPE_VERDICT GUARD: position ∈ {brainstorm, blueprint, init} → skip (scope_verdict = null)
S_QUALITY_MODE: → S_PLANNING_MODE DO: A_DETERMINE_QUALITY_MODE
S_PLANNING_MODE: → S_DECOMPOSE DO: A_DETERMINE_PLANNING_MODE GUARD: lifecycle_position ∈ {brainstorm, blueprint, init, analyze-macro, roadmap} → skip (force independent)
S_DECOMPOSE: → S_BUILD_CHAIN DO: A_DECOMPOSE_TASKS GUARD: broad intent → MUST clarify boundary even if auto_confirm GUARD: narrow intent → auto-derive, skip questions GUARD: position ∈ {brainstorm, blueprint, init} → skip decomposition
S_BUILD_CHAIN: → S_CREATE_SESSION DO: A_BUILD_STEPS
S_CREATE_SESSION: → S_CONFIRM WHEN: not auto_confirm DO: A_CREATE_SESSION → S_TICK_LOCATE WHEN: auto_confirm DO: A_CREATE_SESSION + $maestro-ralph-beta
S_CONFIRM: → S_TICK_LOCATE WHEN: user selects "Proceed" DO: $maestro-ralph-beta → S_BUILD_CHAIN WHEN: user selects "Edit" → END WHEN: user selects "Cancel"
=== Tick phase ===S_TICK_LOCATE: Entry: A_LOCATE_SESSION → S_TICK WHEN: next_pending_step != null → S_COMPLETE WHEN: next_pending_step == null → S_FALLBACK WHEN: no active session
S_TICK: → S_TICK_DECISION WHEN: next_pending_step.decision != null DO: A_CLAIM_DECISION → S_TICK_EXEC WHEN: next_pending_step.decision == null DO: A_RESOLVE_ARGS
S_TICK_EXEC: Entry: A_EXEC_STEP → S_TICK_LOCATE WHEN: ralph complete with DONE|DONE_WITH_CONCERNS DO: $maestro-ralph-beta → S_TICK_LOCATE WHEN: ralph next exit == 2 DO: $maestro-ralph-beta → S_HANDLE_FAIL WHEN: ralph next exit == 1 OR exit >= 3 → S_HANDLE_FAIL WHEN: ralph complete with NEEDS_RETRY|BLOCKED
S_TICK_DECISION: (gate name taken from step.decision)
→ S_TICK_APPLY WHEN: quality-gate (post-execute, post-business-test, post-review, post-test)
DO: A_DELEGATE_EVALUATE
→ S_TICK_APPLY WHEN: goal-gate (post-goal-audit)
DO: A_GOAL_AUDIT_EVALUATE
→ S_TICK_APPLY WHEN: scope-gate (post-analyze-scope)
DO: A_SCOPE_EVALUATE
→ S_TICK_APPLY WHEN: structural (post-milestone, post-debug-escalate)
DO: A_STRUCTURAL_EVALUATE
S_TICK_APPLY: → S_TICK_LOCATE WHEN: verdict == "proceed" DO: A_APPLY_PROCEED + $maestro-ralph-beta → S_TICK_LOCATE WHEN: post-goal-audit + unmet sub-goals DO: A_APPLY_GOAL_FIX + $maestro-ralph-beta → S_TICK_LOCATE WHEN: post-goal-audit + all sub-goals met DO: A_APPLY_GOAL_DONE + $maestro-ralph-beta → S_TICK_LOCATE WHEN: post-analyze-scope DO: A_APPLY_SCOPE_VERDICT + $maestro-ralph-beta → S_TICK_LOCATE WHEN: verdict == "fix" DO: A_APPLY_FIX + $maestro-ralph-beta → S_TICK_LOCATE WHEN: verdict == "escalate" DO: A_APPLY_ESCALATE + $maestro-ralph-beta → S_TICK_LOCATE WHEN: post-milestone + standard + next milestone DO: A_ADVANCE_MILESTONE + $maestro-ralph-beta → END WHEN: post-milestone + standard + no next milestone DO: mark completed → END WHEN: post-milestone + adhoc DO: mark completed (adhoc self-contained) → END WHEN: post-debug-escalate (always STOP) DO: A_PAUSE_ESCALATE GUARD: retry_count >= max_retries → force escalate GUARD: confidence_score < 60 AND proceed → override to fix GUARD: confidence_score > 95 AND fix AND retry > 0 → suggest proceed GUARD: auto_confirm → skip user prompt, apply adjusted verdict GUARD: not auto_confirm → request_user_input with override options
S_HANDLE_FAIL: → S_TICK_LOCATE WHEN: auto + not retried DO: A_RETRY + $maestro-ralph-beta → END WHEN: auto + retried DO: A_PAUSE_SESSION → S_TICK_LOCATE WHEN: interactive + user selects retry DO: A_RETRY + $maestro-ralph-beta → S_TICK_LOCATE WHEN: interactive + user selects skip DO: A_SKIP_STEP + $maestro-ralph-beta → END WHEN: interactive + user selects abort DO: A_PAUSE_SESSION
=== Terminal ===S_STATUS: → END DO: A_SHOW_STATUS
S_COMPLETE: → END DO: A_COMPLETE_SESSION
S_FALLBACK: → S_BUILD_PHASE WHEN: user provides intent (no active session) DO: request_user_input → S_TICK_LOCATE WHEN: user selects "continue active session" DO: A_RESUME_SESSION + $maestro-ralph-beta → END WHEN: user cancels OR no active session for resume
A_SHOW_STATUS
- Find latest ralph session (by created_at)
- Display: Session, Status, Position, Progress, Current step
- List steps: [✓] completion_confirmed, [▸] current, [ ] pending, [◆] decision(
step.decisionnon-empty); execute step appendscommand_scope(global/project) +command_path - If
task_decompositionpresent (absent → skip):Sub-goals ({done}/{total}) source: {session_dir}/status.json#/task_decomposition [x] G1 done_when={done_when} evidence={evidence} confirmed={completion_confirmed} [ ] G2 done_when={done_when} evidence={evidence} confirmed=false ◀ unmet
A_RESOLVE_PHASE
Produces phase + phase_is_new + milestone(D-007 reverse lookup).
Priority:
| Step | Behavior | phase_is_new |
|---|---|---|
| 1 | intent matches phase\s*(\d+) → take the corresponding phase from state.json |
false |
| 2 | intent-derived phrase → look up in state.json.milestones[*].phase_slugs / artifacts[*].path search |
false (match) / true (no match) |
| 3 | not derived → take the phase of the latest in-progress artifact | false |
| 4 | still none → first incomplete phase in state.json | false |
| 5 | position will be brainstorm/blueprint/init/roadmap/analyze-macro → phase = null | n/a |
| 6 | still ambiguous → request_user_input |
determined by user's answer |
D-007 Phase→Milestone reverse lookup(when numeric phase has been resolved):
resolve_milestone(phase_number):
for ms in state.json.milestones:
if str(phase_number) in ms.phase_slugs: return ms.id
return state.json.current_milestone # fallback
Write session.milestone。
Write session: phase, phase_is_new, milestone。
Milestone handling when deriving a new phase:
- state.json current milestone still active → reuse, add new phase
- intent-derived new milestone name → write into session as label only;
state.json.milestonesbymaestro-roadmap/maestro-milestone-releasecreated
A_INFER_POSITION
Intent-based overrides (match in order, first hit wins):
| Pattern | Position |
|---|---|
| brainstorm / 头脑风暴 / explore / ideate / design approach | brainstorm |
| blueprint / spec / formal document / spec-generate / 7-phase | blueprint |
| broad/medium intent without numeric phase (refactor/overall/rewrite/migrate/new feature X) | analyze-macro |
Bootstrap detection:
| Condition | Position |
|---|---|
No .workflow/ + no source files |
brainstorm |
No .workflow/ + has source files |
init |
Has .workflow/ but no state.json |
init |
| Has state.json | → phase-aware artifact inference |
Phase-aware artifact inference(based on session.phase + session.phase_is_new, artifact filtered by session.phase ):
| Condition | Position |
|---|---|
phase_is_new == true |
analyze |
| no milestones AND no roadmap.md AND has analyze macro artifact | roadmap |
| no milestones AND no roadmap.md AND no analyze artifact | analyze-macro |
phase == null |
n/a |
| phase already exists + no artifact at all | analyze |
| phase already exists + latest artifact = analyze | plan |
| phase already exists + latest artifact = plan | execute |
| phase already exists + latest artifact = execute | review |
Refine from review results:
| Condition | Position |
|---|---|
| review.json: verdict=="BLOCK" | review-failed |
| review.json: verdict!="BLOCK" | test |
| uat.md: all passed | milestone-audit |
| uat.md: has failures | test-failed |
A_RESOLVE_SCOPE_VERDICT
Only when lifecycle_position ∈ {analyze-macro, roadmap, plan} and a latest analyze artifact exists.
- Locate latest macro analyze artifact (
type=="analyze"andscope=="macro", by created_at DESC) →session.analyze_macro_id = ANL-xxx - read
{artifact_path}/conclusions.json'sscope_verdictfield (large | medium | small) - write
session.scope_verdict; missing →unknown - route suggestion:
| scope_verdict | path |
|---|---|
large |
analyze-macro → roadmap → analyze → plan → execute → ... |
medium / small |
analyze-macro → plan --from analyze:{ANL_ID} → execute → ... |
unknown |
default large; corrected later by post-analyze-scope node |
A_DETERMINE_QUALITY_MODE
Read session.quality_mode_override(CLI --quality), if not, infer according to the rules:
| Condition | Mode | Pipeline (after execute) |
|---|---|---|
Has specs/REQ-*.md + current phase business scope is clear |
full |
business-test → review → test-gen → test |
| Default | standard |
review → test-gen (when coverage<80%) → test |
--quality quick |
quick |
review --tier quick |
Write to session.quality_mode。
A_DETERMINE_PLANNING_MODE
Auto-resolve rules (by priority):
| Condition | Mode |
|---|---|
| lifecycle_position ∈ {brainstorm, init, roadmap} | independent |
phase_is_new == true |
independent |
| intent explicitly specifies a phase number (e.g. "phase 2", "P3") | independent |
| milestone contains only 1 phase | independent |
milestone contains multiple phases + auto_confirm |
unified |
milestone contains multiple phases + non- auto_confirm |
→ request_user_input |
request_user_input (only when milestone contains ≥2 phases and is not auto_confirm):
question: "当前里程碑含 {N} 个 phase,选择规划模式?"
options:
- label: "统一规划 (Recommended)"
description: "一次性分析+规划整个里程碑所有 phase,analyze/plan 走里程碑级,适合 phase 间关联紧密"
- label: "独立规划"
description: "逐个 phase 走完整生命周期(analyze→plan→execute→verify→...),适合 phase 间独立性高"
Write to session.planning_mode("unified" or "independent")。
A_DECOMPOSE_TASKS
1. Classify intent breadth:
| Pattern | Breadth | Clarify? |
|---|---|---|
| refactor/comprehensive/rewrite/redo/overall/migrate · overhaul/migrate/rewrite/revamp | broad | MUST (ignores auto_confirm) |
| named single file/function/bug, "fix X", "add Y to Z" | narrow | skip — auto-derive |
| otherwise | medium | clarify unless auto_confirm |
2. Clarify boundary (broad/medium) — request_user_input, ≤3 rounds, options pre-filled from intent + a quick Glob/Grep scan of the target module:
| Round | Question | Drives |
|---|---|---|
| Scope | Which directories/files/layers are in scope? What is explicitly excluded? | boundary_contract.in_scope / out_of_scope |
| Constraints | Must it be backward compatible? Is the public API frozen? Behavior/performance budget? Test threshold? | boundary_contract.constraints + execution_criteria |
| Done | What observable result counts as "done"? (e.g.: all tests green + zero behavior changes + X metric) | boundary_contract.definition_of_done |
narrow → derive defaults from intent + codebase, skip questions.
3. Derive execution_criteria: backward-compat、scope-freeze、test/coverage bar、fix-don't-hide、incremental commit。
4. Derive task_decomposition (sub-goal list — outcome-oriented, NOT lifecycle stages). Each entry:
{ "id": "G1", "goal": "<deliverable>", "boundary": "<in/out note>",
"done_when": "<objectively checkable condition>",
"evidence": "verification.json|review.json|uat.md|<test path>",
"lifecycle": ["analyze","execute","verify"], "status": "pending" }
done_when must be objectively verifiable and reference an artifact ralph has already produced;lifecycle fields map to the lifecycle stage that produces the evidence.
5. Persist: boundary_contract, execution_criteria, task_decomposition. Initialize for each sub-goal status: "pending" + completion_confirmed: false。
6. Stage the Goal Prompt (Appendix) for A_CREATE_SESSION to emit.
A_BUILD_STEPS
From session.lifecycle_position generate steps to milestone-complete。
| Stage | Skill (independent) | Skill (unified) | Decision after | quality_mode |
|---|---|---|---|---|
| brainstorm | maestro-brainstorm "{intent}" |
(same) | — | all |
| blueprint | maestro-blueprint "{intent}" |
(same) | — | all |
| init | maestro-init |
(same) | — | all |
| analyze-macro | maestro-analyze "{intent}" |
(same) | post-analyze-scope |
all |
| roadmap | maestro-roadmap --from analyze:{analyze_macro_id} |
(same) | — | all |
| analyze | maestro-analyze {phase} |
maestro-analyze |
— | all |
| plan | maestro-plan {phase} (scope=phase) / maestro-plan --from analyze:{analyze_macro_id} (scope=standalone) / maestro-plan --from blueprint:{blueprint_id} (scope=standalone) |
maestro-plan |
— | all |
| execute | maestro-execute {phase} |
maestro-execute |
post-execute |
all |
| business-test | quality-auto-test {phase} |
quality-auto-test |
post-business-test |
full only |
| review | quality-review {phase} |
quality-review |
post-review |
all (quick: append --tier quick) |
| test-gen | quality-auto-test {phase} |
quality-auto-test |
— | full / standard if coverage<80% |
| test | quality-test {phase} |
quality-test |
post-test |
full, standard |
| milestone-audit | maestro-milestone-audit |
(same) | — | all |
| goal-audit | (decision-only) | (same) | post-goal-audit |
all (only if decomposed) |
| milestone-complete | maestro-milestone-complete |
(same) | post-milestone |
all |
Build rules (applied in order):
- planning_mode select column:
unified→ Skill (unified) column;independent→ Skill (independent) column - starting point: start from
session.lifecycle_positionstart - skip completed: skip stages that already have a completed artifact under the current milestone+phase (filtered by
session.phase); unified filters by milestone - quality_mode filter: exclude non-matching stages by
session.quality_modeexclude non-matching stages - decision nodes: after each stage whose Decision is non-empty, insert
{ decision: "<gate>", retry_count: 0, max_retries: 2, command_scope: null, command_path: null } - goal-audit insertion:
task_decompositionexists, after the last evidence-producing stage (verify/review/test), beforemilestone-completeinsertdecision:post-goal-audit - endpoint hard constraint: the chain ends with
milestone-completeends with - goal_ref propagation:
task_decompositionexists, each step matchesstep.stage ∈ g.lifecyclematchstep.goal_ref = g.id(if multiple match, take the lexicographically smallest); decision nodes do not carry goal_ref - placeholder: independent keeps
{phase}{intent}; unified does not carry{phase} - command_path resolution(execute step):
- skill name = the first token before args
Bash("maestro ralph skills --platform codex --json --quiet")fetch global + project (project overrides global), match skill name:- hit →
command_scope = "global" | "project",command_path = <绝对路径> - miss →
command_scope = "missing",command_path = null→ A_CREATE_SESSION raise E006
- hit →
- initialize each step
completion_confirmed: false,completion_status: null,completion_evidence: null,deferred_reads: [],load: null - scope_verdict gating(only when chain starting point =
analyze-macro):scope_verdict ∈ {medium, small}→ skiproadmap+analyzethe two stages;planselect the standalone column (--from analyze:{analyze_macro_id}), without{phase}scope_verdict == large→ keeproadmap+analyze;planselect the phase column ({phase})scope_verdict == unknown→ default large path; bypost-analyze-scopeDecision node corrects after macro analyze completes (A_APPLY_SCOPE_VERDICT)
- --from auto-injected:
analyze_macro_idexists and current step isroadmap→ args changed to--from analyze:{analyze_macro_id}analyze_macro_idexists andscope_verdict ∈ {medium, small}and current step isplan→ args changed to--from analyze:{analyze_macro_id}blueprint_idexists → current step isplan→ args changed to--from blueprint:{blueprint_id}(lower priority than phase numeric parameter)- write
step.source_artifact_reffor auditing
- Milestone-ref: step containing
{phase}placeholder →step.milestone_id = session.milestone - dynamically inserted steps(A_APPLY_*) likewise apply rules 7-13
A_CREATE_SESSION
- validate
command_scope != "missing"of all steps; otherwise raise E006 + list missing skills - Write
.workflow/.maestro/ralph-{YYYYMMDD-HHmmss}/status.json - Display chain overview: each step shows
{index}. {skill} [{command_scope}] task_decompositionexists → display Goal Prompt block, continue into tick
A_LOCATE_SESSION
- If session_id provided → load
.workflow/.maestro/{session_id}/status.json - Else: scan
.workflow/.maestro/*/status.json, filterstatus ∈ {"running","paused"}, sort DESC by created_at, take first - Extract: session_id, source, steps[], phase, milestone, intent, auto_mode, context, cli_tool, active_step_index, status
- pre-probe
next_pending_step(transient, not persisted):- if
active_step_index != nullandsteps[active_step_index].status == "running"→steps[active_step_index] - otherwise
steps[]instatus == "pending"the minimum index - none of them →
null
- if
A_RESUME_SESSION
- A_LOCATE_SESSION loads active session
- clean up active_step_index: if
active_step_index != nullandsteps[active_step_index].status ∈ {"completed","skipped","failed"}→active_step_index = null; keep the rest - If
session.status == "paused"→session.status = "running" session.consec_exit2_count = 0- Display:
↻ Resume {session_id}
A_CLAIM_DECISION
- validate
session.active_step_index ∈ {null, next_pending_step.index}, otherwise raise E008 session.active_step_index = next_pending_step.indexsteps[next_pending_step.index].status = "running"- Display:
[{index}/{total}] ◆ {step.decision} Retry: {retry}/{max}
A_RESOLVE_ARGS
Placeholder substitution:
| Placeholder | Source |
|---|---|
{phase} |
session.phase |
{milestone} |
session.milestone |
{intent} |
session.intent |
{description} |
session.intent (alias) |
{scratch_dir} |
session.context.scratch_dir or latest artifact path |
{plan_dir} |
session.context.plan_dir |
{analysis_dir} |
session.context.analysis_dir |
{issue_id} |
session.context.issue_id |
{milestone_num} |
session.context.milestone_num |
Per-skill enrichment (when args empty or minimal):
| Skill | Required context | Source |
|---|---|---|
| maestro-brainstorm | topic | "{intent}" |
| maestro-roadmap | description | "{intent}" |
| maestro-analyze | phase or topic | {phase} or "{intent}" |
| maestro-plan | phase or --dir | {phase}, or --dir {scratch_dir} |
| maestro-execute | phase or --dir | {phase}, or --dir {scratch_dir} |
| quality-debug | gap context | Read previous step's error/gap |
| quality-* | phase | {phase} |
Artifact dir resolution for --dir:
Read state.json → filter artifacts by milestone + phase
plan commands: latest type=="analyze" → --dir .workflow/scratch/{path}
execute commands: latest type=="plan" → --dir .workflow/scratch/{path}
Write enriched args back to status.json.
A_EXEC_STEP
- Load —
Bash("maestro ralph next")- exit 0 →
session.consec_exit2_count = 0, execute inline per stdout (enter step 2) - exit 2 →
session.consec_exit2_count += 1; when ≥ 2 throw E010 → S_HANDLE_FAIL; otherwise → S_TICK_LOCATE - exit 1 → E007 → S_HANDLE_FAIL
- exit ≥ 3 → E008 → S_HANDLE_FAIL
- exit 0 →
- Inline execution — execute per stdout; deferred_reading Read on demand
- Complete:
Bash("maestro ralph complete N --status DONE [--evidence <path>]")Bash("maestro ralph complete N --status DONE_WITH_CONCERNS --concerns \"...\"")Bash("maestro ralph retry N")Bash("maestro ralph complete N --status BLOCKED --reason \"...\"")
- Propagate context signals —
PHASE: N/scratch_dir: path/BLP-xxxwritestatus.json.context
A_DELEGATE_EVALUATE
- Resolve artifact dir:
.workflow/scratch/{artifact.path}/with fallback glob - Parse decision metadata:
{ decision, retry_count, max_retries } - Map result files:
Decision Files post-execute verification.json post-business-test .tests/auto-test/report.json post-review review.json post-test uat.md, .tests/test-results.json - Check artifact for confidence section → include as signal
- Execute delegate (run_in_background, STOP, wait for callback):
maestro delegate "PURPOSE: 评估 {decision} 质量门结果 TASK: 读取结果 | 分析状态 | 评估严重性 | 给出建议 EXPECTED: ---VERDICT--- STATUS/REASON/GAP_SUMMARY/CONFIDENCE(high|medium|low)/CONFIDENCE_SCORE(0-100)/WEAKEST_DIMENSION ---END--- CONSTRAINTS: 只评估 | 置信度<60% 倾向 fix | retry {n}/{max} 达上限必须 escalate" --role analyze --mode analysis - On callback: parse verdict; if parse fails → fallback STATUS="fix"
- Confidence adjustment: <60 + proceed → fix; >95 + fix + retry>0 → suggest proceed
- Decision log: Append to
{session_dir}/decisions.ndjson:{ "id": "DEC-{timestamp}", "timestamp": "{ISO}", "source": "ralph", "node_id": "{step.decision}", "type": "quality-gate", "verdict": "{adjusted_verdict}", "confidence_score": {N}, "close_call": {N>=50 && N<=70}, "summary": "{REASON}" }
A_STRUCTURAL_EVALUATE
post-milestone:
- Read state.json → get completed milestone object
milestone_obj.type(default"standard")type == "standard": next milestone exists → insert lifecycle steps; otherwise → ENDtype == "adhoc":END,current_milestone = null
post-debug-escalate: STOP → paused, display "please intervene manually"
A_SCOPE_EVALUATE
triggered by post-analyze-scope .
- locate the just-completed macro analyze artifact →
analyze_macro_id,conclusions_path = {artifact_path}/conclusions.json - read
conclusions.scope_verdict(large | medium | small), missing →unknown - write
session.scope_verdict+session.analyze_macro_id - Append
{session_dir}/decisions.ndjson:{ "id": "DEC-{timestamp}", "type": "scope-gate", "source": "ralph", "node_id": "post-analyze-scope", "verdict": "{scope_verdict}", "analyze_macro_id": "{ANL_ID}" } - → A_APPLY_SCOPE_VERDICT
A_GOAL_AUDIT_EVALUATE
only when task_decomposition exists.
- Read
session.task_decomposition - For each sub-goal
status != "done":resolveevidenceartifact - Delegate read-only audit (run_in_background, STOP, wait):
maestro delegate "PURPOSE: 审计未完成子目标,判定 met / unmet TASK: 1. 读取 status.json.task_decomposition 中 status!=done 的子目标 2. 打开 evidence 产物,对照 done_when 严格判定 3. 输出 met / unmet,unmet 给出 gap + target_phase CONTEXT: status.json = {session_dir}/status.json evidence = {evidence artifacts} execution_criteria = {execution_criteria} boundary_contract = {boundary_contract} EXPECTED: ---VERDICT--- STATUS=all_met|has_unmet UNMET=[{id:G2,gap:'...',target_phase:execute}, ...] CONFIDENCE_SCORE=0-100 ---END--- CONSTRAINTS: - 只评估,不修改文件 - 严格按 done_when 判定;evidence 缺失 → unmet - 不得建议超出 boundary_contract 的修改 " --role analyze --mode analysis - On callback: for each met subgoal, set
task_decomposition[i].status="done"+completion_confirmed=true+completed_at=now - Append
{session_dir}/decisions.ndjsonwith"type": "goal-gate",unmet_count,unmet_ids - Verdict:
all_met→ A_APPLY_GOAL_DONE;has_unmet→ A_APPLY_GOAL_FIX GUARD: retry_count >= max_retries AND still unmet → A_APPLY_ESCALATE
A_APPLY_* release protocol(uniformly applied at the end of all A_APPLY_*):
- completion branch (proceed / goal-done / scope applied / structural advanced):
step.status = "completed",step.completion_confirmed = true,session.active_step_index = null- re-evaluation branch (fix / escalate / goal-fix):
step.status = "pending",step.completion_confirmed = false,session.active_step_index = null,step.retry_count += 1
A_APPLY_PROCEED
- release protocol — completion branch
- Append decisions.ndjson with verdict
- Display: ◆ Decision: {type} → proceed ({reason})
A_APPLY_FIX
- Insert fix-loop commands after current step (Appendix: Fix-Loop Templates)
- release protocol — re-evaluation branch; reindex steps
- Display: ◆ Decision: {type} → fix, +{N} commands inserted
A_APPLY_ESCALATE
- Insert
[quality-debug "{gap_summary}", decision:post-debug-escalate] - release protocol — re-evaluation branch; reindex
A_APPLY_SCOPE_VERDICT
triggered by post-analyze-scope .
- read
session.scope_verdict large: for subsequentroadmapstep inject--from analyze:{analyze_macro_id}; subsequentplanstep select phase columnmedium/small:- delete
goal-auditincomplete beforeroadmap+analyze(phase) step - next incomplete
planstep →maestro-plan --from analyze:{analyze_macro_id}, remove{phase},source_artifact_ref = analyze:{analyze_macro_id} - subsequent
execute/verifysame standalone scope
- delete
unknown: non-auto_confirm → request_user_input choose one of two (large / medium-small); auto_confirm → default large- release protocol — completion branch; reindex
- Display: ◆ Scope verdict: {verdict} → {kept|collapsed to standalone via analyze:{ANL_ID}}
A_APPLY_GOAL_FIX
- for each unmet subgoal
G{n}(grouped and deduplicated bytarget_phase): before thegoal-auditnode, insert a scoped mini-loop (Appendix: post-goal-audit), each stepgoal_ref: "G{n}" - append
decision:post-goal-audit {retry+1} - release protocol — re-evaluate branch; reindex
- Display: ◆ Goal audit: {k} unmet → +{N} steps inserted (G{ids}), retry {r}/{max}
A_APPLY_GOAL_DONE
- Each
task_decomposition[*]→status="done",completion_confirmed=true,completed_at=now; top-leveltask_decomposition_all_done=true - release protocol — completion branch; proceed to
milestone-complete - Display: ◆ Goal audit: all met ✓
A_ADVANCE_MILESTONE
- Update session: milestone, phase, reset passed_gates
- Insert full lifecycle steps for next milestone
- release protocol — completion branch; reindex
A_RETRY
Bash("maestro ralph retry N")- Display:
[{index}/{total}] ↻ {step.skill} retry
A_SKIP_STEP
Manually edit status.json:step.status = "skipped", step.completion_confirmed = false, if active_step_index == step.index then set to null.
A_PAUSE_SESSION
session.status = "paused" (CLI automatically writes via ralph complete N --status BLOCKED ; for manual scenarios, edit directly)
Display: [{index}/{total}] ✗ {step.skill} 失败,会话已暂停。$maestro-ralph-beta continue 恢复。
A_PAUSE_ESCALATE
session.status = "paused"- Display: ◆ Maximum retry count reached, debug has been executed. Manual intervention required.
- Display: $maestro-ralph-beta continue to resume
A_COMPLETE_SESSION
- Validation: all steps
completion_confirmed == true(except skipped); when task_decomposition exists, validatetask_decomposition_all_done == true - If any validation fails → do not mark completed, return to S_TICK_LOCATE or pause
session.status = "completed", write status.json- Display completion report:
Icons:============================================================ SESSION COMPLETE ============================================================ Session: {session_id} [{source}] Steps: {completed}/{total} confirmed: {confirmed}/{completed} [✓] 0. maestro-plan 1 [global] [✓] 1. maestro-execute 1 [project] [✓] 2. quality-review 1 [global] [✓] 3. ◆ post-review [decision] ... ============================================================✓confirmed,—skipped,✗failed,◆decision
</state_machine>
Session Schema
{
"session_id": "ralph-{YYYYMMDD-HHmmss}",
"source": "ralph", "status": "running",
"ralph_protocol_version": "2",
"active_step_index": null,
"intent": "", "lifecycle_position": "",
"phase": null, "phase_is_new": false,
"milestone": "", // D-007 反查结果
"auto_mode": false,
"quality_mode": "standard", // "full" | "standard" | "quick"
"planning_mode": "independent", // "unified" | "independent"
"scope_verdict": null, // "large" | "medium" | "small" | "unknown" | null
"analyze_macro_id": null, // "ANL-xxx"
"blueprint_id": null, // "BLP-xxx"
"cli_tool": "claude", "passed_gates": [],
"consec_exit2_count": 0,
"context": { "issue_id": null, "scratch_dir": null, "plan_dir": null,
"analysis_dir": null, "brainstorm_dir": null, "blueprint_dir": null },
"steps": [{
"index": 0,
"skill": "", // 执行 step 有值;decision 节点为空字符串/null
"args": "",
"stage": "", // brainstorm|blueprint|init|analyze-macro|roadmap|analyze|plan|execute|...
"scope": null, // "phase"|"standalone"|"milestone"|null(plan 等需要)
"decision": null, // null = 执行 step;非 null = decision step (值为 gate 名)
"retry_count": 0, // decision step
"max_retries": 2, // decision step
"command_scope": "global|project|missing|null",
"command_path": "<absolute path> | null",
"milestone_id": null, // 仅含 {phase} 占位符的 step
"source_artifact_ref": null, // "analyze:ANL-xxx" | "blueprint:BLP-xxx" | null
"status": "pending|running|completed|skipped|failed",
"goal_ref": null,
"completion_confirmed": false,
"completion_status": null,
"completion_evidence": null,
"completed_at": null,
"deferred_reads": [],
"load": null // { loaded_at, required_files[], deferred_files[], resolve_version }
}],
"current_step": 0,
"boundary_contract": {
"in_scope": [], "out_of_scope": [], "constraints": [], "definition_of_done": ""
},
"execution_criteria": [],
"task_decomposition": [
{ "id": "G1", "goal": "", "boundary": "", "done_when": "",
"evidence": "", "lifecycle": [], "status": "pending|done",
"completion_confirmed": false, "completed_at": null }
],
"task_decomposition_all_done": false
}
Fix-Loop Templates
Inserted execution steps resolve command_path + command_scope;decision:* entries as decision nodes per A_BUILD_STEPS rule 9.
post-execute:
quality-debug "{gap_summary}"
maestro-plan --gaps {phase}
maestro-execute {phase}
decision:post-execute {retry+1}
post-business-test:
quality-debug --from-business-test "{gap_summary}"
maestro-plan --gaps {phase}
maestro-execute {phase}
decision:post-execute {retry: 0}
quality-auto-test {phase}
decision:post-business-test {retry+1}
post-review:
quality-debug "{gap_summary}"
maestro-plan --gaps {phase}
maestro-execute {phase}
quality-review {phase}
decision:post-review {retry+1}
post-test:
quality-debug --from-uat "{gap_summary}"
maestro-plan --gaps {phase}
maestro-execute {phase}
decision:post-execute {retry: 0}
quality-auto-test {phase}
decision:post-business-test {retry: 0}
quality-review {phase}
decision:post-review {retry: 0}
quality-auto-test {phase}
quality-test {phase}
decision:post-test {retry+1}
post-goal-audit: (per unmet sub-goal group)
# for each unmet sub-goal G{n}, scoped to target_phase:
maestro-plan --gaps {target_phase} "G{n}: {gap}" [goal_ref: G{n}]
maestro-execute {target_phase} [goal_ref: G{n}]
# after all unmet groups inserted:
decision:post-goal-audit {retry+1}
Goal Prompt Template
After decomposition output, display verbatim after the chain overview:
📋 任务分解完成。可随时复制以下 /goal 设定终止条件:
/goal 直到 {session_dir}/status.json 的 task_decomposition[*] 与 steps[*] 全部 completion_confirmed=true 才停。每轮以 status.json 为唯一行动手册,通过 $maestro-ralph-beta 推进 tick;decision 节点由 ralph 内联评估。禁止手动执行 skill 或修改 boundary_contract.out_of_scope。
Error Codes
| Code | Severity | Description | Recovery |
|---|---|---|---|
| E001 | error | No intent and no running session | Prompt for intent |
| E002 | error | Cannot infer lifecycle position | Show raw state, ask |
| E003 | error | Artifact dir not found for decision | Show glob, ask |
| E004 | error | Delegate verdict parse failed | Fallback: "fix" |
| E005 | error | Delegate execution failed | Fallback: "fix" |
| E006 | error | command_scope == "missing" for one or more steps | List missing skills, abort build |
| E007 | error | required_reading referenced file missing | ralph next reject; CLI stderr lists missing paths |
| E008 | error | ralph complete idx ≠ active_step_index |
Edit status.json to fix consistency |
| E009 | error | ralph complete step.status ≠ running |
Duplicate complete or illegal jump; edit status.json |
| E010 | error | status.json schema corrupted OR A_EXEC_STEP exit=2 circuit break (≥2 consecutive idle spins) | ralph check display corrupted fields; circuit break requires manual verification of whether next_pending_step and router routing are consistent |
| W001 | warning | Decision expanded chain | Auto-handled |
| W002 | warning | Max retries, escalating | Auto-handled |
| W003 | warning | Multiple running sessions | Use latest, warn |
| W004 | warning | Low delegate confidence | Show warning |
| W005 | warning | active_step_index points to an already completed step | ralph next continue after automatic cleanup |
| W007 | warning | step.skill ≠ command .md frontmatter.name | prompt but do not block |
Success Criteria
- At the end of Tick, self-invoke
$maestro-ralph-beta, until allcompletion_confirmedor paused - In the same session, only one step holds
active_step_indexat a time; release must occur before switching - Decision step never calls
maestro ralph next/completethroughout; evaluated inline and released by maestro-ralph-beta -
task_decompositionWhen present, chain includesdecision:post-goal-audit, and only the finaltask_decomposition_all_done == trueallows S_COMPLETE - Chain ends with
milestone-complete; after A_APPLY_* modifies steps[], always reindex - 2 consecutive
ralph nextexit=2 → E010 pause (prevents router misrouting idle spins)