fabro/test/attractor/green_test_moderate.dot
2026-04-30 06:48:47 -04:00

592 lines
17 KiB
Text

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_retries=3,
retry_target="impl_setup",
model_stylesheet="
* { model: gpt-5.2-codex;reasoning_effort: medium; }
.hard { model: gpt-5.2-codex;reasoning_effort: high; }
.verify { model: gpt-5.2-codex;reasoning_effort: medium; }
.review { model: gpt-5.2-codex;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=succeeded"
]
// 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=succeeded if the project builds, outcome=failed 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=succeeded if all checks pass, outcome=failed 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=succeeded if builds, outcome=failed 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=succeeded if all pass, outcome=failed 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=succeeded if all tests pass, outcome=failed 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=succeeded if all pass and coverage > 70%, outcome=failed 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=succeeded if all tests pass, outcome=failed 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=succeeded if all pass, outcome=failed 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=succeeded if all tests pass, outcome=failed 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=succeeded if all pass, outcome=failed 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=succeeded if all tests pass, outcome=failed 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=succeeded if all pass, outcome=failed 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=succeeded if all tests pass, outcome=failed 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=succeeded if all pass, outcome=failed 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=succeeded if builds, outcome=failed 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=succeeded if all manual tests pass, outcome=failed 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=succeeded if integration tests pass, outcome=failed 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=succeeded if all pass, outcome=failed 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=succeeded if complete and working, outcome=failed 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=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_types -> verify_types -> check_types
check_types -> impl_robotstxt [condition="outcome=succeeded"]
check_types -> impl_types [condition="outcome=failed", label="retry"]
impl_robotstxt -> verify_robotstxt -> check_robotstxt
check_robotstxt -> impl_checker [condition="outcome=succeeded"]
check_robotstxt -> impl_robotstxt [condition="outcome=failed", label="retry"]
impl_checker -> verify_checker -> check_checker
check_checker -> impl_extractor [condition="outcome=succeeded"]
check_checker -> impl_checker [condition="outcome=failed", label="retry"]
impl_extractor -> verify_extractor -> check_extractor
check_extractor -> impl_crawler [condition="outcome=succeeded"]
check_extractor -> impl_extractor [condition="outcome=failed", label="retry"]
impl_crawler -> verify_crawler -> check_crawler
check_crawler -> impl_report [condition="outcome=succeeded"]
check_crawler -> impl_crawler [condition="outcome=failed", label="retry"]
impl_report -> verify_report -> check_report
check_report -> impl_cli [condition="outcome=succeeded"]
check_report -> impl_report [condition="outcome=failed", label="retry"]
impl_cli -> verify_cli -> check_cli
check_cli -> impl_integration [condition="outcome=succeeded"]
check_cli -> impl_cli [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_cli [condition="outcome=failed", label="fix"]
}