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_retries=3,
        retry_target="impl_setup",
        fallback_retry_target="impl_crawler",
        model_stylesheet="
            * { model: gemini-3-flash-preview; provider: google; reasoning_effort: medium; }
            .hard { model: gemini-3-flash-preview; provider: google; reasoning_effort: high; }
            .verify { model: gemini-3-flash-preview; provider: google; reasoning_effort: medium; }
            .review { model: gemini-3-flash-preview; 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=succeeded"
    ]

    // 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=succeeded if all pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if all criteria pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if all criteria pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if all criteria pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if all criteria pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if all criteria pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if all criteria pass, outcome=failed 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=succeeded if ALL pass, outcome=failed 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=succeeded if complete and correct, outcome=failed 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=succeeded"]
    check_setup -> impl_setup         [condition="outcome=failed", label="retry"]

    impl_crawler -> verify_crawler -> check_crawler
    check_crawler -> impl_robots      [condition="outcome=succeeded"]
    check_crawler -> impl_crawler     [condition="outcome=failed", label="retry"]

    impl_robots -> verify_robots -> check_robots
    check_robots -> impl_checker      [condition="outcome=succeeded"]
    check_robots -> impl_robots       [condition="outcome=failed", label="retry"]

    impl_checker -> verify_checker -> check_checker
    check_checker -> impl_formatter   [condition="outcome=succeeded"]
    check_checker -> impl_checker     [condition="outcome=failed", label="retry"]

    impl_formatter -> verify_formatter -> check_formatter
    check_formatter -> impl_cli       [condition="outcome=succeeded"]
    check_formatter -> impl_formatter [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_integration  [condition="outcome=failed", label="fix"]
}
