mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
A node with no `shape` defaulted to `box`, which resolves to the agent handler. That made a shapeless `script` node run as an LLM call prompted with its own label, while the `script` was reported as inert — wrong behavior behind a warning. `script` is read by the command handler and by nothing else, so a shapeless node that sets it is unambiguously a command node. `shape()` now infers `parallelogram` in that case. An explicit `shape` still wins. Two rules keep the inference honest: - `script_prompt_conflict` — setting both `script` and `prompt` is an error. No handler reads both. It fires regardless of shape so that adding one cannot downgrade the error to a warning. - `command_requires_script` — a command node without a script is an error. Without this the original trap just moves: a node meant as a command that omits its script silently becomes an agent again. Also drops the `tool_command` alias in favor of `script` alone, routing the six read sites through a new `Node::script()` accessor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
396 lines
18 KiB
Text
396 lines
18 KiB
Text
digraph reference_template {
|
|
graph [
|
|
goal="$goal",
|
|
rankdir=LR,
|
|
default_max_retries=3,
|
|
retry_target="implement",
|
|
fallback_retry_target="debate_consolidate",
|
|
provenance_version="1",
|
|
model_stylesheet="
|
|
* { model: DEFAULT_MODEL; provider: DEFAULT_PROVIDER; }
|
|
.hard { model: HARD_MODEL; provider: HARD_PROVIDER; }
|
|
.verify { model: VERIFY_MODEL; provider: VERIFY_PROVIDER; }
|
|
.branch-a { model: BRANCH_A_MODEL; provider: BRANCH_A_PROVIDER; }
|
|
.branch-b { model: BRANCH_B_MODEL; provider: BRANCH_B_PROVIDER; }
|
|
.branch-c { model: BRANCH_C_MODEL; provider: BRANCH_C_PROVIDER; }
|
|
"
|
|
]
|
|
|
|
// =======================================================================
|
|
// TEMPLATE USAGE
|
|
//
|
|
// This template defines TOPOLOGY ONLY: node shapes, edges, routing,
|
|
// and structural patterns. It contains NO prompt text.
|
|
//
|
|
// The ingestor must compose every prompt from scratch based on the
|
|
// actual project spec, DoD, and repo contents. Prompt requirements
|
|
// are listed in comments above each shape=box node. See Phase 4 of
|
|
// the create-dotfile skill for the full prompt contract.
|
|
//
|
|
// Common requirements for ALL shape=box prompts (do not repeat per node):
|
|
// - Reference $goal
|
|
// - Full status contract: write to $KILROY_STAGE_STATUS_PATH,
|
|
// fall back to $KILROY_STAGE_STATUS_FALLBACK_PATH, do not write
|
|
// nested status.json after cd, use schema {"status":"..."}
|
|
// - For status=fail or status=retry: include failure_reason, details,
|
|
// and failure_class
|
|
// =======================================================================
|
|
|
|
exit [shape=Msquare, label="Exit"]
|
|
|
|
subgraph cluster_bootstrap {
|
|
label="Bootstrap"
|
|
start [shape=Mdiamond, label="Start"]
|
|
|
|
// Toolchain gate — fail fast before LLM stages.
|
|
// Replace script with project-specific checks.
|
|
check_toolchain [
|
|
shape=parallelogram,
|
|
max_retries=0,
|
|
script="echo 'Replace with project-specific toolchain checks'; exit 0"
|
|
]
|
|
|
|
// PROMPT: expand_spec
|
|
// Role: create or reuse canonical spec at .ai/spec.md
|
|
// Must address:
|
|
// - Reuse existing .ai/spec.md or repo spec if adequate
|
|
// - Copy user-declared verbatim specs without rewriting
|
|
// - Expand from $goal only when no adequate spec exists
|
|
// - Spec must include: scope, constraints, assumptions, acceptance
|
|
// criteria, verification approach, non-goals
|
|
// Reads: $goal, existing .ai/spec.md (if any), repo docs
|
|
// Writes: .ai/spec.md
|
|
// Note: auto_status=true — no explicit status write needed
|
|
expand_spec [
|
|
shape=box,
|
|
auto_status=true
|
|
]
|
|
|
|
// PROMPT: check_dod
|
|
// Role: determine if .ai/definition_of_done.md is adequate
|
|
// Must address:
|
|
// - Check file existence and content (not placeholder)
|
|
// - Apply DoD rubric: scope, deliverables, AC, verification,
|
|
// quality/safety gates, non-goals
|
|
// - Apply coverage checklist: build, tests, lint, docs,
|
|
// compatibility, security, ops, perf
|
|
// Reads: .ai/definition_of_done.md
|
|
// Outcomes: has_dod or needs_dod
|
|
check_dod [
|
|
shape=box,
|
|
label="DoD exists?"
|
|
]
|
|
}
|
|
|
|
subgraph cluster_dod {
|
|
label="DoD Fanout"
|
|
node [shape=box]
|
|
|
|
dod_fanout [shape=component, label="DoD Fan-Out"]
|
|
|
|
// PROMPT: dod_a, dod_b, dod_c
|
|
// (identical prompt — diversity comes from different models)
|
|
// Role: propose a project DoD from the spec
|
|
// Must address:
|
|
// - Read .ai/spec.md
|
|
// - DoD is outcomes/evidence, not a plan
|
|
// - Each item verifiable (pass/fail)
|
|
// - Don't prescribe implementation approach
|
|
// - Include: scope, deliverables, AC, verification, non-goals
|
|
// - Apply coverage checklist
|
|
// Writes: .ai/dod_a.md (or _b, _c)
|
|
dod_a [class="branch-a"]
|
|
dod_b [class="branch-b"]
|
|
dod_c [class="branch-c"]
|
|
|
|
// PROMPT: consolidate_dod
|
|
// Role: synthesize dod_a/b/c into consensus DoD
|
|
// Must address:
|
|
// - Review every branch result in parallel.results from prompt context
|
|
// - Read branch files from the shared checkout
|
|
// - Read .ai/spec.md for context
|
|
// - Resolve contradictions, apply DoD rubric + coverage checklist
|
|
// Writes: .ai/definition_of_done.md
|
|
consolidate_dod [auto_status=true]
|
|
}
|
|
|
|
subgraph cluster_planning {
|
|
label="Planning Fanout"
|
|
node [shape=box]
|
|
|
|
plan_fanout [shape=component, label="Plan Fan-Out"]
|
|
|
|
// PROMPT: plan_a, plan_b, plan_c
|
|
// (identical prompt — diversity comes from different models)
|
|
// Role: create implementation plan from spec + DoD
|
|
// Must address:
|
|
// - Read .ai/spec.md and .ai/definition_of_done.md
|
|
// - If .ai/postmortem_latest.md exists, incorporate its lessons
|
|
// - Plan must cover all deliverables and acceptance criteria
|
|
// from the DoD, with project-specific implementation detail
|
|
// - Plan must be grounded in the actual project domain,
|
|
// technology stack, and repo structure
|
|
// Writes: .ai/plan_a.md (or _b, _c)
|
|
plan_a [class="branch-a"]
|
|
plan_b [class="branch-b"]
|
|
plan_c [class="branch-c"]
|
|
|
|
// PROMPT: debate_consolidate
|
|
// Role: synthesize plan_a/b/c into best-of-breed final plan
|
|
// Must address:
|
|
// - Review every branch result in parallel.results from prompt context
|
|
// - Read branch files from the shared checkout
|
|
// - If .ai/postmortem_latest.md exists, verify plan addresses
|
|
// every identified issue
|
|
// - Resolve conflicts, ensure dependency order
|
|
// Writes: .ai/plan_final.md
|
|
debate_consolidate [auto_status=true]
|
|
}
|
|
|
|
// OPTIONAL: For porting/reading-existing-source tasks — add analyze cluster here.
|
|
// analyze_fanout [shape=component, label="Analyze Fan-Out"]
|
|
// analyze_module_a [shape=box, auto_status=true, prompt="...read source, write .ai/design_a.md..."]
|
|
// analyze_module_b [shape=box, auto_status=true, prompt="..."]
|
|
// merge_analysis [shape=box, auto_status=true, prompt="...verify all design docs..."]
|
|
// See reference_template.dot OPTIONAL stubs below for pattern.
|
|
|
|
// OPTIONAL: For 5+ discrete deliverable files — use worker pool instead of flat fan-out.
|
|
// plan_work [shape=box, auto_status=true, label="Plan Work Queue", prompt="...write .ai/work_queue.json..."]
|
|
// work_pool [shape=component, label="Worker Pool"]
|
|
// worker_0 [shape=box, auto_status=true, label="Worker 0", prompt="...id%3==0..."]
|
|
// worker_1 [shape=box, auto_status=true, label="Worker 1", prompt="...id%3==1..."]
|
|
// worker_2 [shape=box, auto_status=true, label="Worker 2", prompt="...id%3==2..."]
|
|
// check_work_complete [shape=box, auto_status=true, label="Work Complete?", prompt="...pass counter..."]
|
|
|
|
subgraph cluster_implement_verify {
|
|
label="Implement And Verify"
|
|
|
|
// PROMPT: implement
|
|
// Role: single-writer code implementation (fresh or repair)
|
|
// Must address:
|
|
// - REPAIR FIRST: if .ai/postmortem_latest.md exists, read it
|
|
// FIRST, fix ONLY identified gaps, do NOT regenerate working
|
|
// systems, preserve all passing code and tests
|
|
// - FRESH: if no postmortem, execute .ai/plan_final.md
|
|
// - Read .ai/spec.md and .ai/definition_of_done.md
|
|
// - Implementation instructions must be specific to the project's
|
|
// deliverables, domain, technology, and constraints — derived
|
|
// from the ingestor's reading of the spec and DoD
|
|
// - Incremental implementation: each module complete before next
|
|
// - Log progress to .ai/implementation_log.md
|
|
// Failure: also include failure_signature in meta
|
|
implement [
|
|
shape=box,
|
|
class="hard",
|
|
max_retries=2
|
|
]
|
|
check_implement [shape=diamond, label="Implement OK?"]
|
|
|
|
// Auto-fix formatting before verify gate.
|
|
// Replace script with project-specific auto-formatter.
|
|
fix_fmt [
|
|
shape=parallelogram,
|
|
max_retries=0,
|
|
script="echo 'Replace with project-specific auto-formatter'; exit 0"
|
|
]
|
|
|
|
// Replace script with project-specific formatter check.
|
|
verify_fmt [
|
|
shape=parallelogram,
|
|
max_retries=0,
|
|
script="echo 'Replace with project-specific formatter check'; exit 0"
|
|
]
|
|
check_fmt [shape=diamond, label="Fmt OK?"]
|
|
|
|
// Replace script with project-specific build command.
|
|
verify_build [
|
|
shape=parallelogram,
|
|
script="echo 'Replace with project-specific build check'; exit 0"
|
|
]
|
|
check_build [shape=diamond, label="Build OK?"]
|
|
|
|
// Replace script with project-specific test command.
|
|
verify_test [
|
|
shape=parallelogram,
|
|
script="echo 'Replace with project-specific test check'; exit 0"
|
|
]
|
|
check_test [shape=diamond, label="Tests OK?"]
|
|
|
|
// Replace script with artifact hygiene check.
|
|
// Confirm deliverables meet their interface contract (exports, endpoints,
|
|
// CLI behavior, observable outputs); file existence alone is insufficient.
|
|
verify_artifacts [
|
|
shape=parallelogram,
|
|
max_retries=0,
|
|
script="echo 'Replace with artifact hygiene check'; exit 0"
|
|
]
|
|
check_artifacts [shape=diamond, label="Artifacts OK?"]
|
|
|
|
// PROMPT: verify_fidelity
|
|
// Role: semantic review after all deterministic checks pass
|
|
// Must address:
|
|
// - Read implementation outputs and verify against
|
|
// .ai/definition_of_done.md and .ai/spec.md
|
|
// - Verification must be specific to the project's acceptance
|
|
// criteria — enumerate the actual areas to check, derived from
|
|
// the ingestor's reading of the DoD
|
|
// - Write results to .ai/verify_fidelity.md
|
|
// Failure: also include failure_signature in meta — sorted
|
|
// comma-separated list of specific failed criteria identifiers
|
|
verify_fidelity [
|
|
shape=box,
|
|
class="verify"
|
|
]
|
|
check_impl [shape=diamond, label="Impl OK?"]
|
|
}
|
|
|
|
subgraph cluster_review {
|
|
label="Review Fanout"
|
|
node [shape=box]
|
|
|
|
review_fanout [shape=component, label="Review Fan-Out"]
|
|
|
|
// PROMPT: review_a, review_b, review_c
|
|
// (identical prompt — diversity comes from different models)
|
|
// Role: review implementation against DoD
|
|
// Must address:
|
|
// - Read .ai/definition_of_done.md for acceptance criteria
|
|
// - Read implementation outputs
|
|
// - Check build, completeness, correctness, tests against all
|
|
// DoD criteria — enumerate what to check, derived from the
|
|
// ingestor's reading of the DoD
|
|
// - Verdict: APPROVED or REJECTED with specific evidence
|
|
// Failure: include specific gaps with criteria identifiers
|
|
// Writes: .ai/review_a.md (or _b, _c)
|
|
review_a [class="branch-a"]
|
|
review_b [class="branch-b"]
|
|
review_c [class="branch-c"]
|
|
|
|
// PROMPT: review_consensus
|
|
// Role: synthesize reviews into consensus verdict
|
|
// Must address:
|
|
// - Review every branch result in parallel.results from prompt context
|
|
// - Read branch files from the shared checkout
|
|
// - Read .ai/definition_of_done.md for criteria
|
|
// - Consensus: 2+ APPROVED with no critical gaps -> success;
|
|
// otherwise -> retry with specific issues
|
|
// Writes: .ai/review_consensus.md
|
|
review_consensus [auto_status=true, goal_gate=true, retry_target="postmortem"]
|
|
}
|
|
|
|
subgraph cluster_postmortem {
|
|
label="Postmortem"
|
|
node [shape=box]
|
|
|
|
// PROMPT: postmortem
|
|
// Role: analyze failure and guide next repair iteration
|
|
// Must address:
|
|
// - Read .ai/review_consensus.md (if review stage reached)
|
|
// - Read .ai/verify_fidelity.md (if semantic verify ran)
|
|
// - Review branch status and context updates in parallel.results
|
|
// from prompt context if available
|
|
// - Read .ai/implementation_log.md
|
|
// - Output: root causes, what worked (preserve), what failed
|
|
// (fix), concrete next changes
|
|
// - Must NOT direct from-scratch restart — preserve working code
|
|
// Outcome classification (recovery routing):
|
|
// - impl_repair: code repair needed; plan/toolchain still valid
|
|
// - needs_replan: plan/approach is inadequate; regenerate plan branches
|
|
// - needs_toolchain: environment/bootstrap/toolchain issue detected
|
|
// - When uncertain, default to impl_repair
|
|
// Writes: .ai/postmortem_latest.md (overwrite previous)
|
|
// Note: status reflects analysis completion, not implementation state
|
|
postmortem [auto_status=true]
|
|
}
|
|
|
|
// =========================================================================
|
|
// Flow
|
|
// =========================================================================
|
|
|
|
// Linear start: toolchain gate -> spec -> DoD check
|
|
start -> check_toolchain
|
|
check_toolchain -> expand_spec [condition="outcome=succeeded"]
|
|
check_toolchain -> check_toolchain [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_toolchain -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_toolchain -> postmortem
|
|
expand_spec -> check_dod
|
|
|
|
// DoD fan-out (if needed)
|
|
check_dod -> dod_fanout [condition="outcome=needs_dod"]
|
|
check_dod -> dod_fanout
|
|
dod_fanout -> dod_a
|
|
dod_fanout -> dod_b
|
|
dod_fanout -> dod_c
|
|
dod_a -> consolidate_dod
|
|
dod_b -> consolidate_dod
|
|
dod_c -> consolidate_dod
|
|
consolidate_dod -> plan_fanout
|
|
|
|
// Skip to planning if DoD exists
|
|
check_dod -> plan_fanout [condition="outcome=has_dod"]
|
|
|
|
// Planning fan-in -> debate -> implement
|
|
plan_fanout -> plan_a
|
|
plan_fanout -> plan_b
|
|
plan_fanout -> plan_c
|
|
plan_a -> debate_consolidate
|
|
plan_b -> debate_consolidate
|
|
plan_c -> debate_consolidate
|
|
debate_consolidate -> implement
|
|
|
|
// Verify/check inner loop (tool gates first, semantic review last)
|
|
implement -> check_implement
|
|
check_implement -> fix_fmt [condition="outcome=succeeded"]
|
|
fix_fmt -> verify_fmt
|
|
check_implement -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_implement -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_implement -> postmortem
|
|
verify_fmt -> check_fmt
|
|
check_fmt -> verify_build [condition="outcome=succeeded"]
|
|
check_fmt -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_fmt -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_fmt -> postmortem
|
|
|
|
verify_build -> check_build
|
|
check_build -> verify_test [condition="outcome=succeeded"]
|
|
check_build -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_build -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_build -> postmortem
|
|
|
|
verify_test -> check_test
|
|
check_test -> verify_artifacts [condition="outcome=succeeded"]
|
|
check_test -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_test -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_test -> postmortem
|
|
|
|
verify_artifacts -> check_artifacts
|
|
check_artifacts -> verify_fidelity [condition="outcome=succeeded"]
|
|
check_artifacts -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_artifacts -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_artifacts -> postmortem
|
|
|
|
verify_fidelity -> check_impl
|
|
check_impl -> review_fanout [condition="outcome=succeeded"]
|
|
review_fanout -> review_a
|
|
review_fanout -> review_b
|
|
review_fanout -> review_c
|
|
check_impl -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
|
|
check_impl -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
|
|
check_impl -> postmortem
|
|
|
|
// Review fan-in -> consensus
|
|
review_a -> review_consensus
|
|
review_b -> review_consensus
|
|
review_c -> review_consensus
|
|
|
|
// Consensus routing: success -> exit, anything else -> postmortem
|
|
review_consensus -> exit [condition="outcome=succeeded"]
|
|
review_consensus -> postmortem
|
|
|
|
// Domain-routed recovery: classify failure and choose the right re-entry
|
|
postmortem -> check_toolchain [condition="outcome=failed && context.failure_class=transient_infra"]
|
|
postmortem -> implement [condition="outcome=impl_repair"]
|
|
postmortem -> plan_fanout [condition="outcome=needs_replan"]
|
|
postmortem -> check_toolchain [condition="outcome=needs_toolchain"]
|
|
postmortem -> implement
|
|
}
|