Skip to content

tool:lint (and typecheck/audit) evidence resolver ignores project-context.json — only test/build read user overrides #1122

Description

Version: @cleocode/cleo 2026.7.2 (npm, global install) · Windows 10 · Node 22

Summary

cleo verify --evidence "tool:lint" never consults .cleo/project-context.json, even when the file carries a lint.command key (which cleo detect itself writes). It falls straight through to the per-language default — on a Python project that's ruff check . over the entire repo.

ADR-061 / the module docstring say the resolver "sources the command from .cleo/project-context.json when the user has captured a project-specific override", but the implementation only wires two of the seven canonical tools.

Root cause

packages/core/src/tasks/tool-resolver.tsresolveToolCommand(), Step 2 (dist: @cleocode/core/dist/tasks/tool-resolver.js):

// Step 2 — project-context overrides
if (canonical === 'test')  { readNestedString(ctx, ['testing', 'command']) ... }
if (canonical === 'build') { readNestedString(ctx, ['build', 'command']) ... }
// lint / typecheck / audit / security-scan: no project-context check at all
// → falls to LANGUAGE_DEFAULTS[primaryType], e.g. python.lint = ruff check .

Reproduction

  1. Python project; .cleo/project-context.json:
    { "primaryType": "python",
      "testing": { "command": "python scripts/run_tests.py" },
      "lint":    { "command": "python -m compileall -q scripts" } }
  2. cleo verify T### --gate testsPassed --evidence "tool:test" → runs the configured command ✅
  3. cleo verify T### --gate qaPassed --evidence "tool:lint" → runs ruff check . on the whole repo ❌ (in our case: 245 pre-existing findings unrelated to the task, E_EVIDENCE_TOOL_FAILED on every genuine qaPassed attempt)

Proposed fix

Generalize Step 2 to every canonical tool — test keeps the detector schema key testing.command, everything else reads <canonical>.command:

// Step 2 — project-context overrides (all canonical tools)
const ctx = loadProjectContext(projectRoot).context;
const ctxKey = canonical === 'test' ? 'testing' : canonical;
const cmd = readNestedString(ctx, [ctxKey, 'command']);
const parsed = cmd ? parseCommandString(cmd) : null;
if (parsed) {
    return { ok: true, command: { canonical, displayName: toolName,
        cmd: parsed.cmd, args: parsed.args,
        source: isAlias ? 'legacy-alias' : 'project-context' } };
}

test/build behavior is unchanged; lint.command, typecheck.command, audit.command, security-scan.command become honored. We've been running exactly this as a local dist patch since 2026-07-16 — tool:lint gate verification works as documented (source: 'project-context', exit 0, no override needed).

Happy to open a PR if that's welcome.

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