Skip to content

bug(discover): extensionless scripts with valid shebangs are silently skipped #1199

Closed

Description

@ibaldr89

Version

v0.9.0 and current main at 7d6cdb23ef5ca2fd51f5d5b7509b33e112ef15f3

Platform

Linux (x64)

Install channel

Built from source

Binary variant

standard

What happened, and what did you expect?

An extensionless executable script is silently excluded from discovery even when its first line unambiguously declares a supported language:

#!/usr/bin/env python3

def extensionless_probe():
    return 42

cbm_language_for_filename("tool") returns CBM_LANG_COUNT, and detect_file_language() returns immediately without inspecting the shebang. The file therefore produces no File or Function nodes, and graph/code searches are indistinguishable from a true negative.

Expected behavior: preserve existing special-filename and extension detection, but when they return unknown, inspect a bounded first line and infer a supported scripting language from a valid shebang.

Reproduction

  1. Create a dummy repository:
mkdir -p /tmp/cbm-shebang-repro/bin
cat >/tmp/cbm-shebang-repro/bin/tool <<'PY'
#!/usr/bin/env python3

def extensionless_probe():
    return 42
PY
chmod +x /tmp/cbm-shebang-repro/bin/tool
  1. Index it:
codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-shebang-repro","mode":"full"}'
  1. Search for extensionless_probe with search_graph or search_code.

Actual: zero results; the file is absent from discovery. Expected: Python File/Function nodes and a matching search result.

The executable bit is not essential to parsing; the shebang is the language signal and should also work on platforms without POSIX mode bits.

Proposed fix

  • Keep filename/extension mappings authoritative.
  • Only when they return unknown, read at most the first line.
  • Recognize direct interpreter paths plus /usr/bin/env forms, including env -S.
  • Match interpreter basenames exactly (with version suffixes such as python3.12 where applicable).
  • Reject malformed/NUL-containing lines and read failures as unknown.
  • Add reproduce-first discovery tests for direct Python, env python3, env -S python3 -u, precedence, and negative inputs.

Related work checked

Confirmations

  • I searched existing open and closed issues/PRs and found no equivalent fix.
  • The reproduction uses a shareable dummy snippet, not proprietary code.

Activity

  1. added this to the 0.9.1-rc milestone on Jul 22, 2026
  2. added
    ux/behaviorDisplay bugs, docs, adoption UX
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Jul 22, 2026
  3. DeusData commented on Jul 25, 2026

    @DeusData
    Owner

    Confirmed on current main (97ce23f), and thank you for the unusually careful write-up — particularly the "Related work checked" section, which saved me the duplicate hunt entirely.

    The mechanism is exactly where you place it. detect_file_language() (src/discover/discover.c:651-655) opens with:

    CBMLanguage lang = cbm_language_for_filename(entry_name);
    if (lang == CBM_LANG_COUNT) {
        return CBM_LANG_COUNT;
    }

    Every content-based path in that function — the .m disambiguation, .cls (ObjectScript UDL vs Apex), .inc, and the ObjectScript Export XML probe — sits after that early return and each one keys off strrchr(entry_name, '.'). So content inspection in discovery is structurally reachable only for files that already have a recognised extension. An extensionless name can never reach it.

    Two details that support the shape you propose:

    • The filename table is genuinely filename-based, not extension-only — Dockerfile and Makefile are mapped as whole names (src/discover/language.c:645-647). So "keep filename/extension mappings authoritative, probe the shebang only when they return unknown" layers onto the existing precedence rather than fighting it.
    • Shebang parsing already exists in the codebase, but only in src/pipeline/pass_infrascan.c:815-830, where it records the interpreter of a known shell script for infrastructure scanning. It is downstream of discovery and never feeds language detection, so there is no existing helper being bypassed here — this really is a missing capability rather than a wiring mistake.

    Your point that the executable bit shouldn't be required is right and worth keeping in the design: mode bits don't survive many Windows checkouts, and the shebang is the actual language signal.

    On #1203: it's queued for review, and I'm deliberately not reviewing it in this triage pass. One thing you can act on now — GitHub currently reports it as CONFLICTING against main, which has moved a fair amount since you opened it. A rebase would help; I'd rather review the diff you intend than a stale one.

    Thanks for reproducing against both v0.9.0 and a pinned main SHA, and for keeping the repro to a shareable snippet.

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

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.ux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions