mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
Add kilroy DOT files and parsing compatibility tests
Copy 14 DOT workflow files from the kilroy project and add tests proving arc can parse them. 11 files parse successfully, exercising features including subgraphs, fan-out/fan-in, conditional routing, goal gates, model stylesheets, and large 40+ node workflows. 3 batch test files (batch_*.dot) document a parser gap: arc requires quoted values for strings with hyphens/dots (e.g., "gpt-5.2") while kilroy's parser accepts them unquoted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2a82a4e147
commit
88f2fa245b
15 changed files with 4593 additions and 0 deletions
164
crates/arc-attractor/tests/kilroy_compat.rs
Normal file
164
crates/arc-attractor/tests/kilroy_compat.rs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
use std::path::Path;
|
||||
|
||||
use arc_attractor::parser::parse;
|
||||
|
||||
fn parse_kilroy_dot(filename: &str) -> Result<arc_attractor::graph::types::Graph, String> {
|
||||
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../test/kilroy")
|
||||
.join(filename);
|
||||
let content =
|
||||
std::fs::read_to_string(&path).map_err(|e| format!("failed to read {}: {e}", path.display()))?;
|
||||
parse(&content).map_err(|e| format!("failed to parse {filename}: {e}"))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parsing tests: every kilroy DOT file must parse without error
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_simple_example() {
|
||||
let graph = parse_kilroy_dot("simple_example.dot").unwrap();
|
||||
assert_eq!(graph.name, "Simple");
|
||||
assert_eq!(graph.goal(), "Run tests and report");
|
||||
assert_eq!(graph.nodes.len(), 4);
|
||||
assert_eq!(graph.edges.len(), 3);
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
|
||||
// The batch_*.dot files use unquoted values with hyphens/dots (e.g., `llm_model=gpt-5.2`)
|
||||
// which is valid in kilroy's more lenient Go parser but not in arc's strict DOT parser.
|
||||
// These tests document the parser gap: arc requires quoting such values.
|
||||
#[test]
|
||||
fn parse_kilroy_batch_clean_requires_quoted_model_values() {
|
||||
let err = parse_kilroy_dot("batch_clean.dot").unwrap_err();
|
||||
assert!(
|
||||
err.contains("grammar error"),
|
||||
"expected grammar error for unquoted `gpt-5.2`, got: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_batch_has_errors_requires_quoted_model_values() {
|
||||
let err = parse_kilroy_dot("batch_has_errors.dot").unwrap_err();
|
||||
assert!(
|
||||
err.contains("grammar error"),
|
||||
"expected grammar error for unquoted `gpt-5.2`, got: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_batch_warnings_only_requires_quoted_model_values() {
|
||||
let err = parse_kilroy_dot("batch_warnings_only.dot").unwrap_err();
|
||||
assert!(
|
||||
err.contains("grammar error"),
|
||||
"expected grammar error for unquoted `gpt-5.2`, got: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_solitaire_fast() {
|
||||
let graph = parse_kilroy_dot("solitaire_fast.dot").unwrap();
|
||||
assert_eq!(graph.name, "solitaire");
|
||||
assert_eq!(
|
||||
graph.goal(),
|
||||
"Build a terminal-based solitaire (Klondike) game"
|
||||
);
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
// Large workflow: 2 control + many work nodes + diamond gates
|
||||
assert!(
|
||||
graph.nodes.len() > 15,
|
||||
"expected >15 nodes, got {}",
|
||||
graph.nodes.len()
|
||||
);
|
||||
assert!(
|
||||
graph.edges.len() > 20,
|
||||
"expected >20 edges, got {}",
|
||||
graph.edges.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_consensus_task() {
|
||||
let graph = parse_kilroy_dot("consensus_task.dot").unwrap();
|
||||
assert_eq!(graph.name, "Workflow");
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
// Multi-model consensus: many parallel branches
|
||||
assert!(graph.nodes.len() > 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_semport() {
|
||||
let graph = parse_kilroy_dot("semport.dot").unwrap();
|
||||
assert_eq!(graph.name, "Workflow");
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
// Loop-based workflow with conditional routing
|
||||
assert!(graph.edges.len() > 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_reference_template() {
|
||||
let graph = parse_kilroy_dot("reference_template.dot").unwrap();
|
||||
assert_eq!(graph.name, "reference_template");
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
// Kitchen-sink template: subgraphs, fan-out, parallel, loops
|
||||
assert!(graph.nodes.len() > 30);
|
||||
assert!(graph.edges.len() > 30);
|
||||
// Verify subgraph-derived classes are applied
|
||||
assert!(
|
||||
graph.nodes.contains_key("implement"),
|
||||
"should contain implement node"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_green_test_moderate() {
|
||||
let graph = parse_kilroy_dot("green_test_moderate.dot").unwrap();
|
||||
assert_eq!(graph.name, "linkcheck");
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_green_test_complex() {
|
||||
let graph = parse_kilroy_dot("green_test_complex.dot").unwrap();
|
||||
assert_eq!(graph.name, "dttf");
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
// Very large workflow (40+ stages)
|
||||
assert!(graph.nodes.len() > 40);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_green_test_vague() {
|
||||
let graph = parse_kilroy_dot("green_test_vague.dot").unwrap();
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_refactor_test_moderate() {
|
||||
let graph = parse_kilroy_dot("refactor_test_moderate.dot").unwrap();
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_refactor_test_complex() {
|
||||
let graph = parse_kilroy_dot("refactor_test_complex.dot").unwrap();
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
// Large workflow
|
||||
assert!(graph.nodes.len() > 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_kilroy_refactor_test_vague() {
|
||||
let graph = parse_kilroy_dot("refactor_test_vague.dot").unwrap();
|
||||
assert!(graph.find_start_node().is_some());
|
||||
assert!(graph.find_exit_node().is_some());
|
||||
}
|
||||
6
test/kilroy/batch_clean.dot
Normal file
6
test/kilroy/batch_clean.dot
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
digraph G {
|
||||
start [shape=Mdiamond]
|
||||
exit [shape=Msquare]
|
||||
work [shape=box, llm_provider=openai, llm_model=gpt-5.2, prompt="Do the work."]
|
||||
start -> work -> exit
|
||||
}
|
||||
6
test/kilroy/batch_has_errors.dot
Normal file
6
test/kilroy/batch_has_errors.dot
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
digraph G {
|
||||
start [shape=Mdiamond]
|
||||
exit [shape=Msquare]
|
||||
work [shape=box, llm_model=gpt-5.2, prompt="Do the work."]
|
||||
start -> work -> exit
|
||||
}
|
||||
6
test/kilroy/batch_warnings_only.dot
Normal file
6
test/kilroy/batch_warnings_only.dot
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
digraph G {
|
||||
start [shape=Mdiamond]
|
||||
exit [shape=Msquare]
|
||||
work [shape=box, llm_provider=openai, llm_model=gpt-5.2]
|
||||
start -> work -> exit
|
||||
}
|
||||
158
test/kilroy/consensus_task.dot
Normal file
158
test/kilroy/consensus_task.dot
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
digraph Workflow {
|
||||
graph [
|
||||
label="Consensus Task Workflow",
|
||||
goal="$task",
|
||||
rankdir="LR",
|
||||
context_fidelity_default="truncate",
|
||||
context_thread_default="consensus-task",
|
||||
default_max_retry="3",
|
||||
retry_target="CheckDoD",
|
||||
fallback_retry_target="Start"
|
||||
];
|
||||
|
||||
Start [
|
||||
node_type="start", label="Start", shape="Mdiamond", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="4", timeout="300"
|
||||
];
|
||||
|
||||
CheckDoD [
|
||||
node_type="stack.steer", label="Check DoD", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="2", timeout="120",
|
||||
llm_prompt="Check if definition of done provided.\n\nTASK: $task\nDOD: $definition_of_done\n\nWrite status.json with outcome=needs_dod if DOD is empty or just a placeholder, else outcome=has_dod if a real DOD was provided."
|
||||
];
|
||||
|
||||
DefineDoD_Gemini [
|
||||
node_type="stack.observe", label="Define DoD (Gemini)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300",
|
||||
llm_prompt="Propose definition of done.\n\nTASK: $task\n\nWrite to .ai/dod_gemini.md"
|
||||
];
|
||||
|
||||
DefineDoD_GPT [
|
||||
node_type="stack.observe", label="Define DoD (GPT)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300", reasoning_effort="high",
|
||||
llm_prompt="Propose definition of done.\n\nTASK: $task\n\nWrite to .ai/dod_gpt.md"
|
||||
];
|
||||
|
||||
DefineDoD_Opus [
|
||||
node_type="stack.observe", label="Define DoD (Opus)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300",
|
||||
llm_prompt="Propose definition of done.\n\nTASK: $task\n\nWrite to .ai/dod_opus.md"
|
||||
];
|
||||
|
||||
ConsolidateDoD [
|
||||
node_type="stack.observe", label="Consolidate DoD", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="8", timeout="420",
|
||||
llm_prompt="Synthesize three DoD proposals.\n\nTASK: $task\n\nRead .ai/dod_*.md, write consensus to .ai/definition_of_done.md"
|
||||
];
|
||||
|
||||
PlanGemini [
|
||||
node_type="stack.observe", label="Plan (Gemini)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="8", timeout="420",
|
||||
llm_prompt="Create implementation plan.\n\nTASK: $task\nDOD: .ai/definition_of_done.md or $definition_of_done\n\nWrite to .ai/plan_gemini.md"
|
||||
];
|
||||
|
||||
PlanGPT [
|
||||
node_type="stack.observe", label="Plan (GPT)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="8", timeout="420", reasoning_effort="high",
|
||||
llm_prompt="Create implementation plan.\n\nTASK: $task\nDOD: .ai/definition_of_done.md or $definition_of_done\n\nWrite to .ai/plan_gpt.md"
|
||||
];
|
||||
|
||||
PlanOpus [
|
||||
node_type="stack.observe", label="Plan (Opus)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="8", timeout="420",
|
||||
llm_prompt="Create implementation plan.\n\nTASK: $task\nDOD: .ai/definition_of_done.md or $definition_of_done\n\nWrite to .ai/plan_opus.md"
|
||||
];
|
||||
|
||||
DebateConsolidate [
|
||||
node_type="stack.observe", label="Debate & Consolidate", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="10", timeout="600",
|
||||
llm_prompt="Synthesize three plans.\n\nTASK: $task\n\nRead .ai/plan_*.md, write final to .ai/plan_final.md"
|
||||
];
|
||||
|
||||
Implement [
|
||||
node_type="stack.observe", label="Implement (Opus)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", allow_partial="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="25", timeout="1200",
|
||||
llm_prompt="Execute plan.\n\nTASK: $task\n\nFollow .ai/plan_final.md. Log to .ai/implementation_log.md"
|
||||
];
|
||||
|
||||
ReviewGemini [
|
||||
node_type="stack.observe", label="Review (Gemini)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300",
|
||||
llm_prompt="Review implementation.\n\nTASK: $task\n\nWrite to .ai/review_gemini.md with PASS/FAIL"
|
||||
];
|
||||
|
||||
ReviewGPT [
|
||||
node_type="stack.observe", label="Review (GPT)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300", reasoning_effort="high",
|
||||
llm_prompt="Review implementation.\n\nTASK: $task\n\nWrite to .ai/review_gpt.md with PASS/FAIL"
|
||||
];
|
||||
|
||||
ReviewOpus [
|
||||
node_type="stack.observe", label="Review (Opus)", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300",
|
||||
llm_prompt="Review implementation.\n\nTASK: $task\n\nWrite to .ai/review_opus.md with PASS/FAIL"
|
||||
];
|
||||
|
||||
ReviewConsensus [
|
||||
node_type="stack.steer", label="Review Consensus", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="6", timeout="300",
|
||||
llm_prompt="Reach consensus.\n\nRead .ai/review_*.md\nWrite to .ai/review_consensus.md\n\noutcome=yes if PASS, outcome=retry if FAIL. Set preferred_next_label to \"\" (empty) or to \"yes\"/\"retry\"; do not use \"No\"."
|
||||
];
|
||||
|
||||
Postmortem [
|
||||
node_type="stack.observe", label="Postmortem", shape="box", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="8", timeout="420",
|
||||
llm_prompt="Failure postmortem.\n\nTASK: $task\n\nWrite to .ai/postmortem_NN.md"
|
||||
];
|
||||
|
||||
Exit [
|
||||
node_type="exit", label="Exit", shape="Msquare", style="rounded,filled",
|
||||
is_codergen="true", llm_provider="openai", llm_model="gpt-5.2-codex",
|
||||
max_agent_turns="4", timeout="120"
|
||||
];
|
||||
|
||||
Start -> CheckDoD;
|
||||
CheckDoD -> DefineDoD_Gemini [condition="outcome=needs_dod"];
|
||||
CheckDoD -> DefineDoD_GPT [condition="outcome=needs_dod"];
|
||||
CheckDoD -> DefineDoD_Opus [condition="outcome=needs_dod"];
|
||||
CheckDoD -> PlanGemini [condition="outcome=has_dod"];
|
||||
CheckDoD -> PlanGPT [condition="outcome=has_dod"];
|
||||
CheckDoD -> PlanOpus [condition="outcome=has_dod"];
|
||||
DefineDoD_Gemini -> ConsolidateDoD;
|
||||
DefineDoD_GPT -> ConsolidateDoD;
|
||||
DefineDoD_Opus -> ConsolidateDoD;
|
||||
ConsolidateDoD -> PlanGemini;
|
||||
ConsolidateDoD -> PlanGPT;
|
||||
ConsolidateDoD -> PlanOpus;
|
||||
PlanGemini -> DebateConsolidate;
|
||||
PlanGPT -> DebateConsolidate;
|
||||
PlanOpus -> DebateConsolidate;
|
||||
DebateConsolidate -> Implement;
|
||||
Implement -> ReviewGemini;
|
||||
Implement -> ReviewGPT;
|
||||
Implement -> ReviewOpus;
|
||||
ReviewGemini -> ReviewConsensus;
|
||||
ReviewGPT -> ReviewConsensus;
|
||||
ReviewOpus -> ReviewConsensus;
|
||||
ReviewConsensus -> Exit [condition="outcome=yes"];
|
||||
ReviewConsensus -> Postmortem;
|
||||
Postmortem -> PlanGemini [loop_restart="true"];
|
||||
Postmortem -> PlanGPT [loop_restart="true"];
|
||||
Postmortem -> PlanOpus [loop_restart="true"];
|
||||
}
|
||||
1075
test/kilroy/green_test_complex.dot
Normal file
1075
test/kilroy/green_test_complex.dot
Normal file
File diff suppressed because it is too large
Load diff
592
test/kilroy/green_test_moderate.dot
Normal file
592
test/kilroy/green_test_moderate.dot
Normal file
|
|
@ -0,0 +1,592 @@
|
|||
digraph linkcheck {
|
||||
graph [
|
||||
goal="Build a Go CLI tool that crawls URLs, checks links for HTTP status, and reports broken links with robots.txt support",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="impl_setup",
|
||||
model_stylesheet="
|
||||
* { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: medium; }
|
||||
.hard { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: high; }
|
||||
.verify { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: medium; }
|
||||
.review { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: high; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
// Phase 1: Requirements expansion
|
||||
expand_spec [
|
||||
shape=box,
|
||||
timeout=600,
|
||||
prompt="Given the requirements: Build a Go CLI tool called 'linkcheck' that takes a URL as input, crawls the page, finds all links, checks each link for HTTP status, and outputs a report of broken links (4xx/5xx). It should respect robots.txt, have a configurable crawl depth (default 1), and output in both human-readable and JSON formats.
|
||||
|
||||
Expand this into a detailed spec covering:
|
||||
- CLI interface (flags, arguments, usage)
|
||||
- Package structure (cmd/, pkg/)
|
||||
- Core types and interfaces (URL, Link, Report, etc.)
|
||||
- Robots.txt parsing and compliance
|
||||
- HTTP client configuration (timeouts, retries)
|
||||
- Crawl algorithm (depth limits, visited tracking)
|
||||
- Link extraction from HTML
|
||||
- Status code categorization (2xx=ok, 4xx/5xx=broken)
|
||||
- Output formats (human-readable table, JSON)
|
||||
- Error handling strategy
|
||||
- Test plan for each component
|
||||
|
||||
Write the expanded spec to .ai/spec.md.
|
||||
|
||||
Write status.json: outcome=success"
|
||||
]
|
||||
|
||||
// Phase 2: Project setup
|
||||
impl_setup [
|
||||
shape=box,
|
||||
timeout=600,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md.
|
||||
|
||||
Create the Go project structure:
|
||||
- go.mod (module name: linkcheck)
|
||||
- cmd/linkcheck/main.go (stub with basic CLI parsing)
|
||||
- pkg/crawler/ directory
|
||||
- pkg/robotstxt/ directory
|
||||
- pkg/checker/ directory
|
||||
- pkg/report/ directory
|
||||
|
||||
Run: go build ./...
|
||||
|
||||
Write status.json: outcome=success if the project builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_setup [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify project setup.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
|
||||
Check that:
|
||||
- go.mod exists with correct module name
|
||||
- cmd/linkcheck/main.go exists
|
||||
- pkg/ directories are created
|
||||
|
||||
Write results to .ai/verify_setup.md.
|
||||
Write status.json: outcome=success if all checks pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_setup [shape=diamond, label="Setup OK?"]
|
||||
|
||||
// Phase 3: Core types and interfaces
|
||||
impl_types [
|
||||
shape=box,
|
||||
timeout=900,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md.
|
||||
|
||||
Implement core types and interfaces per the spec.
|
||||
|
||||
Create pkg/crawler/types.go with:
|
||||
- URL type
|
||||
- Link type (URL, source, depth, status code)
|
||||
- CrawlConfig struct (max depth, user agent, timeout)
|
||||
- Crawler interface
|
||||
|
||||
Create pkg/report/types.go with:
|
||||
- Report struct (total links, broken links, results slice)
|
||||
- LinkResult struct (URL, status, error)
|
||||
- Formatter interface
|
||||
|
||||
Include comprehensive documentation for all exported types.
|
||||
|
||||
Run: go build ./...
|
||||
|
||||
Write status.json: outcome=success if builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_types [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify core types implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
|
||||
Check that:
|
||||
- pkg/crawler/types.go defines all required types
|
||||
- pkg/report/types.go defines report types
|
||||
- All types have godoc comments
|
||||
- No compilation errors
|
||||
|
||||
Write results to .ai/verify_types.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_types [shape=diamond, label="Types OK?"]
|
||||
|
||||
// Phase 4: Robots.txt parser
|
||||
impl_robotstxt [
|
||||
shape=box,
|
||||
class="hard",
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on robots.txt compliance.
|
||||
|
||||
Implement robots.txt parser in pkg/robotstxt/:
|
||||
- parser.go: Parse robots.txt files per RFC 9309
|
||||
- matcher.go: Check if a URL is allowed for a given user-agent
|
||||
- cache.go: Cache parsed robots.txt files to avoid repeated fetches
|
||||
|
||||
Handle:
|
||||
- User-agent matching (specific agent, wildcards, default *)
|
||||
- Allow/Disallow rules with path matching
|
||||
- Crawl-delay directive
|
||||
- Missing robots.txt (allow all)
|
||||
- Malformed robots.txt (be permissive)
|
||||
|
||||
Create pkg/robotstxt/parser_test.go with tests for:
|
||||
- Various robots.txt formats
|
||||
- User-agent matching
|
||||
- Path matching edge cases
|
||||
|
||||
Read: pkg/crawler/types.go for interfaces.
|
||||
|
||||
Run: go test ./pkg/robotstxt/...
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_robotstxt [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify robots.txt parser implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./pkg/robotstxt/...
|
||||
3. go test ./pkg/robotstxt/... -v
|
||||
|
||||
Check test coverage:
|
||||
4. go test ./pkg/robotstxt/... -cover
|
||||
|
||||
Write results to .ai/verify_robotstxt.md.
|
||||
Write status.json: outcome=success if all pass and coverage > 70%, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_robotstxt [shape=diamond, label="Robots.txt OK?"]
|
||||
|
||||
// Phase 5: HTTP checker
|
||||
impl_checker [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on HTTP checking.
|
||||
|
||||
Implement HTTP link checker in pkg/checker/:
|
||||
- checker.go: Check URL status codes
|
||||
- Configure HTTP client with reasonable timeouts
|
||||
- Follow redirects (up to a limit)
|
||||
- Categorize status codes (2xx/3xx=ok, 4xx/5xx=broken)
|
||||
- Handle network errors gracefully
|
||||
- Support HEAD requests (fallback to GET if HEAD fails)
|
||||
|
||||
Create pkg/checker/checker_test.go with:
|
||||
- Tests for various HTTP status codes
|
||||
- Mock HTTP server for testing
|
||||
- Timeout handling tests
|
||||
- Redirect handling tests
|
||||
|
||||
Read: pkg/crawler/types.go for types.
|
||||
|
||||
Run: go test ./pkg/checker/...
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_checker [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify HTTP checker implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./pkg/checker/...
|
||||
3. go test ./pkg/checker/... -v
|
||||
|
||||
Check:
|
||||
- Tests cover success, failure, and error cases
|
||||
- No race conditions: go test ./pkg/checker/... -race
|
||||
|
||||
Write results to .ai/verify_checker.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_checker [shape=diamond, label="Checker OK?"]
|
||||
|
||||
// Phase 6: HTML link extractor
|
||||
impl_extractor [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on link extraction.
|
||||
|
||||
Implement HTML link extractor in pkg/crawler/:
|
||||
- extractor.go: Parse HTML and extract all links
|
||||
- Find links in <a href>, <link href>, <img src>, <script src>
|
||||
- Convert relative URLs to absolute using base URL
|
||||
- Filter out non-http(s) schemes (mailto:, javascript:, etc.)
|
||||
- Handle malformed HTML gracefully
|
||||
- Use golang.org/x/net/html for parsing
|
||||
|
||||
Create pkg/crawler/extractor_test.go with:
|
||||
- Tests for relative URL resolution
|
||||
- Tests for various HTML structures
|
||||
- Tests for malformed HTML
|
||||
- Tests for filtering non-http links
|
||||
|
||||
Read: pkg/crawler/types.go for types.
|
||||
|
||||
Run: go test ./pkg/crawler/...
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_extractor [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify HTML extractor implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./pkg/crawler/...
|
||||
3. go test ./pkg/crawler/... -v
|
||||
|
||||
Check:
|
||||
- All test cases pass
|
||||
- Relative URL conversion is correct
|
||||
- Non-http schemes are filtered
|
||||
|
||||
Write results to .ai/verify_extractor.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_extractor [shape=diamond, label="Extractor OK?"]
|
||||
|
||||
// Phase 7: Crawler algorithm
|
||||
impl_crawler [
|
||||
shape=box,
|
||||
class="hard",
|
||||
timeout=1500,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on crawling algorithm.
|
||||
|
||||
Implement the main crawler in pkg/crawler/:
|
||||
- crawler.go: Orchestrate crawling with depth limits
|
||||
- Track visited URLs to avoid cycles
|
||||
- Respect robots.txt using pkg/robotstxt
|
||||
- Extract links using extractor.go
|
||||
- Check each link using pkg/checker
|
||||
- Implement breadth-first crawl up to configured depth
|
||||
- Handle concurrent checking with rate limiting
|
||||
- Collect results into a report
|
||||
|
||||
Key algorithm:
|
||||
1. Start with seed URL at depth 0
|
||||
2. Fetch and parse page
|
||||
3. Extract all links
|
||||
4. For each link:
|
||||
- Check if allowed by robots.txt
|
||||
- Check if already visited
|
||||
- Check HTTP status
|
||||
- If depth < max_depth and status ok, add to queue
|
||||
5. Continue until queue empty or max depth reached
|
||||
|
||||
Create pkg/crawler/crawler_test.go with:
|
||||
- Tests for depth limiting
|
||||
- Tests for visited tracking
|
||||
- Tests for robots.txt integration
|
||||
- Mock HTTP responses for testing
|
||||
|
||||
Read: pkg/crawler/types.go, pkg/crawler/extractor.go, pkg/robotstxt/, pkg/checker/ for dependencies.
|
||||
|
||||
Run: go test ./pkg/crawler/...
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_crawler [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify crawler implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./pkg/crawler/...
|
||||
3. go test ./pkg/crawler/... -v
|
||||
4. go test ./pkg/crawler/... -race
|
||||
|
||||
Check:
|
||||
- Depth limiting works correctly
|
||||
- No infinite loops
|
||||
- Robots.txt is respected
|
||||
- No race conditions
|
||||
|
||||
Write results to .ai/verify_crawler.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_crawler [shape=diamond, label="Crawler OK?"]
|
||||
|
||||
// Phase 8: Output formatters
|
||||
impl_report [
|
||||
shape=box,
|
||||
timeout=900,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on output formats.
|
||||
|
||||
Implement output formatters in pkg/report/:
|
||||
- formatter.go: Format interface
|
||||
- text.go: Human-readable table format showing broken links
|
||||
- json.go: JSON format with all details
|
||||
|
||||
Human-readable format should show:
|
||||
- Summary: X total links, Y broken
|
||||
- Table of broken links: URL, Status Code, Source Page
|
||||
- Color coding (red for errors) if terminal supports it
|
||||
|
||||
JSON format should output:
|
||||
- Structured data with all checked links
|
||||
- Status codes, timestamps, error messages
|
||||
- Easy to parse for automation
|
||||
|
||||
Create pkg/report/formatter_test.go with:
|
||||
- Tests for both formats
|
||||
- Tests for empty results
|
||||
- Tests for large result sets
|
||||
|
||||
Read: pkg/report/types.go for types.
|
||||
|
||||
Run: go test ./pkg/report/...
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_report [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify report formatting implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./pkg/report/...
|
||||
3. go test ./pkg/report/... -v
|
||||
|
||||
Check:
|
||||
- Both text and JSON formats work
|
||||
- Output is properly formatted
|
||||
- All test cases pass
|
||||
|
||||
Write results to .ai/verify_report.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_report [shape=diamond, label="Report OK?"]
|
||||
|
||||
// Phase 9: CLI integration
|
||||
impl_cli [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on CLI interface.
|
||||
|
||||
Implement the CLI in cmd/linkcheck/main.go:
|
||||
- Use flag package for argument parsing
|
||||
- Required argument: URL to check
|
||||
- Optional flags:
|
||||
--depth int (default 1): maximum crawl depth
|
||||
--format string (default text): output format (text or json)
|
||||
--user-agent string (default linkcheck/1.0): HTTP user agent
|
||||
--timeout int (default 10): HTTP timeout in seconds
|
||||
--verbose: enable verbose logging
|
||||
- Wire together: crawler, checker, robots.txt, formatter
|
||||
- Print results to stdout
|
||||
- Exit code 0 if no broken links, 1 if broken links found, 2 on error
|
||||
|
||||
Usage example:
|
||||
linkcheck https://example.com
|
||||
linkcheck --depth 2 --format json https://example.com
|
||||
|
||||
Read: all pkg/ directories for integration.
|
||||
|
||||
Run: go build ./cmd/linkcheck
|
||||
|
||||
Write status.json: outcome=success if builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_cli [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify CLI integration.
|
||||
|
||||
Run:
|
||||
1. go build ./cmd/linkcheck
|
||||
2. ./cmd/linkcheck --help (check help text)
|
||||
3. Test with a real URL: ./cmd/linkcheck https://example.com
|
||||
4. Test JSON output: ./cmd/linkcheck --format json https://example.com
|
||||
5. Test depth flag: ./cmd/linkcheck --depth 0 https://example.com
|
||||
|
||||
Check:
|
||||
- Binary builds successfully
|
||||
- Help text is clear
|
||||
- Flags work correctly
|
||||
- Output is properly formatted
|
||||
- Exit codes are correct
|
||||
|
||||
Write results to .ai/verify_cli.md.
|
||||
Write status.json: outcome=success if all manual tests pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_cli [shape=diamond, label="CLI OK?"]
|
||||
|
||||
// Phase 10: Integration tests
|
||||
impl_integration [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on testing.
|
||||
|
||||
Create integration tests in test/:
|
||||
- integration_test.go: End-to-end tests
|
||||
- Set up local HTTP test server with known structure
|
||||
- Create test pages with working and broken links
|
||||
- Create test robots.txt
|
||||
- Run linkcheck against test server
|
||||
- Verify correct broken links are detected
|
||||
- Test depth limiting
|
||||
- Test robots.txt compliance
|
||||
- Test both output formats
|
||||
|
||||
Run: go test ./test/...
|
||||
|
||||
Write status.json: outcome=success if integration tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_integration [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
goal_gate=true,
|
||||
prompt="Verify integration tests.
|
||||
|
||||
Run:
|
||||
1. go test ./test/... -v
|
||||
2. go test ./... (all tests)
|
||||
3. go build ./...
|
||||
|
||||
Check:
|
||||
- All integration tests pass
|
||||
- All unit tests still pass
|
||||
- Project builds cleanly
|
||||
|
||||
Write results to .ai/verify_integration.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
|
||||
]
|
||||
|
||||
check_integration [shape=diamond, label="Integration OK?"]
|
||||
|
||||
// Phase 11: Final review
|
||||
review [
|
||||
shape=box,
|
||||
class="review",
|
||||
timeout=600,
|
||||
goal_gate=true,
|
||||
prompt="Read .ai/spec.md.
|
||||
|
||||
Perform final review of the complete linkcheck implementation:
|
||||
|
||||
1. Verify all requirements are met:
|
||||
- CLI takes URL as input
|
||||
- Crawls pages and finds all links
|
||||
- Checks each link for HTTP status
|
||||
- Reports broken links (4xx/5xx)
|
||||
- Respects robots.txt
|
||||
- Configurable crawl depth (default 1)
|
||||
- Outputs both human-readable and JSON formats
|
||||
|
||||
2. Code quality:
|
||||
- All packages have tests
|
||||
- Code is well-documented
|
||||
- Error handling is comprehensive
|
||||
- No obvious bugs or race conditions
|
||||
|
||||
3. Functionality:
|
||||
- Run: go build ./cmd/linkcheck
|
||||
- Test with real URLs
|
||||
- Verify robots.txt compliance
|
||||
- Verify depth limiting works
|
||||
- Verify both output formats work
|
||||
|
||||
4. Run complete test suite:
|
||||
- go test ./...
|
||||
- go vet ./...
|
||||
- go build ./...
|
||||
|
||||
Write detailed review to .ai/final_review.md.
|
||||
Write status.json: outcome=success if complete and working, outcome=fail with specific issues that need fixing."
|
||||
]
|
||||
|
||||
check_review [shape=diamond, label="Review OK?"]
|
||||
|
||||
// Graph flow
|
||||
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
|
||||
|
||||
check_setup -> impl_types [condition="outcome=success"]
|
||||
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_types -> verify_types -> check_types
|
||||
check_types -> impl_robotstxt [condition="outcome=success"]
|
||||
check_types -> impl_types [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_robotstxt -> verify_robotstxt -> check_robotstxt
|
||||
check_robotstxt -> impl_checker [condition="outcome=success"]
|
||||
check_robotstxt -> impl_robotstxt [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_checker -> verify_checker -> check_checker
|
||||
check_checker -> impl_extractor [condition="outcome=success"]
|
||||
check_checker -> impl_checker [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_extractor -> verify_extractor -> check_extractor
|
||||
check_extractor -> impl_crawler [condition="outcome=success"]
|
||||
check_extractor -> impl_extractor [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_crawler -> verify_crawler -> check_crawler
|
||||
check_crawler -> impl_report [condition="outcome=success"]
|
||||
check_crawler -> impl_crawler [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_report -> verify_report -> check_report
|
||||
check_report -> impl_cli [condition="outcome=success"]
|
||||
check_report -> impl_report [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_cli -> verify_cli -> check_cli
|
||||
check_cli -> impl_integration [condition="outcome=success"]
|
||||
check_cli -> impl_cli [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_integration -> verify_integration -> check_integration
|
||||
check_integration -> review [condition="outcome=success"]
|
||||
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
|
||||
|
||||
review -> check_review
|
||||
check_review -> exit [condition="outcome=success"]
|
||||
check_review -> impl_cli [condition="outcome=fail", label="fix"]
|
||||
}
|
||||
473
test/kilroy/green_test_vague.dot
Normal file
473
test/kilroy/green_test_vague.dot
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
digraph solitaire {
|
||||
graph [
|
||||
goal="Build a terminal-based Klondike Solitaire game in Go with TUI",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="impl_setup",
|
||||
model_stylesheet="
|
||||
* { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: medium; }
|
||||
.hard { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: high; }
|
||||
.verify { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: medium; }
|
||||
.review { llm_model: gpt-5.2-codex; llm_provider: openai; reasoning_effort: high; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
// Project setup
|
||||
impl_setup [
|
||||
shape=box,
|
||||
timeout=600,
|
||||
prompt="Read .ai/spec.md. Create Go project structure for solitaire game.
|
||||
|
||||
Create:
|
||||
- go.mod with module name 'solitaire'
|
||||
- cmd/solitaire/main.go (stub with hello world)
|
||||
- pkg/game/ directory
|
||||
- pkg/ui/ directory
|
||||
- pkg/storage/ directory
|
||||
- README.md with brief description
|
||||
|
||||
Run: go build ./...
|
||||
|
||||
Write status.json: outcome=success if project builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_setup [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify project setup was completed correctly.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. Check that all required directories exist
|
||||
|
||||
Write results to .ai/verify_setup.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_setup [shape=diamond, label="Setup OK?"]
|
||||
|
||||
// Core types (cards and deck)
|
||||
impl_core_types [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md sections on Game Logic and Package Structure.
|
||||
|
||||
Implement card and deck types in pkg/game/:
|
||||
|
||||
Create pkg/game/card.go with:
|
||||
- Card type (Rank, Suit)
|
||||
- Suit constants (Spades, Hearts, Diamonds, Clubs) with Unicode symbols
|
||||
- Rank constants (Ace through King)
|
||||
- String() method for card display
|
||||
- NewDeck() function that creates standard 52-card deck
|
||||
- Shuffle() method using crypto/rand for secure shuffling
|
||||
|
||||
Create pkg/game/card_test.go with tests for:
|
||||
- Card string representation
|
||||
- Deck creation (52 unique cards)
|
||||
- Shuffle produces different order
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/game/
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_core_types [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify core card types implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/game/ -v
|
||||
4. Check test coverage: go test -cover ./pkg/game/
|
||||
|
||||
Write results to .ai/verify_core_types.md.
|
||||
Write status.json: outcome=success if ALL pass and coverage >70%, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_core_types [shape=diamond, label="Types OK?"]
|
||||
|
||||
// Game logic
|
||||
impl_game_logic [
|
||||
shape=box,
|
||||
class="hard",
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md sections on Game Logic and Core Features.
|
||||
Read pkg/game/card.go for Card and Deck types.
|
||||
|
||||
Implement Klondike Solitaire game logic:
|
||||
|
||||
Create pkg/game/game.go with:
|
||||
- Game struct with:
|
||||
- Tableau (7 piles, [][]Card)
|
||||
- Foundation (4 piles, [][]Card)
|
||||
- Stock pile ([]Card)
|
||||
- Waste pile ([]Card)
|
||||
- Move history for undo
|
||||
- NewGame() initializes properly (7 tableau piles with 1-7 cards, stock gets rest)
|
||||
- IsWon() checks if all cards in foundations
|
||||
- GetValidMoves() returns legal moves from current state
|
||||
|
||||
Create pkg/game/move.go with:
|
||||
- Move type (source, destination, card count)
|
||||
- MoveType constants (TableauToTableau, TableauToFoundation, WasteToTableau, etc.)
|
||||
- ValidateMove() checks Klondike rules:
|
||||
- Tableau: descending rank, alternating colors
|
||||
- Foundation: ascending rank, same suit, starts with Ace
|
||||
- Kings only to empty tableau
|
||||
- ExecuteMove() applies move and updates game state
|
||||
- UndoMove() reverts last move
|
||||
|
||||
Create pkg/game/game_test.go with tests for:
|
||||
- Game initialization (correct card distribution)
|
||||
- Move validation (legal and illegal moves)
|
||||
- Move execution and undo
|
||||
- Win detection
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/game/ -v (all tests pass)
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_game_logic [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify game logic implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/game/ -v
|
||||
4. go test -cover ./pkg/game/ (check for >80% coverage)
|
||||
|
||||
Write results to .ai/verify_game_logic.md.
|
||||
Write status.json: outcome=success if ALL pass with good coverage, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_game_logic [shape=diamond, label="Logic OK?"]
|
||||
|
||||
// UI Rendering
|
||||
impl_ui_render [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md sections on User Interface and Input/Output.
|
||||
Read pkg/game/game.go and pkg/game/card.go for game state types.
|
||||
|
||||
Implement TUI rendering using bubbletea framework:
|
||||
|
||||
First, add dependencies:
|
||||
- go get github.com/charmbracelet/bubbletea
|
||||
- go get github.com/charmbracelet/lipgloss
|
||||
|
||||
Create pkg/ui/render.go with:
|
||||
- RenderGame() function that takes Game state and returns formatted string
|
||||
- Display layout: foundations (top), tableau (middle), stock/waste (bottom)
|
||||
- Card display: face-up shows rank+suit, face-down shows \"##\"
|
||||
- Highlight selected pile/card
|
||||
- Status line showing move count and messages
|
||||
|
||||
Create pkg/ui/tui.go with:
|
||||
- model struct embedding Game and UI state (selected pile, cursor position)
|
||||
- Init() bubbletea init function
|
||||
- Update() stub (minimal, just quit on 'q')
|
||||
- View() calls RenderGame()
|
||||
|
||||
Create basic test in pkg/ui/render_test.go for RenderGame output format.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/ui/
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_ui_render [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify UI rendering implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/ui/ -v
|
||||
4. Check that bubbletea dependency is properly added to go.mod
|
||||
|
||||
Write results to .ai/verify_ui_render.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_ui_render [shape=diamond, label="Render OK?"]
|
||||
|
||||
// UI Input
|
||||
impl_ui_input [
|
||||
shape=box,
|
||||
class="hard",
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on User Interface keyboard navigation.
|
||||
Read pkg/game/game.go, pkg/game/move.go for game operations.
|
||||
Read pkg/ui/tui.go for existing model struct.
|
||||
|
||||
Implement keyboard input handling:
|
||||
|
||||
Update pkg/ui/tui.go:
|
||||
- Extend model with:
|
||||
- selectedPile (which pile is selected)
|
||||
- selectedCardIndex (which card in pile)
|
||||
- message (for feedback)
|
||||
- Update() handles:
|
||||
- Arrow keys / hjkl for navigation
|
||||
- Space/Enter to select source, then destination (execute move)
|
||||
- 'd' to draw from stock
|
||||
- 'u' to undo
|
||||
- 'n' for new game
|
||||
- 'q' to quit
|
||||
- Call game.ValidateMove() before executing
|
||||
- Show feedback messages for invalid moves
|
||||
- Detect and display win condition
|
||||
|
||||
Create pkg/ui/input.go with helper functions for input processing.
|
||||
|
||||
Update cmd/solitaire/main.go to:
|
||||
- Create new game
|
||||
- Initialize bubbletea program with TUI model
|
||||
- Run program
|
||||
|
||||
Acceptance:
|
||||
- go build ./cmd/solitaire
|
||||
- Binary runs and displays game (manual check)
|
||||
- All keyboard controls work (manual check)
|
||||
|
||||
Write status.json: outcome=success if builds and runs, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_ui_input [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify UI input handling.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./...
|
||||
4. go build ./cmd/solitaire
|
||||
5. Check that binary exists at ./cmd/solitaire/solitaire
|
||||
|
||||
Write results to .ai/verify_ui_input.md.
|
||||
Write status.json: outcome=success if ALL pass and binary exists, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_ui_input [shape=diamond, label="Input OK?"]
|
||||
|
||||
// Persistence
|
||||
impl_persistence [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md section on Game State Management and Input/Output.
|
||||
Read pkg/game/game.go for Game struct.
|
||||
|
||||
Implement save/load functionality:
|
||||
|
||||
Create pkg/storage/save.go with:
|
||||
- SaveGame(game *Game, filename string) error
|
||||
- Serialize game state to JSON
|
||||
- Write to file
|
||||
- LoadGame(filename string) (*Game, error)
|
||||
- Read from file
|
||||
- Deserialize JSON to Game struct
|
||||
- Validate loaded state
|
||||
- Make sure Game struct fields are exported for JSON marshaling
|
||||
|
||||
Update cmd/solitaire/main.go to:
|
||||
- Add --load flag
|
||||
- Add 's' key to save game
|
||||
- Auto-save on quit (to .solitaire-save.json)
|
||||
- Load on startup if save exists
|
||||
|
||||
Create pkg/storage/save_test.go with:
|
||||
- Test save/load cycle
|
||||
- Test invalid file handling
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/storage/
|
||||
- Manual test: save game, quit, restart with --load
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_persistence [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify persistence implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/storage/ -v
|
||||
4. go test ./... (all tests)
|
||||
|
||||
Write results to .ai/verify_persistence.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_persistence [shape=diamond, label="Persist OK?"]
|
||||
|
||||
// Integration and polish
|
||||
impl_integration [
|
||||
shape=box,
|
||||
timeout=1200,
|
||||
max_retries=2,
|
||||
prompt="Read .ai/spec.md for full acceptance criteria.
|
||||
Read all existing code to understand current state.
|
||||
|
||||
Polish and integrate the complete solitaire game:
|
||||
|
||||
Tasks:
|
||||
1. Add command-line flags:
|
||||
- --draw (1 or 3 cards, default 3)
|
||||
- --seed (for reproducible games)
|
||||
- --load (load saved game)
|
||||
2. Add statistics display on quit (moves, time)
|
||||
3. Improve error messages and user feedback
|
||||
4. Add help screen ('h' or '?' key)
|
||||
5. Ensure all tests pass
|
||||
6. Update README.md with build instructions and how to play
|
||||
|
||||
Create integration test that:
|
||||
- Initializes game
|
||||
- Executes a sequence of moves
|
||||
- Verifies game state
|
||||
- Tests save/load cycle
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./...
|
||||
- go vet ./...
|
||||
- ./cmd/solitaire/solitaire runs successfully
|
||||
- All keyboard commands work
|
||||
- Game is winnable
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_integration [
|
||||
shape=box,
|
||||
class="verify",
|
||||
timeout=300,
|
||||
prompt="Verify integration and completeness.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./... -v
|
||||
4. go test -cover ./...
|
||||
5. go build ./cmd/solitaire
|
||||
6. Test all command-line flags work
|
||||
7. Verify README exists and has instructions
|
||||
|
||||
Write results to .ai/verify_integration.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_integration [shape=diamond, label="Integration OK?"]
|
||||
|
||||
// Final review
|
||||
review [
|
||||
shape=box,
|
||||
class="review",
|
||||
timeout=600,
|
||||
goal_gate=true,
|
||||
prompt="Read .ai/spec.md in full.
|
||||
Review the complete solitaire implementation against all requirements.
|
||||
|
||||
Verify:
|
||||
1. Core Gameplay:
|
||||
- Game initializes correctly (proper card distribution)
|
||||
- All legal moves accepted
|
||||
- All illegal moves rejected
|
||||
- Win condition works
|
||||
2. User Experience:
|
||||
- Full keyboard control
|
||||
- Clear display
|
||||
- Undo works
|
||||
- Save/load works
|
||||
3. Code Quality:
|
||||
- go build ./... succeeds
|
||||
- go test ./... succeeds
|
||||
- go vet ./... clean
|
||||
- Test coverage >80% for pkg/game/
|
||||
4. Binary Works:
|
||||
- Runs with ./cmd/solitaire/solitaire
|
||||
- All features functional
|
||||
- Can complete a game
|
||||
|
||||
Run full test suite and manual gameplay check.
|
||||
|
||||
Write comprehensive review to .ai/final_review.md.
|
||||
Write status.json: outcome=success if COMPLETE per spec, outcome=fail with specific missing items."
|
||||
]
|
||||
|
||||
check_review [shape=diamond, label="Review OK?"]
|
||||
|
||||
// Wire up the graph
|
||||
start -> impl_setup
|
||||
impl_setup -> verify_setup
|
||||
verify_setup -> check_setup
|
||||
check_setup -> impl_core_types [condition="outcome=success"]
|
||||
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_core_types -> verify_core_types
|
||||
verify_core_types -> check_core_types
|
||||
check_core_types -> impl_game_logic [condition="outcome=success"]
|
||||
check_core_types -> impl_core_types [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_game_logic -> verify_game_logic
|
||||
verify_game_logic -> check_game_logic
|
||||
check_game_logic -> impl_ui_render [condition="outcome=success"]
|
||||
check_game_logic -> impl_game_logic [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_ui_render -> verify_ui_render
|
||||
verify_ui_render -> check_ui_render
|
||||
check_ui_render -> impl_ui_input [condition="outcome=success"]
|
||||
check_ui_render -> impl_ui_render [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_ui_input -> verify_ui_input
|
||||
verify_ui_input -> check_ui_input
|
||||
check_ui_input -> impl_persistence [condition="outcome=success"]
|
||||
check_ui_input -> impl_ui_input [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_persistence -> verify_persistence
|
||||
verify_persistence -> check_persistence
|
||||
check_persistence -> impl_integration [condition="outcome=success"]
|
||||
check_persistence -> impl_persistence [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_integration -> verify_integration
|
||||
verify_integration -> check_integration
|
||||
check_integration -> review [condition="outcome=success"]
|
||||
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
|
||||
|
||||
review -> check_review
|
||||
check_review -> exit [condition="outcome=success"]
|
||||
check_review -> impl_integration [condition="outcome=fail", label="fix"]
|
||||
}
|
||||
569
test/kilroy/refactor_test_complex.dot
Normal file
569
test/kilroy/refactor_test_complex.dot
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
digraph dttf {
|
||||
graph [
|
||||
goal="Build DTTF: a tool that converts bitmap glyph images to valid TrueType fonts",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="impl_setup",
|
||||
fallback_retry_target="impl_loader",
|
||||
model_stylesheet="
|
||||
* { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: medium; }
|
||||
.hard { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: high; }
|
||||
.verify { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: medium; }
|
||||
.review { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: high; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
// Project setup
|
||||
impl_setup [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md sections 4 (Architecture) and 7 (Data Structures).
|
||||
|
||||
Create Go project structure:
|
||||
- go.mod with module github.com/kilroy/dttf
|
||||
- pkg/dttf/ directory for core library
|
||||
- cmd/dttf/ directory for CLI
|
||||
- pkg/dttf/types.go with core data structures from section 7.1: GlyphBitmap, Point, Contour, TracedGlyph, FontMetadata
|
||||
|
||||
Run: go build ./...
|
||||
|
||||
Write status.json: outcome=success if builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_setup [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify project setup was completed correctly.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. Check that pkg/dttf/types.go exists and contains all required types from specs/dttf-v1.md section 7.1
|
||||
|
||||
Write results to .ai/verify_setup.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_setup [shape=diamond, label="Setup OK?"]
|
||||
|
||||
// PNG loader implementation
|
||||
impl_loader [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md sections 1 (Input) and 4.3 (Pipeline).
|
||||
|
||||
Implement PNG loading and parsing:
|
||||
- Create pkg/dttf/loader.go
|
||||
- Implement LoadGlyphs(inputDir string) ([]GlyphBitmap, *FontMetadata, error)
|
||||
- Parse filename pattern: [FontName-GlyphLabel-]U+XXXX.png (section 1.2)
|
||||
- Load PNGs, convert to grayscale (section 1.4)
|
||||
- Load optional font.json metadata (section 1.5)
|
||||
- Apply threshold conversion to 1-bit (section 1.4)
|
||||
|
||||
Create pkg/dttf/loader_test.go with tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_loader [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify PNG loader implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Loader
|
||||
|
||||
Write results to .ai/verify_loader.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_loader [shape=diamond, label="Loader OK?"]
|
||||
|
||||
// Tracer implementation (complex, uses Opus)
|
||||
impl_tracer [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md section 3 (Tracer) completely.
|
||||
|
||||
Implement custom bitmap-to-vector tracer:
|
||||
- Create pkg/dttf/tracer.go
|
||||
- Implement TraceGlyph(bitmap GlyphBitmap, opts TraceOptions) ([]Contour, error)
|
||||
- Phase 1: Path decomposition (boundary following, section 3.2)
|
||||
- Phase 2: Optimal polygon approximation (section 3.2)
|
||||
- Phase 3: Quadratic Bezier fitting (section 3.2)
|
||||
- Phase 4: Font-aware optimization (extrema, winding, intersections, section 3.2)
|
||||
- Coordinate mapping per section 3.3
|
||||
- Configuration parameters from section 3.4
|
||||
|
||||
Create pkg/dttf/tracer_test.go with unit tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v -run Tracer
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_tracer [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify tracer implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Tracer
|
||||
4. Check that tracer outputs quadratic Beziers (not cubic)
|
||||
5. Verify winding direction enforcement (section 3.2 Phase 4)
|
||||
|
||||
Write results to .ai/verify_tracer.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_tracer [shape=diamond, label="Tracer OK?"]
|
||||
|
||||
// Metrics computation
|
||||
impl_metrics [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md section 2.6 (Glyph Metrics).
|
||||
|
||||
Implement glyph metrics computation:
|
||||
- Create pkg/dttf/metrics.go
|
||||
- Compute bounding boxes from traced contours
|
||||
- Compute advanceWidth, leftSideBearing per section 2.6
|
||||
- Implement sidebearing strategy (proportional to UPEm)
|
||||
|
||||
Create pkg/dttf/metrics_test.go with tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v -run Metrics
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_metrics [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify metrics computation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Metrics
|
||||
|
||||
Write results to .ai/verify_metrics.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_metrics [shape=diamond, label="Metrics OK?"]
|
||||
|
||||
// TrueType table assembly (complex, uses Opus)
|
||||
impl_tables [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md sections 2.2 (Required Tables), 2.5 (Vertical Metrics), and 8 (File Assembly).
|
||||
|
||||
Implement TrueType table assembly:
|
||||
- Create pkg/dttf/tables.go
|
||||
- Implement AssembleFont(glyphs []TracedGlyph, meta *FontMetadata) (*Font, error)
|
||||
- Build all 10 required tables: head, maxp, hhea, hmtx, OS/2, name, post, cmap, loca, glyf (section 2.2)
|
||||
- Add gasp table (section 2.3)
|
||||
- Implement vertical metrics per section 2.5
|
||||
- Implement glyf encoding per section 8.4
|
||||
- Implement cmap format 4 per section 8.5
|
||||
- Compute checksums per section 8.3
|
||||
|
||||
Create pkg/dttf/tables_test.go with tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v -run Tables
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_tables [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify TrueType table assembly.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Tables
|
||||
4. Verify all 10 required tables are present
|
||||
5. Verify checksums are computed correctly
|
||||
|
||||
Write results to .ai/verify_tables.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_tables [shape=diamond, label="Tables OK?"]
|
||||
|
||||
// Font writer
|
||||
impl_writer [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md section 8 (File Assembly).
|
||||
|
||||
Implement font file writer:
|
||||
- Create pkg/dttf/writer.go
|
||||
- Implement WriteFont(font *Font, outputPath string) error
|
||||
- Write offset table, table directory, table data (section 8.1)
|
||||
- Order tables alphabetically by tag (section 8.2)
|
||||
- Pad tables to 4-byte boundaries (section 8.1)
|
||||
- Set checksumAdjustment in head table (section 8.3)
|
||||
|
||||
Implement main Build function:
|
||||
- Create pkg/dttf/build.go
|
||||
- Implement Build(inputDir string, outputPath string, opts Options) error
|
||||
- Orchestrate: LoadGlyphs -> TraceGlyph (parallel) -> compute metrics -> AssembleFont -> WriteFont
|
||||
|
||||
Create pkg/dttf/build_test.go with integration tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_writer [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify font writer and Build function.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Writer
|
||||
4. go test ./pkg/dttf/... -v -run Build
|
||||
|
||||
Write results to .ai/verify_writer.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_writer [shape=diamond, label="Writer OK?"]
|
||||
|
||||
// Validator
|
||||
impl_validator [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md section 5.1 (Font Validity) and section 9 (Error Handling).
|
||||
|
||||
Implement font validation:
|
||||
- Create pkg/dttf/validator.go
|
||||
- Implement Validate(fontPath string) (ValidationResult, error)
|
||||
- Check: loadable by golang.org/x/image/font/sfnt parser
|
||||
- Check: contours closed
|
||||
- Check: correct winding direction (signed area test)
|
||||
- Check: no self-intersections (segment-segment test)
|
||||
- Check: points at extrema (derivative roots)
|
||||
|
||||
Create pkg/dttf/validator_test.go with tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v -run Validator
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_validator [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify validator implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Validator
|
||||
|
||||
Write results to .ai/verify_validator.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_validator [shape=diamond, label="Validator OK?"]
|
||||
|
||||
// Rasterizer
|
||||
impl_rasterizer [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md section 11 (Rasterizer).
|
||||
|
||||
Implement font-to-bitmap rasterizer:
|
||||
- Create pkg/dttf/rasterizer.go
|
||||
- Implement Rasterize(fontPath string, outputDir string, opts RasterizeOptions) error
|
||||
- Use golang.org/x/image/font/opentype for rendering
|
||||
- Character set selection: ASCII, All, Chars, Ranges (section 11.3)
|
||||
- Render configuration from section 11.4
|
||||
- Extract and write font.json with real metrics (section 11.5)
|
||||
- PNG naming per section 1.2
|
||||
|
||||
Create pkg/dttf/rasterizer_test.go with tests.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v -run Rasterizer
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_rasterizer [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify rasterizer implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run Rasterizer
|
||||
|
||||
Write results to .ai/verify_rasterizer.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_rasterizer [shape=diamond, label="Rasterizer OK?"]
|
||||
|
||||
// CLI implementation
|
||||
impl_cli [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md sections 4.2 (API Surface) and 12 (CLI Reference).
|
||||
|
||||
Implement CLI commands:
|
||||
- Create cmd/dttf/main.go
|
||||
- Use cobra or similar for CLI structure
|
||||
- Implement: dttf build (section 12)
|
||||
- Implement: dttf rasterize (section 12)
|
||||
- Implement: dttf validate (section 12)
|
||||
- Implement: dttf test (section 12)
|
||||
- All flags and options from section 12
|
||||
|
||||
Acceptance:
|
||||
- go build ./cmd/dttf
|
||||
- ./cmd/dttf/dttf --help
|
||||
- Test each command with --help
|
||||
|
||||
Write status.json: outcome=success if all commands work, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_cli [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify CLI implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./cmd/dttf
|
||||
2. go vet ./cmd/dttf/...
|
||||
3. ./cmd/dttf/dttf --help
|
||||
4. ./cmd/dttf/dttf build --help
|
||||
5. ./cmd/dttf/dttf rasterize --help
|
||||
6. ./cmd/dttf/dttf validate --help
|
||||
7. ./cmd/dttf/dttf test --help
|
||||
|
||||
Write results to .ai/verify_cli.md.
|
||||
Write status.json: outcome=success if ALL commands exist and show help, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_cli [shape=diamond, label="CLI OK?"]
|
||||
|
||||
// Test harness (complex, uses Opus)
|
||||
impl_test_harness [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md section 6 (Test Harness) and section 5 (Quality Function).
|
||||
|
||||
Implement round-trip test harness:
|
||||
- Create pkg/dttf/testharness.go
|
||||
- Implement round-trip test per section 6.1: render -> trace -> compare
|
||||
- Reference fonts from section 6.2 (Roboto, Open Sans, Noto Serif, Source Code Pro, Playfair Display, Roboto Slab)
|
||||
- SSIM computation per section 5.2
|
||||
- Multi-scale testing per section 5.3
|
||||
- Quality metrics from sections 5.4, 5.5, 5.6
|
||||
- Composite score per section 5.7
|
||||
- Auto-download reference fonts from Google Fonts API
|
||||
|
||||
Create pkg/dttf/testharness_test.go with tests.
|
||||
Integrate with dttf test CLI command.
|
||||
|
||||
Acceptance:
|
||||
- go build ./...
|
||||
- go test ./pkg/dttf/... -v -run TestHarness
|
||||
- dttf test --help shows correct options
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_test_harness [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify test harness implementation.
|
||||
|
||||
Run:
|
||||
1. go build ./...
|
||||
2. go vet ./...
|
||||
3. go test ./pkg/dttf/... -v -run TestHarness
|
||||
4. Verify SSIM computation is implemented
|
||||
5. Verify reference font download works
|
||||
|
||||
Write results to .ai/verify_test_harness.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_test_harness [shape=diamond, label="Test Harness OK?"]
|
||||
|
||||
// Integration test
|
||||
impl_integration [
|
||||
shape=box,
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md completely.
|
||||
|
||||
Run full integration test:
|
||||
1. Use dttf rasterize to create test input from a simple reference font
|
||||
2. Run dttf build on the rasterized glyphs
|
||||
3. Run dttf validate on the output font
|
||||
4. Run dttf test to compare output vs reference
|
||||
5. Verify SSIM scores meet thresholds from section 5.2
|
||||
|
||||
Document results in .ai/integration_test.md.
|
||||
|
||||
Acceptance:
|
||||
- All commands succeed
|
||||
- Output font is valid
|
||||
- SSIM > 0.90 (acceptable threshold from section 5.2)
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason and specific metrics otherwise."
|
||||
]
|
||||
|
||||
verify_integration [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify integration test results.
|
||||
|
||||
Run:
|
||||
1. Check .ai/integration_test.md exists and shows passing results
|
||||
2. Verify output font file exists and is valid
|
||||
3. Verify SSIM scores are documented and meet threshold
|
||||
4. go test ./... (full test suite)
|
||||
|
||||
Write results to .ai/verify_integration.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_integration [shape=diamond, label="Integration OK?"]
|
||||
|
||||
// Final review
|
||||
review [
|
||||
shape=box,
|
||||
class="review",
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read specs/dttf-v1.md in full.
|
||||
|
||||
Review complete DTTF implementation against spec:
|
||||
|
||||
1. Input handling (section 1): PNG loading, filename parsing, metadata, thresholding
|
||||
2. Output format (section 2): All required tables, vertical metrics, glyph metrics
|
||||
3. Tracer (section 3): Custom quadratic Bezier tracer with all 4 phases
|
||||
4. Architecture (section 4): Go library + CLI, correct pipeline
|
||||
5. Quality function (section 5): All 6 layers implemented
|
||||
6. Test harness (section 6): Round-trip testing with reference fonts
|
||||
7. Data structures (section 7): All core types present
|
||||
8. File assembly (section 8): Correct TrueType structure
|
||||
9. Error handling (section 9): Fail loudly and early
|
||||
10. Dependencies (section 10): Only Go stdlib + x/image packages
|
||||
11. Rasterizer (section 11): Font-to-bitmap with all features
|
||||
12. CLI (section 12): All commands with correct flags
|
||||
|
||||
Run:
|
||||
- go build ./...
|
||||
- go test ./... -v
|
||||
- dttf test --reference-dir <test-fonts>/ if available
|
||||
|
||||
Write comprehensive review to .ai/final_review.md.
|
||||
Write status.json: outcome=success if complete and spec-compliant, outcome=fail with specific missing/incorrect features otherwise."
|
||||
]
|
||||
|
||||
check_review [shape=diamond, label="Review OK?"]
|
||||
|
||||
// Flow
|
||||
start -> impl_setup -> verify_setup -> check_setup
|
||||
check_setup -> impl_loader [condition="outcome=success"]
|
||||
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_loader -> verify_loader -> check_loader
|
||||
check_loader -> impl_tracer [condition="outcome=success"]
|
||||
check_loader -> impl_loader [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_tracer -> verify_tracer -> check_tracer
|
||||
check_tracer -> impl_metrics [condition="outcome=success"]
|
||||
check_tracer -> impl_tracer [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_metrics -> verify_metrics -> check_metrics
|
||||
check_metrics -> impl_tables [condition="outcome=success"]
|
||||
check_metrics -> impl_metrics [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_tables -> verify_tables -> check_tables
|
||||
check_tables -> impl_writer [condition="outcome=success"]
|
||||
check_tables -> impl_tables [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_writer -> verify_writer -> check_writer
|
||||
check_writer -> impl_validator [condition="outcome=success"]
|
||||
check_writer -> impl_writer [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_validator -> verify_validator -> check_validator
|
||||
check_validator -> impl_rasterizer [condition="outcome=success"]
|
||||
check_validator -> impl_validator [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_rasterizer -> verify_rasterizer -> check_rasterizer
|
||||
check_rasterizer -> impl_cli [condition="outcome=success"]
|
||||
check_rasterizer -> impl_rasterizer [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_cli -> verify_cli -> check_cli
|
||||
check_cli -> impl_test_harness [condition="outcome=success"]
|
||||
check_cli -> impl_cli [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_test_harness -> verify_test_harness -> check_test_harness
|
||||
check_test_harness -> impl_integration [condition="outcome=success"]
|
||||
check_test_harness -> impl_test_harness [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_integration -> verify_integration -> check_integration
|
||||
check_integration -> review [condition="outcome=success"]
|
||||
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
|
||||
|
||||
review -> check_review
|
||||
check_review -> exit [condition="outcome=success"]
|
||||
check_review -> impl_integration [condition="outcome=fail", label="fix"]
|
||||
}
|
||||
400
test/kilroy/refactor_test_moderate.dot
Normal file
400
test/kilroy/refactor_test_moderate.dot
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
digraph linkcheck {
|
||||
graph [
|
||||
goal="Build a Go CLI tool that checks URLs for broken links with configurable depth and multiple output formats",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="impl_setup",
|
||||
fallback_retry_target="impl_crawler",
|
||||
model_stylesheet="
|
||||
* { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: medium; }
|
||||
.hard { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: high; }
|
||||
.verify { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: medium; }
|
||||
.review { llm_model: gemini-3-flash-preview; llm_provider: google; reasoning_effort: high; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
// Spec expansion (vague input — bootstraps .ai/spec.md into existence)
|
||||
expand_spec [
|
||||
shape=box,
|
||||
auto_status=true,
|
||||
prompt="Given the requirements: Build a Go CLI tool called 'linkcheck' that takes a URL as input, crawls the page, finds all links, checks each link for HTTP status, and outputs a report of broken links (4xx/5xx). It should respect robots.txt, have a configurable crawl depth (default 1), and output in both human-readable and JSON formats.
|
||||
|
||||
Expand into a detailed spec covering:
|
||||
- CLI interface (flags, arguments, exit codes)
|
||||
- Package structure (cmd/, pkg/ organization)
|
||||
- Core data types (Link, CrawlResult, Report, etc.)
|
||||
- robots.txt parsing and respect
|
||||
- HTTP client configuration (timeouts, user agent, redirects)
|
||||
- Link extraction from HTML
|
||||
- Status code categorization (broken vs. OK)
|
||||
- Depth control mechanism
|
||||
- Output formatters (text and JSON)
|
||||
- Error handling
|
||||
- Test plan (unit tests for each package, integration test)
|
||||
|
||||
Write the spec to .ai/spec.md.
|
||||
|
||||
Write status.json: outcome=success"
|
||||
]
|
||||
|
||||
// Project setup
|
||||
impl_setup [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md. Create the Go project structure:
|
||||
- Initialize go.mod for linkcheck
|
||||
- Create cmd/linkcheck/main.go with CLI stub
|
||||
- Create pkg/ directories: pkg/crawler/, pkg/checker/, pkg/robots/, pkg/formatter/
|
||||
- Add basic README.md with build/usage instructions
|
||||
|
||||
Acceptance:
|
||||
- `go mod init` must succeed
|
||||
- `go build ./...` must pass
|
||||
- Directory structure matches spec
|
||||
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_setup [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify project setup was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./...`
|
||||
2. `go vet ./...`
|
||||
3. Check that go.mod exists
|
||||
4. Check that cmd/linkcheck/main.go exists
|
||||
5. Check that pkg/ subdirectories exist
|
||||
|
||||
Write results to .ai/verify_setup.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_setup [shape=diamond, label="Setup OK?"]
|
||||
|
||||
// Crawler implementation
|
||||
impl_crawler [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Spec: .ai/spec.md, crawler section.
|
||||
Read: cmd/linkcheck/main.go for understanding the entry point.
|
||||
|
||||
Implement the web crawler in pkg/crawler/:
|
||||
- Crawler struct with configurable depth, user agent
|
||||
- FetchPage function (HTTP GET with proper headers)
|
||||
- ExtractLinks function (parse HTML, find all <a href> tags)
|
||||
- Depth tracking and queue management
|
||||
- URL normalization and deduplication
|
||||
- Tests for all functions
|
||||
|
||||
Create/modify:
|
||||
- pkg/crawler/crawler.go
|
||||
- pkg/crawler/parser.go
|
||||
- pkg/crawler/crawler_test.go
|
||||
|
||||
Acceptance:
|
||||
- `go build ./...` must pass
|
||||
- `go test ./pkg/crawler/...` must pass with all tests
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_crawler [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify crawler implementation was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./...`
|
||||
2. `go vet ./...`
|
||||
3. `go test ./pkg/crawler/... -v`
|
||||
4. Check that pkg/crawler/crawler.go exists
|
||||
5. Check that tests cover FetchPage and ExtractLinks
|
||||
|
||||
Write results to .ai/verify_crawler.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_crawler [shape=diamond, label="Crawler OK?"]
|
||||
|
||||
// Robots.txt implementation
|
||||
impl_robots [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Spec: .ai/spec.md, robots.txt section.
|
||||
Read: pkg/crawler/crawler.go for types you need.
|
||||
|
||||
Implement robots.txt parser and checker in pkg/robots/:
|
||||
- RobotsParser struct
|
||||
- FetchRobotsTxt function
|
||||
- IsAllowed function (check if URL path is allowed for user agent)
|
||||
- Parse user-agent, disallow, allow directives
|
||||
- Tests
|
||||
|
||||
Create/modify:
|
||||
- pkg/robots/robots.go
|
||||
- pkg/robots/robots_test.go
|
||||
|
||||
Acceptance:
|
||||
- `go build ./...` must pass
|
||||
- `go test ./pkg/robots/...` must pass
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_robots [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify robots.txt implementation was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./...`
|
||||
2. `go vet ./...`
|
||||
3. `go test ./pkg/robots/... -v`
|
||||
4. Check that pkg/robots/robots.go exists
|
||||
|
||||
Write results to .ai/verify_robots.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_robots [shape=diamond, label="Robots OK?"]
|
||||
|
||||
// Link checker implementation
|
||||
impl_checker [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Spec: .ai/spec.md, link checker section.
|
||||
Read: pkg/crawler/crawler.go, pkg/robots/robots.go for types.
|
||||
|
||||
Implement HTTP status checker in pkg/checker/:
|
||||
- CheckLink function (HEAD or GET request, return status code)
|
||||
- Categorize status codes (2xx=OK, 3xx=redirect, 4xx/5xx=broken)
|
||||
- Timeout and retry handling
|
||||
- Report struct with URL, status, error message
|
||||
- Tests with mock HTTP server
|
||||
|
||||
Create/modify:
|
||||
- pkg/checker/checker.go
|
||||
- pkg/checker/status.go
|
||||
- pkg/checker/checker_test.go
|
||||
|
||||
Acceptance:
|
||||
- `go build ./...` must pass
|
||||
- `go test ./pkg/checker/...` must pass
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_checker [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify link checker implementation was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./...`
|
||||
2. `go vet ./...`
|
||||
3. `go test ./pkg/checker/... -v`
|
||||
4. Check that pkg/checker/checker.go exists
|
||||
|
||||
Write results to .ai/verify_checker.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_checker [shape=diamond, label="Checker OK?"]
|
||||
|
||||
// Output formatter implementation
|
||||
impl_formatter [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Spec: .ai/spec.md, output section.
|
||||
Read: pkg/checker/checker.go for Report type.
|
||||
|
||||
Implement output formatters in pkg/formatter/:
|
||||
- TextFormatter (human-readable table or list)
|
||||
- JSONFormatter (structured JSON output)
|
||||
- Format function that takes Report slice and format type
|
||||
- Tests for both formatters
|
||||
|
||||
Create/modify:
|
||||
- pkg/formatter/text.go
|
||||
- pkg/formatter/json.go
|
||||
- pkg/formatter/formatter.go
|
||||
- pkg/formatter/formatter_test.go
|
||||
|
||||
Acceptance:
|
||||
- `go build ./...` must pass
|
||||
- `go test ./pkg/formatter/...` must pass
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_formatter [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify formatter implementation was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./...`
|
||||
2. `go vet ./...`
|
||||
3. `go test ./pkg/formatter/... -v`
|
||||
4. Check that both text.go and json.go exist
|
||||
|
||||
Write results to .ai/verify_formatter.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_formatter [shape=diamond, label="Formatter OK?"]
|
||||
|
||||
// CLI integration
|
||||
impl_cli [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Spec: .ai/spec.md, CLI section.
|
||||
Read: pkg/crawler/, pkg/robots/, pkg/checker/, pkg/formatter/ for all functions.
|
||||
|
||||
Wire up the CLI in cmd/linkcheck/main.go:
|
||||
- Parse flags: --depth (default 1), --format (text/json), --user-agent
|
||||
- Parse URL argument
|
||||
- Orchestrate: check robots.txt, crawl, check links, format output
|
||||
- Exit codes: 0 if no broken links, 1 if broken links found, 2 on error
|
||||
- Help text
|
||||
|
||||
Acceptance:
|
||||
- `go build ./cmd/linkcheck` must pass
|
||||
- `./linkcheck --help` shows usage
|
||||
- `./linkcheck https://example.com` runs without error
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_cli [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify CLI integration was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./cmd/linkcheck`
|
||||
2. `./linkcheck --help` (must show help)
|
||||
3. `go vet ./...`
|
||||
4. Check that main.go wires all packages together
|
||||
|
||||
Write results to .ai/verify_cli.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_cli [shape=diamond, label="CLI OK?"]
|
||||
|
||||
// Integration test
|
||||
impl_integration [
|
||||
shape=box,
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Spec: .ai/spec.md, test plan section.
|
||||
|
||||
Create integration test:
|
||||
- Test script or test/integration_test.go
|
||||
- Test with real HTTP calls (or mock server)
|
||||
- Test depth=1 vs depth=2
|
||||
- Test text vs JSON output
|
||||
- Test robots.txt respect
|
||||
|
||||
Acceptance:
|
||||
- `go build ./...` must pass
|
||||
- `go test ./...` must pass (all tests)
|
||||
- Integration test validates end-to-end functionality
|
||||
|
||||
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_integration [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify integration test was completed correctly.
|
||||
|
||||
Run:
|
||||
1. `go build ./...`
|
||||
2. `go test ./... -v`
|
||||
3. Check that integration test covers depth, format, robots.txt
|
||||
|
||||
Write results to .ai/verify_integration.md.
|
||||
Write status.json: outcome=success if ALL pass, outcome=fail with details."
|
||||
]
|
||||
|
||||
check_integration [shape=diamond, label="Integration OK?"]
|
||||
|
||||
// Final review
|
||||
review [
|
||||
shape=box,
|
||||
class="review",
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md. Review the full linkcheck implementation against the spec.
|
||||
|
||||
Check:
|
||||
- All features implemented (crawling, link checking, robots.txt, depth, formats)
|
||||
- All tests pass
|
||||
- CLI works as specified
|
||||
- Error handling is correct
|
||||
- Code quality (no warnings, good structure)
|
||||
- Documentation complete
|
||||
|
||||
Run:
|
||||
1. `go build ./cmd/linkcheck`
|
||||
2. `go test ./...`
|
||||
3. Manual test: `./linkcheck https://example.com --depth 1 --format json`
|
||||
|
||||
Write review to .ai/final_review.md.
|
||||
Write status.json: outcome=success if complete and correct, outcome=fail with what's missing or broken."
|
||||
]
|
||||
|
||||
check_review [shape=diamond, label="Review OK?"]
|
||||
|
||||
// Flow
|
||||
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
|
||||
check_setup -> impl_crawler [condition="outcome=success"]
|
||||
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_crawler -> verify_crawler -> check_crawler
|
||||
check_crawler -> impl_robots [condition="outcome=success"]
|
||||
check_crawler -> impl_crawler [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_robots -> verify_robots -> check_robots
|
||||
check_robots -> impl_checker [condition="outcome=success"]
|
||||
check_robots -> impl_robots [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_checker -> verify_checker -> check_checker
|
||||
check_checker -> impl_formatter [condition="outcome=success"]
|
||||
check_checker -> impl_checker [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_formatter -> verify_formatter -> check_formatter
|
||||
check_formatter -> impl_cli [condition="outcome=success"]
|
||||
check_formatter -> impl_formatter [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_cli -> verify_cli -> check_cli
|
||||
check_cli -> impl_integration [condition="outcome=success"]
|
||||
check_cli -> impl_cli [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_integration -> verify_integration -> check_integration
|
||||
check_integration -> review [condition="outcome=success"]
|
||||
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
|
||||
|
||||
review -> check_review
|
||||
check_review -> exit [condition="outcome=success"]
|
||||
check_review -> impl_integration [condition="outcome=fail", label="fix"]
|
||||
}
|
||||
346
test/kilroy/refactor_test_vague.dot
Normal file
346
test/kilroy/refactor_test_vague.dot
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
digraph solitaire {
|
||||
graph [
|
||||
goal="Build a terminal-based solitaire (Klondike) game",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="impl_setup",
|
||||
fallback_retry_target="impl_game_logic",
|
||||
model_stylesheet="
|
||||
* { llm_model: gemini-3-flash-preview; llm_provider: google; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
// Spec expansion - bootstraps .ai/spec.md into existence
|
||||
expand_spec [
|
||||
shape=box,
|
||||
auto_status=true,
|
||||
prompt="Given the requirements: Build a terminal-based solitaire game (Klondike variant). The game should display cards using ASCII art, support standard solitaire rules (7 tableau piles, 4 foundation piles, stock and waste), allow keyboard-based moves, detect win/loss conditions, and provide a pleasant terminal UI with clear instructions.
|
||||
|
||||
Expand into a detailed spec covering:
|
||||
- Language choice (Python recommended for terminal UI libraries)
|
||||
- Game rules and data structures (Card, Deck, Pile types)
|
||||
- Terminal rendering approach (curses or rich library)
|
||||
- Input handling and move validation
|
||||
- Win/loss detection
|
||||
- User interface layout
|
||||
- Test strategy
|
||||
|
||||
Write the spec to .ai/spec.md.
|
||||
|
||||
Write status.json: outcome=success"
|
||||
]
|
||||
|
||||
// Project setup
|
||||
impl_setup [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md. Set up the project structure based on the chosen language:
|
||||
- If Python: Create pyproject.toml or requirements.txt, src/ directory, main.py stub
|
||||
- If Go: Create go.mod, cmd/solitaire/main.go, pkg/ structure
|
||||
- If Rust: Create Cargo.toml, src/main.rs
|
||||
|
||||
Create the basic directory structure and configuration files needed.
|
||||
|
||||
Run the appropriate build command:
|
||||
- Python: python3 -m py_compile src/*.py (or pytest --collect-only if tests exist)
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
|
||||
Write status.json: outcome=success if project structure is created and builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_setup [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify project setup was completed correctly.
|
||||
|
||||
Run:
|
||||
1. Check that project configuration file exists (pyproject.toml/requirements.txt, go.mod, or Cargo.toml)
|
||||
2. Check that source directories exist
|
||||
3. Run build command appropriate to language (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
4. Verify no syntax errors
|
||||
|
||||
Write results to .ai/verify_setup.md.
|
||||
Write status.json: outcome=success if ALL checks pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
check_setup [shape=diamond, label="Setup OK?"]
|
||||
|
||||
// Core data structures
|
||||
impl_data_structures [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for the data structure design.
|
||||
|
||||
Implement the core game data structures:
|
||||
- Card (suit, rank, face up/down)
|
||||
- Deck (collection of cards with shuffle)
|
||||
- Pile types (Tableau, Foundation, Stock, Waste)
|
||||
- GameState (tracks all piles, move history)
|
||||
|
||||
Include basic validation methods and unit tests for each structure.
|
||||
|
||||
Run appropriate test command:
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_data_structures [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify data structures implementation.
|
||||
|
||||
Run:
|
||||
1. Build command for the language (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all unit tests for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
3. Check that Card, Deck, and Pile types are defined
|
||||
4. Verify basic operations work (create deck, shuffle, deal)
|
||||
|
||||
Write results to .ai/verify_data_structures.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_data_structures [shape=diamond, label="Data structures OK?"]
|
||||
|
||||
// Game logic
|
||||
impl_game_logic [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for Klondike solitaire rules.
|
||||
Read the data structure files for available types and methods.
|
||||
|
||||
Implement the core game logic:
|
||||
- Initial deal (7 tableau piles, stock)
|
||||
- Move validation (tableau to tableau, tableau to foundation, waste to tableau, etc.)
|
||||
- Auto-complete detection
|
||||
- Win condition checking
|
||||
- Undo functionality
|
||||
|
||||
Create comprehensive tests covering:
|
||||
- Legal and illegal moves
|
||||
- Win detection
|
||||
- Edge cases (empty piles, king placement)
|
||||
|
||||
Run tests for the chosen language ONLY (do not run other language commands):
|
||||
- Python: python3 -m pytest tests/ -v
|
||||
- Go: go test ./... -v
|
||||
- Rust: cargo test -- --nocapture
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_game_logic [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify game logic implementation.
|
||||
|
||||
Run:
|
||||
1. Build command (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all tests with verbose output for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/ -v
|
||||
- Go: go test ./... -v
|
||||
- Rust: cargo test -- --nocapture
|
||||
3. Verify move validation works correctly
|
||||
4. Check win condition detection
|
||||
5. Test undo functionality
|
||||
|
||||
Write results to .ai/verify_game_logic.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_game_logic [shape=diamond, label="Game logic OK?"]
|
||||
|
||||
// Terminal UI
|
||||
impl_terminal_ui [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for UI requirements.
|
||||
Read the game logic files to understand the GameState interface.
|
||||
|
||||
Implement the terminal UI:
|
||||
- Card rendering (ASCII art for suits and ranks)
|
||||
- Game board layout (tableau, foundation, stock, waste)
|
||||
- Keyboard input handling (arrow keys, enter, undo)
|
||||
- Move selection interface
|
||||
- Status messages and help text
|
||||
- Graceful exit handling
|
||||
|
||||
Use appropriate library:
|
||||
- Python: curses or rich
|
||||
- Go: termui or bubbletea
|
||||
- Rust: crossterm or tui-rs
|
||||
|
||||
Create integration tests that verify UI components render without crashing.
|
||||
|
||||
Run for the chosen language ONLY (do not run other language commands):
|
||||
- Python: python3 -m pytest tests/ && python3 -m mypy src/ (if using type hints)
|
||||
- Go: go build ./... && go test ./...
|
||||
- Rust: cargo build && cargo test
|
||||
|
||||
Write status.json: outcome=success if builds and tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_terminal_ui [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify terminal UI implementation.
|
||||
|
||||
Run:
|
||||
1. Build the executable (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all tests for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
3. Verify UI components exist (renderer, input handler)
|
||||
4. Check that game can be instantiated
|
||||
5. Test that rendering doesn't crash with empty game state
|
||||
|
||||
Write results to .ai/verify_terminal_ui.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_terminal_ui [shape=diamond, label="Terminal UI OK?"]
|
||||
|
||||
// Integration and polish
|
||||
impl_integration [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for complete requirements.
|
||||
|
||||
Integrate all components into a working game:
|
||||
- Wire up main.go/main.py/main.rs to start game loop
|
||||
- Connect UI input to game logic
|
||||
- Add game over screen with win/loss message
|
||||
- Include help menu (accessible with 'h' key)
|
||||
- Add README with build and run instructions
|
||||
|
||||
Test the complete game:
|
||||
- Build the executable
|
||||
- Play through at least one successful game (can auto-win or test with a fixed seed)
|
||||
- Verify all keyboard controls work
|
||||
- Check that undo works across multiple moves
|
||||
|
||||
Run:
|
||||
IMPORTANT: Run for the chosen language ONLY (do not run other language commands):
|
||||
- Python: python3 src/main.py (manual test) && python3 -m pytest tests/
|
||||
- Go: go build ./cmd/solitaire && go test ./...
|
||||
- Rust: cargo build --release && cargo test
|
||||
|
||||
Write status.json: outcome=success if game runs and all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_integration [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify integration is complete.
|
||||
|
||||
Run:
|
||||
1. Build the final executable (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all tests for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
3. Check README exists with instructions
|
||||
4. Verify the game binary can be executed
|
||||
5. Test that game starts without errors
|
||||
|
||||
Write results to .ai/verify_integration.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_integration [shape=diamond, label="Integration OK?"]
|
||||
|
||||
// Final review
|
||||
review [
|
||||
shape=box,
|
||||
class="review",
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md in full.
|
||||
|
||||
Review the complete implementation against the spec:
|
||||
- All game rules correctly implemented (Klondike solitaire)
|
||||
- Terminal UI works and is intuitive
|
||||
- Keyboard controls responsive
|
||||
- Win/loss detection accurate
|
||||
- Tests comprehensive and passing
|
||||
- README clear and accurate
|
||||
- Code quality good (organized, readable)
|
||||
|
||||
Test the game:
|
||||
- Build and run the executable
|
||||
- Play through a few moves
|
||||
- Verify UI renders correctly
|
||||
- Check that illegal moves are rejected
|
||||
- Test undo functionality
|
||||
- Run full test suite
|
||||
|
||||
Write detailed review to .ai/final_review.md including:
|
||||
- What works well
|
||||
- Any issues found
|
||||
- Compliance with spec
|
||||
|
||||
Write status.json: outcome=success if the game is complete and playable per spec, outcome=fail with what's missing or broken."
|
||||
]
|
||||
|
||||
check_review [shape=diamond, label="Review OK?"]
|
||||
|
||||
// Flow
|
||||
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
|
||||
check_setup -> impl_data_structures [condition="outcome=success"]
|
||||
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_data_structures -> verify_data_structures -> check_data_structures
|
||||
check_data_structures -> impl_game_logic [condition="outcome=success"]
|
||||
check_data_structures -> impl_data_structures [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_game_logic -> verify_game_logic -> check_game_logic
|
||||
check_game_logic -> impl_terminal_ui [condition="outcome=success"]
|
||||
check_game_logic -> impl_game_logic [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_terminal_ui -> verify_terminal_ui -> check_terminal_ui
|
||||
check_terminal_ui -> impl_integration [condition="outcome=success"]
|
||||
check_terminal_ui -> impl_terminal_ui [condition="outcome=fail", label="retry"]
|
||||
|
||||
impl_integration -> verify_integration -> check_integration
|
||||
check_integration -> review [condition="outcome=success"]
|
||||
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
|
||||
|
||||
review -> check_review
|
||||
check_review -> exit [condition="outcome=success"]
|
||||
check_review -> impl_terminal_ui [condition="outcome=fail", label="fix"]
|
||||
}
|
||||
396
test/kilroy/reference_template.dot
Normal file
396
test/kilroy/reference_template.dot
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
digraph reference_template {
|
||||
graph [
|
||||
goal="$goal",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="implement",
|
||||
fallback_retry_target="debate_consolidate",
|
||||
provenance_version="1",
|
||||
model_stylesheet="
|
||||
* { llm_model: DEFAULT_MODEL; llm_provider: DEFAULT_PROVIDER; }
|
||||
.hard { llm_model: HARD_MODEL; llm_provider: HARD_PROVIDER; }
|
||||
.verify { llm_model: VERIFY_MODEL; llm_provider: VERIFY_PROVIDER; }
|
||||
.branch-a { llm_model: BRANCH_A_MODEL; llm_provider: BRANCH_A_PROVIDER; }
|
||||
.branch-b { llm_model: BRANCH_B_MODEL; llm_provider: BRANCH_B_PROVIDER; }
|
||||
.branch-c { llm_model: BRANCH_C_MODEL; llm_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 tool_command with project-specific checks.
|
||||
check_toolchain [
|
||||
shape=parallelogram,
|
||||
max_retries=0,
|
||||
tool_command="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:
|
||||
// - Read branch outputs via parallel_results.json + worktree_dir
|
||||
// - Fall back to current worktree if parallel_results.json missing
|
||||
// - 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:
|
||||
// - Read branch outputs via parallel_results.json + worktree_dir
|
||||
// - Fall back to current worktree if parallel_results.json missing
|
||||
// - 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 tool_command with project-specific auto-formatter.
|
||||
fix_fmt [
|
||||
shape=parallelogram,
|
||||
max_retries=0,
|
||||
tool_command="echo 'Replace with project-specific auto-formatter'; exit 0"
|
||||
]
|
||||
|
||||
// Replace tool_command with project-specific formatter check.
|
||||
verify_fmt [
|
||||
shape=parallelogram,
|
||||
max_retries=0,
|
||||
tool_command="echo 'Replace with project-specific formatter check'; exit 0"
|
||||
]
|
||||
check_fmt [shape=diamond, label="Fmt OK?"]
|
||||
|
||||
// Replace tool_command with project-specific build command.
|
||||
verify_build [
|
||||
shape=parallelogram,
|
||||
tool_command="echo 'Replace with project-specific build check'; exit 0"
|
||||
]
|
||||
check_build [shape=diamond, label="Build OK?"]
|
||||
|
||||
// Replace tool_command with project-specific test command.
|
||||
verify_test [
|
||||
shape=parallelogram,
|
||||
tool_command="echo 'Replace with project-specific test check'; exit 0"
|
||||
]
|
||||
check_test [shape=diamond, label="Tests OK?"]
|
||||
|
||||
// Replace tool_command 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,
|
||||
tool_command="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:
|
||||
// - Read branch outputs via parallel_results.json + worktree_dir
|
||||
// - Fall back to current worktree if parallel_results.json missing
|
||||
// - 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)
|
||||
// - Read branch review outputs via parallel_results.json +
|
||||
// worktree_dir 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=success"]
|
||||
check_toolchain -> check_toolchain [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_toolchain -> postmortem [condition="outcome=fail && 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=success"]
|
||||
fix_fmt -> verify_fmt
|
||||
check_implement -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_implement -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
|
||||
check_implement -> postmortem
|
||||
verify_fmt -> check_fmt
|
||||
check_fmt -> verify_build [condition="outcome=success"]
|
||||
check_fmt -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_fmt -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
|
||||
check_fmt -> postmortem
|
||||
|
||||
verify_build -> check_build
|
||||
check_build -> verify_test [condition="outcome=success"]
|
||||
check_build -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_build -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
|
||||
check_build -> postmortem
|
||||
|
||||
verify_test -> check_test
|
||||
check_test -> verify_artifacts [condition="outcome=success"]
|
||||
check_test -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_test -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
|
||||
check_test -> postmortem
|
||||
|
||||
verify_artifacts -> check_artifacts
|
||||
check_artifacts -> verify_fidelity [condition="outcome=success"]
|
||||
check_artifacts -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_artifacts -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
|
||||
check_artifacts -> postmortem
|
||||
|
||||
verify_fidelity -> check_impl
|
||||
check_impl -> review_fanout [condition="outcome=success"]
|
||||
review_fanout -> review_a
|
||||
review_fanout -> review_b
|
||||
review_fanout -> review_c
|
||||
check_impl -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
|
||||
check_impl -> postmortem [condition="outcome=fail && 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=success"]
|
||||
review_consensus -> postmortem
|
||||
|
||||
// Domain-routed recovery: classify failure and choose the right re-entry
|
||||
postmortem -> check_toolchain [condition="outcome=fail && 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
|
||||
}
|
||||
33
test/kilroy/semport.dot
Normal file
33
test/kilroy/semport.dot
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
digraph Workflow {
|
||||
graph [ label="Semantic Port Tracking Loop", goal="We want to intelligently track and port semantic changes from the upstream openai-agents-python repository to our Go implementation.\n\nWe want to fetch the latest commits from inspiration/openai-agents-python, analyze each new commit for semantic changes (not just syntax), and intelligently coalesce/merge those changes into our Go codebase while respecting Go idioms and our existing architecture.\n\nWe want to track the disposition of each upstream commit in semport/ledger.tsv with three states: 'new' (unprocessed), 'implemented' (changes made), or 'acknowledged' (reviewed but no changes needed).\n\nWe want to make sure we are surgical in this monorepo and follow pre-existing coding conventions and standards.", rankdir="LR", context_fidelity_default="truncate", context_thread_default="semport-tracking", default_max_retry="4" ];
|
||||
|
||||
FinalizeAndUpdateLedger [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="6) Finalize & update ledger", llm_model="gpt-5.2-codex", llm_prompt="**Finalize implementation and update ledger in one step.**\\n\\n1. Synthesize the port plan from .ai/semport_plan_sonnet.md and implementation results into .ai/semport_implementation_summary.md. List which upstream commits were processed, what changes were made (with file:line references), and the disposition ('implemented').\\n\\n2. Update the ledger using:\\n```\\npython3 semport/ledger.py update <shortsha> implemented\\npython3 semport/ledger.py sort\\n```\\n\\n3. Verify with `python3 semport/ledger.py stats` to see progress.\\n\\n4. **Commit all changes** (implementation + ledger update) with a clear message:\\n ```\\n git add -A\\n git commit -m \"semport: implement <shortsha> - <brief description of what was ported>\"\\n ```\\n Example: `git commit -m \"semport: implement a776d80 - nest handoff history by default\"`\\n\\nKeep our goal $goal in mind. Then loop back to process the next commit.", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.observe", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
|
||||
|
||||
TestValidate [allow_partial="true", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="5) Test/Validate changes", llm_model="gpt-5.2-codex", llm_prompt="Keeping our goal in mind: $goal. From repo root, validate that all ported changes work correctly. Run relevant tests (go test ./...), ensure compilation succeeds, and verify the ported functionality matches the upstream semantic intent (not necessarily syntax). Write validation results to .ai/semport_validation_report_NN.md. Use outcome=yes if all tests pass and changes are semantically correct; otherwise use outcome=retry with concrete failure details.", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.steer", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1800"];
|
||||
|
||||
AnalyzeFailureSonnet [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="6a) Analyze failure (sonnet)", llm_model="gpt-5.2-codex", llm_prompt="When tests or validation fail, inspect .ai/semport_validation_report_*.md, logs, diffs, and error messages. Write .ai/semport_failure_sonnet.md summarizing root causes, impacted files (with line references), and what needs to be fixed. Clearly note where failure artifacts are located. Keep our goal $goal in mind and be subjective.", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.observe", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
|
||||
|
||||
FetchUpstreamSonnet [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="1) Fetch upstream & identify next commit (sonnet)", llm_model="gpt-5.2-codex", llm_prompt="Our goal is: $goal\\n\\n---\\n\\n**CRITICAL: Use semport/ledger.py for all ledger operations to ensure proper chronological ordering. Ledger entries MUST always use short git hashes (7 characters, e.g. from `git rev-parse --short`).**\\n\\n1. Run `python3 semport/ledger.py earliest` to get the chronologically earliest commit with disposition='new'\\n2. If a commit is found, write ONLY that single commit (shortsha, iso8601, and full commit message from git show) to .ai/semport_new_commits.md and use outcome=process\\n3. If NO 'new' commits exist:\\n a. Ensure inspiration/openai-agents-python exists (clone if missing)\\n b. Run git fetch && git pull in that directory\\n c. Use git log to find commits newer than the latest in ledger.tsv, capturing a short hash for each commit (e.g. `git log --format='%h %cI' ...`)\\n d. Add new commits using `python3 semport/ledger.py add <shortsha> <timestamp>`\\n e. Run `python3 semport/ledger.py sort` to maintain chronological order\\n f. Then run `python3 semport/ledger.py earliest` to get the first new commit\\n g. If a new commit is found after fetching, write it to .ai/semport_new_commits.md and use outcome=process\\n h. If still no new commits after fetching, write a completion report to .ai/semport_completion.md and use outcome=done\\n\\n**IMPORTANT**: You MUST end with exactly one of these outcomes:\\n- outcome=process (when there is a commit to process)\\n- outcome=done (when fully caught up with no new commits)", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.steer", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
|
||||
|
||||
ImplementPort [allow_partial="true", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="4) Implement port (gpt-5.1)", llm_model="gpt-5.2-codex", llm_prompt="Follow the port plan in .ai/semport_plan_finalized.md. For each upstream commit, port the semantic changes to the Go codebase. Focus on semantic equivalence, not literal translation. Use Go idioms, respect existing architecture, and reference specific files/line ranges. Log all changes and commands to .ai/semport_impl.log.", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.observe", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="2400"];
|
||||
|
||||
FinalizePlanGPT [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="3) Finalize port plan (gpt-5.1)", llm_model="gpt-5.2-codex", llm_prompt="Keeping our goal $goal in mind. Perform a final editorial pass over .ai/semport_plan_sonnet.md and write .ai/semport_plan_finalized.md. Ensure each port task has concrete file:line references, clear acceptance criteria, and is directly executable. Remove vague language and ensure the plan is actionable.", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.observe", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
|
||||
|
||||
Exit [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="Exit", llm_model="gpt-5.2-codex", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="exit", penwidth="1.2", reasoning_effort="high", shape="doublecircle", style="rounded,filled", timeout="1200"];
|
||||
|
||||
Start [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="Start", llm_model="gpt-5.2-codex", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="start", penwidth="1.2", reasoning_effort="high", shape="circle", style="rounded,filled", timeout="1200"];
|
||||
|
||||
AnalyzePlanSonnet [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="2) Analyze & plan port (sonnet)", llm_model="gpt-5.2-codex", llm_prompt="Keeping our goal $goal in mind. Read .ai/semport_new_commits.md which contains a SINGLE commit to process. Examine this one commit in inspiration/openai-agents-python using git show. Analyze the semantic changes (what functionality changed, not just syntax). Decide if this change is relevant to our Go implementation or if it's Python-specific/docs-only/not-applicable.\\n\\nWrite .ai/semport_plan_sonnet.md with sections: Commit Being Processed (shortsha and summary), Semantic Analysis (what changed functionally), DECISION (port or acknowledge with clear reasoning), Port Plan (if porting: concrete tasks with file:line references for Go code), and Disposition Recommendation.\\n\\n**If decision is to ACKNOWLEDGE (skip):**\\n1. Update the ledger: `python3 semport/ledger.py update <shortsha> acknowledged && python3 semport/ledger.py sort`\\n2. Verify with `python3 semport/ledger.py stats`\\n3. **Commit the ledger change** with a clear message summarizing why this commit was acknowledged:\\n ```\\n git add semport/ledger.tsv\\n git commit -m \"semport: acknowledge <shortsha> - <brief reason>\"\\n ```\\n Example: `git commit -m \"semport: acknowledge e3fe4f4 - docs typo fix, no Go changes needed\"`\\n4. Use outcome=skip to loop back for next commit\\n\\n**If decision is to PORT:**\\nUse outcome=port to proceed to implementation.", llm_provider="openai", margin="0.1,0.08", max_agent_turns="8", node_type="stack.steer", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
|
||||
|
||||
FinalizeAndUpdateLedger -> FetchUpstreamSonnet [loop_restart="true"];
|
||||
Start -> FetchUpstreamSonnet;
|
||||
AnalyzeFailureSonnet -> FinalizeAndUpdateLedger;
|
||||
FinalizePlanGPT -> ImplementPort;
|
||||
TestValidate -> FinalizeAndUpdateLedger [condition="outcome=yes", label="pass"];
|
||||
TestValidate -> AnalyzeFailureSonnet [condition="outcome=retry", label="fail"];
|
||||
FetchUpstreamSonnet -> AnalyzePlanSonnet [condition="outcome=process", label="process"];
|
||||
FetchUpstreamSonnet -> Exit [condition="outcome=done", label="done"];
|
||||
ImplementPort -> TestValidate;
|
||||
AnalyzePlanSonnet -> FinalizePlanGPT [condition="outcome=port", label="port"];
|
||||
AnalyzePlanSonnet -> FetchUpstreamSonnet [condition="outcome=skip", label="skip", loop_restart="true"];
|
||||
}
|
||||
17
test/kilroy/simple_example.dot
Normal file
17
test/kilroy/simple_example.dot
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
digraph Simple {
|
||||
graph [
|
||||
goal="Run tests and report",
|
||||
rankdir=LR,
|
||||
model_stylesheet="
|
||||
* { llm_model: gpt-5.2-codex; llm_provider: openai; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
run_tests [shape=box, label="Run Tests", prompt="Run the test suite and report results"]
|
||||
report [shape=box, label="Report", prompt="Summarize the test results"]
|
||||
|
||||
start -> run_tests -> report -> exit
|
||||
}
|
||||
352
test/kilroy/solitaire_fast.dot
Normal file
352
test/kilroy/solitaire_fast.dot
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
digraph solitaire {
|
||||
graph [
|
||||
goal="Build a terminal-based solitaire (Klondike) game",
|
||||
rankdir=LR,
|
||||
default_max_retry=3,
|
||||
retry_target="impl_setup",
|
||||
fallback_retry_target="impl_game_logic",
|
||||
model_stylesheet="
|
||||
* { llm_model: gpt-5.3-codex-spark; llm_provider: openai; }
|
||||
"
|
||||
]
|
||||
|
||||
start [shape=Mdiamond, label="Start"]
|
||||
exit [shape=Msquare, label="Exit"]
|
||||
|
||||
// Spec expansion - bootstraps .ai/spec.md into existence
|
||||
expand_spec [
|
||||
shape=box,
|
||||
auto_status=true,
|
||||
prompt="Given the requirements: Build a terminal-based solitaire game (Klondike variant). The game should display cards using ASCII art, support standard solitaire rules (7 tableau piles, 4 foundation piles, stock and waste), allow keyboard-based moves, detect win/loss conditions, and provide a pleasant terminal UI with clear instructions.
|
||||
|
||||
Expand into a detailed spec covering:
|
||||
- Language choice (Python recommended for terminal UI libraries)
|
||||
- Game rules and data structures (Card, Deck, Pile types)
|
||||
- Terminal rendering approach (curses or rich library)
|
||||
- Input handling and move validation
|
||||
- Win/loss detection
|
||||
- User interface layout
|
||||
- Test strategy
|
||||
|
||||
Write the spec to .ai/spec.md.
|
||||
|
||||
Write status.json: outcome=success"
|
||||
]
|
||||
|
||||
// Project setup
|
||||
impl_setup [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md. Set up the project structure based on the chosen language:
|
||||
- If Python: Create pyproject.toml or requirements.txt, src/ directory, main.py stub
|
||||
- If Go: Create go.mod, cmd/solitaire/main.go, pkg/ structure
|
||||
- If Rust: Create Cargo.toml, src/main.rs
|
||||
|
||||
Create the basic directory structure and configuration files needed.
|
||||
|
||||
Run the appropriate build command:
|
||||
- Python: python3 -m py_compile src/*.py (or pytest --collect-only if tests exist)
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
|
||||
Write status.json: outcome=success if project structure is created and builds, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_setup [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify project setup was completed correctly.
|
||||
|
||||
Run:
|
||||
1. Check that project configuration file exists (pyproject.toml/requirements.txt, go.mod, or Cargo.toml)
|
||||
2. Check that source directories exist
|
||||
3. Run build command appropriate to language (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
4. Verify no syntax errors
|
||||
|
||||
Write results to .ai/verify_setup.md.
|
||||
Write status.json: outcome=success if ALL checks pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
check_setup [shape=diamond, label="Setup OK?"]
|
||||
|
||||
// Core data structures
|
||||
impl_data_structures [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for the data structure design.
|
||||
|
||||
Implement the core game data structures:
|
||||
- Card (suit, rank, face up/down)
|
||||
- Deck (collection of cards with shuffle)
|
||||
- Pile types (Tableau, Foundation, Stock, Waste)
|
||||
- GameState (tracks all piles, move history)
|
||||
|
||||
Include basic validation methods and unit tests for each structure.
|
||||
|
||||
Run appropriate test command:
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_data_structures [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify data structures implementation.
|
||||
|
||||
Run:
|
||||
1. Build command for the language (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all unit tests for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
3. Check that Card, Deck, and Pile types are defined
|
||||
4. Verify basic operations work (create deck, shuffle, deal)
|
||||
|
||||
Write results to .ai/verify_data_structures.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_data_structures [shape=diamond, label="Data structures OK?"]
|
||||
|
||||
// Game logic
|
||||
impl_game_logic [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for Klondike solitaire rules.
|
||||
Read the data structure files for available types and methods.
|
||||
|
||||
Implement the core game logic:
|
||||
- Initial deal (7 tableau piles, stock)
|
||||
- Move validation (tableau to tableau, tableau to foundation, waste to tableau, etc.)
|
||||
- Auto-complete detection
|
||||
- Win condition checking
|
||||
- Undo functionality
|
||||
|
||||
Create comprehensive tests covering:
|
||||
- Legal and illegal moves
|
||||
- Win detection
|
||||
- Edge cases (empty piles, king placement)
|
||||
|
||||
Run tests for the chosen language ONLY (do not run other language commands):
|
||||
- Python: python3 -m pytest tests/ -v
|
||||
- Go: go test ./... -v
|
||||
- Rust: cargo test -- --nocapture
|
||||
|
||||
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_game_logic [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify game logic implementation.
|
||||
|
||||
Run:
|
||||
1. Build command (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all tests with verbose output for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/ -v
|
||||
- Go: go test ./... -v
|
||||
- Rust: cargo test -- --nocapture
|
||||
3. Verify move validation works correctly
|
||||
4. Check win condition detection
|
||||
5. Test undo functionality
|
||||
|
||||
Write results to .ai/verify_game_logic.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_game_logic [shape=diamond, label="Game logic OK?"]
|
||||
|
||||
// Terminal UI
|
||||
impl_terminal_ui [
|
||||
shape=box,
|
||||
class="hard",
|
||||
max_retries=2,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for UI requirements.
|
||||
Read the game logic files to understand the GameState interface.
|
||||
|
||||
Implement the terminal UI:
|
||||
- Card rendering (ASCII art for suits and ranks)
|
||||
- Game board layout (tableau, foundation, stock, waste)
|
||||
- Keyboard input handling (arrow keys, enter, undo)
|
||||
- Move selection interface
|
||||
- Status messages and help text
|
||||
- Graceful exit handling
|
||||
|
||||
Use appropriate library:
|
||||
- Python: curses or rich
|
||||
- Go: termui or bubbletea
|
||||
- Rust: crossterm or tui-rs
|
||||
|
||||
Create integration tests that verify UI components render without crashing.
|
||||
|
||||
Run for the chosen language ONLY (do not run other language commands):
|
||||
- Python: python3 -m pytest tests/ && python3 -m mypy src/ (if using type hints)
|
||||
- Go: go build ./... && go test ./...
|
||||
- Rust: cargo build && cargo test
|
||||
|
||||
Write status.json: outcome=success if builds and tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_terminal_ui [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify terminal UI implementation.
|
||||
|
||||
Run:
|
||||
1. Build the executable (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all tests for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
3. Verify UI components exist (renderer, input handler)
|
||||
4. Check that game can be instantiated
|
||||
5. Test that rendering doesn't crash with empty game state
|
||||
|
||||
Write results to .ai/verify_terminal_ui.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_terminal_ui [shape=diamond, label="Terminal UI OK?"]
|
||||
|
||||
// Integration and polish
|
||||
impl_integration [
|
||||
shape=box,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md for complete requirements.
|
||||
|
||||
Integrate all components into a working game:
|
||||
- Wire up main.go/main.py/main.rs to start game loop
|
||||
- Connect UI input to game logic
|
||||
- Add game over screen with win/loss message
|
||||
- Include help menu (accessible with 'h' key)
|
||||
- Add README with build and run instructions
|
||||
|
||||
Test the complete game:
|
||||
- Build the executable
|
||||
- Play through at least one successful game (can auto-win or test with a fixed seed)
|
||||
- Verify all keyboard controls work
|
||||
- Check that undo works across multiple moves
|
||||
|
||||
Run:
|
||||
IMPORTANT: Run for the chosen language ONLY (do not run other language commands):
|
||||
- Python: python3 src/main.py (manual test) && python3 -m pytest tests/
|
||||
- Go: go build ./cmd/solitaire && go test ./...
|
||||
- Rust: cargo build --release && cargo test
|
||||
|
||||
Write status.json: outcome=success if game runs and all tests pass, outcome=fail with failure_reason otherwise."
|
||||
]
|
||||
|
||||
verify_integration [
|
||||
shape=box,
|
||||
class="verify",
|
||||
prompt="Verify integration is complete.
|
||||
|
||||
Run:
|
||||
1. Build the final executable (ONLY one):
|
||||
- Python: python3 -m py_compile src/*.py
|
||||
- Go: go build ./...
|
||||
- Rust: cargo build
|
||||
2. Run all tests for the chosen language (ONLY one):
|
||||
- Python: python3 -m pytest tests/
|
||||
- Go: go test ./...
|
||||
- Rust: cargo test
|
||||
3. Check README exists with instructions
|
||||
4. Verify the game binary can be executed
|
||||
5. Test that game starts without errors
|
||||
|
||||
Write results to .ai/verify_integration.md.
|
||||
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
|
||||
]
|
||||
|
||||
check_integration [shape=diamond, label="Integration OK?"]
|
||||
|
||||
// Final review
|
||||
review [
|
||||
shape=box,
|
||||
class="review",
|
||||
goal_gate=true,
|
||||
prompt="Goal: $goal
|
||||
|
||||
Read .ai/spec.md in full.
|
||||
|
||||
Review the complete implementation against the spec:
|
||||
- All game rules correctly implemented (Klondike solitaire)
|
||||
- Terminal UI works and is intuitive
|
||||
- Keyboard controls responsive
|
||||
- Win/loss detection accurate
|
||||
- Tests comprehensive and passing
|
||||
- README clear and accurate
|
||||
- Code quality good (organized, readable)
|
||||
|
||||
Test the game:
|
||||
- Build and run the executable
|
||||
- Play through a few moves
|
||||
- Verify UI renders correctly
|
||||
- Check that illegal moves are rejected
|
||||
- Test undo functionality
|
||||
- Run full test suite
|
||||
|
||||
Write detailed review to .ai/final_review.md including:
|
||||
- What works well
|
||||
- Any issues found
|
||||
- Compliance with spec
|
||||
|
||||
Write status.json: outcome=success if the game is complete and playable per spec, outcome=fail with what's missing or broken."
|
||||
]
|
||||
|
||||
check_review [shape=diamond, label="Review OK?"]
|
||||
|
||||
// Flow
|
||||
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
|
||||
check_setup -> impl_data_structures [condition="outcome=success"]
|
||||
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
|
||||
check_setup -> impl_setup [label="fallback"]
|
||||
|
||||
impl_data_structures -> verify_data_structures -> check_data_structures
|
||||
check_data_structures -> impl_game_logic [condition="outcome=success"]
|
||||
check_data_structures -> impl_data_structures [condition="outcome=fail", label="retry"]
|
||||
check_data_structures -> impl_data_structures [label="fallback"]
|
||||
|
||||
impl_game_logic -> verify_game_logic -> check_game_logic
|
||||
check_game_logic -> impl_terminal_ui [condition="outcome=success"]
|
||||
check_game_logic -> impl_game_logic [condition="outcome=fail", label="retry"]
|
||||
check_game_logic -> impl_game_logic [label="fallback"]
|
||||
|
||||
impl_terminal_ui -> verify_terminal_ui -> check_terminal_ui
|
||||
check_terminal_ui -> impl_integration [condition="outcome=success"]
|
||||
check_terminal_ui -> impl_terminal_ui [condition="outcome=fail", label="retry"]
|
||||
check_terminal_ui -> impl_terminal_ui [label="fallback"]
|
||||
|
||||
impl_integration -> verify_integration -> check_integration
|
||||
check_integration -> review [condition="outcome=success"]
|
||||
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
|
||||
check_integration -> impl_integration [label="fallback"]
|
||||
|
||||
review -> check_review
|
||||
check_review -> exit [condition="outcome=success"]
|
||||
check_review -> impl_terminal_ui [condition="outcome=fail", label="fix"]
|
||||
check_review -> impl_terminal_ui [label="fallback"]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue