Skip to content

fix(groovy): func_start no longer mistakes MarkupBuilder/DSL calls for declarations - #2597

Merged
squid-protocol merged 3 commits into
mainfrom
fix/2530-groovy-func-start-dsl-builder-calls
Aug 31, 2026
Merged

fix(groovy): func_start no longer mistakes MarkupBuilder/DSL calls for declarations#2597
squid-protocol merged 3 commits into
mainfrom
fix/2530-groovy-func-start-dsl-builder-calls

Conversation

@squid-protocol

Copy link
Copy Markdown
Owner

Closes #2530.

Root cause

func_start's zero-prefix branch (needed to match real bare constructors,
MyClass(String arg) {) is syntactically indistinguishable from Groovy's
MarkupBuilder/NodeBuilder DSL idiom — a method call with a named-argument map plus
a trailing closure (div(class: "x") { ... }, button(name: "clear", type: "submit") { ... }). Pervasive in Jenkins's own .groovy view templates (which
replaced Jelly) and Jenkins Pipeline DSL steps.

Fix

A real Groovy declaration's parameter list can never contain a key: token (that
syntax is call-site named-argument sugar only), so branch 2 now rejects a
candidate whose parenthesized argument list contains an identifier immediately
followed by : with no intervening whitespace — distinguishing it from a
ternary's cond ? a : b, which conventionally has a space before the colon.

