Repository navigation
fix(core-engine): Mode D anchors a function at its declaration, not its brace (#2758) - #2760
Merged
Merged
Conversation
…ts brace (#2758) Mode D's openers include the bare `{`, so a K&R-style shell function -- declaration on one line, brace on the next, which is how curl's own initscript.sh is written throughout -- reported `start_line` on the BRACE. The lookback in `_slice_by_keywords` already had to walk back a line to find the name (L5183-L5198); it recovered the name and left the position behind. `start_line` is not diagnostic-only: `sarif_recorder.py` uses it as the `locations` line of every SARIF result -- the artifact GitHub code scanning and IDEs annotate from -- and `spatial_correlation.py` builds each function's dampener window as `(start_line, end_line + 1)`, so a window starting a line late can miss a signal sitting on the declaration itself. ONLY THE LINE ANCHOR MOVES, and that limit is the whole fix. The issue's own fix shape said to move the anchor and leave the satellite's text alone, which sounded like it meant the `sat_start_line`/`sat_start_char` pair. It does not: `_calculate_block_metrics` slices the file's `spatial_map` with `bisect(indices, start_char)`, so the char coordinate is the block's own address, not metadata. Moving both was measured first and produced 601 golden-master differences with `versioned_copy` in curl/initscript.sh going from 0 to 36 control-flow branches -- every signal between the declaration and the opener re-attributed into the function. Moving the line alone: 20. Safe in Mode D specifically because it computes `sat_end_line` independently (L5224) rather than as `start_line + loc - 1` the way the brace and label modes do -- so the span EXTENDS backwards over the declaration instead of sliding a line earlier. A test asserts `loc` and `end_line` do not move, since that is the property the change rests on. Measured on the language-crucible: functions whose `start_line` sits BELOW their declaration fall 19 -> 6, and all 13 shell cases are fixed. The 3448 functions whose `start_line` sits ABOVE the name are untouched -- those are annotations and attributes (`@Override`, `@isTest`, `#[proc_macro_derive]`), where the declaration genuinely does begin at the annotation. The residual 6 were each checked by hand and are artifacts of the measuring heuristic matching an unrelated occurrence of the name above the declaration (a `getLogger(EmailService)` call, prose in a comment, a `(provide ...)` export list), not the Mode D shape. Golden masters re-blessed: 20 differences, 0 off-target, 0 topology movement. `Start Line` is the only field that changed inside `5. Function Analysis` (137 functions); the 3 `Documentation Exposure` cells and their 2 roll-ups are the line window now including the declaration, which is the intended effect. Full suite 7634 passed serially; audit_check clear; tree-sitter 30/30 OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WfeHRWra1d6Z5uSReFajSq
Conflict was the two golden-master fixtures again, both auto-generated: #2755 (jcl api/cleanup/globals) and #2759 (yaml args) re-blessed them on main while this branch was in flight. `gitgalaxy/core/detector.py` merged cleanly -- their changes are in the rules, this one is in the Mode D slicer. Resolved per .agents/skills/ci-push-checklist section 6: took origin/main's version of both fixtures to clear the markers, then re-ran `crucible_check.py --update --yes` so they are recalculated from the merged code rather than stitched together. Verified rather than trusted: re-attributed the regenerated fixture against origin/main and got the same profile as before the merge, exactly -- 20 differences, 0 topology movement, `Start Line` the only field changed inside `5. Function Analysis`, and all 17 per-file diffs on `.sh` files. #2755's and #2759's own contributions are in the base and untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WfeHRWra1d6Z5uSReFajSq
Contributor
squid-protocol
added a commit
that referenced
this pull request
Sep 6, 2026
… `args` call sites, m4 arity (#2763, #2782, #2783, #2784) (#2790) * fix(core-engine): apex `args` counts declared parameters, not call sites (#2783) The `args` method arm's return-type group carried a trailing `?`. With the annotation and modifier runs both zero-allowed, that made the whole prefix optional and the arm degenerated to `^[ \t]*IDENT(...)` -- the exact shape of a bare call statement. Per `docs/args_rule_contract.md`, `args` matches the parameters a callable DECLARES; a call site consumes a parameter surface, it does not publish one. Same defect and same fix family as #2773 (objective-c, typescript) and #2782 (groovy). * The return type is now MANDATORY, which is what `java` and `csharp` -- the same C-family rule, same shape -- already require. Apex has no `def`: every method declaration carries an explicit return type, and a constructor carries the class name in that position (`public Foo(x)` backtracks to type=`public`, name=`Foo`). * `return` joins `new` in the return-type slot's exclusion, as csharp branch 1 already does: `return doWork(x);` is a call whose two tokens otherwise satisfy `type name(...)`. * A new branch 3 reaches the one real declaration a mandatory return type cannot -- a modifier-less constructor (`MyClass(Integer x) {`, default private) -- anchored on the body `{` that FOLLOWS the parameter list, the way java/csharp anchor their own constructor branches. It is placed LAST so the method and trigger arms keep their capture-group indices, since `_calculate_block_metrics` reads `group(lastindex)`. A `(?=[ \t\n]*\{)` lookahead was rejected as the PRIMARY anchor: it cannot reach Apex's bodiless interface and `abstract` declarations, and relaxing it to `[{;]` re-admits `foo(x);` verbatim. It is correct only as the secondary constructor branch. Measured (raw rule counts over the prism code stream, both corpora): | corpus | func_start | args before | args after | |-------------------|-----------:|------------:|-----------:| | keyword-rosetta | 13 | 18 | 13 | | language-crucible | 41 | 41 | 39 | keyword-rosetta lands on the 13 planted declarations exactly. The crucible's previous 41-vs-41 was a coincidence, not agreement: tree-sitter ground truth is 38 declarations, so the old rule was 38 real plus 3 false positives in `SOQLRecipes.cls`. Recall is 38/38 unchanged; precision goes 38/41 -> 38/39. Per-function `args_count` has zero differences across all 51 spliced functions -- the removed hits sat inside bodies, and the block search takes the signature first -- so per-function metrics are untouched. Known and deliberately not fixed here: a SOQL clause inside `[...]` of the shape `SELECT SUM(Amount) total` still reads as type=`SELECT`, name=`SUM` (1 hit in language-crucible, 0 in keyword-rosetta, unchanged by this commit). That is a different defect class -- an embedded sub-language scanned as Apex statements, which wants a `_scope_filters` entry rather than another keyword exclusion, and blanket-excluding `select` is unsafe under this rule's `re.I` per the #2671 ledger entry. Golden masters move (`SOQLRecipes.cls` file-level `args` 16->14 plus the corpus-wide X/Y/Z re-solve); blessed once for all four fixes in a later commit on this branch. Closes #2783 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(core-engine): groovy `args` counts declared parameters, not call sites (#2782) The `args` method arm's return-type run was `{0,3}`. At zero repetitions, with the annotation and modifier runs also zero-allowed, the arm collapsed to `^[ \t]*IDENT(...)` -- the shape of a bare call statement -- so `close(conn)`, `assertEquals(kit, kit)` and `probeBranch(argv)` all scored as declared parameter lists. Per `docs/args_rule_contract.md`, `args` matches the parameters a callable DECLARES; a call site consumes a parameter surface, it does not publish one. Same defect and same fix family as #2773 (objective-c, typescript) and #2783 (apex). The terminator lookahead was the right family but insufficient alone, so the declaration form is now two arms: * Arm 1 (bodied) keeps the fully-optional prefix -- Groovy genuinely declares `def entry(argv) {` and bare constructors `FineractPluginExtension(Project project) {` -- and anchors on the body brace via `(?=[ \t\n]*(?:throws ...)?\{)`. It is a lookahead, so `group(0)` still ends at `)`, which a strict test asserts. * Arm 2 (bodyless) reaches the casualty class a `{`-anchor cannot: Retrofit interface methods, `@Managed` model-rule interfaces, abstract Gradle property getters. Allowing a bare `;`/end-of-line here readmitted 163 matches, 60 of them Groovy's paren-less-call DSL sugar (`implementation project(':lib')`, `storePassword getReleaseKeystorePassword()`) -- the same trap typescript hit in #2773. So the construct is named instead: arm 2 makes the declaration type MANDATORY and limits it to a primitive, `def`, or an uppercase-initial type. That is the exact discriminator, since the DSL sugar's receiver is always a lowercase property name. A `{`-lookahead alone yields 890 on the crucible; arm 2 recovers 122 genuine bodyless declarations on top of that -- the measured difference between a correct fix and a merely narrower one. The parameter-list matcher is now string-aware (`[^()"']|"[^"]*"|'[^']*'|\([^()]*\)`), because once you check what FOLLOWS the list, a stray `)` inside a default value's string literal breaks the match -- `test_groovy.py`'s pathological case. This is count-neutral: the crucible match set is byte-identical with and without it. Measured (raw rule counts over the prism code stream): | corpus | func_start | args before | args after | |-----------------------------|-----------:|------------:|-----------:| | keyword-rosetta (4 files) | 13 | 19 | 13 | | language-crucible (304) | 875 | 2157 | 991 | keyword-rosetta lands on the 13 planted declarations exactly, and per file now equals the func_start column (3/3/3/4). a/f goes 2.48 -> 1.13. No corpus authoring change needed. Recall: the new crucible match set is a strict SUBSET of the old -- 0 additions, 1166 removals, verified by diffing both match sets offset-by-offset. The removals are call statements plus 7 `def (a, b) = ...` multiple-assignment destructurings, which declare locals rather than parameters and are correctly out of contract. No real declaration lost. The pattern now has 5 capture groups rather than 3 (the closure group moves 3 -> 5). This is safe: `_calculate_block_metrics` reads `group(lastindex)`, and groovy declares no index-keyed `_args_*_groups` config and no `_scope_filters` -- both confirmed empty. Pre-existing and deliberately not addressed: arm 1's zero-prefix form still admits a DSL call with a closure argument (`configure(foo) {`), the likely source of the residual a/f > 1.00. `func_start` guards that with a DSL-keyword exclusion list plus a named-argument lookahead; porting either is a separate change carrying its own risk. Golden masters move (groovy appears in both audits); blessed once for all four fixes in a later commit on this branch. Closes #2782 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(core-engine): m4 `avg_func_args` takes the highest positional index, not the first (#2784) `m4` has no formal parameter list, so `$1`/`$@` in a macro body is the whole parameter surface. The FILE-LEVEL count of those references is intended morphology, settled in keyword-rosetta's `m4-parameters-are-use-sites` ledger entry, and this change deliberately leaves it untouched: wrapping the existing alternation in one capture group moves no match boundary, so the raw rule count is byte-for-byte identical (verified: keyword-rosetta 18 -> 18, language-crucible 70 -> 70). What moves is the PER-FUNCTION arity. m4 has no `args_search_text` bound -- that exists only for objective-c/c/cpp/dart -- so `_calculate_block_metrics` searches the whole macro body and trusts the single LEFTMOST hit. `shell` hit exactly this in #1518 and solved it with `_args_findall_max_groups`, which makes the shared counter re-scan the block and take the highest positional index via `_count_shell_positional_max`. This ports that mechanism; group 1 is the hook the helper indexes on. CORRECTION TO THE ISSUE: #2784's headline example does not reproduce. Measured old-vs-new on the per-function path: | macro body | before | after | true arity | |-----------------------------------------|-------:|------:|-----------:| | `AT_SETUP($1) AT_CHECK($1)` (the issue) | 1 | 1 | 1 | | `AS_IF([test x$1], [do_thing($3)])` | 1 | 3 | 3 | | `AC_BEFORE([$0],[AC_OTHER])` | 1 | 0 | 0 | | `do_all($@)` | 1 | 1 | 1 | A doubled `$1` already read 1, because the leftmost-hit path takes `"$1"` and stops -- so the issue's "reads arity 2" claim was wrong. The +38% it measured is purely the file-level total, which is the ledgered morphology and correctly stays. The defects the first-match-only path really does cause are the other two rows: every position after the first was silently dropped, and `$0` -- the macro's own NAME, not a parameter -- was counted as one. Both are fixed, which is the issue's actual intent (an arity should reflect declared positions), so it is closed here rather than reopened. Corpus effect, hand-verified: * keyword-rosetta: file-level 18 -> 18, per-function sum 13 -> 13, `avg_func_args` 1.0000 -> 1.0000. Nothing moves; no corpus re-authoring needed. * language-crucible: file-level 70 -> 70, per-function sum 14 -> 0, `avg_func_args` 0.3590 -> 0.0000. This is a precision gain, not a recall loss: every `$N` inside a sliced macro on that corpus is `$0` (curl-confopts 9, zz40-xc-ovr 51, xc-am-iface 3), all the `AC_BEFORE([$0],[...])` idiom inside genuinely argument-less `AC_DEFUN`s -- true arity 0, where the old 1.0 was `$0` misread as a parameter. The only real `$1`/`$2` on that corpus is in `curl/configure.ac`, which has `func_start = 0`, so no satellite gained or lost a count. No macro that actually references a positional argument went to zero. The block is safely bounded for a whole-block re-scan: `_slice_by_m4_brackets` (Mode F) cuts at the macro's own bracket-aware paren span, so no neighbouring macro's positions leak in despite the missing `args_search_text`. Golden masters move -- the crucible per-function sum flows through `avg_func_args` -> `log_avg_func_args` -> magnitude for the `m4/curl/*` entries; blessed once for all four fixes in a later commit on this branch. Closes #2784 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(args-contract): fold the #2782/#2783/#2784 results back into the audit The 46-language audit table still described apex, groovy and m4 as open violations and carried the counts they had BEFORE the three fixes on this branch. Updates each row to the measured post-fix numbers and the anchor shape that got them there. Corollary 3 is also corrected on a point the m4 work disproved. It asserted that `$1 … $1` "is one parameter referenced twice under any reading" and implied the engine read it as 2. It did not -- the per-function path takes the LEFTMOST hit, so a doubled `$1` already read 1. The real failure of a first-match-only arity is that it drops every higher position after the first (`$1 … $3` read 1, not 3) and counts `$0`, the macro's own name, as a parameter. The corollary now states the rule that actually holds: an arity is the highest position DECLARED, not the first one seen. Rows now read as fixed rather than filed: * `apex` 18 -> 13 rosetta, 41 -> 39 crucible. Records that the old 41-vs-41 was a coincidence -- 38 real declarations plus 3 false positives -- so the fix is a precision gain with recall unchanged at 38/38. * `groovy` 19 -> 13 rosetta, 2193 -> 991 crucible, a/f 2.48 -> 1.13. * `m4` file-level 18/70 unchanged (the ledgered morphology), with the correction that its `$1 ... $1` example never reproduced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(core-engine): tcl Mode B anchors the body at the declaration end, not the parameter list (#2763) A tcl `proc` has TWO brace groups -- the parameter list (`proc name {a b}`) and then the body (`{ ... }`). Mode B's generic fallback starts its brace search at `start_idx`, so for tcl it always found the PARAMETER LIST and `_find_balanced_end` closed one character later. Every tcl function in every scan recorded `loc` 1, `complexity` 0 and `struct_branch` 0: 13/13 functions in keyword-rosetta, 389/389 in language-crucible. `func_start` already consumes the parameter group via its optional `(?:[ \t\n]+\{...\})?` arm, so `match.end()` is the correct body anchor. THE FIX IS GATED TO TCL, AND THAT GATING IS THE LOAD-BEARING DECISION. The blanket form -- "search from `match.end()` for every Mode B language" -- was measured first and is catastrophic. 24 languages route to Mode B; 13 have their own `elif` earlier, so 12 reach this generic fallback. Every one of them except tcl either consumes its own body `{` inside `func_start` or stops before the parameter list, so moving the anchor skips the real body and grabs the NEXT declaration's brace. Divergent matches (first `{` from `start_idx` != first `{` from `match.end()`) across both corpora: | c | cpp | scheme | groovy | powershell | apex/css/html/php/swift | |---:|---:|---:|---:|---:|---:| | 1756/1756 | 1247/1247 | 96/96 | 11 | 10 | 0 | scheme's opener is `(` rather than `{`. tcl is the only language in this fallback whose `func_start` consumes a NON-BODY brace group, so it is the only one the anchor may move for. Verified after the change: c (1756 functions) and cpp (1147) produce byte-identical per-function records, as do the other nine. On the #2758/#2760 precedent (Mode D anchoring at the declaration rather than the brace): that change carried no per-language gate and added no reusable flag, but it cuts toward gating rather than away -- it is self-gating by construction, its message justifies scope by a property "Mode D specifically" has, and it measured the broad form (601 golden-master diffs), rejected it, and shipped the narrow one (20). Precedent for measure-then-narrow. A no-brace fallback to the old anchor is retained deliberately. A tcl body need not be a brace group at all -- it is just another word, and `proc faultsim_test_proc {testrc testresult testnfail} $O(-test)` (sqlite/malloc_common.tcl:347) passes a VARIABLE as the body. Without the fallback that real declaration was DROPPED (376 -> 375). With it, every proc either gets a correct body span or exactly what it had before, so the fix is a strict superset and the function count never moves. Also fixes a second, separable defect in the same language's `_build_brace_safe_stream` arm, which carried the C-family `//[^\n]*|/\*.*?\*/` shield -- exactly perl's #1437 bug one language over. Tcl's only comment introducer is `#` (already blanked upstream by prism), but `//` appears constantly as a URL scheme separator, and shielding from it blanked the rest of the line including any `}` on it, silently unbalancing the depth counter. This was invisible before the slicer fix because a tcl body was never brace-walked at all; with it, `portfetch.tcl`'s `bzrfetch` measured loc 515 for a real 37-line proc. 14 of 376 crucible procs over-captured this way; all 14 resolve with the branch removed. Measured: | corpus | functions | loc<=1 | mean loc | branch>0 | |---------|----------:|-------:|---------:|---------:| | rosetta before / after | 13 / 13 | 13 / 0 | 1.00 / 4.62 | 0 / 2 | | crucible before / after | 389 / 389 | 389 / 5 | 1.00 / 35.90 | 1 / 272 | Recall: 0 functions lost, 0 gained on either corpus; `args_count` identical for all of them. The residual `loc<=1` rows are genuine one-line procs plus the variable-bodied one. An independent tcl-aware raw-source brace walker (more reliable here than tree-sitter-tcl, which is itself wrong on this corpus) puts functions with a verifiably correct span at 8/376 -> 370/376. Of the 2 residuals, one is an oracle artifact (nested `proc finish_test`) and one is an upstream `prism.py` defect -- `#` inside `string map {1.#INF ...}` blanked as a comment -- filed separately, out of scope here. Adds 6 regression tests to `tests/extraction/languages/test_tcl_strict.py`, each verified to FAIL on 08a08c6. Golden masters move and are NOT blessed here; the branch blesses once for all four fixes. Quantified against a clean baseline (committed GM vs unmodified-HEAD scan = 0 diffs): 585 diffs, of which 430 are topological X/Y/Z from the corpus-wide re-solve, 152 are tcl-path content fields and 3 are global roll-ups whose inputs include tcl. No off-target content change, and no function added or removed anywhere. Closes #2763 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(tests): bless golden masters for #2763, #2782, #2783, #2784 One bless covering all four engine fixes on this branch, rather than four separate re-blesses of the same two fixtures. The full uncapped diff was regenerated and classified before blessing, because `crucible_check.py`'s own output is capped twice (50 differences in `golden_diff.py`, a 4000-char window in the wrapper) and cannot support a scoped review on its own. Method is CLAUDE.md's: rescan the corpus with the zero-dependency venv, then `deep_compare` against the committed fixture. 1374 differences, every one attributed: | class | count | |---|---:| | topological X/Y/Z coordinates | 1069 | | tcl content | 136 | | groovy content | 131 | | global roll-ups | 27 | | m4 content | 10 | | apex content | 1 | | **off-target content** | **0** | The 1069 coordinate diffs are the corpus-wide 3D layout re-solving, which happens whenever any node's mass changes and therefore ripples into languages this branch never touched; they are attributable as a class, not individually. All 305 content differences fall in the four languages the branch changes, plus 27 global roll-ups whose inputs include them (`directory_groups/tcl/*`, `directory_groups/m4/curl/m4/*`, `health/avg_documentation`, `health/avg_cognitive_load`, and the `exposures/documentation/lowest` leaderboard, whose entries are literally the tcl files whose documentation became measurable once their bodies were sliced correctly). The largest single movers are the expected consequences of #2763: `directory_groups/tcl/ sqlite/total_mass` 1970.88 -> 3819.68 and `avg_exposures/documentation` 0.0 -> 3.97, because every tcl function previously recorded `loc` 1 with no body to carry documentation or complexity. `scope_check.py --expect tcl,groovy,apex,m4` also ran; it exits non-zero because the topological re-solve touches other languages' coordinates, which is expected and is why the classification above was done directly rather than relying on its verdict. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Joe Esquibel <squid-protocol@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2758.
Mode D's openers include the bare
{, so a K&R-style shell function — declaration on one line,brace on the next — reported
start_lineon the brace.curl's owninitscript.shin thelanguage-crucible is written that way throughout.
The lookback in
_slice_by_keywords(L5183–L5198) already had to walk back a line to find thename. It recovered the name and left the position behind.
Why it isn't cosmetic
sarif_recorder.pyL69/L88/L110/L130/L143 —start_lineis thelocationsline of everySARIF result, i.e. the artifact GitHub code scanning and IDEs annotate from.
spatial_correlation.pyL316 — each function's dampener window is(start_line, end_line + 1),so a window starting a line late can miss a signal on the declaration itself. That one is a
scoring input, not reporting.
record_keeper.pyL833,audit_recorder.pyL272,gpu_recorder.pyL269 all persist it.The correction to #2758's own fix shape
The issue said to move the anchor and leave the satellite's text alone, which reads as "move the
sat_start_line/sat_start_charpair". It doesn't mean that, and I measured it the wrong wayfirst.
_calculate_block_metricsslices the file'sspatial_mapwithbisect(indices, start_char)— the char coordinate is the block's own address, not metadata.versioned_copyincurl/initscript.shwent 0 → 36 control-flow branchesSo only the line anchor moves, and the code says why so the next reader doesn't re-derive it.
This is safe in Mode D specifically because it computes
sat_end_lineindependently (L5224)rather than as
start_line + loc - 1the way the brace and label modes do — the span extendsbackwards over the declaration instead of sliding a line earlier. A test asserts
locandend_linedo not move, because that is the property the whole change rests on.Scope, measured
Over the crucible (40 files/language, 28,619 named functions), functions whose
start_linesitsbelow their declaration: 19 → 6. All 13 shell cases fixed.
The 3,448 functions whose
start_linesits above the name are deliberately untouched — thoseare annotations and attributes (
@Override,@isTest,#[proc_macro_derive],[Experimental]),where the declaration genuinely does begin at the annotation. Filing on the raw 12.4% "name not on
start_line" figure would have invited someone to "fix" all of them.
The residual 6 (
dart2,groovy2,assembly1,scheme1) were each read by hand and areartifacts of the measuring heuristic matching an unrelated occurrence of the name above the
declaration — a
LoggerFactory.getLogger(EmailService)call, the wordunrealin a prose comment,a
(provide schemify-linklet)export list. None is the Mode D shape. Two of the groovy ones hint ata separate constructor-anchoring question in another mode; not chased here.
Verification
anchor does not move when the name is already on the opener line.
audit_check.pyclear;tree_sitter_accuracy_audit --ci --all30/30 OK; ruff and mypy at baseline.Golden masters re-blessed: 20 differences, 0 off-target, 0 topology movement.
5. Function Analysis—Start Lineis the only field that changed, 137 functionsDocumentation Exposure+avg_documentation/documentationroll-upsAll 17 per-file diffs are shell (including
fortran/wrf/cleanCMake.sh, a.shfile in afortran-labelled directory group — correctly attributed by extension, not by group). The
documentation cells are the line window now including the declaration, which is the intended effect
and the reason the issue called this a scoring input rather than a reporting nit.
Cross-repo
None. keyword-rosetta's shell shell uses the same-line brace form (
probe_globals() {), so nocorpus count moves — confirmed by running the gate rather than assumed, and no
rosetta:rebless-owedlabel is needed.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WfeHRWra1d6Z5uSReFajSq