Skip to content

tcl: Mode B slices the parameter list as the function body — every tcl function in every scan records loc 1, complexity 0 #2763

Closed

Description

@squid-protocol

Found by a pass over the red cells in the keyword-rosetta bias variance chart (2026-09-05, engine 3b3568e0, corpus main 6183c4b). tcl is the only language in the corpus whose every function records loc = 1, complexity = 0 and struct_branch = 0. It is not morphology — the slicer is measuring the parameter list and calling it the body.

Mechanism

tcl has no ScopeParsingRegistry entry and its lexical_family is line_exclusive, so _function_slice routes it to Mode B (_slice_by_braces, detector.py:3457). Mode B finds the body by taking the first { at or after the match start:

brace_idx = safe_code.find(opener, start_idx, search_limit)   # detector.py:4592

A tcl procedure has two brace groups, not one:

proc probe_globals {env_kit} {
    global region
    ::env
}

tcl.py's func_start already consumes the parameter group — its optional (?:[ \t\n]+\{...\})? arm — so the match ends after {env_kit}, but the brace search restarts at start_idx. The first { from there is the parameter list, and _find_balanced_end closes on } one character later.

Measured on keyword-rosetta/data/tcl/a.tcl (LANGUAGE_DEFINITIONS['tcl']['rules']['func_start'].finditer):

match start_idx match.end() first { from start_idx first { from match.end()
proc probe_globals {env_kit} 208 236 227 (the parameter list) 237 (the body)
proc probe_test {kit} 270 291 286 292
proc probe_safety {value} 330 355 348 356

Incidence: 100%, and it is not corpus-specific

galaxyscope --db-only over language-crucible/data/tcl (MacPorts port API + registry, SQLite test suite):

functions: 352
loc <= 1:  352   (100%)
mean loc:  1.00   max loc: 1
complexity > 0: 1

Every real tcl function in the crucible records a one-line body. Control languages on the same scan pass, same commit: python mean 4.54, lua 4.92, perl 5.54.

So for tcl, engine-wide: avg_func_loc, avg_func_complexity, max_func_complexity, func_complexity_gini, func_internal_density and every per-function signal vector are computed over a signature line. function_data.token_mass reads 6–8 for a body that is 20–40 tokens. The file-level rule counts are unaffected (coding_analysis runs over the whole stream), which is why verify_language.py tcl passes 76 assertions while the shape descriptors sit at −100%.

Candidate shape

Anchor the body search after the signature the regex already consumed, as the c/cpp args_sig_end bound below it already does for the argument slice:

body_search_start = match.end() if lang_id == "tcl" else start_idx
brace_idx = safe_code.find(opener, body_search_start, search_limit)

match.end() is safe for tcl specifically because func_start's trailing lookahead (?=[ \t\n]*\{|[ \t\n]|$) guarantees the next non-space character is either the body brace or nothing. A proc whose parameter list is written on its own line is covered too — the pattern's [ \t\n] classes already span newlines (the "vertical proc shield" its own comment describes).

The generic form of the same fix — "start the body search at match.end() whenever func_start consumed the parameter list" — is worth considering, but every other Mode B language in the corpus uses (...) for parameters and {...} for the body, so nothing else in the 46 is affected and a tcl-scoped gate needs no re-audit. Strict tests belong in tests/extraction/languages/test_tcl_strict.py: single-line proc, vertical proc, nested proc inside a namespace eval, and a proc whose parameter list carries a braced default value (proc f {{a 1}} {...} — the depth-aware case _args_tcl_pattern_list_groups already exists for).

Blast radius

Golden-master movement is confined to directory groups containing .tcl/.itcl/.tm/.tbc files, and to per-function columns only — loc, complexity, token_mass, the function-scoped signal vector, and the equations a function-scoped dampener changes. Scope the diff check the way #2634 did.

Corpus pairing

keyword-rosetta/data/tcl needs no authoring change — the shells already have three-line bodies. The re-bless moves tcl's manifest per-function values and retires tcl from the function-body-length-morphology ledger entry, which currently reads "tcl 1.0 (−80%, a proc body the slicer captures as a single line)" and files it under intended morphology. It is not morphology; that line is this bug, described accurately and classified wrongly.

Part of #2669 (rosetta[tcl] is #2603).

Activity

  1. added
    bugUnintended behavior or logic failure in the engine
    metricsHeuristics, risk exposures, and topological math updates
    core-engineModifications to the central physics and parsing engine
    on Sep 5, 2026
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

    bugUnintended behavior or logic failure in the enginecore-engineModifications to the central physics and parsing enginemetricsHeuristics, risk exposures, and topological math updates

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions