{"schema_version":"1.0","config_path":"eddi-ci.yaml","skill":"heyeddi-ci-config","docs_url":"https://ci.heyeddi.com/docs#policy","contract_url":"https://cihook.heyeddi.com/api/public/eddi-ci-policy","knobs":[{"path":"version","default":"\"1.0\"","effect":"Required. Only version 1.0 is accepted; other values fail validation.","cost":"none"},{"path":"ai_review.validation_max_attempts","default":"5 (range 2–50)","effect":"Max Reviewer↔validator retries per review when claims lack evidence.","cost":"Each attempt can re-invoke the Reviewer model; values above ~10 raise the cost ceiling with little quality gain."},{"path":"ai_review.max_auto_reviews_per_pr","default":"5 (range 0–50; 0 = unlimited)","effect":"Cap automatic reviews from PR opened/synchronize. After the cap, HeyEddi posts once and only runs again on `/heyeddi review` (or other manual commands).","cost":"Bounds push-loop model spend; manual commands still bill when used."},{"path":"ai_review.on_ci_failure","default":"false","effect":"When true, a failed external Check on a PR head queues the same analysis as `/heyeddi fails` (one auto review per head SHA). Ignored when wait_for_ci is true. Never triggers on HeyEddi’s own Check.","cost":"Billable model review per failing head when enabled. Prefer wait_for_ci + include_ci_in_review for a single post-CI pass."},{"path":"ai_review.wait_for_ci","default":"false","effect":"When true, automatic PR open/sync reviews wait for external CI to finish (or grace/timeout fallback) before one post-CI review. Manual `/heyeddi review` is never deferred.","cost":"Usually one auto review per push instead of racing CI; may add latency up to wait_for_ci_max_minutes."},{"path":"ai_review.wait_for_ci_max_minutes","default":"60 (range 5–180)","effect":"Hard cap before HeyEddi reviews anyway with a partial/timeout CI note (never blocks forever).","cost":"Bounds worst-case wait when CI is stuck (billing, hung jobs)."},{"path":"ai_review.wait_for_ci_grace_seconds","default":"90 (range 30–600)","effect":"After push, wait this long for the first external check to appear before treating the repo as no-CI.","cost":"Small delay for repos without CI; avoids instant diff-only then duplicate review."},{"path":"ai_review.include_ci_in_review","default":"true","effect":"When wait_for_ci releases, attach CI check summary and failure logs to the normal Reviewer pack.","cost":"Extra Checks/Actions fetch + larger pack when CI ran; off keeps post-CI timing without CI log context."},{"path":"ai_review.related_context","default":"true","effect":"When true (default), Reviewer indexes ≤8 neighbor/import path names around changed paths (Python + JS/TS). Bodies are fetched via explore tools. Findings still require evidence on changed_paths/ci_refs.","cost":"Pennies of Contents API for the graph + tool rounds when agents read neighbors; set false for diff-only packs."},{"path":"ai_review.reply_comments","default":"true","effect":"When true (default), HeyEddi answers `/heyeddi ask`, `@mention`, and inline finding replies. Set false to silent-skip debate with no bot reply or model spend.","cost":"Debate/ask tokens when enabled; off avoids interactive spend."},{"path":"ai_review.pr_status_labels","default":"true","effect":"When true (default), HeyEddi keeps exactly one PR label among `heyeddi: reviewing`, `heyeddi: blocking`, and `heyeddi: lgtm` on the tip. Set false to skip all status-label writes.","cost":"none (GitHub Issues API only; not a merge gate)."},{"path":"ai_review.test_generation","default":"off","effect":"off | suggest | suggest_and_verify. suggest asks for test/fixture Apply patches when coverage is thin. suggest_and_verify runs tests on Spot (unavailable → Check fails closed).","cost":"~+$0.06 model per review when suggest is on; verify adds Spot infra seconds."},{"path":"ai_review.self_heal.mode","default":"off","effect":"off | suggest | apply_tests_only. suggest proposes heals on CI failures. apply_tests_only executes on Spot (unavailable → Check fails closed).","cost":"~+$0.05–0.15 model on failing heads when suggest is on; apply adds Spot seconds."},{"path":"ai_review.self_heal.max_attempts","default":"2 (range 1–5)","effect":"Cap for apply_tests_only heal loops on Spot. Ignored while mode is off/suggest-only.","cost":"Bounds Spot re-run spend."},{"path":"ai_review.self_heal.allow_push","default":"false","effect":"When true, may push heal commits to the PR branch via Spot coding steps. Never agent-merges. Default false.","cost":"Write path; keep false until you trust Spot verify."},{"path":"ai_review.review_scope_mode","default":"auto","effect":"auto | full_pr | incremental_only. auto uses full PR diff on first completed review, then compare parent→head on sync. manual `/heyeddi review` always uses full diff.","cost":"incremental_only may miss cross-commit coupling on large pushes; full_pr costs more tokens."},{"path":"ai_review.graph_neighbor_hops","default":"2 (range 0–4)","effect":"Graph BFS hops added around changed paths for incremental scope and explore tools.","cost":"Higher hops widen packs and tool rounds on sync reviews."},{"path":"ai_review.coverage_gap_fill","default":"true","effect":"When true, one extra partition wave targets paths not assigned to a review job before publish.","cost":"Extra model partition when gaps remain; does not block merge."},{"path":"ai_review.structural_notes","default":"true","effect":"When true, warn/fail spaghetti metrics append an informational structural note on the PR (never a blocking Check).","cost":"none beyond internal graph metrics already computed."},{"path":"pipeline.<job_id>","default":"(empty — no runner jobs)","effect":"Declares isolated Spot runner jobs: stage, image, run (argv-safe), optional needs, filter.paths. Jobs are sealed and executed on Spot for paid workspaces.","cost":"Runner billing scales with selected jobs; without filter.paths a job matches every change."},{"path":"pipeline.<job_id>.filter.paths","default":"(none — match all changes)","effect":"Path prefixes (e.g. backend/**) that select the job for a PR.","cost":"Missing filters waste runner budget on unrelated PRs."},{"path":"pipeline.<job_id>.run","default":"(required)","effect":"One command or list of commands. Shell operators, substitution, sudo/curl/wget are rejected.","cost":"none beyond runner time"},{"path":"policy.allow_external_prs","default":"false","effect":"Controls reviews of fork PRs (head repo ≠ base repo): a contributor opens a PR into your installed repo from their fork/account/org. They do not install the GitHub App — webhooks still fire on your installation. When false (default), those PRs are skipped (no model spend). When true, they may consume review budget. Secrets never go to forks; runners stay deny-by-default. Same-repo branch PRs are never treated as external.","cost":"Outside contributors can drive billable review activity on your install when enabled."}],"rules":["Only documented keys are accepted. Unknown top-level keys fail validation (extra=forbid).","Invalid or missing YAML → HeyEddi ignores it and falls back to safe defaults (no runners, on_ci_failure off, allow_external_prs off).","Fork PRs are skipped unless policy.allow_external_prs is true.","Normal PR review does not attach CI failure logs unless you run /heyeddi fails, enable ai_review.on_ci_failure, or enable ai_review.wait_for_ci with include_ci_in_review.","The webhook service never executes repository code; only validated pipeline jobs may reach isolated runners.","Policy is configuration you control, not an AI decision."],"guide":"eddi-ci.yaml contract (HeyEddi v1.0) — authoritative for this review:\n- Policy is configuration you control, not an AI decision.\n- Only documented keys are accepted. Unknown top-level keys fail validation (extra=forbid).\n- Invalid YAML → HeyEddi ignores it and falls back to safe defaults (no runners, on_ci_failure off, wait_for_ci off, allow_external_prs off; related_context stays on).\n- Normal PR review does not attach CI failure logs unless the user runs `/heyeddi fails`, enables on_ci_failure, or enables wait_for_ci with include_ci_in_review.\n- Prefer advisory improvements that respect the user's intent; never invent unsupported keys.\n\nSupported knobs:\n- `version` (default \"1.0\"): Required. Only version 1.0 is accepted; other values fail validation. Cost/safety: none\n- `ai_review.validation_max_attempts` (default 5 (range 2–50)): Max Reviewer↔validator retries per review when claims lack evidence. Cost/safety: Each attempt can re-invoke the Reviewer model; values above ~10 raise the cost ceiling with little quality gain.\n- `ai_review.max_auto_reviews_per_pr` (default 5 (range 0–50; 0 = unlimited)): Cap automatic reviews from PR opened/synchronize. After the cap, HeyEddi posts once and only runs again on `/heyeddi review` (or other manual commands). Cost/safety: Bounds push-loop model spend; manual commands still bill when used.\n- `ai_review.on_ci_failure` (default false): When true, a failed external Check on a PR head queues the same analysis as `/heyeddi fails` (one auto review per head SHA). Ignored when wait_for_ci is true. Never triggers on HeyEddi’s own Check. Cost/safety: Billable model review per failing head when enabled. Prefer wait_for_ci + include_ci_in_review for a single post-CI pass.\n- `ai_review.wait_for_ci` (default false): When true, automatic PR open/sync reviews wait for external CI to finish (or grace/timeout fallback) before one post-CI review. Manual `/heyeddi review` is never deferred. Cost/safety: Usually one auto review per push instead of racing CI; may add latency up to wait_for_ci_max_minutes.\n- `ai_review.wait_for_ci_max_minutes` (default 60 (range 5–180)): Hard cap before HeyEddi reviews anyway with a partial/timeout CI note (never blocks forever). Cost/safety: Bounds worst-case wait when CI is stuck (billing, hung jobs).\n- `ai_review.wait_for_ci_grace_seconds` (default 90 (range 30–600)): After push, wait this long for the first external check to appear before treating the repo as no-CI. Cost/safety: Small delay for repos without CI; avoids instant diff-only then duplicate review.\n- `ai_review.include_ci_in_review` (default true): When wait_for_ci releases, attach CI check summary and failure logs to the normal Reviewer pack. Cost/safety: Extra Checks/Actions fetch + larger pack when CI ran; off keeps post-CI timing without CI log context.\n- `ai_review.related_context` (default true): When true (default), Reviewer indexes ≤8 neighbor/import path names around changed paths (Python + JS/TS). Bodies are fetched via explore tools. Findings still require evidence on changed_paths/ci_refs. Cost/safety: Pennies of Contents API for the graph + tool rounds when agents read neighbors; set false for diff-only packs.\n- `ai_review.reply_comments` (default true): When true (default), HeyEddi answers `/heyeddi ask`, `@mention`, and inline finding replies. Set false to silent-skip debate with no bot reply or model spend. Cost/safety: Debate/ask tokens when enabled; off avoids interactive spend.\n- `ai_review.pr_status_labels` (default true): When true (default), HeyEddi keeps exactly one PR label among `heyeddi: reviewing`, `heyeddi: blocking`, and `heyeddi: lgtm` on the tip. Set false to skip all status-label writes. Cost/safety: none (GitHub Issues API only; not a merge gate).\n- `ai_review.test_generation` (default off): off | suggest | suggest_and_verify. suggest asks for test/fixture Apply patches when coverage is thin. suggest_and_verify runs tests on Spot (unavailable → Check fails closed). Cost/safety: ~+$0.06 model per review when suggest is on; verify adds Spot infra seconds.\n- `ai_review.self_heal.mode` (default off): off | suggest | apply_tests_only. suggest proposes heals on CI failures. apply_tests_only executes on Spot (unavailable → Check fails closed). Cost/safety: ~+$0.05–0.15 model on failing heads when suggest is on; apply adds Spot seconds.\n- `ai_review.self_heal.max_attempts` (default 2 (range 1–5)): Cap for apply_tests_only heal loops on Spot. Ignored while mode is off/suggest-only. Cost/safety: Bounds Spot re-run spend.\n- `ai_review.self_heal.allow_push` (default false): When true, may push heal commits to the PR branch via Spot coding steps. Never agent-merges. Default false. Cost/safety: Write path; keep false until you trust Spot verify.\n- `ai_review.review_scope_mode` (default auto): auto | full_pr | incremental_only. auto uses full PR diff on first completed review, then compare parent→head on sync. manual `/heyeddi review` always uses full diff. Cost/safety: incremental_only may miss cross-commit coupling on large pushes; full_pr costs more tokens.\n- `ai_review.graph_neighbor_hops` (default 2 (range 0–4)): Graph BFS hops added around changed paths for incremental scope and explore tools. Cost/safety: Higher hops widen packs and tool rounds on sync reviews.\n- `ai_review.coverage_gap_fill` (default true): When true, one extra partition wave targets paths not assigned to a review job before publish. Cost/safety: Extra model partition when gaps remain; does not block merge.\n- `ai_review.structural_notes` (default true): When true, warn/fail spaghetti metrics append an informational structural note on the PR (never a blocking Check). Cost/safety: none beyond internal graph metrics already computed.\n- `pipeline.<job_id>` (default (empty — no runner jobs)): Declares isolated Spot runner jobs: stage, image, run (argv-safe), optional needs, filter.paths. Jobs are sealed and executed on Spot for paid workspaces. Cost/safety: Runner billing scales with selected jobs; without filter.paths a job matches every change.\n- `pipeline.<job_id>.filter.paths` (default (none — match all changes)): Path prefixes (e.g. backend/**) that select the job for a PR. Cost/safety: Missing filters waste runner budget on unrelated PRs.\n- `pipeline.<job_id>.run` (default (required)): One command or list of commands. Shell operators, substitution, sudo/curl/wget are rejected. Cost/safety: none beyond runner time\n- `policy.allow_external_prs` (default false): Controls reviews of fork PRs (head repo ≠ base repo): a contributor opens a PR into your installed repo from their fork/account/org. They do not install the GitHub App — webhooks still fire on your installation. When false (default), those PRs are skipped (no model spend). When true, they may consume review budget. Secrets never go to forks; runners stay deny-by-default. Same-repo branch PRs are never treated as external. Cost/safety: Outside contributors can drive billable review activity on your install when enabled.\n\nPR commands (not config keys): `/heyeddi review`, `/heyeddi again`, `/heyeddi check`, `/heyeddi deep`, `/heyeddi ci` / `/heyeddi fails`, `/heyeddi deepci`, `/heyeddi ask`.","minimal_example":"version: \"1.0\"\npolicy:\n  allow_external_prs: false\nai_review:\n  validation_max_attempts: 5\n  wait_for_ci: false\n  wait_for_ci_max_minutes: 60\n  wait_for_ci_grace_seconds: 90\n  include_ci_in_review: true\n  on_ci_failure: false\npipeline: {}\n","safe_defaults":{"ai_review.related_context":true,"ai_review.reply_comments":true,"ai_review.pr_status_labels":true,"ai_review.on_ci_failure":false,"ai_review.wait_for_ci":false,"ai_review.wait_for_ci_max_minutes":60,"ai_review.wait_for_ci_grace_seconds":90,"ai_review.include_ci_in_review":true,"ai_review.test_generation":"off","ai_review.self_heal.mode":"off","ai_review.self_heal.allow_push":false,"ai_review.review_scope_mode":"auto","ai_review.graph_neighbor_hops":2,"ai_review.coverage_gap_fill":true,"ai_review.structural_notes":true,"policy.allow_external_prs":false,"pipeline":{}},"cost_ballpark":{"usd_per_typical_review":0.04,"usd_heal_on_failing_head_extra":0.0,"runner_jobs_declared":0,"notes":["baseline review ~$0.03","related_context on (+$0.01)","test_generation=off","self_heal.mode=off","on_ci_failure=off","wait_for_ci=off","Spot verify/apply not included until runners are enabled"]}}