Skip to content

[Bug] Repair rounds derived from findings on default-excluded paths (secrets/, .env.*) are unsatisfiable: derived inScope contains what the completion gate classifies out_of_scope #183

Description

@nanami-0713

[Bug] Repair rounds derived from findings on default-excluded paths (secrets/, .env.*) are unsatisfiable — the derived inScope contains what the completion gate classifies out_of_scope

Summary

Since v0.1.20 (6cc84f6, "fix: resolve inherited exclusions after deriving repair scope"), repairScopeFromFindings() unconditionally collects finding.file and every path extracted from requiredFix into the repair round's inScope. But the completion gate resolves classifications in the opposite order — classifyChangedPath() checks isDefaultExcluded() before inScope:

// src/quality-gates.ts
export function classifyChangedPath(path, inScope, outOfScope) {
  if (normalizeWorkspacePath(path) === undefined) return 'illegal'
  if (isDefaultExcluded(path)) return 'out_of_scope'     // ← wins over inScope
  if (outOfScope.some(...)) return 'out_of_scope'
  if (inScope.some(...)) return 'in_scope'
  ...
}

and isDefaultExcluded() matches any path segment named secrets, plus any basename equal to .env or starting with .env.:

if (segments[0] === '.git' || segments[0] === '.dsh') return true
if (base === '.env' || base.startsWith('.env.')) return true
if (segments.includes('secrets')) return true
if (base.startsWith('id_rsa')) return true

So when a review finding points at a file like lib/secrets/parser.ts, src/secrets/redactor.ts (secrets is a fairly common source directory name for secret-handling code — precisely the code reviews tend to flag), or .env.example, the auto-generated repair contract becomes:

  • acceptance is derived from the finding's requiredFix text → it demands a change in that file;
  • inScope includes that file (nothing filters it);
  • but the moment the member honestly reports that path in changedPaths, the gate classifies it out_of_scope → repair cannot complete: <path> is out_of_scope.

An honest completion does not exist: the only ways out are burning all repair rounds and escalating. This looks like the same "unsatisfiable repair contract" family you have been fixing in #155/#173 follow-ups (#174/#175 are about staleness and wiring; this one is a systematic conflict between scope derivation and the default exclusion set).

Repro sketch

  1. A task reviews code under a path containing a secrets segment or a .env.example file (e.g. the reviewer flags a leaking log line in src/secrets/redactor.ts).
  2. Review fails with a finding whose file is that path and whose requiredFix mentions it.
  3. planQualityFollowUp() derives the repair round: acceptance ← requiredFix, inScope ← repairScopeFromFindings([finding]) → contains the path.
  4. Member edits the file and reports changedPaths: ['src/secrets/redactor.ts'].
  5. Completion gate: isDefaultExcluded('src/secrets/redactor.ts') → out_of_scope → repair cannot complete. Repeat until rounds exhaust → escalate.

Suggested fix

Pick one (or both):

  • In repairScopeFromFindings(), drop paths where isDefaultExcluded(normalized) is true (keeping the source.inScope fallback alive when everything is filtered out), so the contract never demands what the gate forbids;
  • Or in planQualityFollowUp(), refuse to generate a repair round when the entire derived scope is default-excluded and escalate to the captain instead — with a surfaced reason, so the human/captain can decide whether the file genuinely needs a human-blessed exception.

Also worth noting for whoever picks this up: withoutScopeConflicts() already reconciles explicit inherited outOfScope against the derived inScope, so wiring the implicit default exclusions into the same reconciliation would be the most consistent place.

Environment

  • v0.1.20 (also present on current main @ 87c95c9)

中文版:v0.1.20 起 repairScopeFromFindings() 会把 finding 的文件与 requiredFix 中出现的路径无条件收录进 repair 轮的 inScope,但完成门禁 classifyChangedPath() 是先查 isDefaultExcluded() 再查 inScope——secrets 作为任意路径段、.env/.env.* 作为文件名都会被判 out_of_scope。当 review finding 指向 lib/secrets/parser.ts、src/secrets/redactor.ts(把 secrets 当源码目录的项目不少,恰恰是 review 最爱挑的地方)或 .env.example 时,自动生成的 repair 合同变成:acceptance 要求改它、inScope 含它、但成员如实上报 changedPaths 就被判 out_of_scope、repair cannot complete——诚实完成不存在,只能烧光轮次 escalate。这是 #155/#173 系列在修的"不可满足 repair 合同"的残留死角(#174/#175 是过期与接线问题,这个是派生 scope 与默认排除集的系统性冲突)。建议:repairScopeFromFindings() 剔除被默认排除的路径(保留 fallback);或 planQualityFollowUp() 检测派生 scope 全被排除时拒绝生成、升级 captain 并说明原因。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions