Summary
The self-host config loader treats the global .loopover.yml (at the config-dir root) as a
fallback candidate only — it's consulted purely when a repo has no per-repo config file of its
own. Since every currently-configured repo (loopover, gittensory, metagraphed, awesome-claude)
already has its own per-repo file, the global file is never actually read for any of them today.
Meanwhile, ~25 fleet-wide feature/behavior toggles are controlled exclusively by LOOPOVER_* env
vars in .env (e.g. LOOPOVER_DUPLICATE_WINNER, LOOPOVER_REVIEW_RAG, LOOPOVER_REVIEW_MEMORY,
LOOPOVER_SKIP_AUTOMATION_BOT_PRS, ...), with each per-repo .loopover.yml only able to express
inherit | off | enabled — "inherit" always defers to the env var, never to anything in the global
yml. This violates the config-as-code mandate this project otherwise holds everywhere else: every
review feature should be controllable from .loopover.yml, with env vars reserved for genuine
bootstrap-level config (secrets, DB/connection strings, fleet membership) needed before any config
file can even be located.
What surfaced this
PRs #6248 and #6249 both linked the same issue; #6248 (created first) should have survived as the
duplicate-cluster winner but was auto-closed identically to #6249. Root cause: the duplicate-winner
adjudication feature (#dup-winner) that exists specifically to spare the earliest claimant was off
fleet-wide, controllable only via LOOPOVER_DUPLICATE_WINNER in .env — not from .loopover.yml,
global or per-repo, even though the per-repo schema already exposes a duplicateWinnerMode: inherit | off | enabled field that (today) can only ever narrow the always-off global default, never widen
it via config-as-code.
Plan
Phase 1 (first sub-issue): make the global .loopover.yml a genuine base layer that per-repo
inherit values actually defer to (not just a fallback for repos with no config at all), and migrate
duplicateWinnerMode off LOOPOVER_DUPLICATE_WINNER end to end as the proof case.
Phase 2+: once the base-layer pattern is proven, migrate the remaining feature-toggle env vars
one at a time (each gets its own sub-issue once scoped):
LOOPOVER_OPEN_PR_FILE_COLLISION, LOOPOVER_SKIP_AUTOMATION_BOT_PRS
LOOPOVER_REVIEW_RAG, LOOPOVER_REVIEW_GROUNDING, LOOPOVER_REVIEW_IMPACT_MAP,
LOOPOVER_REVIEW_MEMORY, LOOPOVER_REVIEW_SAFETY, LOOPOVER_REVIEW_REPUTATION,
LOOPOVER_REVIEW_SCREENSHOTS, LOOPOVER_REVIEW_IMPROVEMENT_SIGNAL,
LOOPOVER_REVIEW_CULTURE_PROFILE, LOOPOVER_REVIEW_DRAFT, LOOPOVER_REVIEW_INLINE_COMMENTS,
LOOPOVER_REVIEW_FIX_HANDOFF, LOOPOVER_REVIEW_PLANNER, LOOPOVER_REVIEW_OPS,
LOOPOVER_REVIEW_SELFTUNE, LOOPOVER_REVIEW_CONTINUOUS
LOOPOVER_SWEEP_WATCHDOG, LOOPOVER_MAINTAINER_RECAP, LOOPOVER_PUBLIC_STATS,
LOOPOVER_PR_RECONCILIATION, LOOPOVER_AUTO_FILE_DRIFT_ISSUES,
LOOPOVER_ENABLE_UNSAFE_CODEX_REVIEWER, LOOPOVER_EXPERIMENTAL_GITTENSOR
Explicitly out of scope — these stay env-var/.env-only, they're genuine bootstrap config, not
review features: secrets/tokens (LOOPOVER_API_TOKEN, LOOPOVER_MCP_TOKEN,
LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN, LOOPOVER_DRIFT_ISSUE_TOKEN), release metadata
(LOOPOVER_VERSION), and fleet membership (LOOPOVER_REVIEW_REPOS — needed before any per-repo
config file could even be located).
Requirements
- The global
.loopover.yml must be loaded as a base layer for EVERY repo, not just repos with no
per-repo file of their own.
- Per-repo
inherit must resolve against the global yml's own value first, falling back to the env
var only when the global yml doesn't specify it either — never silently ignoring an explicit global
yml setting.
- No behavior change for any repo/setting that doesn't explicitly opt into the new global yml value
(byte-identical until an operator actually sets something in the global file).
Summary
The self-host config loader treats the global
.loopover.yml(at the config-dir root) as afallback candidate only — it's consulted purely when a repo has no per-repo config file of its
own. Since every currently-configured repo (loopover, gittensory, metagraphed, awesome-claude)
already has its own per-repo file, the global file is never actually read for any of them today.
Meanwhile, ~25 fleet-wide feature/behavior toggles are controlled exclusively by
LOOPOVER_*envvars in
.env(e.g.LOOPOVER_DUPLICATE_WINNER,LOOPOVER_REVIEW_RAG,LOOPOVER_REVIEW_MEMORY,LOOPOVER_SKIP_AUTOMATION_BOT_PRS, ...), with each per-repo.loopover.ymlonly able to expressinherit | off | enabled— "inherit" always defers to the env var, never to anything in the globalyml. This violates the config-as-code mandate this project otherwise holds everywhere else: every
review feature should be controllable from
.loopover.yml, with env vars reserved for genuinebootstrap-level config (secrets, DB/connection strings, fleet membership) needed before any config
file can even be located.
What surfaced this
PRs #6248 and #6249 both linked the same issue; #6248 (created first) should have survived as the
duplicate-cluster winner but was auto-closed identically to #6249. Root cause: the duplicate-winner
adjudication feature (
#dup-winner) that exists specifically to spare the earliest claimant was offfleet-wide, controllable only via
LOOPOVER_DUPLICATE_WINNERin.env— not from.loopover.yml,global or per-repo, even though the per-repo schema already exposes a
duplicateWinnerMode: inherit | off | enabledfield that (today) can only ever narrow the always-off global default, never widenit via config-as-code.
Plan
Phase 1 (first sub-issue): make the global
.loopover.ymla genuine base layer that per-repoinheritvalues actually defer to (not just a fallback for repos with no config at all), and migrateduplicateWinnerModeoffLOOPOVER_DUPLICATE_WINNERend to end as the proof case.Phase 2+: once the base-layer pattern is proven, migrate the remaining feature-toggle env vars
one at a time (each gets its own sub-issue once scoped):
LOOPOVER_OPEN_PR_FILE_COLLISION,LOOPOVER_SKIP_AUTOMATION_BOT_PRSLOOPOVER_REVIEW_RAG,LOOPOVER_REVIEW_GROUNDING,LOOPOVER_REVIEW_IMPACT_MAP,LOOPOVER_REVIEW_MEMORY,LOOPOVER_REVIEW_SAFETY,LOOPOVER_REVIEW_REPUTATION,LOOPOVER_REVIEW_SCREENSHOTS,LOOPOVER_REVIEW_IMPROVEMENT_SIGNAL,LOOPOVER_REVIEW_CULTURE_PROFILE,LOOPOVER_REVIEW_DRAFT,LOOPOVER_REVIEW_INLINE_COMMENTS,LOOPOVER_REVIEW_FIX_HANDOFF,LOOPOVER_REVIEW_PLANNER,LOOPOVER_REVIEW_OPS,LOOPOVER_REVIEW_SELFTUNE,LOOPOVER_REVIEW_CONTINUOUSLOOPOVER_SWEEP_WATCHDOG,LOOPOVER_MAINTAINER_RECAP,LOOPOVER_PUBLIC_STATS,LOOPOVER_PR_RECONCILIATION,LOOPOVER_AUTO_FILE_DRIFT_ISSUES,LOOPOVER_ENABLE_UNSAFE_CODEX_REVIEWER,LOOPOVER_EXPERIMENTAL_GITTENSORExplicitly out of scope — these stay env-var/
.env-only, they're genuine bootstrap config, notreview features: secrets/tokens (
LOOPOVER_API_TOKEN,LOOPOVER_MCP_TOKEN,LOOPOVER_CONTRIBUTOR_ISSUE_TOKEN,LOOPOVER_DRIFT_ISSUE_TOKEN), release metadata(
LOOPOVER_VERSION), and fleet membership (LOOPOVER_REVIEW_REPOS— needed before any per-repoconfig file could even be located).
Requirements
.loopover.ymlmust be loaded as a base layer for EVERY repo, not just repos with noper-repo file of their own.
inheritmust resolve against the global yml's own value first, falling back to the envvar only when the global yml doesn't specify it either — never silently ignoring an explicit global
yml setting.
(byte-identical until an operator actually sets something in the global file).