digraph CodeReview {
    graph [
        goal="Review the committed change with independent discovery jobs -- one single pass at low; grouped local-correctness passes, whole-change angles, and path-matched rule audits at every tier above -- verify every surviving candidate, and report only findings that pass.",
        default_max_retries=0,
        default_fidelity="compact",
        on_failure="exit",
        stall_timeout="14400s",
        model_stylesheet="
            {% set tiers = ['low', 'medium', 'high', 'xhigh', 'max'] %}
            {% set effort = inputs.effort if inputs.effort in tiers else 'medium' %}
            {% if 'kimi' in inputs.model %}
            {% set finders = {'low': 'low', 'medium': 'high', 'high': 'high', 'xhigh': 'max', 'max': 'max'} %}
            {% set verifiers = {'low': 'low', 'medium': 'high', 'high': 'high', 'xhigh': 'high', 'max': 'high'} %}
            {% set sweeps = {'low': 'low', 'medium': 'high', 'high': 'high', 'xhigh': 'max', 'max': 'max'} %}
            {% else %}
            {% set finders = {'low': 'low', 'medium': 'medium', 'high': 'high', 'xhigh': 'xhigh', 'max': 'max'} %}
            {% set verifiers = {'low': 'low', 'medium': 'medium', 'high': 'medium', 'xhigh': 'high', 'max': 'xhigh'} %}
            {% set sweeps = {'low': 'low', 'medium': 'medium', 'high': 'high', 'xhigh': 'xhigh', 'max': 'xhigh'} %}
            {% endif %}
            * { model: {{ inputs.model }}; reasoning_effort: {{ finders[effort] }}; }
            .grouping { model: {{ inputs.model }}; reasoning_effort: low; }
            .verification { model: {{ inputs.model }}; reasoning_effort: {{ verifiers[effort] }}; }
            .sweep { model: {{ inputs.model }}; reasoning_effort: {{ sweeps[effort] }}; }
        "
    ]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]
    prepare [
        shape=parallelogram,
        label="Resolve and size the review target",
        timeout="300s",
        output_schema="routing",
        stdin_source="context.internal.run_id",
        script="python3 -c \"import hashlib,sys; pairs=list(zip(sys.argv[1::2],sys.argv[2::2])); sys.exit(0 if pairs and all(hashlib.sha256(open(path,'rb').read()).hexdigest()==expected for path,expected in pairs) else 91)\" .fabro/workflows/code-review/scripts/code_review.py 0926622b190a45b2abfd478f17bcdb7c49e3b20615867b8f84d0bb5b067ac663 .fabro/workflows/code-review/scripts/git_readonly.py bcd4364ba3aca2ee1e12d5909204f645c16bdf22e3753a39d74c79d8d37cf73e .fabro/workflows/code-review/scripts/render_report.py 5b92107f1173a45900933931c4e48cb0378a9c2d72d00889e313f8b0c8ba22b5 .fabro/workflows/code-review/scripts/rule_loader.py f885409c64c631075d7e31fcb6e7a100592430c5eba9a6e989222006061a9f52 .fabro/workflows/code-review/specs/report-spec.md 6a6925d3a4f74baec99e6ce26d0a04f64f767ff3a78ef14158b6ba14e8d34d60 .fabro/workflows/code-review/templates/report.html 64eefc612bcf51d4bdd53282a3feeccc37555b1ef584dd179a254450e3c010c6 .fabro/workflows/code-review/schemas/findings.schema.json 6fdf7c63fb6183c8bef64a874cd56f805d92c2aa3c011a513ee07f07b0797b6d .fabro/workflows/code-review/schemas/verdict.schema.json 4cb752e624f1b88809017ac5db472d59850ffe09ec5a93d3b63ba636364b8b19 .fabro/workflows/code-review/schemas/file-groups.schema.json b53c4e1c0bbd07bbf70e83f4f3b35fd96cb880c621c7c424e95b9aea34e13d7c .fabro/workflows/code-review/prompts/finder.md.j2 86c2e6a032f7c54c1bbab1c12496a8f0d6bf48703abe6017eb175330608cf223 .fabro/workflows/code-review/prompts/verify.md.j2 6528cffd1bf199f27a32c19547aebf6f0c78a546698aaef81c6a89441b80f7da .fabro/workflows/code-review/prompts/sweep.md.j2 e6f89b47b11c57030a6ef7d5896ccb37dbd2a2982e9fa7eb7f2e3df73acab82c .fabro/workflows/code-review/prompts/group-files.md.j2 5b291313a1266d1d658f80ea7989cdefcd8609b6893b7197b17914d553dab041 .fabro/workflows/code-review/prompts/partials/finding-fields.md.j2 b91ddeb86dc7f552323f7ac04823984da9365d013bfe7965c3eb6f51fa8abe0f .fabro/workflows/code-review/prompts/partials/guidance.md.j2 53bc0c40bb917288708bed1f9ba478fbd89b9790c92497762224cc752f40bef5 .fabro/workflows/code-review/prompts/partials/output-schema.md.j2 811994bb357739f2562d84f66dc05075ebe3c7f8d58034f8c25ee1c36bee996b .fabro/workflows/code-review/prompts/partials/read-only-explorer.md.j2 44a0244e7aa62fdb0dbbfdbadcffbfb640af249bae3e96895dedd5c7a33bad10 .fabro/workflows/code-review/prompts/partials/review-target.md.j2 abffeeff0e16b89a0754cd53f1833b3744494cd54ff761798b782a80467446ea .fabro/workflows/code-review/prompts/partials/safe-git-history.md.j2 4ddd8d36d5c51d7e166a6b7f1dff51b72cce0e64108cc7e892002ca909af8b3a .fabro/workflows/code-review/rules/builtin-manifest.json 47e3123fb25c560e36216dc9b8323c86e8301f1868a4fa3219bfd59bda3a533a && python3 .fabro/workflows/code-review/scripts/code_review.py prepare --review-id-stdin --mode {{ inputs.mode }} --effort {{ inputs.effort }} --scope {{ inputs.scope }} --base {{ inputs.base }} --commit {{ inputs.commit }} --range {{ inputs.range }} --model {{ inputs.model }} --guidance {{ inputs.guidance }}"
    ]

    grouping [
        label="Group target files",
        class="grouping",
        prompt="@prompts/group-files.md.j2",
        output_schema="@schemas/file-groups.schema.json",
        output_retries=2,
        max_retries=2,
        on_failure="succeed",
        timeout="1800s",
        project_memory=false
    ]
    merge_grouping [
        shape=parallelogram,
        label="Merge the grouping proposal",
        stdin_source="context.output.grouping",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py merge grouping",
        output_schema="routing",
        timeout="180s"
    ]
    plan_finders [
        shape=parallelogram,
        label="Plan discovery jobs",
        timeout="180s",
        output_schema="routing",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py plan-finders"
    ]

    finders [
        shape=component,
        label="Finder jobs",
        for_each="context.finder_jobs",
        max_parallel=10,
        on_failure="succeed"
    ]
    finder [
        label="Finder job",
        class="finder",
        prompt="@prompts/finder.md.j2",
        output_schema="@schemas/findings.schema.json",
        output_retries=2,
        max_retries=2,
        on_failure="succeed",
        timeout="7200s",
        project_memory=false
    ]
    finder_join [shape=tripleoctagon, label="Gather finder outputs"]
    merge_finders [
        shape=parallelogram,
        label="Merge finder outputs",
        stdin_source="context.parallel.results",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py merge finders",
        output_schema="routing",
        timeout="180s"
    ]
    plan_verify [
        shape=parallelogram,
        label="Deduplicate, rank, and plan verification",
        timeout="180s",
        output_schema="routing",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py plan-verify"
    ]

    verify [
        shape=component,
        label="Verify candidates",
        for_each="context.verify_jobs",
        max_parallel=24,
        on_failure="succeed"
    ]
    verifier [
        label="Verify candidate",
        class="verification",
        prompt="@prompts/verify.md.j2",
        output_schema="@schemas/verdict.schema.json",
        output_retries=2,
        max_retries=2,
        on_failure="succeed",
        timeout="3600s",
        project_memory=false
    ]
    verify_join [shape=tripleoctagon, label="Gather verdicts"]
    merge_verify [
        shape=parallelogram,
        label="Merge verdicts",
        stdin_source="context.parallel.results",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py merge verify",
        output_schema="routing",
        timeout="180s"
    ]
    tally [
        shape=parallelogram,
        label="Apply verdicts and plan the sweep",
        timeout="180s",
        output_schema="routing",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py tally"
    ]

    sweeper [
        label="Gap-fill sweep",
        class="sweep",
        prompt="@prompts/sweep.md.j2",
        output_schema="@schemas/findings.schema.json",
        output_retries=2,
        max_retries=2,
        on_failure="succeed",
        timeout="7200s",
        project_memory=false
    ]
    merge_sweep [
        shape=parallelogram,
        label="Merge sweep output",
        stdin_source="context.output.sweeper",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py merge sweep",
        output_schema="routing",
        timeout="180s"
    ]
    sweep_verify [
        shape=component,
        label="Verify sweep candidates",
        for_each="context.sweep_verify_jobs",
        max_parallel=24,
        on_failure="succeed"
    ]
    sweep_verifier [
        label="Verify sweep candidate",
        class="verification",
        prompt="@prompts/verify.md.j2",
        output_schema="@schemas/verdict.schema.json",
        output_retries=2,
        max_retries=2,
        on_failure="succeed",
        timeout="3600s",
        project_memory=false
    ]
    sweep_verify_join [shape=tripleoctagon, label="Gather sweep verdicts"]
    merge_sweep_verify [
        shape=parallelogram,
        label="Merge sweep verdicts",
        stdin_source="context.parallel.results",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py merge sweep_verify",
        output_schema="routing",
        timeout="180s"
    ]

    final_tally [
        shape=parallelogram,
        label="Write the canonical review bundle",
        timeout="300s",
        output_schema="routing",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py final-tally"
    ]
    render_report [
        shape=parallelogram,
        label="Derive Markdown, HTML, JSONL, and revision metadata",
        timeout="300s",
        output_schema="routing",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py render-report"
    ]
    verify_expectations [
        shape=parallelogram,
        label="Verify configured report expectations",
        timeout="30s",
        output_schema="routing",
        script="python3 .fabro/workflows/code-review/scripts/code_review.py verify-expectations --expected-min-findings '{{ inputs.expected_min_findings }}' --expected-file '{{ inputs.expected_file }}' --expected-min-rule-findings '{{ inputs.expected_min_rule_findings }}'"
    ]

    start -> prepare
    prepare -> exit [condition="outcome=succeeded && context.empty_target=true"]
    prepare -> grouping [condition="outcome=succeeded && context.use_grouping=true"]
    prepare -> plan_finders [condition="outcome=succeeded && context.use_planner=true"]
    prepare -> finders

    grouping -> merge_grouping [condition="outcome=succeeded"]
    grouping -> plan_finders
    merge_grouping -> plan_finders
    plan_finders -> finders

    finders -> finder [fidelity="truncate"]
    finder -> finder_join -> merge_finders
    merge_finders -> plan_verify

    plan_verify -> verify [condition="outcome=succeeded && context.run_verify=true"]
    plan_verify -> tally
    verify -> verifier [fidelity="truncate"]
    verifier -> verify_join -> merge_verify
    merge_verify -> tally

    tally -> sweeper [condition="outcome=succeeded && context.run_sweep=true"]
    tally -> final_tally
    sweeper -> merge_sweep [condition="outcome=succeeded"]
    sweeper -> final_tally
    merge_sweep -> sweep_verify [condition="outcome=succeeded && context.run_sweep_verify=true"]
    merge_sweep -> final_tally
    sweep_verify -> sweep_verifier [fidelity="truncate"]
    sweep_verifier -> sweep_verify_join -> merge_sweep_verify
    merge_sweep_verify -> final_tally

    final_tally -> render_report
    render_report -> verify_expectations
    verify_expectations -> exit
}
