Skip to content

fix(router): normalize Java named capture groups in route constraints to non-capturing - #2982

Closed
wheels-bot[bot] wants to merge 3 commits into
developfrom
fix/bot-2976-router-java-named-capture-groups-in-route-constrai
Closed

wheels-bot[bot] wants to merge 3 commits into
developfrom
fix/bot-2976-router-java-named-capture-groups-in-route-constrai

Conversation

@wheels-bot

@wheels-bot wheels-bot Bot commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The $nonCapturingConstraint() fix for #2976 landed on develop via #2989 (commit 1c9915e69, merged 2026-06-10) before this branch could merge. That PR's ReFind-based rewrite is strictly more complete than this branch's original Mid()-based implementation (it also fail-fast errors on \k<name> backreferences via $compileRegex), so the conflict-resolution merge took develop's version wholesale.

What remains on this branch is a single nested-named-group test case in vendor/wheels/tests/specs/mapperModernSpec.cfc (5 lines) that exercises the scanner resuming immediately after a consumed (?<name> opener and re-detecting the inner opener at the very next character. The duplicate vendor/wheels/tests/specs/mapper/NonCapturingConstraintSpec.cfc was folded into the canonical spec and deleted, since the other assertions were 1:1 duplicates of coverage already merged in #2989.

Related Issue

Closes #2976 (the underlying bug — fix landed via #2989)

Type of Change

  • Bug fix
  • New feature
  • Enhancement to existing feature
  • Documentation update
  • Refactoring

Feature Completeness Checklist

  • DCO sign-off -- Every commit carries Signed-off-by: (use git commit -s); see CONTRIBUTING.md
  • Tests -- Added the nested-named-group expectation to vendor/wheels/tests/specs/mapperModernSpec.cfc (the existing $nonCapturingConstraint coverage merged in fix(router): normalize Java named capture groups in route constraints #2989 lacked this specific case). Local: wheels.tests.specs.mapperModernSpec — 35/35 pass; full wheels.tests.specs.mapper directory — 94/94 pass on Lucee 7 + SQLite.
  • Framework Docs -- N/A (test-only diff)
  • AI Reference Docs -- N/A (test-only diff)
  • CLAUDE.md -- N/A (test-only diff)
  • CHANGELOG.md -- Fragment file changelog.d/router-named-capture-groups.fixed.md added per CLAUDE.md conventions (carried in from fix(router): normalize Java named capture groups in route constraints #2989's merge into this branch).
  • Test runner passes -- Mapper bundle: 35/35; mapper directory: 94/94 (Lucee 7 + SQLite).

Test Plan

  • The added expectation in mapperModernSpec.cfc covers the nested-groups case: (?<outer>(?<inner>\d{2})\-\d{2}) → (?:(?:\d{2})\-\d{2}).
  • Cross-engine coverage runs through CI's compat matrix once this PR is taken out of draft.

… to non-capturing

Extends $nonCapturingConstraint in vendor/wheels/Mapper.cfc to rewrite
`(?<name>...)` to `(?:...)` so a named capture group inside a route
constraint no longer shifts the position of every subsequent route
variable when $mergeRoutePattern extracts values by group position.

The previous fix (#2944) bounded the extraction loop so an extra group
could no longer crash param assembly, but a named group inside a
constraint was still position-shifting because the scanner skipped
every `(?...)` flavor without distinguishing the named-capturing form
from lookbehinds.

The scanner now disambiguates: `(?<=...)` / `(?<!...)` lookbehinds are
left untouched (non-capturing already), while `(?<name>...)` consumes
the `<name>` token and emits `(?:` in its place. Bare `(...)`, explicit
`(?:...)`, lookaheads, and parens inside character classes continue to
behave as before.

Covered by a new spec at
vendor/wheels/tests/specs/mapper/NonCapturingConstraintSpec.cfc that
asserts both the unit-level normalization and the $patternToRegex
integration shape, plus regression cases for the existing lookbehind /
lookahead / non-capturing / bare-capture paths.

Fixes #2976

Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
bpamiri added 2 commits June 10, 2026 14:03
…r-java-named-capture-groups-in-route-constrai

Signed-off-by: Peter Amiri <peter@alurium.com>

# Conflicts:
#	CHANGELOG.md
#	vendor/wheels/Mapper.cfc
…p superseded duplicate spec

PR #2989 (develop commit 1c9915e) already landed the named-capture-group
normalization for issue #2976 with reviewed tests in mapperModernSpec.cfc.
The only case in this branch's NonCapturingConstraintSpec.cfc not covered
there was nested named groups, which exercises the scanner resuming right
after the consumed `(?<name>` opener. Port that single expectation into
the canonical spec and delete the otherwise-duplicative spec file.

Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri

bpamiri commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Conflict resolution + supersession cleanup

This branch was fully superseded before it could merge: issue #2976 was fixed on develop by PR #2989 (commit 1c9915e69, merged 2026-06-10), which landed a reviewed ReFind-based named-group rewrite at the same site in vendor/wheels/Mapper.cfc::$nonCapturingConstraint, tests in vendor/wheels/tests/specs/mapperModernSpec.cfc, and the fragment changelog.d/router-named-capture-groups.fixed.md. The merged version also covers a case this branch missed (fail-fast Wheels.InvalidRegex when a constraint backreferences the named group via \k<name>). Issue #2976 is closed.

Conflicts resolved (merge commit 171b7bab)

  • vendor/wheels/Mapper.cfc — took develop's implementation wholesale. The two sides were competing implementations of the same fix (this branch: char-by-char Mid() probing; develop: ReFind("^\(\?<[A-Za-z][A-Za-z0-9]*>") rewrite). Develop's version went through two rounds of reviewer consensus on fix(router): normalize Java named capture groups in route constraints #2989 and is strictly more complete, so this branch's intent is fully preserved by it. Verified byte-identical to origin/develop after resolution.
  • CHANGELOG.md — took develop's side. This branch's direct [Unreleased] bullet is represented by develop's changelog.d/router-named-capture-groups.fixed.md fragment; keeping the direct edit would both duplicate the entry at release cut and reintroduce the conflict pattern the fragment system (feat(docs): changelog.d fragment system for conflict-free unreleased entries #2994) retired.

Residual diff (commit 39a589e6)

  • Deleted vendor/wheels/tests/specs/mapper/NonCapturingConstraintSpec.cfc — its assertions duplicate the merged coverage in mapperModernSpec.cfc (named-group normalization, lookbehind preservation, char-class literals, bare-capture regression, $patternToRegex integration) nearly 1:1.
  • Ported the single non-duplicative case into the canonical spec: nested named capture groups (vendor/wheels/tests/specs/mapperModernSpec.cfc:397-400), which exercises the scanner resuming immediately after a consumed (?<name> opener and re-detecting the inner opener at the very next character.

The PR's entire diff vs develop is now that one 5-line test addition.

Findings skipped

  • No reviewer feedback existed to implement (draft PR — no reviews, no inline comments, no threads).

Test evidence

Ran from this worktree via the prebuilt Lucee 7 docker image (dir-only mount), SQLite:

  • testBundles=wheels.tests.specs.mapperModernSpec — HTTP 200, 35 pass / 0 fail / 0 error (includes the new nested-group expectation)
  • directory=wheels.tests.specs.mapper — HTTP 200, 94 pass / 0 fail / 0 error

Cross-engine coverage is CI's gate (the draft status has so far kept the compat matrix from running).

Disposition note

Given the supersession, closing this PR as duplicate-of-#2989 remains a reasonable call; alternatively it can be taken ready and merged as the small test-only addition described above. Leaving that decision to the maintainer.

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR. The net diff is +5 lines of test code in mapperModernSpec.cfc — a single nested-named-group assertion that fills the last gap in the $nonCapturingConstraint unit coverage. The logic is correct, the placement is right, and there are no cross-engine concerns. Two documentation nits prevent an approve: the PR body still describes the full implementation (Mapper.cfc edits, the now-deleted NonCapturingConstraintSpec.cfc) as though it were being landed here, and the changelog checklist entry implies a direct [Unreleased] edit rather than the fragment approach that's actually in place. Neither is blocking; marking as comment.


Correctness

The new expectation is logically sound. Tracing $nonCapturingConstraint (Mapper.cfc:136–189) against the input (?<outer>(?<inner>\d{2})\-\d{2}):

  1. i=1 → (, next char is ?; ReFind("^\(\?<[A-Za-z][A-Za-z0-9]*>", ...) matches (?<outer> (9 chars); emits (?:, advances i += 8 → i=9.
  2. For-loop increments → i=10 → ( (start of (?<inner>); same branch fires; emits (?:, advances i += 8 → for-loop leaves i=19.
  3. Remainder \d{2})\-\d{2}) passes through unchanged.

Assembled result: (?:(?:\d{2})\-\d{2}) — matches the .toBe(...) argument exactly. ✓

The comment in the test is also accurate: "the scanner must resume right after the outer > and re-detect the inner opener at the very next character." After consuming (?<outer>, position 10 is the ( that opens (?<inner>.


Docs

1. PR body is stale (vendor/wheels/tests/specs/mapperModernSpec.cfc, net diff lines 397–400)

The summary describes the full implementation — edits to vendor/wheels/Mapper.cfc, creation of vendor/wheels/tests/specs/mapper/NonCapturingConstraintSpec.cfc with 10 assertions, a 4-of-10 pre/post fix count, and a test plan that refers to the NonCapturingConstraintSpec describe block — but none of that is in the actual net diff. The commit message for 39a589e correctly explains the situation ("PR #2989 (develop commit 1c9915e) already landed the named-capture-group normalization"), but the PR body was never updated to match. A reviewer reading only the PR description would expect Mapper.cfc changes that don't appear in the diff.

Suggested fix: replace the Summary and Test Plan sections with a short note that the implementation landed on develop via #2989 and this PR contributes the single missing nested-groups coverage case.

2. Changelog checklist wording (PR description, Feature Completeness Checklist)

- [x] **CHANGELOG.md** -- Entry added under `[Unreleased]` then Fixed

This implies a direct edit to the [Unreleased] section, which CLAUDE.md § "Changelog entries" explicitly forbids ("a direct CHANGELOG.md [Unreleased] edit is a finding — it recreates the same-anchor merge conflicts the fragment system removes"). The actual state is correct — changelog.d/router-named-capture-groups.fixed.md exists and contains the right entry — but the checklist description is misleading.

Suggested fix: update the line to [x] **CHANGELOG.md** -- Fragment file added under changelog.d/router-named-capture-groups.fixed.md per CLAUDE.md conventions.


Commits

Both commits are within the commitlint rules:

  • fix(router): normalize Java named capture groups in route constraints to non-capturing — 86 chars, type fix, DCO signed by claude[bot]. ✓
  • test(router): fold nested named-group case into mapperModernSpec, drop superseded duplicate spec — 96 chars, type test, DCO signed by Peter Amiri with matching identity. ✓

@wheels-bot

wheels-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's review is accurate and well-reasoned. The correctness trace checks out, the two doc nits are real, and COMMENT is a defensible verdict for a 5-line test-only diff whose only issues are in PR metadata. I'm aligned.

Sycophancy

None detected. A provided a step-by-step positional trace to justify the correctness verdict rather than approving on vibes.

False positives

None detected.

  • Correctness trace: A's walk through $nonCapturingConstraint is accurate. Input (?<outer>(?<inner>\d{2})\-\d{2}): after consuming (?<outer> (9 chars), emitting (?:, and advancing i by 8, the for-loop increment lands exactly on the ( that opens (?<inner>. The inner group is consumed the same way; the remainder passes through unchanged. Result (?:(?:\d{2})\-\d{2}) matches the .toBe() argument exactly.
  • Changelog fragment claim: A states changelog.d/router-named-capture-groups.fixed.md exists and is correct. Verified — the file is present on this branch.
  • Commit lint: both non-merge commits (c48cf2b, 39a589e) are within the 100-char header limit and use valid types. A skipped the bpamiri merge commit (171b7bab), which is correct — merge commits are exempt from commitlint.

Missed issues

None detected. This is test code only; no cross-engine concerns apply. No security surface. The PR title is 78 chars, under the 100-char gate.

Verdict alignment

COMMENT is consistent with the findings: two actionable doc nits (stale PR body, misleading checklist entry) with no code issues — neither rises to request-changes, but together they are enough to hold off an outright approve.

Convergence

Aligned. The code change is correct and complete; the two open items are both PR description updates:

  1. Replace the stale Summary/Test Plan sections with a short note reflecting that the implementation landed on develop via fix(router): normalize Java named capture groups in route constraints #2989 and this PR contributes only the nested-groups coverage case.
  2. Update the CHANGELOG.md checklist line from the direct-[Unreleased]-edit description to reference the fragment file (changelog.d/router-named-capture-groups.fixed.md) per CLAUDE.md conventions.

Joint recommendation: apply both PR body updates and approve.

@wheels-bot

wheels-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Address Review (round 1)

Applied the consensus findings from Reviewer A and Reviewer B's convergence (round 1 of the review loop):

  • PR body — stale Summary / Test Plan / Tests-checklist (Reviewer A finding 1; Reviewer B aligned). Replaced the old description (which still described the full Mapper.cfc implementation and the deleted vendor/wheels/tests/specs/mapper/NonCapturingConstraintSpec.cfc) with a short note explaining that the implementation landed on develop via #2989 and this branch's residual diff is the single nested-named-group case in vendor/wheels/tests/specs/mapperModernSpec.cfc:397-400. Updated the Tests checklist line to reference the canonical spec instead of the deleted file. Reframed the Test Plan section around the actual remaining change.
  • CHANGELOG.md checklist wording (Reviewer A finding 2; Reviewer B aligned). Changed the checklist line from Entry added under [Unreleased] then Fixed (which implied a direct edit forbidden by CLAUDE.md § "Changelog entries") to Fragment file changelog.d/router-named-capture-groups.fixed.md added per CLAUDE.md conventions. The fragment file itself was carried in from the develop merge and was already correct.

No code commit was produced — both consensus findings were PR-metadata edits only. The repo diff (5 test lines + 1 spec deletion) is unchanged from 39a589e6. The convergence loop's usual "fresh SHA → fresh Reviewer A" trigger does not apply here; Reviewer B's joint recommendation was explicitly apply both PR body updates and approve, and both updates are now in place.

Findings skipped

None — both converged findings were addressed.

@bpamiri

bpamiri commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Closing as superseded: the named-capture-group constraint fix for #2976 shipped to develop via #2989 (1c9915e) with strictly broader coverage (\k backref fail-fast). The one non-duplicative test from this branch (nested named groups) was salvaged and re-filed as #2996.

@bpamiri bpamiri closed this Jun 10, 2026
@bpamiri
bpamiri deleted the fix/bot-2976-router-java-named-capture-groups-in-route-constrai branch June 10, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Router: Java named capture groups in route constraints silently shift matched-value positions

1 participant