Scope (per the issue's own framing)

The issue explicitly asked to investigate "whether branch 2 can be tightened to
exclude the named-argument-map-first-parameter shape... without also rejecting
legitimate bare constructors" and to "re-run the recall audit against the full
crucible corpus." Did both:

  • Full corpus differential scan (galaxyscope --db-only, 201 files, before
    vs. after): 44 of the 57 misdetections in jenkins_view_groovy/ resolved, zero
    removals anywhere else
    in the corpus — no legitimate bare constructor lost
    recall, including edge cases I specifically probed for (a Map-literal default
    = [:], a ternary default value).
  • ReDoS-scaling probed the new lookahead (linear to n=16000, both an unclosed
    plain paren and a colon-heavy pathological input).
  • 13 residual FPs remain, explicitly out of scope: single-positional-arg DSL
    calls (stage('Build') { }, node('x') { }, dir(x) { }) have no key: token
    to key off of — the issue itself flagged this as needing "a curated
    builder-tag-name denylist" that's "fragile and incomplete by construction," so I
    didn't try to force it into this fix.

A second, unrelated defect this unmasked

Fixing the button(...) false positive in one file also unmasked a second,
independent func_start bug: raw _("Dismiss") (a paren-less call, Groovy's _()
i18n-helper convention) was already being misdetected by branch 1 (a completely
different mechanism — treating the bare identifier raw as a modifier/type
prefix), but was silently absorbed as "nested inside button" until button
stopped being wrongly detected as an enclosing function. Confirmed via git stash
that this raw-regex match pre-dates this PR. Filed separately as
#2558 rather than
bundled in here — different branch, different root cause, same reasoning the
original issue used to defer itself from #2532.

Docs / data updated

Verification

  • Reproduced the exact FP locally and confirmed the fix.
  • Added cases to tests/extraction/languages/test_groovy.py (invalid-match cases,
    plus a positive case for the Map-literal-default edge case) and
    test_groovy_strict.py (dedicated regression test covering the DSL-call
    rejections and the ternary/map-literal preservation).
  • test_groovy.py + test_groovy_strict.py: 150 passed.
  • tests/core_engine/test_language_standards_strict.py: 21 passed.
  • Full pytest tests/: 7154 passed, 2 skipped, 9 xfailed, 3 xpassed (unrelated
    pre-existing).
  • tests/tools/audit_check.py: clean (ruff/mypy/dead-key/ast-accuracy).
  • tests/tools/tree_sitter_accuracy_audit.py --all --ci: clean (groovy correctly
    stays excluded as gg_only, per fix(groovy): tri-comparison manual verification -- 3 real engine defects fixed #2532).
  • tests/tools/crucible_check.py (both full_precision and zero_dependency
    modes, full corpus): re-blessed after confirming every one of the 631 diffs
    traces to either the direct fix (43 net, isolated to jenkins_view_groovy/) or
    the standard topological/aggregate ripple class CLAUDE.md's Differential Scan
    protocol pre-approves (588) — zero unexplained/off-target diffs, verified via a
    full uncapped golden_diff.deep_compare() categorization, not just the
    pytest-truncated summary. Both modes PASS clean after re-bless.

🤖 Generated with Claude Code

squid-protocol and others added 3 commits August 31, 2026 13:15
…r declarations

Closes #2530. func_start's zero-prefix branch (needed to match real bare
constructors, `MyClass(String arg) {`) was syntactically indistinguishable
from Groovy's MarkupBuilder/NodeBuilder DSL idiom -- a method call with a
named-argument map plus a trailing closure (`div(class: "x") { ... }`,
`button(name: "clear", type: "submit") { ... }`). Pervasive in Jenkins's
own `.groovy` view templates and Jenkins Pipeline DSL steps.

Fix: a real Groovy declaration's parameter list can never contain a `key:`
token (that syntax is call-site named-argument sugar only), so branch 2
now rejects a candidate whose parenthesized argument list contains an
identifier immediately followed by `:` with no intervening whitespace --
distinguishing it from a ternary's `cond ? a : b`, which conventionally
has a space before the colon.

Verified via a full corpus differential scan (galaxyscope --db-only, 201
files): 44 of the 57 misdetections in jenkins_view_groovy/ resolved, zero
removals anywhere else in the corpus -- no legitimate bare constructor
lost recall, including edge cases like a Map-literal default (`= [:]`)
or a ternary default value. ReDoS-scaling probed (linear to n=16000).

13 residual FPs remain, out of scope per the issue: single-positional-arg
DSL calls (`stage('Build') { }`, `node('x') { }`, `dir(x) { }`) have no
`key:` token to key off of -- same class the issue explicitly deferred
(a curated builder-tag-name denylist is fragile/incomplete by
construction). Fixing the button(...) false positive also unmasked one
new, unrelated func_start defect in a different branch (a paren-less
call, `raw _("Dismiss")`, previously silently absorbed as "nested inside
button") -- filed separately as #2558 rather than bundled in here.

Golden masters re-blessed (both precision modes): 43 net diff (44 direct
removals - 1 new #2558 surfacing), rest is the standard topological/
aggregate ripple class already documented in CLAUDE.md's Differential
Scan protocol. Verified with a full uncapped diff categorization: zero
unexplained/off-target changes anywhere in the 201-file corpus.

Also updates docs/language_status/groovy.md, manual_verification.json,
and regenerates the tri-comparison chart/ledger to reflect the corrected
groovy function precision (661/718 -> 661/675, 92.1% -> 97.9%).

Verification: full groovy extraction gauntlet + strict suite (150
passed, new regression test added), cross-language strict registry (21
passed), full pytest suite (7154 passed), audit_check.py clean,
tree_sitter_accuracy_audit.py --all --ci clean (groovy correctly
excluded as gg_only per #2532), crucible_check.py both modes PASS after
re-bless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…start-dsl-builder-calls

# Conflicts:
#	docs/self_scan/tri_comparison_ledger.json
Resolves the merge conflict in tri_comparison_ledger.json from main's
own auto-update-tri-comparison-data bot commit (#2553) landing while
this branch's crucible verification was running. Took main's version
as the base and regenerated via tri_comparison_chart.py --all --write
on top of the merged code state, so the output reflects both main's
other-language refreshes and this branch's groovy fix together.
Re-verified clean: audit_check.py, full pytest suite (7154 passed),
crucible_check.py both modes PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol
squid-protocol merged commit 3f6449e into main Aug 31, 2026
30 checks passed
@squid-protocol
squid-protocol deleted the fix/2530-groovy-func-start-dsl-builder-calls branch August 31, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

groovy func_start: MarkupBuilder/Jenkins-DSL trailing-closure calls misdetected as zero-prefix method definitions

1 participant