Skip to content

0.7.0: nested-call wrap, record headers, parameter alignment, and layout decisions that stop reading layout - #50

Open
barrycaceres wants to merge 49 commits into
mainfrom
caceres-0.7.0
Open

0.7.0: nested-call wrap, record headers, parameter alignment, and layout decisions that stop reading layout#50
barrycaceres wants to merge 49 commits into
mainfrom
caceres-0.7.0

Conversation

@barrycaceres

Copy link
Copy Markdown
Contributor

Formatting release, promoted from 0.6.1 to 0.7.0 because it adds normative
rules to docs/java-coding-standards.md, removes two behaviours that document
described, and reformats 324 of the 504 files in the four-repo consumer trial
corpus. Adopters should bump the pin on its own commit.

Measured against 0.6.0 (504 files: senzing-commons-java, sz-sdk-java, sz-sdk-java-grpc, data-mart-replicator)

0.6.0 0.7.0
lines over 80 1618 1573
files reformatted 324 / 504
net lines +1,437 (+0.65%)
files needing a second formatting pass 26 6, all converging
files that never converge 1 0
structural AST changes 1 (an else-less Tier 1 brace collapse)
new parse errors 0
formatter advisories 282

725 tests pass on the pinned tree-sitter 0.26.0. That figure needs a consumer
checkout — without a corpus, test_fuzz_corpus.py skip-marks and the 210
skipped parametrisations are exactly the AST-equivalence and idempotency
checks. The new corpus-gate CI job supplies one.

The theme: layout decisions must not read layout

Nine defects in this release came from a predicate consulting how the source
happened to be written, which the formatter then rewrites — so the answer
changed on the next pass. That includes the headline change (retiring source
preservation's width-based fallback) and five off-by-one errors of one family:
a width measured over a span that included something the construct did not
write, or excluded something it did.

New normative rules

  • Nested-call wrap (rules 1–3): the two-line comma-packed shape is
    withdrawn wherever a call is embedded in another expression.
  • Argument-list priority 2b: break after ( with every argument on one
    continuation line. Renumbered from an earlier "3b" that documented it as
    running after priority 3 when the code runs it before.
  • "If an argument breaks, the argument list breaks" now holds at priority 3,
    not just 1 and 2.
  • Enhanced-for header wrapping: break before :, Allman brace.
  • Record headers run the cascade the spec already described.
  • Wrapped parameter lists generate the type/name column alignment the spec
    has always required but the formatter never produced.
  • Priority 3 is skipped when it would not gain room — decided by the column of
    the (, not the method name.

Behaviours removed

  • Source preservation's width-based fallback, and the array-creation preserve
    path. Preservation now fires only where reflow would corrupt (interleaved
    comments, CSOFF).
  • The 0.6.0 factory-chain tier. Two argument shapes 0.6.0 produced are now
    unreachable by design.

Review

Four review rounds. Rounds 3 and 4 each found a MUST-FIX regression introduced
by the round before, including one where a fix of mine was itself wrong in the
same way as the bug it fixed. Round 4 also overturned one of its own
clearances. Two fixes were rejected after measurement rather than reasoning:
suppressing an escalation for preserved arguments (it re-introduced
history-dependence and left a file oscillating), and balancing javadoc reflow
unconditionally (135 files of churn for no benefit).

Known open items, all pre-existing and tracked for 0.8

  • Inline block comments between arguments produce output that does not
    parse
    — the argument list is built from is_named children and tree-sitter
    exposes comments as named nodes. Byte-identical at 0.6.0.
  • A silent 81-column line that rewrites currently-compliant source.
  • Field access after a call is unreserved: int n = call(a, b).someField;
    emits 85 columns.
  • Receiver parameters (Foo this) are silently dropped.
  • Calls embedded in lambda bodies are not treated as embedded (~179 sites).

…dy max_width, multi-catch wrap

Three of the 0.6.1 scope items land here (E, A, D). The remaining
items (B+C+F+H idempotency spike; G Javadoc prose reflow) follow
in subsequent commits after their spike work is complete.

E — Class-body trailing side comment attaches inline:
`_emit_class_body_members` and `_emit_interface_body_members`
now call `_attach_trailing_side_comments` after each member
emission, matching the method-body iterators
(`_emit_indented_member_list`, `_emit_block`). A `//` comment on
the same source row as a field's `;` or method's `}` now stays
inline; pre-0.6.1 it moved to its own class-body-level line.
Locked by `comment_preservation/10`.

A — Single-arg lambda body excluded from P1 fit check:
`_emit_argument_list` single-arg cascade adds a `_is_block_body
_lambda` fast path that runs P1 with a fit check restricted to
the CALL LINE and CLOSING LINE. The lambda body's own line
widths are the body's responsibility. Pre-0.6.1 an over-80 line
inside a lambda body caused the entire outer arg-list to reject
P1 and cascade to P4, breaking `.method(() -> {` to `.method(\n
    () -> {` and pushing the body +4 cols deeper — often creating
NEW overflows. Consumer trial on sz-sdk-java-grpc showed 22
sites (all `this.performTest(() -> { ... })` calls) regressing
this way; all 22 recover the baseline idiomatic shape.
Locked by `arg_list_wrap/15`.

D — Multi-catch union type wrap engine:
`_emit_catch_clause` gains a two-tier cascade for `catch (A | B
| C e)` when there are 2+ alternative types. P1 emits the whole
header inline if it fits under 80 chars (including the `{` that
follows). P2 breaks each subsequent `| Type` to its own
continuation line at the column right after `catch (`, with the
parameter name + `) ` on the last type's line. Fires C1
`multi-catch` overflow advisory if a single type name is itself
too wide at the paren-aligned column. Consumer trial site:
`sz-sdk-java-grpc/WrapperMain.java:66` — a 125-char four-type
catch that previously emitted silently. Locked by
`multi_catch_wrap/01`,`02`.

698/698 pytest passing.
…gate

Bug-fix release. Five formatter defects surfaced by trial-formatting
0.6.0 across four consumer source bases, plus a dependency bump the
drift guard makes impossible for Dependabot to land alone, and two
CI/tooling corrections.

Nested-call wrap (three rules). A call embedded in another expression
— a positional argument of another call, or the receiver of a chain —
no longer uses the P2 two-line comma-packed shape:
  Rule 1: a sole method-invocation argument that cannot stay on one
    line breaks so it lands at the enclosing call's line start + 4.
  Rule 2: an embedded call's own arg list skips P2 (P1 -> P3 -> P4),
    regardless of the enclosing call's arity.
  Rule 3: chain segments following an embedded call go one per line,
    anchored at the chain's own start column + 4.
`object_creation_expression` counts as a call for these rules.

Two layouts are now deliberately unreachable: the enclosing call left
inline with the inner arg list paren-aligned, and the first chain
segment hung off the inner call's closing paren with the rest
dot-aligned. Both required ranking two candidate continuation columns
against each other, which is not stable across passes — the mechanism
behind the builder(...).records(-1).build() oscillation. Selection is
now a single monotone "did the nested call stay on one line" test.

Source preservation declines for the two shapes these rules own, since
their layout is formatter-determined and echoing source rows
re-anchored them to the current emit column. This also stops a
preserved arg list from suppressing the chain cascade's Q-CHAIN-4
backoff.

switch brace placement. `switch (value)` now keeps its opening brace
on the same line per the standards' "Switch Statements and
Expressions" section. The emitter wrote an unconditional newline, so
every switch got an Allman brace — 98 sites across the four trial
trees, with the correct form never produced. Checkstyle does not gate
brace placement on LITERAL_SWITCH, so it went unnoticed. The
multi-line-condition exception still applies.

Also fixed: class/interface body trailing side comments now attach
inline; the single-argument cascade no longer counts block-bodied
lambda body widths in its P1 fit check (22 performTest sites in
sz-sdk-java-grpc had been rewritten); multi-catch union types now wrap
instead of emitting a silent 125-char line.

tree-sitter 0.25.2 -> 0.26.0. Binding only; tree-sitter-java stays at
0.23.5. Output byte-identical across all four trial trees with
matching advisory logs. requirements.txt now documents that
GRAMMAR_VERSION must be bumped in the same commit, since Dependabot
can only edit the pin and its PRs therefore always arrive red.

Build/CI: dropped the redundant /tooling/scripts/tests pip ecosystem
from dependabot.yml (both entries covered the same dependency set via
`-r ../requirements.txt`, so every bump arrived as two identical PRs);
added a corpus-gate job so the fuzz AST-round-trip, idempotency and
perf gates actually run — they resolve their corpus to <consumer>/src
and had been skipping on every CI run.

Verification: 704/704 pytest, six new nested_call_wrap fixtures.
Trial-formatted senzing-commons-java, sz-sdk-java, sz-sdk-java-grpc
and data-mart-replicator (504 files): zero AST changes, so no
formatting decision alters program meaning. Corpus idempotency 25 ->
14 non-idempotent with no new regressions. Lines over 80 went 1598 ->
1592; the three files that gain one each are unsplittable string
literals pushed over by rule 1's extra indent level.
…line

Two more defects from the four-project trial, both pre-existing in
0.6.0 rather than introduced by the nested-call wrap.

Pack-all-or-nothing for method chains. Removed the 0.6.0 "P1F"
factory-chain tier, which packed receiver + factory + the FIRST
CHAIN SEGMENT onto line 1 when the receiver was a PascalCase
identifier and the chain had 3+ segments. The all-on-one-line shape
is only available when the WHOLE chain fits; once it does not, the
break belongs at the first chain continuation dot, not wherever 80
characters ran out. The tier also rendered the same idiom two ways
depending on whether segment 1 happened to fit — Files.walk(a).sorted()
got the packed shape while Files.walk(b) kept the correct one.
Chains now fall through to P2F. Removes emit_p1f_factory, its
Q-CHAIN-4 signal, and the now-dead _chain_receiver_is_factory.

The same-method greedy tier (sb.append(a).append(b)) is untouched;
that horizontal density is deliberate. Shape-C sites across the four
trial trees drop 73 -> 24, and all 24 remaining are same-method
chains.

Arguments that wrap get their own line. Neither P1 nor P2 rejected a
shape where an argument was packed onto the call line and then
wrapped internally: every emitted line stayed under the cap, so the
width check passed and a partial break committed — the layout the
standards' "Anti-pattern" section forbids. Both tiers now reject an
ordinary argument that had to wrap, leaving it room to render whole
one line down.

Arguments that inherently own multiple rows (block-bodied lambdas,
text blocks) are exempt, so performTest(() -> { ... }) keeps P1. The
exemption tests only structural properties of the node and never the
source layout — consulting the source makes the answer depend on
whether an earlier pass already wrapped the argument, which
oscillated arguments(Rectangle.class, Set.of(...), ...) between two
shapes on alternate passes.

Fixture 18 renamed from 18_p1f_factory_deep_dot to
18_factory_chain_breaks_at_first_dot to describe what it now locks.
Fixtures 05/06/07 in arg_list_wrap and 14/16 in method_chain_wrap
take the new shapes; all were reviewed and confirmed as
improvements.

Verification: 704/704 pytest. Across the 504-file trial: zero AST
changes, lines over 80 went 1598 -> 1592, corpus idempotency 25 ->
12 non-idempotent with no new regressions, shape C 73 -> 24, and
switch-Allman 98 -> 0.
…s' rule

Documents the behavior committed in 763f682. An argument too wide for
the space left on the call line must not be packed onto it and then
wrapped internally — that satisfies the 80-char limit while still
producing the partial-break anti-pattern, since the argument's
continuation column ends up set by where the line ran out rather than
by structure.

Covers the scope explicitly: it applies to any argument complex enough
to wrap (nested call, lambda, object creation, compound expression such
as a long concatenation), does not affect simple arguments — which
cannot wrap and so keep priority 2 two-line packing — and exempts
block-bodied lambdas and text blocks, for which spanning lines is
inherent rather than a wrap.
…gaps

Applies the unambiguous findings from the Opus code review of
e3d52e9..be1c04e. The one MUST-FIX and two judgment calls are held
back for a decision.

Rule 1 now covers object_creation_expression. CHANGELOG and the
_is_nested_or_chained_call docstring both already stated that
`new Foo(...)` counts as a call for these rules, but rule 1 and the
source-preserve decline tested only method_invocation — so a sole
`new Foo(...)` argument got rule 2 without rule 1 and landed in the
deep-column shape rule 1 exists to prevent.

Anonymous classes are carved out. `new Runnable() { ... }` parses as
object_creation_expression, so the above change initially broke the
idiomatic spec-C8 shape by pushing the whole body a level deeper.
New _is_anonymous_class helper; an anonymous class body owns its rows
the way a block-bodied lambda does, so it is excluded from rule 1,
from the source-preserve decline, and from _arg_owns_its_rows. Caught
by test_anonymous_class_as_call_argument.

Test gaps closed:
- TestGrammarVersionPins compared two files and never the environment,
  despite a docstring claiming otherwise — the review passed 704 tests
  with tree-sitter 0.25.2 installed against a 0.26.0 pin. New
  test_installed_versions_match_pins checks importlib.metadata.
- New switch_brace fixtures (statement, expression, multi-line
  condition). The 98-site brace change had only incidental coverage and
  the Allman branch had none. The multi-line case uses a binary
  condition because a wrapping call condition exposes pre-existing
  two-pass drift that also affects if/while under 0.6.0.
- 17 table-driven tests for _is_nested_or_chained_call and
  _is_anonymous_class, pinning the True cases and — equally important
  — the parent shapes the rules deliberately do not reach
  (parenthesized, cast, ternary, lambda, binary, field access, array
  access, statement-level).

Cleanups: corrected the docstring claiming a constructor cannot be a
chain receiver (it can — `new Foo(a).bar()` nests it as the object
field); deleted a superseded rule-3 comment block whose description
did not match the code; removed a duplicated named-argument list;
dropped a dead `if segments` guard; explained why p3_col takes a max;
and collapsed a duplicated advisory-and-return into a fall-through.

Docs: fixture-golden count corrected from two to nine plus the
renamed directory; before/after example made the same call; rule 3's
narrower scope stated in the definition rather than contradicted 90
lines later; the NOT-PRODUCED caption no longer blames paren-alignment
the document endorses elsewhere; three cspell words reworded rather
than whitelisted.

Verification: 725/725 on the pinned tree-sitter 0.26.0 (the suite now
refuses to run green on a stale binding). Release gates unchanged
against 0.6.0 across 504 files — over-80 1598 -> 1592, non-idempotent
25 -> 12 with no new regressions, shape C 73 -> 24, switch-Allman
98 -> 0, zero AST changes.
identifier-receiver dot-align

Closes the review's MUST-FIX by rolling in the deferred
source-preservation work, and applies the one judgment-call
SHOULD-FIX.

MUST-FIX — preservation bypassed the break rule. "If an argument
breaks, the argument list breaks" lives in the wrap engine's P1/P2
commit checks, but source preservation is consulted first and
short-circuits the cascade. An arg list authored in the
packed-then-wrapped shape was echoed straight back, so the formatter
emitted the exact layout the standards document publishes under "NOT
PRODUCED", and two semantically identical inputs formatted
differently depending only on how they were typed. The escape hatch
was a cast_expression argument, which _is_nested_or_chained_call
declines; parenthesized and ternary have the same hole. Fixed at the
class level rather than by enumerating parent shapes: preservation
now declines when any argument spans rows in source and is not one
that owns its rows. _arg_owns_its_rows is promoted to module level
for the purpose, keeping block-bodied lambdas, text blocks and
anonymous classes on the preservation path so
`execute(new Runnable() { ... })` stays idiomatic.

Preserved continuations are re-anchored. The path replayed author
columns literally, so re-indenting the enclosing statement left the
continuation aligned with nothing (67 sites in sz-sdk-java). Rows now
shift by the construct's own displacement, and the previously
separate "respect a deeper author indent" and "shift to target"
branches collapse into one delta: max(re-anchor, to-target). The
to-target term is a floor — an unfloored re-anchor drove
continuations to column 0 when the displacement was large and
negative, which the corpus idempotency gate caught. Idempotency holds
by construction: on a later pass the source column is the emit
column, so the re-anchor term is zero.

SHOULD-FIX 8 — the chain P2 suppression now also requires
`head is None`. It exists for chains rule 1 broke out, whose receiver
is itself a wrapping call; when the receiver is a bare identifier the
dot-aligned form is readable and compact, and suppressing it turned a
common test idiom into four lines for no benefit. Now matches the
`head is None` condition p3_col already used, so the tail anchor and
the tier gate key on the same shape.

Three fixtures lock the new behavior: a pre-wrapped cast argument
being re-wrapped, a continuation re-anchoring on dedent, and an
identifier-receiver chain keeping its dot-align.

Verification: 728/728 on the pinned tree-sitter 0.26.0. Across the
504-file trial versus 0.6.0 — over-80 1598 -> 1590, non-idempotent
25 -> 11 with no new regressions, shape C 73 -> 24, switch-Allman
98 -> 0, zero AST changes.
…ot fit

Deep-orphan fix. An argument list whose opener sits mid-line at a deep
column could emit an argument at a shallower anchor than the `(` it
belongs to — drift up to 49 columns — because the argument's own
cascade ran out of tiers and committed its block-relative C1 fallback,
whose anchor has nothing to do with that list.

The old code chose paren-alignment because the EARLY arguments fit,
then discovered a later one could not and orphaned it. That is why the
same construct rendered correctly at one argument and wrong at the
next:

    new SzInterestingEntity(100L,
                            1,
                            Arrays.asList(
                                "FLAG"),          <- correct
                            Arrays.asList(
        createSampleRecord("DS", "R")));          <- orphaned at col 12

Rule, per the 0.6.2 scope decision: if ANY argument cannot render at
the paren-align column without escaping to a shallower anchor, then
ALL arguments behave as if the first had not fit, and the list
escalates to P4 (break before the first argument, one per line at
line-start + 4):

    new SzInterestingEntity(
        100L,
        1,
        Arrays.asList("FLAG"),
        Arrays.asList(createSampleRecord("DS", "R")));

Testing every argument is the point — the decision is made once for the
whole list instead of being discovered partway through.

Measured across the 504-file trial, orphans fall 37 -> 12 (0.6.1 alone
had reached 21). The shapes this replaces were badly mixed, e.g.
assertEquals with a chain argument stranded at col 12 under a col-21
paren-align now renders every argument in one column with the chain
properly nested.

TRADE-OFF, flagged deliberately: lines over 80 move 1590 -> 1599. The
additions are concentrated in five files and are almost entirely one
repeated string literal that now sits four columns deeper because a
lambda body is correctly nested one level in — where it previously sat
at col 12 beneath a col-35 paren-align, which was the defect. Correct
nesting costs those columns; the literal needs a manual split and the
C1 advisory fires on it. This is checkstyle-visible and worth
confirming before release.

Remaining 12 orphans have a different root cause: 7 are chain segments
(.addRecord(, .getEntityByRecordId(, .endsWith(), where the CHAIN
should back off to a shallower tier via Q-CHAIN-4 rather than the
argument list escalating. Tracked separately.

Verification: 729/729 on the pinned tree-sitter 0.26.0, zero AST
changes across 504 files, non-idempotent 25 -> 11, no new
non-idempotent files, no fixture churn beyond the new lock.
Second half of the deep-orphan work. A chain RHS emitted inline can
push its tail to a column where the tail's own arguments do not fit;
the argument list then reaches its block-relative last-resort tier,
whose anchor bears no relation to the `(` it belongs to:

    String nativeResult = engine.getNativeApi()
                                .getEntityByRecordId(
                    dataSourceCode, recordID);

Step 3 of the declarator cascade could not detect this: every line
above is under 80, so its overflow test passes and it commits. The
signal is not width.

New `Emitter._anchor_escaped`, set at the one place the escape happens
— the argument-list paren-defer candidate's block-relative branch —
and read by the declarator to treat the inline shape as failed and
back off. Added to __slots__ and to snapshot()/restore() so
speculative emits unwind it, matching `_arg_list_p4_fired`.

Geometry was tried first and rejected: the legitimate chain-P3 ladder
(segments at block+4) also places rows left of the value column, so a
column test cannot tell a clean ladder from a real orphan. It
false-positived on method_chain_wrap/02, 03, 06, 07 and 11. An
explicit flag at the escape site has no such ambiguity, and all five
of those fixtures pass unchanged.

A narrower first attempt -- preferring break-at-`=` for any wrapping
multi-segment chain -- was also rejected: it cost a line on every
wrapped chain that orphaned nothing, which is most of them. The
backoff is a remedy, not a preference.

SCOPE NOTE: this changes zero files across the 504-file trial today,
because the five real chain sites are blocked upstream by a different
mechanism -- their orphan is ALREADY IN THE SOURCE (e.g.
SzCoreEngineReadTest.java:110-111) and source preservation echoes it
back verbatim. Those are self-perpetuating orphans written by an
earlier release. Declining preservation for an orphaned layout is the
missing piece; this commit is what produces the correct shape once
that lands.

Verification: 730/730 on the pinned tree-sitter 0.26.0. Trial gates
unchanged -- orphans 37 -> 12, non-idempotent 25 -> 11, zero AST
changes, no new non-idempotent files.
…or wrap

Three changes that only work as a set. Retiring the preservation
fallback alone was NOT a clean win; the other two make it one.

1. Source preservation's width-based fallback is retired. Preservation
   keeps its two correctness triggers (interleaved comments, CSOFF) and
   drops the third: "the source spans rows and its first line fits, so
   echo the author's layout". That fallback is a propagation channel
   for the formatter's own past output -- the deep orphan at
   SzCoreEngineReadTest.java:110-111 survived every pass because it IS
   in the source and the gate re-emitted it. It also made layout
   history-dependent: two semantically identical files formatted
   differently according to how they were typed.

2. New P4-packed tier: break after `(` and put ALL arguments on one
   continuation line at line_start + 4. This is the
   zero-args-on-the-call-line member of the greedy family, whose rule
   is two lines maximum -- zero or more arguments on the call line, all
   remaining on ONE continuation line, else fall back to one per line
   paren-aligned. Sits inside the same rule-2 guard as P2, because it
   is a greedy tier and rule 2 withdraws greedy shapes from embedded
   calls; outside the guard it re-introduced the packed-inside-an-
   enclosing-construct shape rule 2 removed.

3. Enhanced-for headers now wrap: primary break BEFORE the `:`, colon
   leading the continuation line, Allman brace when the header breaks
   -- the same Multi-line Conditions exception that governs if/while/
   switch. Previously written straight out with no cascade, so 25 sites
   simply overflowed.

Why the set matters: (1) alone cost +660 lines, because the author's
packed block+4 shape is not in the formatter's vocabulary and every
such site expanded to one argument per line. (2) supplies that shape,
turning +660 into -168. (1) also EXPOSED the gap (3) fixes --
preservation had been wrapping long enhanced-for headers by accident.

Measured across the 504-file trial vs the prior tip (f0ba014):

  lines over 80        1599 -> 1578
  enhanced-for over 80    25 -> 0
  non-idempotent files    11 -> 8
  deep orphans            12 -> 3
  AST changes              0 -> 0
  files changed          265, net -168 lines

Three files gain one over-80 line each; all are literal-dense demo or
flag files already carrying hundreds.

732/732 pytest on the pinned tree-sitter 0.26.0. One existing golden
updated -- explicit_constructor_invocation/01 now takes the formatter's
own P2 cascade rather than the echoed author layout, which is the point
of change (1); its tail-reserve purpose is still exercised.
…ANGELOG

Documentation and labelling for the three changes folded in from the
preservation spike. No behavior change.

Standards document:
- New "Priority 3b: Next-line, all arguments on one line" between
  priorities 3 and 4, stating the greedy family's two-line-maximum
  rule explicitly (zero or more arguments on the call line, all
  remaining on ONE continuation line, else one per line
  paren-aligned) and noting it is skipped for embedded calls like
  priority 2.
- New "Enhanced-`for` header wrapping" section: break before the `:`
  with the colon leading the continuation, Allman brace when the
  header breaks, cross-referencing the Multi-Line Conditions rule it
  follows. Includes the fits-on-one-line counter-example.

CHANGELOG: the three separate "Source preservation" sections are
consolidated into one covering all three narrowings (fallback
retired, continuations re-anchored, yields to the nested-call rules).
Verification figures updated to the folded-in totals: 732/732,
over-80 1598 -> 1578, non-idempotent 25 -> 8, orphans 37 -> 3, 265
files reformatted for a net -168 lines, plus a note that retiring the
fallback alone would have cost +660 and priority 3b is what turns
that into a reduction.

Version labels normalised: nine `0.6.2` markers in format_java.py
became `0.6.1`, and `.claude/062_SCOPE.md` is renamed
`061_REMAINING_SCOPE.md`, since all of this ships as 0.6.1.

Gates: 732/732 on the pinned tree-sitter 0.26.0; CI corpus-gate
simulation 218 passed; prettier, cspell and markdownlint clean across
every added range.
…ndent

Promotes the release from 0.6.1 to 0.7.0. It adds normative rules to the
standards document and removes two behaviors that document described, so
the minor bump is the honest version: 322 of the 504 trial-corpus files
are reformatted.

Record headers wrap. They had no cascade at all, so an over-long header
simply stayed over-long. The spec's "Record Headers" priorities are now
implemented as written: `implements` moves to its own line first, and the
components only break if they still do not fit. Components run the shared
parameter cascade with `force_wrap=True`, which also retires source
preservation for them -- preservation was replaying an author's packed
layout as an 88-column row. A component list that wraps itself and leaves
`implements` trailing its closing paren is now rejected explicitly; width
alone accepted it because every row was under the limit.

Wrapped parameter lists align their names. The spec has always required
names at the first 4-space tab stop past the longest type, and the
cascade never generated it -- every aligned list in the corpus was
author-written and source-preserved, leaving the corpus split roughly
evenly between aligned and single-spaced lists. Single parameters are
never padded (no column to form) and lists with varargs or receiver
parameters are not padded (their prefix is not a bare type). Both
carve-outs are documented.

Indented javadoc lines are structural, not prose. This fixes the last
non-converging construct in the corpus: paragraph runs split at non-prose
lines, and reflow then erased the indent that did the splitting, so each
pass regrouped and reflowed differently. Javadoc prose reflow also now
balances rather than packing greedily when greedy strands three words or
fewer, sharing one helper with `//` reflow; scoped to two-line paragraphs
because the soft-target rebuild misbehaves at three or more.

Round-2 review fixes. The argument-list escalation scanned one row too
many -- `line_count` excludes the in-progress line, so for argument 0 the
scan began on the call line, whose indent is always left of the
continuation column, making every wrapping first argument report a false
escape and skip priority 3. The wrapped enhanced-`for` path reserved
nothing for its own closing paren and landed it in column 81, silently
and idempotently. The variable declarator no longer clears an enclosing
construct's `_anchor_escaped` on its way out.

Dead code removed. Retiring the source-preservation width fallback left
every remaining branch in `_arg_list_takes_source_preserve_path`
returning the same False as its fallthrough -- around 155 lines that read
as live policy, one of them walking the AST 2,113 times across 250 files
to compute an unobservable answer. Those branches and the helpers that
served only them are gone; output is byte-identical across all 504 files.
The reasoning is preserved in a new `source-preservation-history` FAQ.

Priority 3b is renumbered 2b and moved in the document to where it runs:
after priority 2, before priority 3.

Measured against 0.6.0 over 504 files: lines over 80 1618 -> 1581, files
needing a second pass to settle 26 -> 6 with none failing to converge,
deep orphans 37 -> 3, over-long enhanced-`for` headers 25 -> 0, one
structural AST change (an else-less Tier 1 brace collapse), no new parse
errors. 722 tests pass on the pinned tree-sitter 0.26.0.
…d code

Round-3 code review plus edge-case probing found two ways the new
parameter alignment could make output worse, and two places the
formatter declined to act without saying so.

Alignment no longer overflows the terminal candidate. Padding a short
type out to a long type's column carries the short parameter's full
name with it, so a compliant 50-column line became 82. Priority 3 has
no tier below it, so that committed. The aligned and unaligned forms
are now compared, and alignment is given up only when doing so actually
buys width back. Comparing them matters: testing the aligned form
against the cap alone consulted `last_lines_max_width`, which starts at
the row already open when the parameters began — the signature row —
and that stripped alignment from three files whose parameter rows were
nowhere near the limit purely because the method name above them
reached column 80.

Double-indenting only when it gains room. Priority 3 breaks after the
`(` to escape a paren column pushed right by a long return type and
name. When the paren already sits at or left of the double-indent
column, that break moves every parameter further right, so priority 2
is kept instead. The test is the column of the `(`, not the length of
the method name, so return type, modifiers and type parameters all
count. No corpus file changes; the shape needs a very short signature
beside unusually long parameters.

Two silent exits now report. A parameter list that cannot fit with
every parameter on its own line at the deepest indent had no advisory
at all, and a javadoc line preserved as structural had none either.
Both are spec C1 emit-and-warn sites missing their warn half, and the
failure mode was a developer hitting a checkstyle LineLength failure,
running the formatter, and getting neither a change nor an explanation.
Both respect checkstyle's own LineLength ignorePattern, without which
`@see <a href=...>` javadoc alone produced 123 unactionable advisories;
with it the release adds 17, each naming a line that genuinely fails
the build.

Dead code the previous commit left behind: `_arg_list_takes_source_
preserve_path` still carried a `column` parameter, a `col` local and a
docstring contract describing the removed width triggers, none of which
survived the retirement. Parameter and docstring removed, both callers
updated. Also dropped the unused `re` import, the orphaned
`_ESTIMATE_VERBATIM_NODE_TYPES` constant, and an unreachable branch in
the javadoc paragraph scan that the structural-indent change subsumed.
A record with no `implements` clause no longer re-emits its components
to no effect.

Corrections to this release's own documentation: the standards document
claimed receiver parameters are emitted unpadded when they are in fact
silently dropped (pre-existing, now recorded where a reader will find
it); the cited spec section name was wrong; four CHANGELOG figures were
wrong, including a subtraction and a 6-versus-7 mismatch; and a claim
to have fixed "the last non-idempotent construct" conflated never
settling with needing a second pass. Three constructs still need a
second pass and all converge on it; what this release removes is output
that never settles.

Corpus unchanged by every fix in this commit except where stated: 1581
lines over 80, 6 files needing a second pass, all converging. 723 tests
pass on the pinned tree-sitter 0.26.0.
The rule was enforced at priorities 1 and 2 but not priority 3, so a
paren-aligned argument that wrapped internally put its own continuation
at the column its SIBLINGS use, and the continuation read as another
argument:

    multilineFormat(rr.getFormat()
                    + " record not as expected:",
                    "RECORDS TEXT: ",

Breaking the whole list moves the arguments to their own column and
leaves the continuation clearly subordinate. Arguments that inherently
own rows -- block-bodied lambdas, text blocks, anonymous classes -- stay
exempt, as everywhere else this rule is applied. The round-2 off-by-one
fix to the escape scan is what made priority 3 reachable for wrapping
first arguments, which is why the gap became visible now.

Also fixes a latent off-by-one the change exposes: emit_p4_multi_arg
appends a comma or closing paren after every argument and reserved for
neither, so an argument measuring itself as exactly 80 wide committed and
the separator landed in column 81 -- invisible to the argument, which
fit, and to the loop, which had already committed.

That reserve drops the INHERITED tail reserve for every argument but the
last. It stands for characters the parent appends after the whole
construct (an enclosing statement's semicolon), which land on the final
line only; carrying it onto a middle argument's row makes the budget one
char too tight and splits an 80-column argument that is legal. This is
the fourth off-by-one of the same family found in this release.

Measured on the 504-file corpus against the previous commit: lines over
80 unchanged at 1581, total lines +660 (+0.30%), 121 files change shape,
files needing a second pass unchanged at 6 with all converging,
advisories 291 -> 290. Total files reformatted against 0.6.0 moves only
322 -> 324, because those 121 files were already in the reformat set.
Spec text for the rule at priority 3, a CHANGELOG entry for it and for
the argument-separator reserve, and the seven fixture goldens the change
moves. Five of the seven are improvements -- a binary, chain or nested
call that was split at the paren-aligned column comes back whole; two
cost a line moving from paren-aligned to block+4, which is the shape the
rule exists to produce.

Verification figures updated for the combined release: 324 of 504 trial
files reformatted, net +1,491 lines against 0.6.0.
…reserve

Round 4 found two ways the new priority-3 escalation fires on rows the
argument list did not put there, and corrected its own earlier clearance
of the second.

Both priority-3 escape signals now share the same exemptions. A row left
of the continuation column is evidence of an escape only when this
argument list's cascade chose that anchor; a text block's content starts
where the author wrote it, often column 0, and an argument replaying
source rows carries its own columns. `p3_arg_escaped` tested every row
regardless, pushing 8 corpus lines over 80.

The array-creation source-preserve path is retired. It replayed a
multi-row `new Type[] { ... }` verbatim with no re-anchoring, so when the
escalation moved the argument's first row to block+4 the preserved
continuation stayed where the author left it, leaving the two halves of
one argument 24 columns apart as a stable fixed point.

Suppressing the escalation for preserved arguments was implemented first
and then rejected: it made the choice of shape depend on whether the
array happened to be written across rows, which is exactly the
history-dependence this release exists to remove, and it left
AbstractSchedulingServiceTest.java oscillating between two shapes.
Retiring the path makes array layout a function of the AST. Verified
layout-independent: the same call written flat and pre-split now produces
identical output.

A new `Emitter._raw_rows_emitted` flag, set inside `write_raw_lines` and
read save-reset-check-restore like `_arg_list_p4_fired`, lets a wrap
decision distinguish "this construct wrapped" from "this construct's rows
came out of the source" -- indistinguishable in `line_count` but opposite
in meaning. It still guards the four preserve channels that remain
reachable from argument emission: switch rules, formal parameters, and
the argument-list comment and CSOFF cases. Correct for comments and CSOFF,
which are content rather than layout; a stopgap for the other two.

Measured on the 504-file corpus: lines over 80 1581 -> 1573, total lines
-54, advisories 290 -> 282, files needing a second pass back to 6 from
the 7 the rejected stopgap caused, 324 of 504 files reformatted against
0.6.0, one structural AST change (an else-less Tier 1 brace collapse),
zero parse errors. 723 tests pass on the pinned tree-sitter 0.26.0, with
no fixture goldens moved.
S1 — the argument-separator reserve had no test. Replacing its
`(tail_reserve + 1) if is_last else 1` with the uniform `tail_reserve + 1`
used at every other reserve site passed all 723 tests, so a maintainer
normalising it to the surrounding idiom would have seen green while
costing 26 corpus lines. Two fixtures added, and the first was checked to
actually discriminate: `19_middle_argument_keeps_full_budget` fails under
the uniform reserve, and `20_wrapped_argument_escalates_past_paren_align`
locks the priority-3 escalation. A first attempt at the guard did NOT
discriminate — its argument measured 77 columns, inside both budgets —
which is why the discriminating width was found from a real corpus site
rather than assumed.

S2 — five fixture names contradicted their regenerated goldens, worst
being `18_arg0_wraps_but_paren_aligned_still_fits`, whose golden now
shows paren-alignment deliberately not used. Renamed to describe what
they lock.

S3 — the spec justified the priority-3 rule as the continuation landing
at its siblings' column, which is true of a binary concatenation and
false of a nested call, whose continuation sits at its own paren column
and is never ambiguous, yet still breaks the list. The rationale now
states the real reason, uniformity, and "every priority" is qualified to
exclude priority 4, the terminal fallback where wrapping is permitted of
necessity.

S4 — the spec's headline example did not reproduce: as written it
formats to priority 2, producing neither documented shape. Replaced with
the shape and depth that actually reach priority 3, with a note that the
depth is what gets it there.

S5 — completed a truncated CHANGELOG heading.

S6, N1 — the CHANGELOG implied the change usually saves lines while
illustrating it with a shrinking example. Of 126 shape-changed files 94
grow, 15 shrink, 17 hold; and 124 of the 126, not all of them, were
already in the reformat set.

N2, N3 — reworded a comment that stated its worked example on two
different bases, and corrected a comment block that still presented
fixture 18's paren-aligned shape as the outcome the escape-scan fix
produces. The argument-breaks rule now escalates that case regardless;
what the fix still buys is that the escape signal means what it says.

N4 — recorded that the pytest figure needs a consumer checkout: the fuzz
corpus skip-marks without one, and the 210 skipped parametrisations are
precisely the AST-equivalence and idempotency checks.

N5 — deleted the dead `any_multiline_arg`, a source-dependent
computation with no readers.

Corpus output byte-identical to the measured build; 725 tests pass.
…e venv

The spellcheck workflow runs on every PR to main and would have failed:
ten words introduced by this release's commits were not in the dictionary
(`illegit` in eleven places, plus `neighbours`, `idempotently`,
`unfloored`, `dedented`, `ungated`). Fixed by rewording the prose and
renaming `p1_illegit_wrap` -> `p1_invalid_wrap` and its siblings, per the
repo's rule against whitelisting invented words. Only `venv` was added to
the dictionary, a real tool name sitting beside the existing `virtualenv`
entry. Verified behaviour-neutral: corpus output is byte-identical across
all 504 trial files and the suite is unchanged.

The consumer-trial checklist's idempotency gate demanded `0 modified` on
the second formatting pass, which this release does not satisfy and no
recent release has. It conflated two different outcomes. The gate is now
convergence to a fixed point: a small tracked set of files changing on
the second pass and then settling is tolerated and reported, while a file
that never settles, or oscillates, is blocking. That is the distinction
this release actually turns on -- 26 files needed a second pass under
0.6.0 and 6 do now, with none failing to converge.

New FAQ `building/formatter-python-environment` covers the pinned
tree-sitter environment and the three-way check between
`requirements.txt`, `GRAMMAR_VERSION` and the installed distribution --
the trap being that a stale interpreter runs the suite green while the
pinned grammar goes untested.
@barrycaceres
barrycaceres requested review from a team as code owners August 14, 2026 19:01
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITHUB_ACTIONS_ZIZMOR Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
JSCPD Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅
YAML_PRETTIER Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@github-actions

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 2

Code Review — Part 1 of 2

Reviewed the tooling/docs changes for the 0.7.0 formatter release (CHANGELOG, standards docs, CI, and tooling/scripts/format_java.py). This repo's "product" is a Python-based Java formatter, so most of the generic Java checklist items don't directly apply — I've evaluated the equivalent Python/docs concerns instead.

Code Quality

  • ❌ Code follows standard style conventions — Mostly yes, but there's a documentation/implementation mismatch. CHANGELOG.md:452 states: "Both advisories respect checkstyle's own LineLength ignorePattern, so a line the build will not reject does not generate noise... every one names a line that genuinely fails the build." This refers to two advisory sites: (1) the javadoc structural-line advisory and (2) the parameter-list-can't-fit advisory. Checking the actual code: _line_length_exempt() (tooling/scripts/format_java.py:3318) is defined and called exactly once, at format_java.py:3843, inside _emit_javadoc_block. The parameter-list advisory paths — format_java.py:7736 (P3-skipped/terminal-P2 case) and format_java.py:7772 (emit_p3_and_warn, the P3 terminal case) — call _fire_wrap_overflow_advisory directly with no _line_length_exempt guard, and _fire_wrap_overflow_advisory itself (format_java.py:791) has no such check internally either. So the parameter-list advisory will fire even on a line checkstyle's ignorePattern would exempt (e.g. a parameter type carrying a URL/@see-style token), contradicting the documented guarantee and the "every one names a line that genuinely fails the build" claim. Either the exemption check needs to be applied at the two parameter-list call sites too, or the changelog text needs correcting to describe only the javadoc case.

  • ✅ No commented-out code — none found; several long code comments are explanatory rationale, not dead code.

  • ✅ Meaningful variable/function names — names like _arg_owns_its_rows, _is_nested_or_chained_call, p3_arg_escaped are precise and self-documenting.

  • ✅ DRY principle — good consolidation work here: _balanced_reflow_words now shared between //-comment and javadoc reflow (format_java.py ~3415-3520); _emit_formal_parameter_prefix shared between measurement and emission (avoids the two disagreeing, per its own docstring). The large deletion of _arg_list_single_line_estimate/_estimate_normalize/_SEMANTIC_WRAP_ARG_TYPES/_arg_list_has_semantic_multi_row_arg (dead code that had become unreachable) is a good cleanup, not a regression.

  • Defects found:

    • See the LineLength ignorePattern mismatch above (moderate — noisy/incorrect advisories, not a crash).
    • Verify, not confirmed as a bug: emit_p4_packed (new priority 2b tier, format_java.py:9190) does not locally reserve a character for its trailing ) the way emit_p4_multi_arg (format_java.py:9123) now does for its trailing ,/) (the fix described in the CHANGELOG's "Argument separators are now reserved for" section). In practice this looks safe because the caller checks last_lines_max_width(...) <= effective_max on the actual committed output before accepting priority 2b and falls back to P3 otherwise — but it's worth double-checking there's no case where a nested argument's own internal wrap decision (made under the stale, un-reserved budget) produces a subtly wrong internal line break that still measures under the cap after the ) lands, rather than a clean reject/retry. Given this release fixed four other instances of exactly this off-by-one family, this tier is the one place that pattern wasn't obviously applied — worth a second look or an explicit test case (e.g. an argument in the priority-2b tier that renders at exactly column 79).
  • ✅ CLAUDE.md review.claude/070_REMAINING_SCOPE.md is new, but it's a retrospective decision log (design record), not a CLAUDE.md-style project-memory config, so the "must be generic, not locally-specific" rule doesn't apply. No .claude/CLAUDE.md appears in this diff chunk.

Testing

  • ⏳ Deferred to part 2. The CHANGELOG's "Verification" section (CHANGELOG.md, ~line 830+) references 18 updated golden fixtures and new fixtures for nested-call wrap, first-argument-wraps, and the Boolean/chain oscillation regressions, but no test files are visible in this diff chunk. Will confirm coverage once part 2 arrives.

Documentation

  • ✅ CHANGELOG.md updated — extensive, clear 0.7.0 entry with before/after examples; format matches Keep a Changelog conventions and the existing file's style.
  • ✅ New FAQ docs are well-structureddocs/faqs/building/formatter-python-environment.md and docs/faqs/building/source-preservation-history.md are clear, CommonMark-valid (fenced code blocks properly closed, consistent heading levels), and cross-link related docs appropriately.
  • ✅ Standards doc updated in lockstep with behaviordocs/java-coding-standards.md additions (enhanced-for wrapping, priority 2b, nested-call wrap, parameter alignment carve-outs) correspond 1:1 with the code changes described.
  • Minor: docs/faqs/building/java-formatting-standards.md correctly adds a "Superseded in 0.7.0" callout above the now-obsolete 0.4.3 bullets rather than deleting history — good practice, no issue.
  • No obvious CommonMark violations (trailing whitespace, malformed lists, unclosed fences) spotted in the diff hunks shown.

Security

  • ✅ No hardcoded credentials.
  • ✅ No .lic files or AQAAAD-prefixed strings in this diff chunk.
  • Minor/NIT: .github/workflows/pytest.yaml's new corpus-gate job pins actions/checkout@v7.0.0 and actions/setup-python@v6.2.0 by version tag rather than commit SHA. Tags are mutable in principle (supply-chain hardening best practice favors SHA pinning), though this is a common and generally accepted pattern for actions/* from GitHub itself. Good practices already present: persist-credentials: false on both checkouts, explicit permissions: contents: read, and pinning the content corpus (senzing-commons-java) to a release tag rather than main — all correctly reasoned in the accompanying comments.

I'll fold in a review of Part 2 once it arrives to complete the full picture (particularly test coverage, which is currently the biggest open checklist item).


Review Part 2 of 2

Code Review — Part 2 of 2

(Note: only Part 2's diff was provided to me; I cross-checked the claims below against the actual repository state at HEAD, which reflects the full merged change, to verify things the diff alone couldn't confirm.)

Code Quality

  • Style/naming: consistent with surrounding code (chain_is_sole_arg, inline_orphan, prev_escaped are all self-explanatory).
  • No commented-out code: the large emit_p1f_factory deletion is a real removal with an explanatory comment left behind, not dead code disabled in place.
  • Verified no dangling references to the removed 0.6.0 "P1F" tier — grep -n "p1f_segment_wrapped\|emit_p1f_factory\|_chain_receiver_is_factory" returns nothing outside the explanatory comments (format_java.py:9718, 10236, 10246).
  • _anchor_escaped mechanism (format_java.py:10695-10769, _emit_variable_declarator): I initially suspected the backtrack path might leak a stale escape flag from an abandoned inline attempt into the committed break-at-= shape. Checked EmitterState.snapshot()/restore() (lines 495-572) — _anchor_escaped is part of the snapshot tuple, so emitter.restore(saved) at line 10765 correctly resets it to the pre-attempt value before the backtrack re-emission runs. No bug; the design is sound and the comments accurately describe it.
  • Test-deletion sanity check: TestEstimateNormalize/TestArgListSingleLineEstimate (195 lines removed from test_format_java.py) looked like a possible silent coverage regression. Confirmed _estimate_normalize and _arg_list_single_line_estimate no longer exist anywhere in format_java.py (0 grep hits) — the functions were genuinely deleted, so removing their tests is correct, not a regression.

Testing

  • ✅ New predicates _is_nested_or_chained_call / _is_anonymous_class get explicit tests, including the False cases the docstring calls out as important (cast, ternary, lambda body, field access, array index — test_format_java.py:~4060-4110).
  • test_installed_versions_match_pins (test_format_java.py) closes a real gap the docstring documents (704 tests passed with 0.25.2 installed against a 0.26.0 pin) — good defense-in-depth test.
  • ✅ Large batch of new/renamed fixtures (arg_list_wrap, nested_call_wrap, method_chain_wrap, record_header_wrap, multi_catch_wrap, switch_brace, enhanced_for_wrap, source_preserve_reanchor) covers the new chain_is_sole_arg / nested-call rules with both positive and negative shapes (e.g. 07_identifier_receiver_chain_keeps_dot_align correctly locks in the case the gating logic is not supposed to affect).
  • ⚠️ Could not run the suitepython3 -m pytest was blocked by sandbox approval in this session, so I can't confirm coverage % or that all new tests actually pass; verification here is static (code reading) only.

Documentation

  • requirements.txt: comment correction is accurate (tree-sitter's own Requires-Python >=3.10 declaration vs. the looser "0.25.x dropped 3.9" framing), and the new note tying the pin to GRAMMAR_VERSION + TestGrammarVersionPins is a useful, non-obvious cross-reference for future Dependabot bumps.
  • GRAMMAR_VERSION dict (format_java.py:113) matches the requirements.txt pins (tree-sitter==0.26.0, tree-sitter-java==0.23.5) — no drift.
  • CHANGELOG.md has a dated [0.7.0] - 2026-08-14 entry matching today's date, describing the P1F removal and nested-call rules consistent with this diff.
  • ✅ Inline comments throughout (P1F removal rationale, chain_is_sole_arg gating, _anchor_escaped) explain non-obvious why, in line with this project's own commenting standard.

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings anywhere in this diff.

Summary: No defects found in this half of the diff. The two areas that looked most likely to hide bugs (test deletion, and the snapshot/restore handling around the new orphan-detection flag) both check out correctly against the current codebase.



Automated code review analyzing defects and coding standards

@github-actions

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 2

I reviewed Part 1 of the diff (docs, CI/config changes, and the bulk of format_java.py). Summary against the checklist:

Code Quality ✅ mostly — the refactor is unusually well-documented (rationale comments throughout), DRY is respected (_balanced_reflow_words now shared between javadoc and // reflow), and all functions the changelog claims were deleted (_chain_receiver_is_factory, _arg_list_single_line_estimate, _estimate_normalize, _arg_list_has_semantic_multi_row_arg, _SEMANTIC_WRAP_ARG_TYPES) are verifiably gone with zero dangling references, and the now-unused import re was correctly removed. No commented-out code found. Two issues reported via findings above (one correctness nit in multi-catch wrapping, one CI verification concern on Action version pins).

Testing — no test files appear in this chunk of the diff; can't assess coverage from Part 1 alone (may be in Part 2).

Documentation ✅ — CHANGELOG, standards doc, and two new FAQ docs are thorough and cross-referenced. Couldn't run prettier/cspell directly (sandboxed bash commands with certain grep patterns were blocked by approval requirements), so CommonMark/formatting compliance is based on visual inspection only, not tool-verified.

Security ✅ — no hardcoded credentials, no .lic/AQAAAD strings, new CI job correctly scopes permissions: contents: read and persist-credentials: false.

Both findings above are low/medium severity — nothing blocking. Waiting on Part 2 of the diff to complete the full review.


Review Part 2 of 2

Code Review — Part 2 of 2

Scope note: Only part 2 of the diff was provided in this context (no part 1), so this review covers the second half of the changes: format_java.py's nested-call/method-chain wrap engine, _emit_variable_declarator's anchor-escape tracking, requirements.txt version pins, and the associated test fixtures/unit tests. I cross-referenced the diff against the actual checked-out repo state (this branch is already merged into the working tree) to verify the final code, not just the patch text.

Code Quality

  • Style/idioms — Consistent with the rest of the file (heavy rationale comments, Final typing, structural predicates). No issues.
  • No commented-out code — The large removed blocks (P1F factory-chain tier, TestEstimateNormalize, TestArgListSingleLineEstimate) are real deletions of dead code/tests for functions that were fully removed — I confirmed _estimate_normalize and _arg_list_single_line_estimate no longer exist anywhere in format_java.py, so the corresponding test removal is correct, not an orphaned-coverage regression.
  • Meaningful nameschain_is_sole_arg, _arg_owns_its_rows, _is_nested_or_chained_call, inline_orphan are all self-explanatory given their doc comments.
  • DRY_arg_owns_its_rows and _segment_emit_is_legitimately_multi_line correctly share the same structural predicate rather than duplicating the block-lambda/text-block/anonymous-class checks.
  • Defect check — I traced the two trickiest pieces of logic by hand against the actual source (not just the diff):
    • _emit_variable_declarator's _anchor_escaped save/restore (tooling/scripts/format_java.py:10695-10762): snapshot()/restore() (lines 480-572) already include _anchor_escaped in the tuple, so the manual emitter._anchor_escaped = prev_escaped reset on the commit path (line 10758) is not redundant — it's the one exit that doesn't call restore(), and the value being restored is provably identical to what saved captured. No leak found.
    • chain_is_sole_arg gating (lines 9747-9754, consumed at 10226/10273/10288): requires head is None, which correctly excludes the someReceiverObject.methodOne(a).methodTwo(b) case (explicit bare-identifier receiver) from the sole-arg suppression — verified this matches fixture nested_call_wrap/07_identifier_receiver_chain_keeps_dot_align/expected.java, which still gets the dot-aligned two-line P2 shape rather than one-segment-per-line P3.
    • No bugs found in either.
  • .claude/CLAUDE.md — Not touched by this diff. (Note: the working directory has several untracked scratch files — chunk_000, pr_diff.txt, prompt-header.md, build-resources/, etc. — that appear to be artifacts of this review tooling itself, not part of the PR. I did not evaluate them as part of the change under review.)

Testing

  • Unit tests for new functionsTestIsNestedOrChainedCall and TestIsAnonymousClass (added in test_format_java.py) directly cover _is_nested_or_chained_call and _is_anonymous_class, including negative cases (cast, ternary, lambda body, array index, field access) that guard against silently widening the rule's scope.
  • ⚠️ _arg_owns_its_rows has no dedicated unit-test class — it's only exercised indirectly through fixture tests (method_chain_wrap/24, arg_list_wrap/15). Given it's a 4-line composition of three already-tested predicates, this is low risk, but a direct TestArgOwnsItsRows would make the "structural, not source-layout" invariant explicit and regression-proof on its own, independent of the fixtures.
  • Edge cases — The new fixtures target real, previously-oscillating edge cases (chain-arg source rows vs. legitimate multi-line, single-arg block-lambda body, factory chain first-dot break) with paired input/expected files.
  • test_installed_versions_match_pins (test_format_java.py:65-88) is a good addition — it explicitly catches the exact class of bug called out in its own docstring (installed package version silently drifting from the pin while the two static files still agree with each other). I verified requirements.txt (tree-sitter==0.26.0, tree-sitter-java==0.23.5) matches GRAMMAR_VERSION in format_java.py:113-116 — no drift.
  • ℹ️ I was unable to execute pytest in this sandbox (command required approval that wasn't available), so I could not confirm the full suite passes; verification here is by static/manual trace only.

Documentation

  • CHANGELOG.md — Updated with a detailed [0.7.0] entry covering the nested-call wrap rules, removed P1F tier, and the "layout decisions no longer read layout" theme, matching the code changes reviewed here.
  • Inline comments — Every non-obvious change (P1F removal, _anchor_escaped reset semantics, _arg_owns_its_rows rationale) has a "why," including concrete before/after examples of the oscillation bugs being fixed.
  • N/A Markdown/CommonMark — No .md files changed in this part of the diff.

Security

  • No hardcoded credentials — checked for .lic files and AQAAAD-prefixed strings across the working tree; the only matches are in review-tooling scratch files that describe this very check (build-resources/.claude/CLAUDE.md:51, prompt-header.md:47), not actual embedded keys. Nothing in the reviewed diff itself contains such content.
  • N/A Input validation / error handling / sensitive data in logs — This diff is internal formatter logic operating on parsed source ASTs, not user-facing input or logging paths.

Summary

No defects found in this half of the diff. The changes are unusually well-documented for a formatter internals change — each removed/added tier includes a concrete before/after example of the bug it fixes or the shape it eliminates, and the two subtlest changes (anchor-escape restore, sole-arg chain gating) check out correctly against the actual committed source. Only suggestion: add a small direct unit test for _arg_owns_its_rows rather than relying solely on fixture coverage.



Automated code review analyzing defects and coding standards

The wrap engine treats every named child of an `argument_list` as an
argument, and tree-sitter exposes a comment as a named child, so a comment
between arguments was counted as an argument and given a separator. The
formatter emitted Java that does not parse:

    in   outer.call(inner(alphaValue, /* note */ betaValue), tag);
    out  outer.call(inner(alphaValue, /* note */, betaValue), tag);

Source preservation is already the answer to "the wrap engine has no
concept of an inter-argument comment" -- that justification is written in
the predicate. It was simply gated behind the multi-row test, so a
single-row list fell through to the engine. The comment check now runs
first, independent of row span.

Latent rather than live: no argument list in the 504-file trial corpus
contains a comment child, which is why four review rounds did not surface
it. Corpus output is byte-identical after the fix.

Found by the round-4 review of PR #50, which initially cleared this by
testing only a trailing `//` and a leading `/* */` on its own row -- both
of which already hit a preserve path.
`_emit_formal_parameters` filtered its parameter list to
`formal_parameter` and `spread_parameter`, and no emitter was registered
for `receiver_parameter`, so an explicit receiver was silently discarded:

    in   void method(T this, String name)
    out  void method(String name)

Dropping a bare `T this` is semantically inert -- the construct exists
only to give annotations somewhere to attach -- but dropping an annotated
one discards the annotation, which is not inert. Either way the formatter
was altering source it was asked to format, and silently, where its
convention for an unsupported construct is an explicit refusal.

Receiver parameters are now emitted verbatim, which preserves annotations
exactly. They stay out of the name-alignment machinery: it has nothing to
align here, and `_formal_param_name_col_offset` already declines any list
whose members are not all plain `formal_parameter` nodes, because one
measured prefix width would not describe them.

No occurrence in the 504-file trial corpus, so corpus output is unchanged.
`field_access` had no wrap tier. Its reserve was correct -- the receiver
emits under `tail_reserve + 1 + len(field)` -- but when the receiver
exhausted its own cascade and committed its terminal candidate, the field
was appended to a row that was already full:

    int n = methodBeingCalled(
            argumentOne,
            argumentTwo).someFieldNameHere;    <- 85 columns

A fitting shape existed and the formatter could not reach it. Breaking
before the `.` is already the documented rule -- the spec lists `.` among
the break-before operators -- so the field now moves to its own line at
single indentation when the inline form does not fit.

Restricted to a COMPUTED receiver: a call, array index, object creation,
cast or parenthesized expression. The same node type also spells qualified
names and constant references, where the dots belong to the name, and a
first attempt at this tier shredded them:

    java                     Boolean
        .util                    .FALSE.equals(...)
        .Objects.requireNonNull(...)

Caught by fixtures `arg_list_wrap/09` and `nested_call_wrap/06`. The
narrowed tier also declines when breaking buys no width, since a receiver
that overflows on its own is not helped by moving the field off its last
row and the inline form costs one line fewer.

Corpus output byte-identical across all 504 trial files; the shape needs a
call whose arguments reach the limit before a field access is applied to
it. Correcting my own earlier description: this was never a missing
reserve, and the case did emit an advisory before the fix.
A value that commits at exactly 80 columns leaves the `;` in column 81,
and the formatter said nothing. The wrap engine's emit-and-warn exits run
while the semicolon is still unwritten, and `tail_reserve` does not carry
it, so the advisory measured 80 and declined to fire. The result is
idempotent, so it survives every reformat -- and it takes compliant source
and makes it non-compliant with no output at all:

    in   String s = Option.sourceDescriptor(
             COMMAND_LINE, CONFIG, "--config");
    out  String s
             = Option.sourceDescriptor(COMMAND_LINE, CONFIG, "--config");

The check now runs after the `;` is written, so it measures what actually
reaches disk. 23 declarations in the 504-file trial corpus report, at 81
to 84 columns; none matches checkstyle's LineLength ignorePattern, so
every one is a line the build will reject.

This REPORTS only. Threading the semicolon into `tail_reserve` so the
value's own cascade breaks earlier was implemented and reverted: it fixed
the target case but double-charged the semicolon against the declarator's
hardcoded `+ 1` allowance, and after correcting that it left
`MessageConsumerFactory.java` changing on every pass -- the one failure
mode 0.7.0 eliminated. Layout is deliberately unchanged here; corpus
output is byte-identical, over-80 stays 1573, and files needing a second
pass stay 6 with none failing to converge. The layout fix is deferred.
Three change behaviour and one adds an advisory; all four were found by
the round-4 review of this PR and all four reproduce identically at
0.6.0, so none is a regression from this release.

  1. Inline argument comments emitted Java that does not parse.
  2. A declaration whose semicolon lands in column 81 said nothing.
  3. Field access could not break before its dot, stranding an 85-column
     line the cascade had a fitting shape for.
  4. Receiver parameters were silently dropped, discarding annotations.

Corpus layout is byte-identical to the release build: none of the three
behavioural fixes occurs in the 504-file trial corpus, which is why they
survived four review rounds, and the fourth reports without relaying out.
over-80 stays 1573, files needing a second pass stay 6 with none failing
to converge, and 23 declarations newly report at 81 to 84 columns.
Two more decisions were reading source layout the formatter then
rewrites, so pass 1 answered from the author's layout and pass 2
answered from pass 1's own output. Neither changes the fixed point:
all 504 corpus files converge to byte-identical output, with lines
over 80 (1571) and advisories (301) unchanged. They reach it a pass
sooner.

**Basic-`for` headers (2 files).** The single-row path backtracked to
the paren-aligned one-clause-per-line form only when
`single_line_header and header_too_wide`. When a CLAUSE wrapped
internally the first conjunct was false, the backtrack was skipped,
and the header committed in the partial-break shape the standards'
Anti-pattern section forbids — two clauses packed on the header line
and one stranded beneath at an unrelated column. Escalation now fires
when the header overflows OR any clause wrapped, which is the same
rule as "if an argument breaks, the argument list breaks".

**Tier 1 brace collapse (3 files).** The gate included
`not _node_spans_multiple_rows(condition)`, on the reading that an
author who spread a condition over rows wanted the Allman brace a
multi-line condition triggers. But the emitter collapses that
condition onto one line whenever it fits and emits it with no brace
at all, so the gate contradicted its own behaviour — and being a
source read it made the answer depend on layout about to be
rewritten. The Tier 1 branch already decides by speculative emission
measuring RENDERED widths, so the gate was redundant for
correctness. It blocked 70 of 1,402 candidate sites; only 5 fit once
collapsed, and those 5 are what the second pass already produced.

Second-pass files: 6 to 1. The remaining one,
`AbstractSchedulingService.java`, is layout-dependent but its
mechanism is NOT established — a predicate trace diverges inside
`_arg_list_takes_source_preserve_path`, but preservation fires zero
times on that file, so that answer is discarded and cannot be the
cause. An earlier draft of this entry asserted an ordering problem in
the chain cascade; that was inferred from a comparison of two inputs
that already agreed on output, which could not have detected what it
was used to rule out. The claim is withdrawn rather than replaced.

Also corrects figures this release had left stale: files reformatted
against 0.6.0 (337, stated as both 322 and 336 in different places),
net lines (+1,565), over-80 (1,571), and a per-category breakdown
that summed above its own total.

797/797 pytest. Two fixtures and four unit tests, each verified to go
red under the matching revert, the two revert sets disjoint.
CI's spellcheck flagged ten words introduced by this release's prose.
Per the project's cspell discipline these are reworded rather than
whitelisted: invented words get replaced with real ones and symbols
get renamed, rather than the dictionary being widened to accept them.

British to American, matching the rest of the repo: behaviour,
materialises, minimising, equalises. Coinages to real words: dedups
to de-duplicates, fixpoints to fixed points, unsplittable to unsplit.
"idempotently" is replaced by plain prose ("and the result is
idempotent"), which reads better anyway. And the test constant
MULTIROW_CONDITION is renamed WRAPPED_CONDITION, since "multirow" is
not a word.

Comments, docstrings and release notes only: 797/797 pytest, corpus
unchanged at 1571 over-80, 301 advisories, 6 to 1 second-pass.
The previous rewording pass missed these because it matched on word
boundaries, and `_` is a word character — so `\bunsplittable\b` never
matched inside `test_..._unsplittable_value`. cspell tokenizes on the
underscore, and it also scans PATHS, which is how a fixture directory
name slipped through.

Renamed: `test_equalises_rather_than_packing` to `..._equalizes_...`;
`test_still_reports_a_genuinely_unsplittable_value` to
`test_still_reports_a_value_with_no_split_point`;
`test_multirow_condition_*` to `test_wrapped_condition_*`; and the
fixture `need_braces/23_multirow_source_condition_still_collapses` to
`23_wrapped_source_condition_still_collapses`.

Test and fixture names only: 797/797, corpus unchanged at 1571
over-80, 301 advisories, 1 second-pass file.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

PR Code Review — Part 1 of 3

Reviewing the diff shown so far (.claude/070_REMAINING_SCOPE.md, CI/dependabot config, cspell, CHANGELOG.md, docs FAQs, docs/java-coding-standards.md, and the first ~half of tooling/scripts/format_java.py). I cross-checked the new checkstyle-mirroring code against the actual checkstyle/senzing-checkstyle.xml in the repo. Final verdict on test coverage / doc completeness needs parts 2–3 (test files and remaining formatter code haven't arrived yet).

Code Quality

❌ Defect — new "LineLength ignorePattern" advisory-suppression is incomplete and inconsistently applied (tooling/scripts/format_java.py, around the _LINE_LENGTH_EXEMPT_MARKERS definition and _emit_field_declaration's new advisory call)

The actual checkstyle rule is:

checkstyle/senzing-checkstyle.xml:17
value="^package.*|^import.*|a href|href|http://|https://|@snippet|static final.*&lt;.*&gt;"

The new _LINE_LENGTH_EXEMPT_MARKERS tuple only captures "a href", "href", "http://", "https://", "@snippet" — it drops ^package.*, ^import.*, and static final.*<.*>. The docstring claims it "Mirrors the ignorePattern," but it doesn't.

That third missing pattern (static final.*<.*>) is the one that actually matters here: the new declaration-semicolon overflow advisory added in this PR (_emit_field_declaration's call to _fire_wrap_overflow_advisory(emitter, node, decl_start, "declaration", ...)) never calls _line_length_exempt at all. A very common Java shape — private static final Map<String, List<Integer>> FOO = ...; — matches checkstyle's static final.*<.*> ignore pattern and would not fail the build, but this new advisory will still fire on it. That's exactly the noise problem the changelog says this release fixes ("warning about a line the build will not reject is noise... With it, 17 advisories are added in total and every one names a line that genuinely fails the build") — but that guarantee is only wired up for the javadoc-structural-line call site, not for the declaration advisory or the parameter-list advisory (emit_p3_and_warn, in the part of the diff covering _emit_formal_parameters), both of which call _fire_wrap_overflow_advisory directly with no _line_length_exempt check.

Failure scenario: a field like private static final Map<String, SzFlagMetaData> LOOKUP = buildLookup(...)...; that overflows only because of its generic-typed static-final declaration will now get a spurious FormatterWarning, even though checkstyle's own LineLength check ignores it — reintroducing the "advisory becomes noise, developer stops trusting it" failure mode this same PR set out to close for javadoc {@snippet}/href lines.

Recommendation: either move the _line_length_exempt check into _fire_wrap_overflow_advisory itself (so every call site gets it for free and can't forget it), or add the check at the declaration/parameter-list call sites, and complete _LINE_LENGTH_EXEMPT_MARKERS to match all patterns in senzing-checkstyle.xml (or better, parse it from the XML given the comment already flags "the cost is that the two can drift").

✅ No commented-out code. All new comment blocks are prose rationale (design history, invariants), not disabled code.

✅ Meaningful names. _field_access_receiver_is_computed, _formal_param_name_col_offset, _balanced_reflow_words, etc. are clear and consistent with the file's existing naming style.

⚠️ DRY / drift risk (minor, self-acknowledged): _LINE_LENGTH_EXEMPT_MARKERS duplicates checkstyle/senzing-checkstyle.xml's ignorePattern as a separate hardcoded literal rather than reading the XML. The comment already flags this tradeoff explicitly, but given the finding above, this duplication has already caused real drift, not just theoretical risk.

No .claude/CLAUDE.md project memory file exists at the repo root (only .claude/settings.json and dated SCOPE.md planning docs), so that checklist item doesn't apply here.

✅ CI change looks sound. The new corpus-gate job in .github/workflows/pytest.yaml pins the external senzing-commons-java checkout to a release tag (4.0.1) rather than main, with a stated rationale (avoid an unrelated consumer commit turning this repo's CI red) — reasonable. One low-confidence note: actions/checkout@v7.0.0 and actions/setup-python@v6.2.0 are newer major versions than I have confirmed knowledge of; worth double-checking these tags actually exist before merge, since a bad pin here would silently break CI.

Dependabot dedup fix looks correct — removing the second pip entry for /tooling/scripts/tests is justified with concrete evidence (duplicate PR pairs #23/#24, #38/#39, #43/#44, #46/#47) and the remaining entry's directory does cover both requirements files via -r ../requirements.txt.

Documentation

✅ CHANGELOG.md updated — extremely detailed 0.7.0 entry with before/after examples and measured corpus effects.

✅ New FAQs are well-formed markdown (formatter-python-environment.md, source-preservation-history.md) — headers, fenced code blocks, and lists look CommonMark-clean from the diff; no stray trailing whitespace visible.

Note (not a defect): the 0.7.0 changelog entry is unusually long (over 1000 lines). Given how detailed .claude/070_REMAINING_SCOPE.md already is as a design-decision record, there's real duplication of content between that file and the changelog — not wrong, just worth knowing this doubles the maintenance surface for future edits.

Security

✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings found anywhere in this part of the diff.

Low-severity note: the corpus-gate job checks out and formats a third-party open-source repo's source as test input only (not executed) — negligible risk, flagging only per the checklist's supply-chain-awareness intent.


Pending Parts 2–3: I have not yet seen _arg_list_takes_source_preserve_path's full body (cut off mid-function at the end of this chunk), the rest of format_java.py, or any test files, so I can't yet evaluate: unit/integration test coverage, whether new fixtures back the behaviors changed here (e.g. _attach_trailing_side_comments, which is called but not yet shown), or whether _anchor_escaped/_raw_rows_emitted (initialized here) are actually read/used correctly downstream. I'll fold those into the final review once the remaining parts arrive.


Review Part 2 of 3

I used the wrong output format for this task — the prompt asks for a checklist-style review, not the findings tool. Here's the proper review of Part 2 of the diff.

Review — PR Diff (Part 2 of 3)

Scope note: this part covers format_java.py's argument-list/method-chain wrap engine (0.7.0 changes), requirements.txt, dozens of new/renamed test fixtures, and test_format_java.py. I don't have parts 1/3, so items like README/CHANGELOG may be addressed there.

Code Quality

  • Style/naming/idioms: ✅ Consistent with the rest of the file — snake_case, type hints, Final, extensive rationale comments matching the project's established convention of embedding "why" history in comments (e.g., "0.7.0 nested-call wrap (rule N)").
  • No commented-out code: ✅ No commented-out code found; the large comment blocks are design rationale, not disabled code.
  • Meaningful variable names: ✅ Names like p1_invalid_wrap, p3_arg_escaped, chain_is_sole_arg are verbose but clear.
  • DRY principle: ⚠️ Minor — format_java.py:10365 (_segment_emit_is_legitimately_multi_line). The args_emit_column parameter is still computed at both call sites (emit_p1's emit_seg_strict ~line 10498, emit_p2's emit_seg_track_wrap ~line 10566) via a name_node lookup + text extraction + column arithmetic, but the parameter is never read inside the function body. This is dead code left over from removing the width-based column= argument from _arg_list_takes_source_preserve_path. The docstring (lines 10376–10381) and a comment at the call site (10486–10491) still claim the column is needed for a "first_line_fits" check that no longer exists — actively misleading for future maintainers.
  • Defects: I traced several of the trickier flows for correctness (verified against the actual post-merge source, not just the diff):
    • _emit_variable_declarator's _anchor_escaped/inline_orphan handling (lines ~11300–11421): initially looked like a stale-flag leak across the backtrack path, but emitter.snapshot()/restore() include _anchor_escaped in the saved tuple (confirmed at lines 520–570), so emitter.restore(saved) correctly resets the flag before the backtrack re-emit. No bug — flagging only because it's a subtle mechanism worth double-checking in review.
    • emit_p4_packed setting emitter._arg_list_p4_fired = True (line 9779): initially looked like conflating a "2b" (packed) tier with true P4, but _arg_list_p4_fired is documented and used elsewhere as a general "this construct needed extra wrapping" signal consumed by the binary-wrap engine and outer arg-list decisions, not literally "P4 fired" — consistent with existing usage. No bug.
  • CLAUDE.md: Not present/modified in this diff slice — N/A.

Testing

  • Unit tests for new functions: ✅ Strong — TestIsNestedOrChainedCall, TestIsAnonymousClass, TestGroupInlineTags, TestSplitsInlineTag, TestMinRaggedLines, TestJavadocBalancedReflow, TestJavadocReflowIsBoundary, TestTagDescriptionSkipsStabilityCheck, TestDeclarationSemicolonReserve, TestReceiverReserveIgnoresArgumentLayout, plus a new test_installed_versions_match_pins guarding environment drift.
  • Dead tests removed alongside dead code: ✅ TestEstimateNormalize and TestArgListSingleLineEstimate (~200 lines) were correctly deleted along with the retired _estimate_normalize/_arg_list_single_line_estimate functions — good hygiene, no orphaned tests for removed functionality.
  • Edge cases covered: ✅ Fixture set is extensive (20+ new arg_list_wrap/nested_call_wrap/method_chain_wrap fixtures), including idempotency-lock cases explicitly testing the two-pass-stability bugs described in the comments.
  • Coverage: Cannot compute a percentage from a diff; qualitatively very thorough for the new logic.

Documentation

  • Inline comments for complex logic: ✅ Exceptionally thorough — arguably the diff's dominant characteristic, with rationale, before/after examples, and links to a building/source-preservation-history FAQ for deeper context.
  • Minor: tooling/scripts/tests/test_format_java.py:4550 (TestReceiverReserveIgnoresArgumentLayout class docstring) — one line ("reserving the declaration semicolon is what pushed it into. So this class guards the reserve") is ~97 chars, far past the ~79-char wrap width used everywhere else in the file, and reads as a grammatically broken run-on sentence. Looks like text was inserted without rewrapping the paragraph. Cosmetic only.
  • README/API docs/CHANGELOG: Not visible in this part of the diff — can't verify; check parts 1/3.
  • Markdown/CommonMark: N/A — no .md files in this part.

Security

  • No hardcoded credentials: ✅ None present.
  • Input validation: N/A — internal formatter logic operating on parsed ASTs, not external input.
  • Error handling: ✅ Uses try/finally consistently around set_tail_reserve mutations to guarantee restoration.
  • Sensitive data in logs: ✅ N/A — no logging of user/sensitive data.
  • .lic files / AQAAAD strings: ✅ None found in this diff.

Summary

Two minor findings, both non-functional:

  1. format_java.py:10365 — dead args_emit_column parameter (and its computation at two call sites) plus stale docstring/comments claiming column-sensitivity that was removed. Low-effort cleanup.
  2. tooling/scripts/tests/test_format_java.py:4550 — malformed/un-rewrapped docstring line with a grammar error. Cosmetic.

No functional defects found in the wrap-engine logic reviewed; the flag-management mechanisms I traced (_anchor_escaped, _arg_list_p4_fired) are correctly threaded through the snapshot/restore machinery. Test coverage for the new behavior is strong.


Review Part 3 of 3

Review — PR Diff (Part 3 of 3)

Scope: this part covers the tail end of tooling/scripts/tests/test_format_java.py — the last two methods of TestReceiverReserveIgnoresArgumentLayout and the entirety of the new TestSecondPassConvergence class. I cross-referenced this against the actual merged file and the rest of the test suite (parts 1–2 already reviewed format_java.py and the bulk of the fixtures/tests).

Code Quality

  • Style/naming/idioms: ✅ Consistent with the file's conventions (snake_case, docstring-as-rationale, Final/type hints where applicable).

  • No commented-out code: ✅

  • Meaningful names: ✅ test_for_header_converges_on_the_first_pass, test_wrapped_condition_still_collapses_to_tier_1, etc. are descriptive and self-documenting.

  • DRY principle: ❌ TestSecondPassConvergence duplicates coverage the suite already has, tooling/scripts/tests/test_format_java.py:4593–4663.

    Both new fixture pairs this class reads from disk —
    fixtures/condition_wrap/12_for_clause_wrap_escalates_whole_header/ and
    fixtures/need_braces/23_wrapped_source_condition_still_collapses/ — are new fixtures added by this same PR, and both are automatically picked up by the pre-existing generic harness test_fixture_golden in tooling/scripts/tests/test_fixtures.py:97–125, which for every fixture directory already asserts:

    1. format_source(input.java) == expected.java (byte-exact), and
    2. format_source(actual) == actual (second-pass fixed point).

    That means:

    • test_for_header_converges_on_the_first_pass (line 4635) and test_wrapped_condition_converges_on_the_first_pass (line 4646), which assert first == second == third across three formatter passes, prove nothing beyond what test_fixture_golden already guarantees for these exact fixtures — if pass 1 == expected.java and format(expected.java) == expected.java (both already checked), then pass 2 == pass 1 and pass 3 == pass 2 follow trivially.
    • test_for_clause_wrap_breaks_the_whole_header (line 4639) asserts "line != null;\n" in text / "; line\n" not in text — but I confirmed expected.java for that fixture (line 8 of the fixture file) already contains line != null; on its own line, so this is already locked in byte-for-byte by the golden test.
    • test_wrapped_condition_still_collapses_to_tier_1 (line 4652) asserts a substring that is the entire content of expected.java for that fixture — again already covered byte-for-byte.

    Why this matters: this isn't just redundant test code — it's a second, independently-maintained copy of the same assertion. If a future change intentionally alters one of these two fixtures' expected.java, a developer has to remember to also update the hardcoded substring literals here, or this class fails for a reason unrelated to what it claims to guard (the exact "drift" problem the class's own FOR_HEADER/WRAPPED_CONDITION docstring — "Read from the fixture so the two cannot drift apart" (line 4618) — was explicitly trying to avoid, just one layer up).

    Recommendation: either delete this class (the two properties reading input.java and the convergence assertions add no coverage beyond test_fixture_golden), or, if the intent is to document why these two fixtures were added (the "second pass" bug narrative), fold that rationale into the fixture's own comments/README or a docstring rather than a duplicate executable test.

  • Contrast — the other two new methods are fine: test_both_layouts_reach_the_same_output (line 4571) and test_each_layout_is_a_fixed_point_after_one_pass (line 4580) operate on inline WRAPPED/INLINE string literals, not on files under fixtures/, so they are not covered by test_fixture_golden and are legitimate, targeted regression tests for the two-cycle divergence bug described in the class docstring. No issue with these.

  • Defects: None found in the formatter-facing logic here (this part is entirely new test code, not format_java.py itself). _passes() (line 4602) correctly threads out[-1] forward each iteration and returns out[1:], discarding only the original unformatted body — no off-by-one or stale-reference bug.

  • CLAUDE.md: Not present/modified in this diff slice — N/A (consistent with parts 1–2).

Testing

  • Unit tests for new functions: ✅ present, though see the DRY finding above regarding duplicate coverage.
  • Edge cases covered: ✅ for the two TestReceiverReserveIgnoresArgumentLayout methods (genuinely new coverage for a two-cycle bug).
  • Coverage: Cannot compute a percentage from a diff.

Documentation

  • Inline comments/docstrings: ✅ Thorough, consistent with the file's house style of embedding design rationale directly in test docstrings.
  • No README/CHANGELOG/API doc content in this part — N/A (see parts 1–2).

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings.
  • Input validation / error handling / logging: N/A — pure test code operating on in-memory literals and local fixture files, no external input.

Combined Summary (Parts 1–3)

# Finding Location Severity
1 _LINE_LENGTH_EXEMPT_MARKERS is missing 2 of 4 patterns from senzing-checkstyle.xml's ignorePattern (notably static final.*<.*>), and the new declaration/parameter-list advisory call sites never call _line_length_exempt at all — reintroducing the noisy-advisory problem this release claims to fix tooling/scripts/format_java.py (_LINE_LENGTH_EXEMPT_MARKERS, _emit_field_declaration, _emit_formal_parameters) High — functional regression risk
2 Dead args_emit_column parameter (computed at two call sites, never read) with stale docstring/comment claiming a "first_line_fits" check that no longer exists tooling/scripts/format_java.py:10365 and call sites ~10498/10566 Low — cleanup only
3 Malformed/un-rewrapped docstring line, grammatically broken tooling/scripts/tests/test_format_java.py:4550 Cosmetic
4 TestSecondPassConvergence duplicates assertions already guaranteed by test_fixture_golden for its two new fixtures tooling/scripts/tests/test_format_java.py:4593–4663 Low — redundant test / maintenance-drift risk (this part's finding)

Everything else across all three parts — style, naming, idempotency/snapshot/restore correctness in the wrap engine, CI pin rationale, dependabot dedup, changelog/FAQ documentation, and security — checked out clean (✅). Item 1 is the one finding I'd treat as blocking before merge; items 2–4 are good follow-up cleanup but not release blockers.



Automated code review analyzing defects and coding standards

CI's automated review found this and rated it blocking; it was right.
The release claims to have fixed noisy advisories, and had only half
done it.

`_LINE_LENGTH_EXEMPT_MARKERS` mirrored five of the eight
`ignorePattern` alternatives in `checkstyle/senzing-checkstyle.xml`.
The three omitted are not plain substrings and so could not live in a
substring list: `^package.*`, `^import.*` and `static final.*<.*>`.
They are now matched structurally in `_line_length_exempt`, the two
anchored ones with `startswith` on the raw text since a `package`
keyword inside a line is not a package declaration.

More consequentially, `_line_length_exempt` had exactly ONE call
site, in the javadoc path. Every wrap engine reaches the advisory
channel through `_fire_wrap_overflow_advisory`, which never consulted
it. It now excludes exempt lines from the width accounting, so the
exemption is inherited by every site at once and an advisory whose
only over-long lines are exempt does not fire at all.

Corpus advisories 301 to 288. The exemption suppresses 257 sites but
most were already deduplicated away, so thirteen is the net change.
By rule, 250 suppressed sites are a `static final` constant whose
generic type makes the declaration unbreakable, seven are URLs.
Declaration advisories drop 19 to 12 and their reported widths narrow
from 84-94 to 84-85, since the `static final` cases were the wide
ones at 88 and 93 columns.

Output is untouched: token streams identical in all 504 files and
zero files differ, so this is advisory-channel only. Lines over 80
(1571), line count and convergence (1 second-pass, 0 third-pass) all
unchanged. 797/797.

Also from the same review: removed the dead `args_emit_column`
parameter and the `name_node`/`name_text` locals that existed only to
compute it, at both chain call sites. Its docstring justified it by
the source-preserve gate being column-sensitive — that gate lost its
column parameter when preservation was reduced to the comment and
CSOFF cases. Rewrapped one run-on docstring line.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Review — Part 1 of 3

I reviewed this against the repo as checked out at the merge commit (0b6a39e), not just the raw diff text, and cross-checked a few claims against files still on disk (e.g. checkstyle/senzing-checkstyle.xml). This covers only the hunks in Part 1; I'll fold Parts 2 and 3 in as they arrive since several state fields introduced here (_anchor_escaped in particular) are read/written later in the file, outside this chunk.

Code Quality

  • ⚠️ Docstring drift in Emitter.snapshot()tooling/scripts/format_java.py:486-492. The type signature was widened from a 9-tuple to an 11-tuple (two new bool fields: _anchor_escaped and _raw_rows_emitted, confirmed in the return statement at lines 529-530). The docstring's enumerated tuple (lines_count, current, indent, tail_reserve, paren_align_col, paren_expr_col, arg_list_p4_fired, array_init_inline_only, anchor_escaped, warnings_count) was updated to add anchor_escaped but not raw_rows_emitted — the prose still lists only 9 names for an 11-element tuple. restore()'s unpacking (lines 553-563) is correct and matches emission order, so this is docs-only, but worth a one-line fix since this docstring is explicitly the contract other wrap-engine authors are told to follow ("suitable for restore()").
  • ✅ Checkstyle-exemption mirroring is accurate: _LINE_LENGTH_EXEMPT_MARKERS (format_java.py:3388) exactly matches the five plain-substring alternatives in checkstyle/senzing-checkstyle.xml:17's ignorePattern, and the three structural ones (^package.*, ^import.*, static final.*<.*>) are handled correctly by _line_length_exempt — verified the find-based static-final/angle-bracket check is logically equivalent to the regex, and the startswith("package ")/("import ") checks are actually more correct than the raw regex (which would spuriously match an identifier like importantFlag at line start).
  • ✅ Checked _field_access_receiver_is_computed, the new _emit_catch_clause multi-catch cascade, the enhanced-for reserve accounting, and the record-header wrap logic by hand for off-by-one/reserve mismatches (this release explicitly calls out fixing four such bugs, which raised my suspicion). No new instance found — the +1/+3 reserve math in each new cascade is internally consistent with how the corresponding fit-check reads emitter.column/tail_reserve afterward.
  • Investigated one plausible off-by-one: _formal_param_name_col_offset (format_java.py:7977) measures each parameter's speculative prefix width starting from the emitter's column before ( is written, one column left of where it's actually emitted in P2/P3 (paren_col). This only matters if a type's own emitter can wrap in response to available column width — traced _emit_generic_type/_emit_type_arguments (format_java.py:5605-5635) and confirmed neither ever calls newline(), so wrapped can't actually be triggered by this discrepancy today. Not a live bug, but fragile if a wrapping type emitter is ever added — no action needed now.
  • No commented-out code, no obvious naming issues, no DRY violations spotted in this chunk.

Testing

Not assessable from this chunk — no test files appear in Part 1's diff. Deferring to Parts 2/3 (the CHANGELOG references many new fixtures and unit tests; I'll check they exist and match the described coverage once visible).

Documentation

  • ✅ New FAQ docs (formatter-python-environment.md, source-preservation-history.md) and the .claude/070_REMAINING_SCOPE.md census read as internally consistent with the code changes I inspected (source-preservation retirement, grammar pin mechanism).
  • Could not mechanically verify Markdown/CommonMark fence-balance or Prettier formatting for the large CHANGELOG.md/docs/java-coding-standards.md additions — grep/wc/Python execution were blocked by the sandbox in this session. Spot-checked fence pairing by eye across the diff for the sections I traced (record headers, catch clause, enhanced-for, field access) and found no unclosed/mismatched ``` `` blocks. Recommend running `prettier --check` / `markdownlint` in CI rather than relying on this review for that item.
  • CHANGELOG version entry (## [0.7.0] - 2026-08-14) matches the __version__ bump in format_java.py:107 and the GRAMMAR_VERSION bump to tree-sitter 0.26.0 is consistent with the new formatter-python-environment.md guidance and the corpus-gate CI job. ✅

Security

  • ✅ No hardcoded credentials.
  • ✅ No .lic files in this chunk's diff, and no strings starting with AQAAAD found.
  • ✅ New corpus-gate CI job (.github/workflows/pytest.yaml) sets persist-credentials: false on both checkouts and pins the external corpus checkout (senzing-garage/senzing-commons-java) to a release tag rather than main — good practice, and the comment explains why (prevents an unrelated consumer commit from turning this repo's CI red, and avoids an untrusted moving target).
  • One thing to double-check when Part 2/3 land: actions/checkout@v7.0.0 and actions/setup-python@v6.2.0 are pinned to major versions past what I can verify existed as of my knowledge cutoff — not flagging as a defect (my knowledge cutoff is Jan 2026, before the PR's Aug 2026 date), but worth a human confirming these tags are real/expected in your environment rather than a typo (e.g. v4/v5).

Still pending

Part 1 shows no .claude/CLAUDE.md changes to review. Waiting on diff Parts 2 and 3 — in particular I want to verify the _anchor_escaped usage sites (format_java.py:9447, 11330-11404, outside this chunk) actually match the docstring's claim ("read by the variable-declarator cascade to backtrack to break-at-="), since that mechanism doesn't obviously line up with the semicolon-reserve approach described in the CHANGELOG for the same feature.


Review Part 2 of 3

Code Review — Part 2 of 3

This chunk covers format_java.py (the Java formatter's argument-list/method-chain wrap engine), requirements.txt, and a large batch of new/renamed test fixtures + unit tests. Since this is 1 of 3 diff parts, verdicts below are scoped to what's visible here; some checklist items (README, full test-coverage %, CHANGELOG) can't be fully assessed without parts 1/3.

Code Quality

  • Style conventions: ✅ Consistent with the surrounding codebase's idioms (mutable-list closures for flags like p1_invalid_wrap[0], emitter.snapshot()/restore() pattern, extensive rationale comments).

  • No commented-out code: ✅ All removed code is genuinely deleted (verified _estimate_normalize, _arg_list_single_line_estimate, _SEMANTIC_WRAP_ARG_TYPES no longer exist anywhere in the file — their removal is real, not commented out, and their tests were correctly deleted alongside them).

  • Meaningful variable names: ✅

  • DRY principle: ❌ tooling/scripts/format_java.py — the exact same 5-line set_tail_reserve/try/finally block plus its 9-line explanatory comment ("0.7.0: the VALUE must wrap knowing a ; follows it...") is duplicated verbatim four times at lines ~11261, ~11291, ~11332, and ~11416 inside _emit_variable_declarator/_emit_variable_declarator_with_array_rhs. Given the codebase already extracts small helpers elsewhere in this same diff (e.g. the new _shift() closure), this is a good candidate for a small context manager, e.g.:

    @contextmanager
    def _reserve(emitter, extra):
        prev = emitter.set_tail_reserve(emitter.tail_reserve + extra)
        try:
            yield
        finally:
            emitter.set_tail_reserve(prev)

    This isn't a correctness issue, just repeated logic that will need four synchronized edits if the reserve calculation ever changes.

  • Defects: No correctness bugs found. Specific things I checked and confirmed sound:

    • _anchor_escaped orphan-detection flag (_emit_variable_declarator, lines ~11330–11411): the flag is part of Emitter.snapshot()/restore() state, so the backtrack path's emitter.restore(saved) automatically restores the pre-reset value — the manual emitter._anchor_escaped = prev_escaped restore is correctly scoped to only the commit path, as the accompanying comment claims. No leak across nested declarators.
    • _min_ragged_lines's DP (new, lines 3777–3836): verified the recurrence and the "single oversized token still gets a line" edge case (end > start guard) against its unit tests by hand-tracing; it's correct.
    • _is_nested_or_chained_call traversal (including the block-lambda-opaque / expression-lambda-transparent distinction) is well covered by TestIsNestedOrChainedCall's parametrized cases and matches its own docstring.
    • _emit_receiver_parameter (format_java.py:8284) is a genuine bug fix — it stops the formatter from silently dropping receiver_parameter nodes (e.g. void m(T this, String s) was emitting as void m(String s), losing any annotations on this). Good catch, has a dedicated fixture (method_decl_wrap/06_receiver_parameter_preserved).
    • Minor/low-severity asymmetry: emit_p4_packed (the new "P4-packed" tier) doesn't reserve tail budget per-argument the way the sibling emit_p4_multi_arg was just fixed to do (comment there explains the exact-80-then-separator-at-81 bug). For emit_p4_packed this isn't a correctness bug — an under-reserved candidate that overflows is caught by the post-hoc last_lines_max_width <= effective_max check and the whole tier falls back to P3, which is still valid — but it means a case that could have fit as the more compact 2-line "packed" shape may unnecessarily fall back to one-argument-per-line. Worth a comment noting this is intentional (if it is), since the pattern was fixed everywhere else it appears.
  • CLAUDE.md: Not present in this diff chunk; no comment on it possible here.

Testing

  • ✅ Strong: new predicates (_is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_stable) all ship with dedicated unit test classes covering both positive and negative/edge cases (including "never raises on detached node", stability-oscillation regression cases, and infeasible-DP cases).
  • ✅ Numerous new/renamed fixture pairs (arg_list_wrap/1320, nested_call_wrap/0109, method_chain_wrap/18, 21, 2426, etc.) exercise the new nested-call/chain rules end-to-end, including idempotency-sensitive ones (07_binary_positional_arg_idempotency_lock).
  • New test_installed_versions_match_pins (test_format_java.py) is a good defensive addition — cross-checks the installed package versions against GRAMMAR_VERSION/requirements.txt, closing a real gap the comment says caused a prior review to pass 704 tests against an uncalibrated tree-sitter binding.

Documentation

  • The requirements.txt comment now explicitly documents the GRAMMAR_VERSION / TestGrammarVersionPins / dependabot-cooldown contract — good, this is exactly the kind of non-obvious constraint worth writing down. Bump tree-sitter==0.26.0 is mirrored correctly (confirmed GRAMMAR_VERSION and the new installed-version test reference it).
  • Can't verify CHANGELOG.md/README changes from this chunk alone — recent commit history (86e8ebf, c6f7123, etc.) shows CHANGELOG.md is being actively maintained for 0.7.0 elsewhere in this branch, so likely fine but unconfirmed here.
  • Markdown/CommonMark formatting: not applicable — no .md file diffs appear in this chunk.

Security

  • ✅ No hardcoded credentials, no logging of sensitive data, no .lic files or AQAAAD-prefixed strings anywhere in this chunk — this is internal Python tooling for a formatter with no external I/O beyond reading/writing source files.

Review Part 3 of 3

Code Review — Part 3 of 3

Note: this chunk contains only test code (pytest tests for format_java.py's Javadoc reflow, declaration-semicolon reserve, receiver-reserve, and formatter convergence/idempotency). No file path header was included in this diff fragment, so line numbers below are approximate, anchored to class/method names instead of absolute file lines.

Code Quality

  • Style conventions — Consistent with pytest idioms (parametrize, type-annotated test methods, docstrings). Long strings are deliberately wrapped, suggesting the project applies its own line-length discipline to this Python test file too.
  • No commented-out code — none found.
  • Meaningful namesWRAPPED/INLINE, FOR_HEADER, WRAPPED_CONDITION, PREFIX, WORDS are all clear and purposeful.
  • ⚠️ DRY_FIXTURES path construction and the _passes() helper are reused well; the WRAPPED/INLINE literal Java source blocks in TestReceiverReserveIgnoresArgumentLayout are necessarily duplicated (that's the point of the regression test — comparing two source layouts), so this is acceptable duplication, not an issue.
  • Potential defect — undocumented pre-existing bug baked into an assertion. In TestDeclarationSemicolonReserve.test_still_reports_a_value_with_no_split_point, the test asserts "max line width 86" in the warning message, with a comment admitting the correct value should be 87 but a de-duplication path swallows the accurate warning and lets a stale one "survive" one column short. Encoding a known-wrong warning message into a passing test means this off-by-one will now silently persist — future contributors won't notice it's wrong, they'll just see a green test. Recommend filing a follow-up issue for the de-dup bug rather than only leaving a comment, so it doesn't get lost. (TestDeclarationSemicolonReserve, in the diff above.)
  • ⚠️ Unverifiable fixture dependencyTestSecondPassConvergence reads fixtures/condition_wrap/12_for_clause_wrap_escalates_whole_header/input.java and fixtures/need_braces/23_wrapped_source_condition_still_collapses/input.java via @property. This chunk doesn't show these fixture files being added; if they aren't part of this PR (or another already-merged commit), these tests will fail with FileNotFoundError. Worth confirming fixtures exist in the full PR diff (parts 1–2, which weren't shown to me).

Testing

  • Unit tests for new functions — this entire chunk is new/expanded unit tests (_javadoc_reflow_is_boundary, _javadoc_balanced_reflow, declarator/array-rhs semicolon reserve, receiver reserve, second-pass convergence).
  • Edge cases covered — single-token-with-no-split-point case, array RHS, wrapped vs. inline source layouts that previously alternated forever (two-cycle bug), multi-pass convergence checks.
  • N/A Integration tests for new endpoints — not applicable, this is a formatter/CLI tool, not a service.
  • ⚠️ Test coverage > 80% — can't be measured from a diff fragment; the tests shown are thorough and specifically regression-guard prior idempotency bugs.

Documentation

  • N/A — no README/API docs/CHANGELOG touched in this chunk.
  • Inline comments for complex logic — class docstrings do a good job explaining why each regression test exists (e.g., the "true two-cycle, not merely a second pass" explanation in TestReceiverReserveIgnoresArgumentLayout), which is appropriate given these tests encode subtle, non-obvious formatter bugs.

Security

  • ✅ No hardcoded credentials, no license file content, nothing logged in this chunk.

Summary

Solid, well-targeted regression tests for previously-fixed idempotency/convergence bugs in the Java formatter. The one real concern: test_still_reports_a_value_with_no_split_point locks in a documented-but-unfixed off-by-one in the warning message rather than fixing or ticketing it — that's the kind of thing that's easy to forget once a test goes green. Also flag for confirmation: the two on-disk fixtures referenced by TestSecondPassConvergence should be verified present in the full PR (parts 1–2 weren't visible to this review).



Automated code review analyzing defects and coding standards

No correctness findings this round; CI confirmed the LineLength
exemption fix and noted that `startswith("package ")` is actually
MORE correct than the regex it mirrors, which would match an
identifier like `importantFlag` at line start.

Four things acted on:

**DRY.** The semicolon-reserve block — five lines plus a nine-line
comment — was duplicated verbatim five times across
`_emit_variable_declarator` and
`_emit_variable_declarator_with_array_rhs`. Extracted as an
`_extra_tail_reserve` context manager carrying the explanation once.
Verified output-neutral (token streams identical in all 504 files,
zero files differ) and verified the guard is not weakened: making
the single helper a no-op now fails five tests, where before the
same change had to be made in five places to be caught.

**`Emitter.snapshot()` docstring** enumerated ten field names for an
eleven-element tuple — `raw_rows_emitted` was missing. That docstring
is explicitly the contract other wrap-engine authors follow.

**`emit_p4_packed`'s missing per-argument reserve** is intentional
and now says so. Its sibling `emit_p4_multi_arg` can reserve because
each argument owns its line, so what follows is known; here every
argument shares one line and what follows argument N is the rest of
that line, unknown until emitted. The tier relies on the post-hoc
width check instead, which is sound — an overflowing candidate falls
to P3. Reserving only for the last argument, the one the `)` really
does follow, was implemented and measured at zero corpus change, so
it is documented rather than carried as speculative code.

**`actions/checkout`** was pinned at `v7.0.1` in the pytest job but
`v7.0.0` in both corpus-gate checkouts; aligned on `v7.0.1`.

Declined: CI called `TestSecondPassConvergence` redundant with
`test_fixture_golden`. Reverting either convergence fix fails the
unit tests as well as the fixtures, and the golden test checks
output-matches-expected rather than convergence-from-input, so they
are complementary. The maintenance-drift risk it was really pointing
at is already gone — those tests read the fixture inputs.

Ticketed rather than silently locked in: the deduped advisory
under-reports width by one (86 for an 87-column line). Pre-existing,
unchanged here, and now tracked as its own task so the test asserting
86 does not quietly become the specification.

797/797. Corpus unchanged: 1571 over-80, 288 advisories, 1
second-pass file, 0 third-pass.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

All review work is complete — both background agents finished and I independently verified their two strongest findings by reading the actual source. Here's the full checklist review.

Senzing Code Review — PR 0.7.0 (formatter release)

Scope: .claude/070_REMAINING_SCOPE.md, dependabot/CI workflow, CHANGELOG.md, 4 docs/FAQ files, docs/java-coding-standards.md, tooling/scripts/format_java.py (~3,100 line diff), requirements.txt, ~100 new/changed test fixtures, test_format_java.py. No ./.claude/CLAUDE.md exists in this repo, so that checklist item is N/A.

Code Quality

  • Style conventions ✅ — Consistent with the file's existing conventions throughout.
  • No commented-out code ✅ — None found; the very long inline comments are rationale/history, not disabled code.
  • Meaningful variable names
  • DRY principle ⚠️tooling/scripts/format_java.py:5855-5859 duplicates the "modifiers on catch_formal_parameterNotImplementedError" check that _emit_catch_formal_parameter already performs for the single-type path. Not a regression, but the two copies can drift if one is updated later (e.g. to support annotations) without the other.
  • Defects ❌ — two confirmed:
    1. tooling/scripts/format_java.py:8101-8107_emit_formal_parameters's docstring says receiver parameters (Foo this) are "NOT supported and, worse, are silently DROPPED" — but this very PR fixes exactly that: the params filter at line 8120 now includes "receiver_parameter", _emit_receiver_parameter is defined at line 8309, and it's registered in the dispatch table at line 11434. The docstring directly contradicts the code six lines below it in the same function. A maintainer trusting the comment could "re-fix" an already-working path or distrust correct output.
    2. tooling/scripts/format_java.py:8468-8557_emit_field_access's new wrap tier (explicitly documented as brand-new: "field_access previously had no wrap tier at all") never calls _fire_wrap_overflow_advisory, unlike every other cascade touched in this release (catch clause, formal parameters, argument list, etc.). When both the inline and dot-broken forms still overflow 80 columns, the function silently commits the "less bad" one with no FormatterWarning — inconsistent with the file's own C1 emit-and-warn convention and denying adopters the diagnostic signal they'd get from an equivalent overflow anywhere else.

Testing

  • Unit tests for new functions ✅ — Every major 0.7.0 feature (nested-call rules 1-3, record header wrap, enhanced-for wrap, switch brace, multi-catch, parameter alignment, field-access dot-break, javadoc reflow/stability, P1-P3 argument-breaks-the-list, P2b, semicolon reserve) has at least one fixture, auto-discovered by test_fixtures.py.
  • Edge cases covered ⚠️multi_catch_wrap/ and enhanced_for_wrap/ each have only 2 fixtures (fit + one overflow shape); no 4+ exception-type or deeply-nested case, despite the CHANGELOG citing a real 4-exception example (WrapperMain.java:66).
  • Integration tests N/A — CLI formatter, no network endpoints.
  • Test coverage > 80% — not independently measured, but coverage looks extensive given ~100 fixture pairs plus 41 new unit tests cited in the CHANGELOG; no red flags found.
  • Spot-checked 10 fixture pairs directly (e.g. record_header_wrap/02, enhanced_for_wrap/02, multi_catch_wrap/01, method_chain_wrap/26) — all input.java/expected.java pairs are internally consistent and plausible.

Documentation

  • CHANGELOG.md updated ✅ — extensive 0.7.0 entry, cross-checked several specific claims (function names, fixture names, tree-sitter 0.26.0 pin) against actual source — all accurate.
  • Stale reference ❌ — .claude/070_REMAINING_SCOPE.md:24 cites fixture arg_list_wrap/18_arg0_wraps_but_paren_aligned_still_fits, which does not exist. The actual fixture is arg_list_wrap/18_arg0_wraps_so_whole_list_breaks (confirmed via directory listing), which locks the opposite outcome (list escalates/breaks rather than "still fits"). This will mislead anyone using the doc to find the regression test.
  • Inline comments for complex logic ✅ — thorough to the point of verbosity, but genuinely explains non-obvious rationale (off-by-one histories, rejected alternatives).
  • Markdown/CommonMark ✅ — checked heading nesting and internal anchors (#nested-call-wrap, #exception-multi-line-conditions) in docs/java-coding-standards.md and the new FAQ files; all resolve correctly, no malformed structure found.
  • README N/A — not touched, appropriately (this is internal formatter-behavior documentation, already covered by CHANGELOG + standards docs).

Security

  • No hardcoded credentials
  • No .lic files or AQAAAD strings ✅ — none found in the diff.
  • Proper error handling ✅ — deliberate NotImplementedError refusals for unhandled grammar shapes (documented "fail loud" policy) rather than silent misformatting.
  • No sensitive data in logs ✅ — FormatterWarning messages only ever include line/column/wrap-context text.
  • CI workflow (corpus-gate job) ✅ — pins the third-party checkout (senzing-garage/senzing-commons-java) to a release tag rather than main, uses persist-credentials: false on both checkouts, permissions: contents: read. Reasonable posture.
  • Minor inconsistency (not a security issue): .github/workflows/pytest.yaml:41 uses actions/setup-python@v7.0.0 in the main job but the new corpus-gate job at line 87 uses actions/setup-python@v6.2.0. Both actions/checkout calls are consistently pinned to v7.0.1; only setup-python drifted. Worth aligning for consistency, though not a functional or security problem.

Summary

Overall this is a well-tested, extensively documented release. Two real defects worth fixing before merge: the self-contradicting docstring in _emit_formal_parameters, and the missing overflow advisory in _emit_field_access. One doc-hygiene fix: the wrong fixture name in 070_REMAINING_SCOPE.md. Everything else (security, CI, CHANGELOG accuracy, CommonMark formatting) checks out clean.


Review Part 2 of 3

Code Review — Part 2 of 3

Reviewing the format_java.py core formatter changes (arg-list wrap engine, method-chain wrap engine, nested-call rules, variable-declarator tail-reserve handling) plus new/renamed test fixtures and requirements.txt.

Code Quality

✅ Style conventions — Consistent with the file's existing (very verbose, rationale-heavy) documentation style. No formatting inconsistencies observed.

✅ No commented-out code — All large comment blocks are prose/rationale, not disabled code.

⚠️ Meaningful names — Minor: emit_p4_packed() (tooling/scripts/format_java.py, ~line 9812 in this diff) implements what the surrounding comments and the spec call "P2b" — the function's own docstring says Named emit_p4_packed for historical reasons; the spec calls it priority 2b. This is self-acknowledged, so not a real defect, but a maintainer grepping for "P2" logic will miss it. Low severity.

✅ DRY — This diff is largely a DRY improvement over the prior version:

  • _arg_owns_its_rows, _is_block_body_lambda, _is_anonymous_class, _is_nested_or_chained_call factor out logic that was previously duplicated/inline across the P1/P2/P3 tiers and the chain-wrap discriminator.
  • _extra_tail_reserve(...) context manager (used 4× in _emit_variable_declarator, ~lines 11292-11431) replaces what would otherwise be repeated manual set_tail_reserve/restore boilerplate.
  • _shift() helper (new, inside _emit_argument_list, ~line 9155) unifies what were two separate shift implementations in the old code.

Defects / edge cases — No confirmed logic bugs found in this chunk. Two items I could not fully verify because they depend on code outside this diff slice (the Emitter class definition, presumably in part 1 or 3):

  • emitter._anchor_escaped (introduced at format_java.py ~line 9430, consumed at ~line 11350-11400) relies on Emitter.snapshot()/restore() correctly saving/restoring this new field. The comment at ~line 11390 ("Only this exit needs it... emitter.restore(saved) ... accumulates the re-emission's own escapes") is coherent if snapshot()/restore() include the new attribute — I could not see that definition in this part of the diff to confirm. Worth a quick grep to confirm _anchor_escaped is included in Emitter's snapshot/restore tuple.
  • Similarly, _emit_receiver_parameter is registered into _NODE_EMITTERS (~line 11594) but its definition isn't in this diff chunk — presumably defined in part 1/3. Fixture method_decl_wrap/06_receiver_parameter_preserved (input == expected verbatim) suggests it's a straightforward passthrough; nothing to flag, just noting it's unverified here.

N/A — CLAUDE.md: no .claude/CLAUDE.md changes present in this part of the diff.

Testing

✅ Unit tests for new functionsTestIsNestedOrChainedCall, TestIsAnonymousClass, TestGroupInlineTags, TestSplitsInlineTag, TestMinRaggedLines, TestJavadocBalancedReflow, TestJavadocReflowIsBoundary all added, with good positive/negative coverage including the "deliberately not yet handled" shapes (parenthesized expr, cast, ternary, binary, field/array access) explicitly locked as False via test_traversal.

✅ Regression coveragetest_installed_versions_match_pins (tooling/scripts/tests/test_format_java.py, ~line 65-85) is a good addition: it catches drift between the pinned requirements.txt version and the actually-installed package, which the comment says was a real gap exploited in a prior review cycle (704 tests passing against a mismatched tree-sitter binding). This directly improves determinism guarantees the file already claims to provide.

✅ Golden fixture coverage — ~20 new/renamed fixtures under arg_list_wrap/, nested_call_wrap/, method_chain_wrap/, source_preserve_reanchor/, javadoc_reflow/, etc., covering the new escalation rules (arg0 wraps → whole list breaks, nested/chained call suppresses greedy tiers, chain-as-sole-argument tail anchoring, idempotency locks). Notably, fixtures were renamed (not just added) where old behavior changed (e.g. 18_p1f_factory_deep_dot18_factory_chain_breaks_at_first_dot), which correctly reflects the removed P1F tier rather than leaving a stale/misleading fixture name.

✅ Test cleanupTestEstimateNormalize and TestArgListSingleLineEstimate were deleted along with the _estimate_normalize/_arg_list_single_line_estimate functions they tested (removed as part of retiring the width-based source-preservation fallback). No orphaned tests left behind.

Coverage >80% — Cannot verify a percentage from a diff alone; the breadth of new fixtures/tests strongly suggests good coverage of the new branches introduced.

Documentation

N/A in this chunk — No README/CHANGELOG.md/markdown changes appear in this part of the diff; requirements.txt comment was updated to reflect the new pin and cross-reference the new TestGrammarVersionPins/pin-drift test, which is accurate and helpful.

Inline comments for complex logic — ✅ Exceptionally thorough — every new predicate/tier (_is_nested_or_chained_call, _arg_owns_its_rows, emit_p4_packed, the P1/P2/P3 "argument breaks → list breaks" escalation) documents the concrete before/after shape it fixes and why alternatives were rejected (e.g., the measured "reserve only last argument" alternative in emit_p4_packed, or the rejected geometric predicate mentioned in _arg_list_takes_source_preserve_path).

Security

✅ No hardcoded credentials, no sensitive logging, no license files — Scanned the diff text for .lic files and strings starting with AQAAAD; none present. This diff touches only formatter logic, Python tests, Java test fixtures, and requirements.txt.

N/A — input validation / error handling — This is a pure AST-transform code path operating on already-parsed source; no new external inputs, network calls, or user-facing error paths introduced.


Summary

This is a well-engineered, heavily self-documented change with strong test/fixture coverage for every new behavior. I found no confirmed defects in this chunk — only two low-confidence items that depend on the Emitter class definition not shown in this diff slice (worth a quick manual check that snapshot()/restore() cover the new _anchor_escaped field), and one cosmetic naming nit (emit_p4_packed vs. spec's "P2b").


Review Part 3 of 3

PR Code Review — Part 3 of 3

Scope note: This fragment contains only Python test code (pytest test classes for format_java.py's Javadoc reflow, declaration-semicolon reserve, and pass-convergence logic). No diff hunk headers (diff --git / @@ markers) are included in this excerpt, so I cannot map findings to absolute file:line numbers — line numbers below are estimated from context and should be verified against the actual file.

Code Quality

  • Style conventions: Consistent PEP8-style naming (snake_case methods, PascalCase test classes), consistent with pytest idioms (@pytest.mark.parametrize, class-scoped fixtures via properties).

  • No commented-out code: All comments/docstrings are explanatory prose about why a test exists, not disabled code.

  • Meaningful names: Test names are unusually descriptive and self-documenting (e.g., test_semicolon_does_not_land_in_column_81, test_each_layout_is_a_fixed_point_after_one_pass), and class docstrings explain the underlying defect being regression-tested. This is a strong pattern.

  • DRY: FOR_HEADER/WRAPPED_CONDITION are pulled from shared fixture files rather than duplicated inline strings, explicitly to prevent drift between test and fixture (TestSecondPassConvergence). Good.

  • ⚠️ Defect / accepted-debt callout: In TestDeclarationSemicolonReserve.test_still_reports_a_value_with_no_split_point, the test asserts "max line width 86" and documents inline:

    "86, not 87, because a declarator-level advisory for the same construct de-duplicates the post-semicolon one away and the survivor reports one column short. Pre-existing."

    This encodes a known off-by-one bug as expected behavior rather than fixing it. It's honestly documented, which is good practice, but it means a real (if minor) defect in the warning message is being locked in by a test rather than tracked/fixed. Recommend filing a follow-up issue if one doesn't already exist, so "Pre-existing" doesn't silently become permanent.

  • Defects/edge cases: The new tests directly target real bugs (off-by-one column overflow from unreserved semicolons, a two-cycle oscillation from reserve computation depending on source argument layout, and non-convergence requiring multiple format passes). These are well-chosen regression tests for genuinely subtle correctness issues (idempotency of a formatter is a legitimate, easy-to-miss defect class).

  • N/A .claude/CLAUDE.md review: Not part of this diff chunk.

Testing

  • Unit tests for new functionality: This entire chunk is unit test additions covering the boundary predicate, tag-description reflow skip, semicolon reserve (declarator + array RHS), receiver reserve, and second-pass convergence.
  • Edge cases covered: Explicitly includes the "no split point available" case (test_still_reports_a_value_with_no_split_point), array RHS variant, and both source layouts for the receiver-reserve oscillation bug.
  • Idempotency/convergence testing: TestSecondPassConvergence and TestReceiverReserveIgnoresArgumentLayout.test_each_layout_is_a_fixed_point_after_one_pass explicitly test that formatting is a fixed point after one pass — a valuable property for a code formatter that isn't always tested but should be.
  • ℹ️ Cannot assess overall coverage % from this fragment alone.

Documentation

  • Inline comments for complex logic: Docstrings on each test class explain the why (root cause of the bug being guarded against) rather than restating what the code does — matches good documentation practice for non-obvious invariants.
  • N/A README/API docs/CHANGELOG — not visible in this chunk (would be in parts 1–2 if changed).

Security

  • ✅ No hardcoded credentials, no license files, no sensitive data — none present in this test-only chunk.

Summary

This chunk is test-only and high quality: thorough regression coverage for real formatter defects (off-by-one line-width overflow, reserve-computation oscillation, multi-pass non-convergence), with unusually good self-documentation of why each test exists. The only actionable item is the one flagged ⚠️ above — an off-by-one warning-message bug intentionally locked in as "pre-existing" rather than fixed, which is worth a follow-up ticket if one doesn't already exist.



Automated code review analyzing defects and coding standards

No confirmed defects this round. Three items raised; two acted on,
one declined with evidence.

**Renamed `emit_p4_packed` to `emit_p2b_packed`.** The function
implements what the spec and its own docstring call priority 2b —
it is tried between P2 and P3 — so the `p4` name was actively
misleading and, as CI noted, a maintainer grepping for the P2 family
would miss it. The docstring's "named for historical reasons"
apology goes away with it.

**De-duplicated the catch-parameter modifiers refusal.** The
multi-type wrap engine and `_emit_catch_formal_parameter` each
carried their own copy of the same `NotImplementedError`, so
relaxing one when annotation support lands would silently leave the
other in force. Now one `_refuse_catch_parameter_modifiers` helper
called from both; a single copy of the message remains in the file.

**Declined the multi-catch fixture-coverage finding.** CI reported
that `multi_catch_wrap/` has no 4+ exception-type or deeply-nested
case. It has both:
`01_multi_catch_paren_align_when_inline_overflows` carries four types
(`ClassNotFoundException | NoSuchMethodException |
InvocationTargetException | IllegalAccessException`) and sits nested
inside an `if` within a method. I wrote the suggested fixture before
checking, found it duplicated fixture 01's shape with only the names
changed, and removed it rather than add the redundant coverage CI
flags elsewhere. `enhanced_for_wrap/` likewise already covers a
nested generic iterable and an over-long header.

Also verified, since CI raised it as low-confidence: `_anchor_escaped`
is in `Emitter.__slots__`, in the `snapshot()` tuple and in
`restore()`'s unpacking, so the speculative-emission contract does
cover it.

797/797. Output untouched — token streams identical in all 504 files.
Corpus unchanged: 1571 over-80, 288 advisories, 1 second-pass.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Senzing Code Review — 0.7.0 Formatter Release

Reviewed via the local checklist (build-resources/claude/pr-prompt.md, network fetch not needed since it's cached on disk and identical to what was already provided). Scope: full diff already committed at HEAD (tooling/scripts/format_java.py, CHANGELOG.md, new/changed docs under docs/faqs/building/, docs/java-coding-standards.md, .github/workflows/pytest.yaml, .github/dependabot.yml, .vscode/cspell.json, .claude/070_REMAINING_SCOPE.md). This is an unusually well-self-documented release (extensive rationale comments and a matching CHANGELOG); most of the checklist passes cleanly. Findings below are the exceptions, confirmed by direct inspection.

Code Quality

  • Stale/self-contradicting docstringtooling/scripts/format_java.py:8108-8114. _emit_formal_parameters's docstring says receiver parameters are "NOT supported and, worse, are silently DROPPED... no emitter is registered for receiver_parameter." This is false as of this PR: _emit_receiver_parameter is defined at format_java.py:8316, registered in the dispatch table at format_java.py:11441, and receiver_parameter is already included in the params filter a few lines below the stale comment (format_java.py:8125-8128). The new function's own docstring even says "Before this existed the parameter was simply DROPPED" — confirming the fix landed but this caller's docstring wasn't updated to match. Low risk but worth a one-line fix; a future reader could "fix" an already-fixed bug based on this.
  • Stale project doc contradicts the release's own CHANGELOG.claude/070_REMAINING_SCOPE.md:104-114. The file's header claims "All four are resolved in 0.7.0. Nothing in this document is outstanding," yet its "Deferred to 0.8" list still names two items that CHANGELOG.md documents as shipped within 0.7.0:
    • "Javadoc reflow distribution for paragraphs of three or more lines, including inline-tag atomicity" (line 109-110) — but CHANGELOG.md's "Javadoc paragraphs of any length distribute, and inline tags stay whole" section explicitly says both were "carried on the 0.8 backlog and both resolved here."
    • "The six files that still need a second pass to settle" (line 111) — but CHANGELOG.md ("Five of the six files that needed a second pass now settle on the first," and the Verification section's "26 files needed a second pass under 0.6.0, 1 under this release") says the count is down to one file, not six.
      This looks like a doc that wasn't updated after later review rounds folded more fixes into the same release. Worth reconciling before merge since this file is explicitly meant to be the authoritative "what's left" record.
  • ✅ No commented-out code found (only the project's characteristic long explanatory comments, which are deliberate style here, not dead code).
  • ✅ Meaningful names throughout the new machinery (_arg_owns_its_rows, _is_nested_or_chained_call, p1_invalid_wrap, etc.) — consistent with existing naming conventions.
  • ⚠️ DRY: _emit_class_body_members, _emit_interface_body_members, and _emit_indented_member_list (format_java.py:1557-1618, 7683-7717, 2815-2852) now implement near-identical iteration loops (push/pop indent, blank-line preservation, _attach_trailing_side_comments, index/prev bookkeeping). The interface-body version even comments "mirrors _emit_class_body_members" rather than sharing code — this is the exact class of duplication that let the pre-0.7.0 side-comment gap exist in one copy but not the others. Not a defect, but a real simplification opportunity given the changelog itself flags this pattern as a past source of divergence bugs.
  • ⚠️ Possible, unverified: in the argument-list cascade, P1 and P3 escalate the entire candidate when an argument invalid-wraps, but P2 (emit_p2_greedy, format_java.py ~9596-9613) patches only the offending argument in place and relies on the pre-existing two-line cap (p2_line_count <= 1) to catch any resulting bad shape. I traced several scenarios and couldn't construct a concrete failing input, so this is flagged as worth a targeted fixture rather than a confirmed bug.
  • No other logic defects, off-by-one errors, or state-restore bugs found in the areas most likely to hide them: the 11-field snapshot()/restore() tuple, the _anchor_escaped/_raw_rows_emitted flag save/reset/restore pairing, the p3_arg_escaped off-by-one fix, and the _min_ragged_lines DP / _javadoc_reflow_is_stable convergence simulation all check out correct and terminate.

Testing

  • ✅ Substantial new test coverage claimed and plausible given the code added (fixture pairs, 41 new unit tests for reflow helpers, idempotency-guard-revert verification described in CHANGELOG). Not independently re-run here (no test execution performed), so this is a documentation-consistency check, not a verified pass/fail.
  • ⚠️ Not verifiable from the diff alone whether coverage stays >80% — no coverage report included in this changeset.

Documentation

  • CHANGELOG.md retains Keep-a-Changelog structure (empty [Unreleased], dated version headers, no collision with the existing [0.6.0] entry).
  • ✅ New FAQ docs (formatter-python-environment.md, source-preservation-history.md) are internally consistent with each other and with docs/java-coding-standards.md's new sections; no broken relative links found.
  • ⚠️ Minor stylistic inconsistency: source-preservation-history.md's "Related" section cites other docs as plain inline code (`building/java-formatting-standards`) rather than markdown links, unlike the "See also" sections in the other two new FAQ files — cosmetic only.
  • No CommonMark violations found (heading levels step correctly, consistent - bullets, no stray trailing whitespace) in the changed markdown files.

Security

  • ✅ No hardcoded credentials found.
  • ✅ No .lic files anywhere in the repo; no strings starting with AQAAAD except as literal documentation of the checklist rule itself (e.g. build-resources/.claude/CLAUDE.md, build-resources/.vscode/cspell.json) — confirmed clean.
  • .claude/070_REMAINING_SCOPE.md contains no machine-specific paths, usernames, or local config — safe as general project memory (aside from the staleness issue noted above).
  • No eval/exec/unsafe deserialization introduced.

CI/Build

  • Version drift within the same workflow file.github/workflows/pytest.yaml:41 pins actions/setup-python@v7.0.0 in the pre-existing pytest job, while the new corpus-gate job added by this PR at line 87 pins actions/setup-python@v6.2.0 — an older tag, in the same file, added in the same PR. Likely a copy/paste oversight rather than a deliberate choice; worth aligning to v7.0.0 unless there's a reason for the older pin. (Could not verify tag validity offline — network access disabled — but the internal inconsistency is real regardless.)
  • dependabot.yml duplicate-entry removal and the new corpus-gate job's workspace path composition (.corpus checkout path + SENZING_JAVA_FUZZ_CORPUS=${{ github.workspace }}/.corpus/src) are internally consistent.

Summary: two real, fixable documentation defects (the stale _emit_formal_parameters docstring and the stale "Deferred to 0.8" list in 070_REMAINING_SCOPE.md), one CI version-pin inconsistency worth a quick fix, one DRY opportunity flagged but not blocking, and one unverified possible edge case in the P2 argument cascade worth a fixture check. No security issues, no confirmed runtime bugs, no CommonMark/markdown formatting problems.


Review Part 2 of 3

I'll present the review directly here instead of writing to file.

Senzing Code Review — Part 2 of 3

Scope of this chunk: the bulk of tooling/scripts/format_java.py's argument-list / method-chain wrap cascade (source-preservation, nested-call wrap rules 1-3, emit_p2b_packed, variable-declarator inline-orphan detection, receiver-parameter dispatch registration), tooling/scripts/requirements.txt, dozens of new/renamed test fixtures, and additions to tooling/scripts/tests/test_format_java.py. This is a fragment of a larger diff (parts 1 and 3 not shown here), so findings are limited to what's visible in this fragment; several checklist items (README, CHANGELOG, most markdown docs, CI workflow files) aren't present here and can't be evaluated from this chunk alone.

Code Quality

  • ⚠️ Redundant/dead conditionformat_java.py, in _emit_method_chain_wrapped (the greedy dot-aligned tier guard, right after the removed P1F block):
    if (
        not chain_is_sole_arg
        and head is not None
        and _chain_segments_share_method_name(source, segments)
    ):
    chain_is_sole_arg is only ever set True inside if chain_is_positional_arg and head is None: — it can be True only when head is None. This guard also requires head is not None, which is mutually exclusive with chain_is_sole_arg being True. So not chain_is_sole_arg is always True whenever head is not None holds — the clause is dead weight here, unlike its other two uses on the P3F and P2 tiers just below, which don't also gate on head. Not a behavior bug (the branch evaluates identically with or without it), but it reads as if headless sole-arg chains are being excluded here too, when they can never reach this branch at all. Worth deleting the clause or adding a one-line note that it's intentionally redundant.
  • ✅ New structural predicates (_is_block_body_lambda, _is_anonymous_class, _arg_owns_its_rows, _is_nested_or_chained_call) are well-named, single-purpose, and each documents the specific source-layout-dependent oscillation bug it replaces.
  • _shift, factored out of _emit_argument_list's source-preserve reanchoring, is a faithful extraction of the previous inline loop — verified line-by-line, no behavior drift.
  • ✅ Tail-reserve bookkeeping added around _emit_node calls (emit_p4_single_arg_block_indent, emit_p4_multi_arg, _emit_variable_declarator, _emit_variable_declarator_with_array_rhs) consistently uses save/restore in try/finally, so an exception mid-emit can't leave reserve state corrupted.
  • _emit_variable_declarator's _anchor_escaped flag save/restore is asymmetric by design (only restored on the commit path, not the backtrack path) and the comment explains why — a genuinely non-obvious invariant, well documented.
  • No commented-out code, no hardcoded credentials, no unsafe eval/exec in this chunk.

Testing

  • ✅ Strong fixture coverage for every new rule: nested-call wrap rules 1-3 (nested_call_wrap/0109), the P2b packed tier (arg_list_wrap/17), whole-list escalation on any-unfittable-argument (arg_list_wrap/16), the "argument breaks the list breaks" rule at P1/P2/P3 (arg_list_wrap/05,06,18,20), single-row inline-comment preservation (comment_preservation/10_single_row_inline_argument_comment — the fixture directly proving the comment-preservation reordering fix), reanchor-on-dedent (source_preserve_reanchor/02), and chain-RHS orphan backoff (method_chain_wrap/21).
  • ✅ New unit tests (TestIsNestedOrChainedCall, TestIsAnonymousClass) exercise the traversal's edge cases (block-lambda opacity, non-embedded calls) thoroughly.
  • test_installed_versions_match_pins closes a real gap: the pre-existing tests only compared requirements.txt to GRAMMAR_VERSION against each other, never against the actually-installed environment. The docstring cites a specific, credible failure mode (704 passing tests against a mismatched installed version).
  • ⚠️ Coverage percentage isn't verifiable from a diff alone.

Documentation

  • Not evaluable from this chunk — no README/CHANGELOG/markdown-doc hunks appear in this fragment.
  • requirements.txt's updated comment accurately describes the new GRAMMAR_VERSION mirroring contract.

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings in this chunk.
  • ✅ No new I/O, deserialization, or subprocess surface introduced.

Summary

One confirmed-but-harmless finding: a dead not chain_is_sole_arg clause in the method-chain greedy-tier guard, always trivially true given the accompanying head is not None check — worth a quick cleanup for clarity, not a functional bug. Everything else checked out correct against its own documentation and accompanying fixtures: the source-preservation simplification, the three nested-call wrap rules, the P2b tier, and the tail-reserve/anchor-escape bookkeeping. No security issues found. Documentation/CHANGELOG items are out of scope for this fragment (not present in the diff shown).


Review Part 3 of 3

Code Review — PR Diff (Part 3 of 3)

Note: I only have this final chunk of the diff (test additions to what appears to be test_format_java.py, exercising format_java.py's javadoc-reflow and line-wrap logic). Findings about repo-wide items (README, CHANGELOG, CLAUDE.md, overall coverage %) can't be confirmed from this chunk alone — flagged as "not visible" below rather than assumed passing.

Code Quality

  • Style conventions: ✅ Consistent with pytest/PEP8 idioms — type hints on all methods, @pytest.mark.parametrize used appropriately, docstrings on every test class explaining intent.
  • No commented-out code: ✅ All comments are explanatory (e.g. the # 86, not 87, because... note), not dead code.
  • Meaningful names: ✅ Test names are unusually descriptive (test_semicolon_does_not_land_in_column_81, test_array_rhs_also_reserves_the_semicolon) and self-document the regression being guarded.
  • DRY: ✅ FOR_HEADER/WRAPPED_CONDITION are read from shared fixture files via a class-level _FIXTURES path rather than duplicated inline, explicitly to prevent drift ("Read from the fixture so the two cannot drift apart").
  • Defects: ⚠️ One item worth flagging, not blocking: in test_still_reports_a_value_with_no_split_point, the test explicitly documents and locks in a known off-by-one bug in warning-column reporting ("86, not 87... Pre-existing"). This is transparently labeled pre-existing/out-of-scope, but since the test now asserts the buggy value ("max line width 86"), it will silently prevent that bug from ever being fixed without a test failure forcing someone to notice and re-evaluate. Consider a tracking issue reference in the comment so the debt isn't invisible.
  • Weak assertion: TestTagDescriptionSkipsStabilityCheck.test_skip_changes_the_outcome only asserts with_skip != with_check without asserting either value's actual shape. Given the docstring scope ("Guards the flag") this is a deliberate, minimal regression guard rather than a correctness test — acceptable, but worth being aware it wouldn't catch a wrong-but-still-different output.
  • CLAUDE.md: Not present in this chunk — can't evaluate.

Testing

  • Unit tests for new functions: ✅ Strong — _javadoc_reflow_is_boundary, _javadoc_balanced_reflow, declarator/array-RHS semicolon reserve, and chain-receiver reserve computation all get dedicated regression tests with root-cause docstrings.
  • Edge cases: ✅ Good coverage — empty line as boundary, over-long unsplittable identifier, array RHS path, two-cycle fixed-point convergence (both layouts converging to identical output, and each becoming a fixed point after one pass).
  • Test coverage > 80%: Not determinable from this chunk alone.

Documentation

  • Inline comments for complex logic: ✅ Excellent — docstrings for TestDeclarationSemicolonReserve and TestReceiverReserveIgnoresArgumentLayout explain the exact mechanism of the bug (off-by-one reserve, two-cycle oscillation) rather than just what the test does.
  • README / CHANGELOG.md / API docs: Not visible in this chunk — verify these were updated elsewhere in the PR given this is versioned as a 0.7.0 change per recent commit history.

Security

  • ✅ No hardcoded credentials, no .lic files, no sensitive data — this chunk is pure test code operating on synthetic Java snippets.

Summary

No defects introduced by this chunk; it's well-constructed regression testing with unusually good root-cause documentation. Only actionable note: the deliberately-preserved off-by-one warning bug should have a tracking reference so it isn't forgotten. I'd need parts 1–2 (or the full diff) to assess README/CHANGELOG/CLAUDE.md and overall coverage checklist items.



Automated code review analyzing defects and coding standards

Three ❌ findings, all real, plus four ⚠️ items. No correctness defect.

**Stale docstring claiming a bug this PR fixed.**
`_emit_formal_parameters` still said receiver parameters "are NOT
supported and, worse, are silently DROPPED... no emitter is
registered for `receiver_parameter`" — while `_emit_receiver_parameter`
exists, is registered, and `receiver_parameter` is kept by the filter
a few lines below. As CI put it, a future reader could "fix" an
already-fixed bug from this. Rewritten, and it now also records the
padding carve-out.

**`.claude/070_REMAINING_SCOPE.md` contradicted the CHANGELOG.** Its
header said nothing was outstanding while its "Deferred to 0.8" list
still named three items shipped inside 0.7.0. Split into what was
folded in after the census was written and what genuinely remains —
the latter being the one chain-with-lambda file whose mechanism is
still unidentified.

**`actions/setup-python` drift**: `v7.0.0` in the pytest job, `v6.2.0`
in the corpus-gate job added by this same PR. Aligned on `v7.0.0`.

**Dead clause removed.** The greedy dot-aligned chain tier guarded on
`not chain_is_sole_arg and head is not None`, but
`chain_is_sole_arg` is only ever assigned inside
`if chain_is_positional_arg and head is None`, so it cannot be True
when `head is not None`. Verified by measurement: removing it leaves
output byte-identical. It read as though headless sole-argument
chains were excluded from that tier, when they can never reach it.

**P2's local patch is safe, and now says why.** CI flagged that P2
patches only the offending argument where P1 and P3 escalate the
whole candidate, and could not construct a failure. Neither could I,
and the reason is structural: the caller's two-line cap plus its
width check mean a patched argument that still wraps spills to a
second continuation line and the tier is rejected, while one that
fits but overflows is rejected on width. Documented rather than
given a fixture that cannot fail.

**Accepted debt made visible.** The test asserting "max line width
86" now says explicitly that the assertion must move to 87 when the
deduped-advisory off-by-one is fixed, and that it is asserted so the
fix cannot land unnoticed. Tracked as its own task.

**Declined for this release**: unifying the three near-identical
member-iteration loops. A genuine simplification, and CI is right
that this duplication class caused the pre-0.7.0 side-comment gap —
but it touches the most-exercised emission path in a release that is
otherwise converged and green. Tracked for early 0.8 with a
byte-identical-output gate.

Also: FAQ "Related" entries now use markdown links, matching the
sibling FAQs.

797/797. Output untouched — token streams identical in all 504 files,
zero files differ. Corpus: 1571 over-80, 288 advisories, 1
second-pass, 0 third-pass.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Verification complete. I've now personally confirmed the top findings by reading the actual source. Compiling the final consolidated review.


Senzing Code Review — PR #50 (java-coding-standards, 0.7.0 formatter release)

Reviewed against the actual repository state (this PR is already merged into the checked-out working tree: 9451c950960c40, 139 files, +6447/−1034). Findings below were independently verified by reading the real files and, where noted, by hand-tracing execution rather than trusting comments/CHANGELOG claims.

Top findings (ranked)

1. ❌ ~130 lines of dead code in the argument-list source-preserve path, described in the CHANGELOG as active behavior.
tooling/scripts/format_java.py:9121-9289. _arg_list_takes_source_preserve_path (line 8807) returns True only when has_comment or (multi-row and CSOFF). Immediately inside the if at 9121, lines 9150-9161 recompute the exact same two conditions and return on either being true. Since entry into the outer if already guarantees one of them, this inner check is a tautology — everything from line 9162 to 9289 (the target_col/delta/_shift "re-anchor" logic and its own overflow advisory at 9267-9278) can never execute. The CHANGELOG's "Preserved continuation columns are re-anchored" section (and the in-code comments at 9123-9149, 9203-9236) describe this as live, load-bearing behavior; it isn't. I confirmed the dedicated fixture tooling/scripts/tests/fixtures/source_preserve_reanchor/02_continuation_reanchored_on_dedent doesn't actually exercise this path either — its input has no comment and no CSOFF marker, so it never enters the _arg_list_takes_source_preserve_path branch at all; its expected (dedented) output is produced by the ordinary P3 wrap engine, coincidentally. The fixture's name implies coverage of a feature it never touches.

2. ❌ Basic for statements still read source-row layout, contradicting this release's central invariant.
tooling/scripts/format_java.py:6068-6070 and 6211. source_was_multi_row = body.start_point[0] != node.start_point[0] does not test "did the header span multiple source rows" (as its comment at 6049-6067 claims) — it tests whether the body's opening brace sits on a different source row than the for keyword. Traced by hand: for

for (int i = 0; i < arr.length; i++)
{
    process(arr[i]);
}

(a short single-line header with an Allman-style body brace — a very ordinary shape in non-canonical input), source_was_multi_row evaluates True even though the header fits in ~40 columns. This skips the single-line attempt entirely and forces the fully-expanded paren-aligned multi-clause header via emit_header_paren_aligned() (line 6144), then forces Allman brace placement via the same conflated check at line 6211. Worse: after this incorrect reformat, the header genuinely is multi-row, so a second pass reproduces the same wrong shape — it's a stable, convergent, but incorrect fixed point, meaning the project's own idempotency gate cannot catch it. This is the exact bug class ("layout decisions read layout, formatter's own output becomes next pass's source") that this release's headline theme claims to have eliminated, right next to a sibling function (_emit_enhanced_for_statement) that was explicitly fixed to avoid it in this same PR. No fixture directory tests a short-header/Allman-body basic for loop (allman_braces/06_for_wrapped_header tests a genuinely multi-row header instead), which is consistent with why 4 review rounds + the 504-file corpus trial didn't surface it — likely because the trial corpus already uses this project's own K&R-by-default convention. I was unable to execute the formatter in this sandbox to get a runtime repro (permission-gated), so this is a rigorous static trace, not an executed reproduction.

3. ❌ array_creation_expression missing from _COMPUTED_RECEIVER_TYPES — misclassifies a valid computed field-access receiver.
tooling/scripts/format_java.py:8449-8455 (used by _field_access_receiver_is_computed, 8465-8474, called from _emit_field_access, 8531). The set lists method_invocation, array_access, object_creation_expression, parenthesized_expression, cast_expression, but not array_creation_expression — a real, separately-dispatched node type in this same file (_emit_array_creation_expression, registered at line 11579; also referenced at 6507/6522). So new int[computeSize()].length is treated as a "name" rather than a computed value, and _emit_field_access refuses to break before the dot even when the line overflows — it silently commits the overflowing inline form (line 8531-8532 returns with no advisory; see finding 4).

4. ❌ _emit_field_access never fires an overflow advisory, unlike every sibling wrap engine added in 0.7.0.
tooling/scripts/format_java.py:8477-8565. Both silent-decline exits — the non-computed-receiver early return (8531-8532) and the "breaking bought nothing, revert to inline" path (8560-8565) — commit a possibly-overflowing line with no call to _fire_wrap_overflow_advisory. This directly undercuts the CHANGELOG's own "Advisories where the formatter declines to reflow" section, which frames exactly this class of silent-overflow gap (parameter lists, javadoc structural lines) as fixed in 0.7.0; field access was missed.

5. ❌ The new "convergence detector" script in the docs can never actually detect non-convergence.
docs/faqs/building/consumer-trial-checklist.md:137-143. The loop commits after every non-quiet pass (git commit -aqm "format pass $pass"), so the working tree is clean by the time the loop exits — whether it broke early (quiet diff) or fell through after 4 iterations (which itself ends in a commit). The post-loop check git diff --quiet || echo "NOT CONVERGED..." therefore always sees a clean tree and never fires, even for a file that oscillates or keeps changing through all 4 passes — precisely the case this script exists to catch. Traced by hand; not executed.

Medium/lower-confidence findings

  • Single-type catch never fires a width/overflow advisory (format_java.py:5863-5871) — unlike the multi-catch path added right beside it in this same PR. Docstring calls it "pre-existing behavior preserved," so likely a known, accepted gap rather than a new regression.
  • No whole-file runtime idempotency check. format_source (11616-11667) does a single pass with no fixed-point verification; the only stability simulation in the file is local to javadoc reflow (_javadoc_reflow_is_stable). Non-javadoc non-idempotency (e.g. finding #1 Initial content #2 above) has no runtime safety net — it's caught only by the external fixture/fuzz suite, if a fixture happens to exercise it.
  • Trivial dead snapshot, p2_saved = emitter.snapshot() at ~line 10937 in _emit_method_chain_wrapped, taken unconditionally but never used/restored when chain_is_sole_arg is True. Harmless, just wasted/misleading.
  • _formal_param_name_col_offset measures each parameter prefix's width starting one column left of where P2 actually emits it (paren_col - 1 vs paren_col) — could misjudge alignment only for a parameter type with its own column-dependent internal wrap sitting exactly at that boundary. Narrow edge case.
  • _JAVADOC_BLOCK_TOKENS (3383-3387) omits <h1>-<h6>, <blockquote>, <dl>/<dt>/<dd>, <div> — these get folded into ordinary prose reflow without the atomicity protection <p>/<ul>/<pre> get. Rare trigger.
  • No recursion-depth guard anywhere in the file (pre-existing pattern across the whole recursive-descent emitter, not new to this PR, but this PR adds extra stack frames per nesting level in several new wrap tiers — e.g. nested-call-wrap's speculative emit_p1()).
  • Speculative-emit indent-stack imbalance if a candidate raises mid-emit (_push_indent_to_col/pop_indent pairs with no try/finally in several P4/packed emitters) — generic pre-existing pattern, needs an unexpected exception to trigger.
  • _min_ragged_lines DP is O(n²) — unreachable in practice for hand-written javadoc, only relevant to adversarial/generated input.
  • Style nit: git diff --check flags one new blank line at EOF, tooling/scripts/tests/test_format_java.py:4667.

Checklist summary

Code Quality

  • Style conventions: ✅ (consistent, idiomatic; _refuse_catch_parameter_modifiers DRY extraction is a good catch-refusal consolidation)
  • No commented-out code: ✅ (checked directly)
  • Meaningful names: ✅
  • DRY: ✅ mostly — but see finding 1, dead code that duplicates/shadows logic already handled by the early return above it
  • Defects: ❌ — findings 1-5 above
  • ./.claude/CLAUDE.md: N/A — no such file exists in this repo

Testing

  • Unit tests for new functions: ✅ — 276 def test_ functions, 234 fixture pairs (confirmed by find, matches the updated docstring claim exactly), with dedicated fixture directories for essentially every new feature
  • Edge cases: mostly ✅, but finding 1's dead code has a fixture whose name implies coverage it doesn't provide, and finding 2's trigger shape (short-header Allman for) has no fixture at all
  • Coverage >80%: likely ✅ based on static evidence; could not execute pytest/coverage tooling in this sandbox (command execution required approval that wasn't grantable in this run)

Documentation

  • CHANGELOG.md updated: ✅ present and extensive, but contains at least one factually incorrect claim (finding 1 — "preserved continuation columns are re-anchored" describes unreachable code)
  • API/spec docs (docs/java-coding-standards.md, docs/faqs/building/*.md): ✅ updated and cross-consistent with the code, except the convergence-detector script bug (finding 5)
  • CommonMark/whitespace: ✅ — git diff --check found zero trailing-whitespace issues in any changed .md file; heading hierarchy and fenced code blocks are well-formed by manual inspection
  • Inline comments: ✅ extensive and generally accurate, except where they describe the dead code in finding 1 as live

Security

  • No hardcoded credentials: ✅ (grepped the diff)
  • No .lic files or AQAAAD-prefixed strings: ✅ (the only AQAAAD hits are in untracked scratch files that just quote this review checklist's own instruction text, not real license data)
  • No eval/exec/subprocess/shell invocation: ✅ (confirmed by sub-review and my own grep)
  • Error handling: ✅ appropriate use of NotImplementedError refusals for unhandled grammar shapes, consistent with existing file conventions
  • No sensitive data in logs: ✅ (FormatterWarning messages carry only line/column/shape text)

Review Part 2 of 3

Code Review — Part 2 of 3

Scope note: this segment covers format_java.py's argument-list/method-chain wrap engine, requirements.txt, and the bulk of new/renamed test fixtures. I cross-checked several claims against the actual merged file at HEAD (tooling/scripts/format_java.py, CHANGELOG.md, requirements.txt) rather than relying on the diff hunks alone. No CHANGELOG/README/Javadoc-implementation source is visible in this chunk (likely Part 1/3), so those items are marked N/A here.

Code Quality

  • Style/idioms — ✅. Consistent with the rest of the file; heavy use of rationale comments is this project's established convention (matches prior "absorb CI review round N" commits), not stray clutter.
  • No commented-out code — ✅. All removed code (_estimate_normalize, _arg_list_single_line_estimate, _SEMANTIC_WRAP_ARG_TYPES, _arg_list_has_semantic_multi_row_arg, the 0.6.0 P1F factory-chain tier, _chain_receiver_is_factory) is deleted outright, and matching tests (TestEstimateNormalize, TestArgListSingleLineEstimate) are removed too — clean rather than left dangling.
  • Meaningful names — ✅. _is_nested_or_chained_call, _arg_owns_its_rows, chain_is_positional_arg, p3_arg_invalid_wrap, etc. are all self-describing.
  • DRY — ✅. The new _shift() closure and _extra_tail_reserve context manager (format_java.py:3677, used at lines 11192/11320/11338) replace what used to be repeated push/pop/shift boilerplate.
  • Defects — no confirmed issues found. I specifically chased two candidate bugs and both turned out to be non-issues once checked against the full file:
    • Suspected the nested-call single-arg inline-success branch (format_java.py:9989-10009) skips _fire_wrap_overflow_advisory. Verified: there's a shared fallback call at format_java.py:10197-10199 outside the if len(args)==1 / else block that covers every non-returning path, so this is not a gap.
    • Suspected _anchor_escaped might not survive emitter.restore(), which would break the orphan-detection logic in _emit_variable_declarator (~line 11360-11450). Verified: Emitter.snapshot()/restore() explicitly include _anchor_escaped (format_java.py:531, 572), so the "restore naturally resets the flag, only the commit-and-return path needs a manual reset" comment is accurate.
    • chain_is_sole_arg is only ever assigned inside if chain_is_positional_arg and head is None: (format_java.py:10412-10419), confirming the comment at line 10870 that it can never be true when head is not None — the guard omission at the shared-method-name tier is not a bug.
  • Project CLAUDE.md — not present in this diff chunk; nothing to flag here.

Testing

  • Unit tests for new functions — ✅. TestIsNestedOrChainedCall, TestIsAnonymousClass, TestGroupInlineTags, TestSplitsInlineTag, TestMinRaggedLines, TestJavadocBalancedReflow all directly exercise the new predicates/helpers, including negative cases (curried lambdas, block- vs expression-bodied lambdas, detached nodes).
  • Edge cases — ✅. New fixtures are unusually well-targeted at specific regressions rather than generic smoke tests (e.g. 18_arg0_wraps_so_whole_list_breaks, 19_middle_argument_keeps_full_budget, 21_chain_rhs_backs_off_when_anchor_escapes, 26_receiver_reserve_ignores_arg_layout — each pins down exactly the non-idempotency bug its accompanying comment describes).
  • New test_installed_versions_match_pins (tests/test_format_java.py) — ✅ good addition; it closes a real gap where the two existing pin-consistency tests only compared files to each other, never to the actual environment. Minor note: this test will hard-fail in any environment whose installed tree-sitter/tree-sitter-java don't exactly match the pin (including CI images that haven't been rebuilt yet after this bump) — that's intended per the docstring, but worth flagging as something that could cause a wave of "environment" CI failures right after merge until images pick up the new pin.
  • Coverage % — cannot verify from a diff alone; would need to run pytest --cov.

Documentation

  • CHANGELOG.md — confirmed updated (verified against HEAD; 0.7.0 entry documents the nested-call wrap rules, P2b, the removed width-based source-preservation fallback, and the factory-chain tier removal) — ✅, though the actual diff for it is not in this chunk.
  • requirements.txt — ✅ comment update is accurate and consistent with GRAMMAR_VERSION in format_java.py:114-117 (both say tree-sitter==0.26.0).
  • Inline comments — ✅, arguably the most heavily-commented diff I've seen, every non-obvious rule has a rationale + example. No complaints.
  • Markdown/CommonMark — N/A for this chunk (no .md content shown here).

Security

  • No hardcoded credentials, no .lic files, no sensitive logging — ✅, none present in this chunk (pure formatter/tooling code and Java test fixtures).

Summary

This chunk is a large but internally consistent refactor of the argument-list/method-chain wrap cascade, removing a documented source of non-idempotency (the old width-based source-preservation fallback) and replacing several ad hoc heuristics with structurally-defined predicates (_arg_owns_its_rows, _is_nested_or_chained_call). Every behavior change I checked has a corresponding fixture, and the two things I flagged as possible defects both checked out fine against the actual merged source. I have no unresolved findings from this segment.


Review Part 3 of 3

Code Review — Part 3 of 3

Note on scope: This chunk contains no diff --git / @@ hunk headers, so I can't anchor findings to exact file paths or absolute line numbers — only to the content shown. Content strongly suggests this is a test file for a Java-source formatter (format_java.py), likely something like tests/test_format_java.py, testing internals like _javadoc_balanced_reflow, _javadoc_reflow_is_boundary, and declarator/receiver wrap-width reserves. All content in this chunk is test code only (new Test* classes and methods).

Code Quality

  • Style/naming: Consistent with the surrounding style (parenthesized line-wrapping, snake_case, descriptive class/method names like TestDeclarationSemicolonReserve, TestReceiverReserveIgnoresArgumentLayout). Test names read as specifications rather than generic test_1, test_2.
  • No commented-out code: All comments are explanatory prose (rationale for regression tests), not disabled code.
  • Meaningful names: WRAPPED/INLINE, FOR_HEADER/WRAPPED_CONDITION, _passes, _format are all clear and self-documenting.
  • DRY: _passes helper in TestSecondPassConvergence avoids repeating the multi-pass-format-and-compare boilerplate across two tests. _format in TestDeclarationSemicolonReserve centralizes source-wrapping boilerplate.
  • ⚠️ Potential concern — asserting a known bug rather than fixing it: In test_still_reports_a_value_with_no_split_point, the test hardcodes "max line width 86" with a comment admitting the correct value should be 87, but a pre-existing de-duplication bug suppresses one warning and produces an off-by-one message. The comment says this is "tracked as its own task," which is a reasonable practice (locking in current behavior with a clear paper trail so a future fix is deliberate, not accidental regression-noise). Flagging only so reviewers are aware a known-wrong value is being asserted as correct — worth confirming there's an actual tracked ticket, since the comment references "its own task" without a ticket ID/link.
  • Defects: No logic errors spotted in the new test code itself. The tests exercise real fixed-point/idempotency properties (format_source applied twice yields the same output), which is a good defect-catching pattern for a formatter.
  • N/A: No .claude/CLAUDE.md changes in this chunk.

Testing

  • ✅ These additions are the tests — good coverage of edge cases: javadoc reflow stability/oscillation, boundary detection ({@, <, @, indentation, blank lines), tag-description stability-check skip semantics, semicolon-reserve off-by-one for both scalar and array-RHS declarators, receiver-reserve layout-independence, and second-pass convergence for for-header and condition-wrap cases.
  • ✅ Edge cases are explicit and well-motivated (each docstring explains why the case matters, e.g., the two-cycle oscillation bug, the 81-column semicolon overflow).
  • ⚠️ Cannot verify overall test-coverage percentage from this fragment alone.

Documentation

  • ✅ Docstrings are excellent — each explains the why (a real bug/regression) rather than restating the what, consistent with good practice for regression tests.
  • N/A: README/CHANGELOG/API docs — not shown in this chunk (may be addressed in parts 1–2).
  • One minor nit: class docstrings (e.g., TestReceiverReserveIgnoresArgumentLayout, TestSecondPassConvergence) are quite long/multi-paragraph — reasonable here since they document subtle formatter invariants, but on the edge of what the checklist's "inline comments for complex logic" guidance intends (this is more "design rationale" than "inline comment").

Security

  • ✅ No hardcoded credentials, no license files, no logging of sensitive data — this is pure formatter test code operating on synthetic Java source snippets.

Overall: This is high-quality, well-justified regression-test code. No blocking issues found in this chunk; the one item worth a human's attention is confirming the pre-existing off-by-one bug (86 vs. 87) referenced in test_still_reports_a_value_with_no_split_point has an actual tracking ticket, since the test intentionally locks in the current (admittedly wrong) behavior.



Automated code review analyzing defects and coding standards

…lines

Five findings, four in the formatter. The first is a genuine
formatting defect that a golden file was enshrining.

**Short `for` headers were exploded whenever the brace was Allman.**
`_emit_for_statement` asked "was the source header multi-row?" using
`body.start_point[0] != node.start_point[0]` — the BODY BRACE's row,
which is true for every Allman-braced `for` no matter how short the
header. A 36-column header came out as three paren-aligned clauses,
and it self-perpetuated, since the reformatted header genuinely is
multi-row. `assignment_wrap/02` had a golden locking in the wrong
shape, which is how it survived review until now. The check now
measures the header's own span; the Allman decision keeps the
body-row test under its own name, `body_on_new_row`.

**Removed ~124 unreachable lines** from the argument-list
source-preserve path. After 0.7.0 narrowed preservation to comments
and CSOFF, the branch re-tested exactly those two and returned, so
the column-remap below could never run. Proven twice: tautological by
construction, and by poisoning the block and formatting all 504
corpus files with zero hits. The release notes had described it as
live behavior.

**`array_creation_expression` added to `_COMPUTED_RECEIVER_TYPES`**,
so `new int[computeSize()].length` is treated as a computed receiver
rather than a name.

**Field access advises when it declines.** Both silent exits
committed a possibly-overflowing line without an advisory — the same
gap this release closed for parameter lists and javadoc. Advisories
288 to 289: one previously-silent overflow now reports.

**The trial checklist's convergence detector could not detect
non-convergence.** It committed after every changed pass, so the tree
was clean when the loop exited and the final check never fired — for
exactly the oscillating file it exists to catch.

Corpus: over-80 1571 unchanged, second-pass 1, third-pass 0, net vs
0.6.0 now +1,563 lines across the same 337 files. 797/797.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Code Review — 0.7.0 Formatting Release

Reviewed via three parallel deep-dives across the full diff (~9,400 lines: CI/docs/CHANGELOG, the core format_java.py wrap-engine rewrite, and new unit tests), plus direct verification of the flagged items against the raw diff.

Code Quality

✅ Style conventions — Python style is consistent with the existing codebase; Markdown is consistent elsewhere except one paragraph noted below.

✅ No commented-out code — The very long inline comments are deliberate historical-rationale documentation (a consistent house style in this file), not dead code.

✅ Meaningful variable names — No issues found; names like p1_invalid_wrap, p3_arg_escaped, _arg_owns_its_rows are precise and self-documenting.

✅ DRY_refuse_catch_parameter_modifiers extraction, shared _balanced_reflow_words/_javadoc_balanced_reflow helper, and _attach_trailing_side_comments reuse between class/interface body iterators are good deduplication. No new duplication introduced.

✅ Defects (formatter logic) — Two independent reviewers traced the highest-risk areas — Emitter.snapshot()/restore() completeness for the two new flags (_anchor_escaped, _raw_rows_emitted), the P1/P2/P3/P4 "argument-breaks-the-list" symmetry, the escape-scan off-by-one fix, and the enhanced-for/record-header reserve arithmetic — against the live merged source and found them internally consistent and correctly restored on every path. No snapshot/restore leaks or tier asymmetries found.

❌ CHANGELOG.md — stale summary figure contradicts the release's own final numbers (CHANGELOG.md, intro paragraph, and repeated in the new docs/faqs/building/source-preservation-history.md):

  • The 0.7.0 intro states "Files needing a second pass to settle fall from 26 to 6" and this exact "26 to 6" is repeated in source-preservation-history.md's closing "Related" section.
  • But the release's own dedicated line later in the same file states plainly: "Files needing a second pass: 6 to 1", and the final "Verification" section confirms: "26 files needed a second pass under 0.6.0, 1 under this release."
  • The intro was written before the later "Five of the six second-pass files" fix landed and was never updated — it should say 26 to 1, and the FAQ file inherited the stale number rather than the corrected one.

❌ CHANGELOG.md — numeric contradiction: 288 vs 289 advisories ("A method chain's receiver reserve..." section): states "309 advisories against the 288 this release ships", but every other place in the same file states the shipped total as 289 ("Corpus advisories: 301 to 289"; "taking advisories to 289"). The same paragraph also separately cites "305 advisories" as the count "at the time it was measured" — three different numbers (288/289/305) appear for what should be a single tracked figure, with no explanation reconciling them.

❌ CHANGELOG.md — malformed paragraph wrapping (same "receiver reserve" section, the "The reserve is now 1 + len(name) + 1..." paragraph): line wrapping is irregular compared to the rest of the file's disciplined ~72-column wrap — several isolated 3-5 word lines ("further reintroduces the", "comma-spacing trap documented in...") sit next to one line that runs far longer than the surrounding prose ("below — with layout changes confined to 2 files. The unchanged counts, not output identity,"). Reads like an edit that was never re-flowed; should be re-wrapped with the rest of the file (and would need Prettier/consistent wrapping per the checklist's Markdown item).

✅/ℹ️ Project CLAUDE.md config — Not applicable; no .claude/CLAUDE.md exists in this repo (.claude/ contains only scope docs, commands/, and settings.json).

Testing

New predicates (_is_nested_or_chained_call, _is_anonymous_class, _min_ragged_lines, _javadoc_balanced_reflow, _splits_inline_tag, _javadoc_reflow_is_boundary) all have dedicated parametrized unit tests covering true/false cases, boundary conditions, and regression fixtures for the specific bugs the PR describes (oscillation, off-by-one, stability). Fixture names cited in comments (18_arg0_wraps_so_whole_list_breaks, 26_receiver_reserve_ignores_arg_layout, etc.) were confirmed to exist as real fixture pairs in the diff.

ℹ️ Minor — known bug pinned as an expected test value (tooling/scripts/tests/test_format_java.py, test_still_reports_a_value_with_no_split_point): asserts "max line width 86" in message where 87 would be numerically correct, with a comment explicitly acknowledging this is a pre-existing off-by-one "tracked as its own task." This is a deliberate canary (so a silent fix doesn't go unnoticed) rather than a defect, and it's consistent with the same caveat documented in CHANGELOG.md — flagging only because there's no linked issue/ticket to verify the "tracked" claim, so this could be easy to lose track of.

✅ Edge cases — Not exercised: _min_ragged_lines with max_lines=0 or degenerate input. Low severity, not load-bearing per current call sites.

Documentation

❌ See CHANGELOG findings above (stale "26 to 6" figure, 288/289 contradiction, malformed paragraph).

✅ Inline comments — Spot-checked against the actual code for the riskiest new logic (escape-scan fix, snapshot/restore, reserve arithmetic) and found accurate, not contradictory.

✅ New/changed Markdown FAQs (formatter-python-environment.md, source-preservation-history.md) — Well-formed CommonMark, reasonable structure, no stray whitespace issues found.

✅ Bash snippet correctness (consumer-trial-checklist.md bounded convergence loop) — Verified the converged flag logic by hand: correctly distinguishes "broke early because quiet" from "fell through all 4 passes still dirty," fixing the real bug it describes (git diff --quiet || echo ... after committing every pass can never fire). No defect found.

ℹ️ Minor inconsistency: the older "Gate 5" section (under "What changed in 0.5.0 → 0.5.1") was edited to say "Gate 5 — convergence" and duplicates the format_file.py invocation, but doesn't apply the new bounded-loop rigor that the main "Idempotency" section just introduced elsewhere in the same file — a cosmetic inconsistency between two sections describing the same concept, not a functional bug.

Security

✅ No hardcoded credentials, unsafe eval/exec, path traversal, or logged sensitive data found anywhere across all three chunks.

✅ CRITICAL check — license files: searched the entire diff for .lic file paths and for any string starting with AQAAADnone found. Clear.

✅ Dependency pin changes (tree-sitter 0.25.2→0.26.0) are version-only bumps guarded by a new three-way TestGrammarVersionPins check against drift — a reasonable defensive addition, not a concern.


Summary

This is a very well-documented, internally cross-checked release, and the deep-dive into the actual wrap-engine logic (snapshot/restore discipline, tier symmetry, off-by-one fixes) held up under scrutiny — no functional defects found in format_java.py itself. The concrete, actionable issues are all documentation bookkeeping in CHANGELOG.md: a stale summary figure ("26 to 6" vs the file's own final "26 to 1"), a numeric contradiction (288 vs 289 advisories, in a paragraph that also cites 305), and one paragraph with broken line-wrapping. All three are easy fixes and don't block on code correctness, but should be corrected before tagging since this CHANGELOG is treated as the release's own source of truth (multiple other docs cite figures from it).


Review Part 2 of 3

Code Review — Part 2 of 3 (format_java.py argument-list/chain/field-access wrap engine, requirements.txt, test fixtures)

Code Quality

✅ Style conventions — Consistent with the rest of the file; naming (p1_invalid_wrap, p3_arg_escaped, _arg_owns_its_rows, chain_is_sole_arg) is precise and matches house style.

✅ No commented-out code — The long prose comments are deliberate historical-rationale documentation, consistent with the rest of the file.

✅ Meaningful variable names — No issues.

✅ DRY — Good consolidation: _arg_owns_its_rows replaces the old _SEMANTIC_WRAP_ARG_TYPES/_arg_list_has_semantic_multi_row_arg duplication and is now shared by the P1/P2/P3 "argument breaks the list" checks and _segment_emit_is_legitimately_multi_line. Removing _arg_list_single_line_estimate/_estimate_normalize/_SEMANTIC_WRAP_ARG_TYPES (~124 dead lines per the commit message) along with their now-unreachable branch in _arg_list_takes_source_preserve_path is a clean simplification, and the corresponding TestEstimateNormalize/TestArgListSingleLineEstimate test classes were correctly removed alongside.

❌ Defect — _emit_field_access can silently ship an overflowing line with no advisory (tooling/scripts/format_java.py:8496-8608, specifically the fall-through after line 8589):

  • After the "break before the dot" candidate is emitted (lines 8581–8589), the function only fires _fire_wrap_overflow_advisory in the if emitter.last_lines_max_width(saved[0]) >= inline_width: branch (line 8590) — i.e. only when breaking is no better than the inline form, in which case it reverts to inline and warns.
  • When breaking does reduce the max width but the result still exceeds _MAX_LINE (e.g. the receiver itself is an unsplittable call/expression whose own rendered width already exceeds 80, so breaking the dot away only trims the trailing .field but the receiver's own line is still too wide), the >= inline_width condition is false, so the function falls off the end of the function body with the overflowing broken form committed and no FormatterWarning is ever fired.
  • Every other terminal commit point in this file (the two other branches in this very function, _emit_variable_declarator's Step 3/4, every try_priorities-based cascade) follows a strict "commit-and-warn" discipline: the last-resort candidate is always followed by an unconditional _fire_wrap_overflow_advisory call (which itself is a no-op if the committed lines actually fit — see its docstring at line 793). This function breaks that pattern by only calling it inside one of the two conditional branches rather than unconditionally after the whole cascade.
  • Concretely reachable: int n = extremelyLongUnsplittableMethodCallThatByItselfAlreadyExceedsEightyColumnsXXXX().someField; — the receiver has no arguments to wrap, so breaking the dot cannot rescue it, breaking's width is still less than the (even wider) inline width, and the function returns silently with an over-80 line on disk. Fix: move the _fire_wrap_overflow_advisory call so it fires unconditionally after the break-before-dot commit (matching the pattern in the sibling branch), not only inside the >= branch.

✅ Defects (rest of cascade) — Traced the P1/P2/P2b/P3/P4 "if an argument breaks, the list breaks" symmetry (_emit_argument_list, lines ~9310–10125), the new _is_nested_or_chained_call/_arg_owns_its_rows/_is_anonymous_class predicates, and the _extra_tail_reserve context-manager usage in _emit_variable_declarator/_emit_variable_declarator_with_array_rhs — all snapshot/restore round-trips (including the new _anchor_escaped propagation logic at lines 11291–11362) are internally consistent and correctly unwound on every exit path. No leaks found there.

ℹ️ Same 288/289/309-advisory-count inconsistency flagged against CHANGELOG.md is duplicated verbatim in sourcetooling/scripts/format_java.py:10958 ("...costing extra advisories for no line-length gain: 309 advisories against the 288 this release ships.") reproduces the exact contradictory figure already flagged in the CHANGELOG review. Since the same sentence lives in two places, correcting only CHANGELOG.md will leave this in-code comment stale — both need the reconciled number.

✅/ℹ️ .claude/CLAUDE.md — Confirmed still absent from the repo; not applicable.

Testing

New predicates (_is_nested_or_chained_call, _is_anonymous_class) have dedicated parametrized unit tests (TestIsNestedOrChainedCall, TestIsAnonymousClass) covering true/false cases and a detached-node defensive check. _emit_receiver_parameter is covered by fixture method_decl_wrap/06_receiver_parameter_preserved. The new test_installed_versions_match_pins test (tooling/scripts/tests/test_format_java.py:64-88) is a good defensive addition that closes a real gap (a stale virtualenv previously let the whole suite pass against an uncalibrated tree-sitter binding).

❌ Testing gap directly enabling the field-access defect above — There is no unit test or fixture exercising _emit_field_access for the case where breaking before the dot reduces but does not eliminate overflow (receiver itself unsplittable and >80 chars alone). The one new fixture, method_chain_wrap/25_field_access_breaks_before_dot, only covers cases where breaking fully resolves the overflow. This is exactly the kind of case the fixture corpus should lock down given the new advisory machinery this release is built around.

✅ Edge cases (arg-list cascade)p1_invalid_wrap/p2_greedy invalid-wrap/p3_arg_escaped/p3_arg_invalid_wrap all have corresponding regression fixtures (16_any_arg_unfittable_escalates_whole_list, 18_arg0_wraps_so_whole_list_breaks, 19_middle_argument_keeps_full_budget, 20_wrapped_argument_escalates_past_paren_align) — good, targeted coverage that maps 1:1 to the code comments' worked examples.

Documentation

requirements.txt — The new comment explaining the GRAMMAR_VERSION dict / TestGrammarVersionPins two-way sync requirement is clear and directly useful for the dependabot-cooldown workflow it describes.

✅ Inline comments — Spot-checked the riskiest new logic (field-access break tier, nested-call rule 1/2/3, _extra_tail_reserve, p3_arg_escaped scan) against the code and found them accurate — except that the field-access comment block never states or accounts for the case identified above (it explains why the two existing branches exist but doesn't note that a third outcome — "helped some, not enough" — falls through uncovered).

Security

✅ No hardcoded credentials, unsafe eval/exec, or logged sensitive data in this chunk.

✅ CRITICAL check — license files: no .lic paths and no AQAAAD-prefixed strings anywhere in this diff chunk.

✅ Dependency pin bump (tree-sitter 0.25.2 → 0.26.0) is a version-only change now guarded by the new test_installed_versions_match_pins check.


Summary (this chunk)

The wrap-engine changes are well-reasoned and mostly hold up under tracing — the dead-code removal is clean and the new "argument/segment breaks the list" symmetry is consistently applied everywhere I checked. One genuine, reproducible defect: _emit_field_access (format_java.py:8496-8608) can commit an overflowing line with no FormatterWarning when breaking before the dot helps but doesn't fully resolve the overflow — it violates this file's own commit-and-warn discipline, and there's no fixture covering that scenario, which is presumably why it wasn't caught. Also flagging that the 288/309-advisory number mismatch already raised against CHANGELOG.md is duplicated verbatim as a source comment at format_java.py:10958, so both should be fixed together.


Review Part 3 of 3

I verified the actual file paths and line numbers by locating this fragment in pr_diff.txt (chunk_002 = lines 8900–9368, all part of one hunk in tooling/scripts/tests/test_format_java.py starting at new-file line 4018) and cross-checked the DP/boundary logic in tooling/scripts/format_java.py. Here's the review of this section.

Part 3 of 3 — tooling/scripts/tests/test_format_java.py (lines ~4199–4667)

This chunk is pure test-suite additions (no production code) covering: _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow/stability, javadoc-boundary detection, and two formatter idempotency/convergence bug fixes (semicolon-reserve, receiver-reserve, second-pass convergence).

Code Quality

  • Style/idioms: consistent pytest conventions (@pytest.mark.parametrize, class-per-behavior grouping), matches rest of the file.
  • No commented-out code: all comments are explanatory docstrings/rationale, not dead code.
  • Meaningful names: test/class names read as specifications (TestReceiverReserveIgnoresArgumentLayout, test_uses_fewer_lines_when_they_suffice), which is good practice for this style of test suite.
  • DRY: helper methods (_legacy, _format, _passes) avoid repetition well; TestSecondPassConvergence reads its two fixtures from disk (test_format_java.py:4614-4629) specifically to avoid drift between prose/data.
  • Defects: I traced the _min_ragged_lines DP (format_java.py:3802-3861) by hand against test_infeasible_returns_none (test_format_java.py:4260-4265, 6×4-char tokens into 1 line of width 14) and it correctly returns None. I also confirmed _splits_inline_tag's true-brace-depth tracking (format_java.py:3764-3799) matches all nine parametrized cases in TestSplitsInlineTag (test_format_java.py:4203-4227), including the nested-body and prose-brace edge cases the comments call out. No logic mismatches found between tests and implementation.
  • ℹ️ Self-documented pre-existing quirk, not a new defect: test_still_reports_a_value_with_no_split_point (test_format_java.py:4494-4520) asserts a warning message reads "max line width 86" when the true overflow is 87 columns, because a de-dup step swallows the more accurate advisory. This is cross-referenced with an identical explanation already in format_java.py:1680-1685 and is intentionally pinned (with a comment saying to bump the assertion to 87 once fixed) so a future silent regression is caught. Not something to change here, but worth being aware it locks in a known-wrong message string on purpose.
  • CLAUDE.md: not touched in this diff chunk.

Testing

  • ✅ Good edge-case coverage for _min_ragged_lines: empty input, hard cap, max-lines cap, infeasibility, oversize-token overflow (test_format_java.py:4256-4269).
  • TestJavadocBalancedReflow explicitly tests the "never regress the floor" invariant and an "unstable candidate rejected" case, which is exactly the kind of regression class this formatter has been bitten by before (per the docstrings).
  • ✅ The two convergence-fix test classes (TestReceiverReserveIgnoresArgumentLayout at 4540, TestSecondPassConvergence at 4597) each assert both the correct output and that the fixed point is now reached in fewer passes — good targeted regression tests for genuinely subtle idempotency bugs.
  • N/A Integration tests / >80% coverage: can't be assessed from a test-only diff fragment in isolation.

Documentation

  • N/A for this chunk — no README/CHANGELOG/Markdown changes appear in this section (would have been in parts 1–2 if present).

Security

  • ✅ No hardcoded credentials, no license files, no logging of sensitive data — this section is entirely formatter test fixtures and synthetic Java snippets.

Summary for Part 3: No defects found. This is a well-constructed, well-documented set of regression tests for a text-formatting engine, with unusually good "why" commentary tying each test back to a specific historical bug. The one item worth a reviewer's eye is the intentionally-pinned pre-existing off-by-one warning message, but it's already tracked and explained in both the test and the source.



Automated code review analyzing defects and coding standards

CI review round 6. One real defect, in the fix I made for round 5.

**`_emit_field_access` still shipped an over-long line in silence.**
Round 5 added advisories to two of its three terminal commits. The
third — the broken form itself, when breaking before the dot NARROWS
the line without getting it under the limit — sits after the
revert-to-inline branch, so the branch's advisory never covered it.
Reproduced: an unsplittable receiver already over 80 on its own, as
`return longCall().someField;`, emitted a 90-column line with no
warning. The advisory now fires unconditionally after the commit,
matching every other terminal commit point in the file; it is a
no-op when the lines fit, so one call covers all three paths.

Three tests added, verified to go red when the advisory is moved back
inside the branch. My first attempt at that revert did NOT reproduce
the bug — it early-returned inside the `if`, leaving the
fall-through still covered — so the tests appeared to pass against a
variant that was not actually the pre-fix shape.

**Stale figures reconciled.** The release intro and the
source-preservation FAQ both still said second-pass files fall "26 to
6"; the final number is 1. The receiver-reserve section claimed "309
advisories against the 288 this release ships" while every other
place says 289 — and re-running that comparison now gives a much
larger, less meaningful gap, because the LineLength-exemption and
field-access changes moved what an advisory counts. It is now stated
as +19 against the 305 baseline in place when it was measured, with
that caveat attached. The same sentence was duplicated in
`format_java.py` and is corrected there too. Also rewrapped a
paragraph whose line breaks had gone irregular.

800/800. Corpus: over-80 1571, advisories 289, second-pass 1,
third-pass 0.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Senzing Code Review — 0.7.0 formatting release (a8e4e06^1..a8e4e06^2)

Reviewed via direct git diff against the actual merge commit (140 files, +6570/-1097), not just the pasted excerpt — this let me verify claims against the post-merge source rather than trusting diff context alone. Also ran a dedicated adversarial pass (background agent) over the trickiest new state machinery in format_java.py (snapshot/restore plumbing, new escape-hatch flags, reserve arithmetic).

Code Quality

  • Style/naming/idioms ✅ — consistent with existing conventions; extensive, purposeful comments explaining why, not what.

  • No commented-out code

  • Meaningful variable names

  • DRY ✅ — this PR actively improves DRY over the prior version (e.g. _refuse_catch_parameter_modifiers extracted and shared between tooling/scripts/format_java.py:5877 and :5946; _balanced_reflow_words now shared between line-comment and javadoc reflow).

  • Defects ❌ — one real, verified gap found (below). Everything else checked out clean, including the parts most likely to hide bugs (per-cascade snapshot()/restore() state, the new _anchor_escaped/_raw_rows_emitted flags, _min_ragged_lines DP indexing, _group_inline_tags brace-depth walk).

    _emit_field_declaration (format_java.py:1621) / _emit_variable_declarator (format_java.py:11132) — under-reserved tail for non-last declarators in a multi-variable statement.

    0.7.0's headline declaration fix raises tail_reserve by exactly 1 around each declarator's value emission (_extra_tail_reserve(emitter, 1) at format_java.py:11253, :11271, :11300, :11372) so the value's own wrap cascade leaves room for a trailing ;. That's correct for the last declarator in a statement, but _emit_field_declaration's loop (format_java.py:1657-1665) can hold several variable_declarator children joined by ", " before the final ;. For a non-last declarator, what actually follows on the line isn't a bare ; — it's , name2 (or more) ;. The reserve is hard-coded to 1 regardless of position, so the inline-fit check at format_java.py:11255-11258 (emitter.column + 1 <= effective_max) can pass and commit a value shape that overflows once , name2; is appended.

    Concrete failure: int result = someCallThatFillsToColumnSeventyNine(), other; — the first declarator's value renders to column 79, the check sees 79 + 1 = 80 <= 80 and commits inline, then , other; pushes the actual line past 80. The overflow isn't silent (the statement-level _fire_wrap_overflow_advisory at format_java.py:1719 catches it on-disk width and fires), but the formatter ships an avoidably-overflowing line with a generic "shorten a name or split the value" advisory, when accounting for the real suffix length would have let the declarator's own cascade (break-at-=, etc.) pick a shape that actually fits — exactly the class of bug this release's declaration-semicolon fix targets, just not extended to the multi-declarator case. Not mentioned in CHANGELOG.md or the round 1-5 commentary, and no fixture under tooling/scripts/tests/fixtures/ exercises a multi-declarator statement with a value near the 80-column boundary, so it wasn't caught by the trial corpus. Low severity (multi-declarator statements are uncommon and the advisory still fires), but worth a fixture + fix before tagging.

    Minor/non-blocking: _emit_catch_clause's multi-catch P1 fit check (format_java.py, priority-1 branch) uses emitter.column rather than last_lines_max_width the way its sibling cascades do — harmless today since Java type names never introduce internal wraps, but it's an inconsistency worth a one-line comment if someone extends catch-type wrapping later.

  • .claude/CLAUDE.md — N/A, no such file exists in this repo.

Testing

  • Unit tests for new functions ✅ — 12 new test classes added (TestIsNestedOrChainedCall, TestGroupInlineTags, TestMinRaggedLines, TestJavadocBalancedReflow, TestDeclarationSemicolonReserve, TestReceiverReserveIgnoresArgumentLayout, TestFieldAccessCommitAndWarn, etc. — tooling/scripts/tests/test_format_java.py).
  • Integration tests — N/A (CLI formatter, no endpoints).
  • Edge cases ✅ mostly, ❌ one gap — the multi-declarator case above isn't covered; everything else (lambda boundaries, chain oscillation, idempotency regressions, javadoc stability) has dedicated fixtures and unit tests, confirmed by counting fixture directories: 234, matching the number now cited in format_java.py's module docstring.
  • Coverage > 80% — not independently verifiable in this sandboxed session (test execution and coverage tooling were blocked by the environment's permission gate); the PR's own verification notes cite 800/800 pytest with a corpus checkout, which the new corpus-gate CI job now enforces.

Documentation

  • CHANGELOG.md updated ✅ — extremely thorough, and the latest commit (cd42886) specifically reconciles previously-stale figures.
  • Docs updated ✅ — docs/java-coding-standards.md additions (enhanced-for wrapping, priority 2b, nested-call wrap, parameter alignment) match what the code implements; spot-checked several code examples against the described cascade logic.
  • Cross-references ✅ — verified #nested-call-wrap and #exception-multi-line-conditions anchors resolve to real headings; verified all four docs/faqs/building/*.md files referenced by relative path actually exist.
  • CommonMark/prettier formatting⚠️ not independently verifiable: npx prettier --check was blocked (this session has no network access, consistent with the task's stated constraint), and direct shell inspection for trailing whitespace was also blocked by the permission gate. Manual visual inspection of the diff found no obvious CommonMark violations (consistent blank lines around headings/fences, closed code fences, no raw HTML misuse). Minor style nit only: new cross-references like `building/source-preservation-history` (in CHANGELOG.md and the new FAQ docs) are backtick-quoted slugs rather than markdown links — consistent with a pre-existing convention in this doc set, not a regression.
  • Inline comments for complex logic ✅ — if anything, over-documented (in a good way) given how subtle this logic is.

Security

  • No hardcoded credentials ✅ — only match is persist-credentials: false in the new/existing GitHub Actions checkout steps, which is the correct, security-positive setting.
  • No .lic files, no AQAAAD-prefixed strings ✅ — confirmed via search across the full diff.
  • Input validation / error handling ✅ — new code follows the existing "refuse via NotImplementedError" pattern for unsupported grammar shapes rather than silently mis-emitting.
  • No sensitive data in logs ✅ — FormatterWarning messages only ever include source line/column and generic remedy text.
  • CI workflow security ✅ — new corpus-gate job in .github/workflows/pytest.yaml uses minimal permissions: {contents: read}, pins the external corpus checkout to a release tag rather than tracking main (correct — prevents an unrelated upstream commit from turning this repo's CI red), and doesn't interpolate any untrusted (PR-controlled) input into a run: shell block, so there's no Actions script-injection risk.
  • Dependabot config ✅ — verified the claimed duplication (tooling/scripts/tests/requirements.txt pulling -r ../requirements.txt) is real, so removing the redundant /tooling/scripts/tests pip entry is a correct, low-risk cleanup.

Summary

One real defect (multi-declarator tail-reserve gap), otherwise an unusually well-scrutinized change set — this PR already absorbed five internal review rounds, and it shows: the state-machine plumbing (snapshot/restore, escape flags, reserve arithmetic) that would normally be the highest-risk surface in a diff this size held up under adversarial review. Recommend adding a fixture for the multi-declarator case and fixing the reserve to account for trailing sibling declarators before tagging.


Review Part 2 of 3

Good — CHANGELOG.md is being maintained across these commits. Now let me finalize the review for this diff segment.

PR Code Review — Part 2 of 3

Scope note: This part covers the argument-list wrap cascade, field-access wrapping, method-chain wrap rules, variable-declarator RHS wrapping, and associated test fixtures/requirements-pin changes in tooling/scripts/format_java.py. I cross-checked the diff against the actual merged file on disk (this checkout already contains these commits) to verify state-management claims (Emitter.snapshot()/restore() at format_java.py:481-573 does capture _arg_list_p4_fired, _anchor_escaped, and _raw_rows_emitted), which ruled out a couple of flag-leak hypotheses I initially suspected in emit_p2b_packed and the variable-declarator orphan tracking — both are correctly scoped.

Code Quality

  • ✅ Style/naming is consistent with the rest of the file (functions, tiers, and flags follow existing conventions like p1_p4_fired, _arg_owns_its_rows).
  • ✅ No commented-out code. The extensive comments are explanatory rationale (design history, rejected alternatives), not dead code — appropriate given this is a hand-rolled line-wrapping algorithm where the "why" is genuinely non-obvious.
  • ✅ Good dead-code removal: _arg_list_single_line_estimate, _estimate_normalize, _SEMANTIC_WRAP_ARG_TYPES, the ~124-line column-remap fallback in _emit_argument_list, and the 0.6.0 "P1F" factory-chain tier are all removed along with their now-obsolete tests, rather than left as unreachable branches.
  • ✅ Defects: none found with high confidence in this segment. I specifically checked for state-leak bugs around emitter._arg_list_p4_fired = True in the new emit_p2b_packed (format_java.py:9774-9821) and _anchor_escaped handling in _emit_variable_declarator (~format_java.py:11298-11360), since neither has an explicit save/restore wrapped around the risky candidate the way emit_p1's P4-detection does. Both are safe because Emitter.snapshot()/restore() (format_java.py:481-573) already include these flags in their captured state, so emitter.restore(packed_snap) / emitter.restore(saved) correctly rolls them back on a rejected candidate.
  • ⚠️ Minor DRY note (pre-existing pattern, not introduced here): effective_max = _MAX_LINE - emitter.tail_reserve is recomputed many times across the multi-arg cascade rather than computed once — cosmetic only, consistent with the file's existing style, not worth blocking on.
  • .claude/CLAUDE.md: not touched in this part of the diff.

Testing

  • ✅ Every new predicate/behavior added here has direct test coverage: TestIsNestedOrChainedCall, TestIsAnonymousClass, test_installed_versions_match_pins, plus ~20 new fixture pairs (arg_list_wrap/13–20, nested_call_wrap/01–09, method_chain_wrap/18/21/24/25/26, source_preserve_reanchor/*, record_header_wrap/*, switch_brace/*, multi_catch_wrap/*, enhanced_for_wrap/*). The TestIsNestedOrChainedCall parametrization is notably thorough — it locks both the True cases and the deliberately-excluded False cases (casts, ternaries, field access, array index) so a future change can't silently widen the predicate.
  • ✅ Edge cases: block-bodied vs. expression-bodied lambdas, anonymous classes, chains-as-sole-argument, idempotency (07_binary_positional_arg_idempotency_lock, 06_idempotent_nested_call_in_parens) are all explicitly covered — idempotency in particular is a known failure class for this kind of multi-pass formatter, and it's good to see it tested directly rather than assumed.
  • ❓ Coverage percentage: not computable from a diff; no coverage report included in this part.

Documentation

  • requirements.txt bump (tree-sitter 0.25.2 → 0.26.0) is paired with a new enforcement test (test_installed_versions_match_pins) and an explicit comment cross-referencing GRAMMAR_VERSION/TestGrammarVersionPins, which is exactly the right way to keep a version pin and its consumer in sync.
  • ❓ CHANGELOG.md/README: not part of this diff chunk, but git log confirms CHANGELOG.md is being actively updated across this commit series, so this is very likely already handled in a part not shown here.
  • N/A Markdown/CommonMark: no .md files in this part of the diff.

Security

  • ✅ No hardcoded credentials, no logging of sensitive data, no network/external I/O introduced — this is a local static-analysis/formatting tool operating on Java source text.
  • ✅ No .lic files or AQAAAD-prefixed strings present in this diff segment.

Overall: This is a well-scoped, well-tested continuation of the 0.7.0 argument-list/chain-wrap rework. I did not find a genuine defect in this part after verifying the flag-lifecycle concerns against the actual Emitter implementation — the state management is more disciplined than it first appears from the diff alone.


Review Part 3 of 3

I don't have write access to that file, so here's the Part 3 review directly.

PR Code Review — Part 3 of 3

Scope note: This part is the tail of one large addition hunk in tooling/scripts/tests/test_format_java.py (new content ends at line 4730, matching the file's actual line count on disk). It covers tests for inline-tag grouping/detection (_group_inline_tags, _splits_inline_tag), the minimum-raggedness fill (_min_ragged_lines), the new javadoc balanced reflow and its stability/boundary checks, the declaration-semicolon reserve fix, the chain-receiver reserve fix, second-pass convergence regression tests, and the field-access commit-and-warn fix. No production code is in this chunk — it's purely new tests. I cross-checked every class's claims against the actual implementation in tooling/scripts/format_java.py (present in this checkout) rather than trusting docstrings at face value; pytest execution itself was blocked by this session's permission gate (consistent with Parts 1/2), so verification here is a static, line-by-line trace of each algorithm against its asserted expectations.

Code Quality

  • ✅ Style/naming consistent with the rest of the test file and the conventions already noted in Parts 1/2.
  • ✅ No commented-out code.
  • ✅ Meaningful names — test method names double as behavior specs.
  • ✅ DRY — TestSecondPassConvergence (test_format_java.py:4433-4445) reads its two regression bodies from existing fixtures instead of duplicating source text inline, "so the two cannot drift apart." Confirmed both fixture files exist: tooling/scripts/tests/fixtures/condition_wrap/12_for_clause_wrap_escalates_whole_header/input.java and .../need_braces/23_wrapped_source_condition_still_collapses/input.java.
  • ✅ Defects: none found in the test code itself. I traced each class against the real implementation:
    • _group_inline_tags (format_java.py:3719) — brace-depth walk matches TestGroupInlineTags's oversize/nested/self-contained/unterminated cases exactly.
    • _splits_inline_tag (format_java.py:3764) — "only {@ opens counting" correctly keeps prose braces ("the set {a, b} of things") from being misread as a split tag; true-depth tracking correctly handles the nested-body cases in TestSplitsInlineTag (test_format_java.py:4203-4218).
    • _min_ragged_lines (format_java.py:3802) — hand-traced the DP for test_oversize_token_gets_its_own_line (test_format_java.py:4269-4276): the end > start guard lets a lone 40-char token occupy its own line inside a 20-char budget, producing a solution rather than None, matching the test.
    • _javadoc_balanced_reflow (format_java.py:3941) — the "floor" framing in TestJavadocBalancedReflow's class docstring (test_format_java.py:4278-4279) matches the code's legacy/candidate comparison exactly.
    • _emit_variable_declarator's +1 semicolon reserve (format_java.py:11253/:11271/:11300/:11372) matches TestDeclarationSemicolonReserve (test_format_java.py:4465-4473).
    • _emit_method_invocation's chain-receiver reserve (format_java.py:10965-10971: trailing = 1 + len(name_text) + 1) is now layout-independent, exactly as TestReceiverReserveIgnoresArgumentLayout (test_format_java.py:4540-4556) describes.
    • _emit_field_access's three commit points (format_java.py:8519-8611) match TestFieldAccessCommitAndWarn (test_format_java.py:4670-4677) precisely, including the previously-silent third case.
  • ℹ️ Non-blocking notetest_format_java.py:4514-4520 (TestDeclarationSemicolonReserve.test_still_reports_a_value_with_no_split_point) asserts the literal string "max line width 86" and documents inline that the mathematically correct value is 87, off by one due to a pre-existing advisory-deduplication quirk (_fire_wrap_overflow_advisory's line-range suppression, format_java.py:862-874). This is disclosed responsibly — flagged as pre-existing, tracked separately, pinned so a fix can't land silently — not a defect introduced here, but worth knowing: when that dedup bug is eventually fixed, this assertion must move from 86 to 87 or it'll fail for an unrelated reason.
  • .claude/CLAUDE.md — not present in this repo.

Testing

  • ✅ This chunk is test coverage — 10 classes covering the javadoc-reflow subsystem, both "oscillation" bugs (declaration semicolon reserve, chain-receiver reserve), a general second-pass convergence regression class, and the field-access silent-overflow gap.
  • ✅ Edge cases are thorough: empty input, infeasible packing, oversize atomic tokens, and instability detection where a locally-better candidate is rejected for being globally unstable (test_unstable_candidate_is_rejected + a companion test that independently confirms the rejected layout really is unstable, rather than just trusting the rejection).
  • TestFieldAccessCommitAndWarn.test_every_overflow_is_accounted_for (test_format_java.py:4707-4718) checks two independent overflows in different enclosing constructs are both reported — a real regression guard against a warning that fires once and stops.
  • Coverage % not computable from a diff; consistent with Parts 1/2.

Documentation

  • N/A — no markdown/README/CHANGELOG changes in this chunk.
  • ✅ Every non-obvious test carries a docstring explaining the failure mode it guards against — appropriate given how subtle these fixed-point bugs are.

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings, no sensitive data logging — pure test code over synthetic Java snippets.

Overall: Clean. This is regression-test scaffolding for fixes already verified correct in Parts 1/2, and every claim a test docstring makes about the underlying algorithm checks out against the actual format_java.py implementation. No action needed beyond the non-blocking note above about the 86/87 assertion.



Automated code review analyzing defects and coding standards

Spellcheck failed on `cd42886`: "unsplittable" came back in the
round-5 and round-6 comments, and inside the synthetic Java
identifier the new field-access tests use. I had reworded it once
already and reintroduced it while writing those fixes.

Reworded to "unbreakable" per the project's cspell discipline —
reword rather than widen the dictionary. The identifier is renamed
`extremelyLongUnbreakableXMethodCall...`, one character padded so
the rendered width is unchanged: these tests turn on whether a line
lands over 80, so a shorter name would have quietly stopped
exercising the case. Verified by re-running the revert experiment —
moving the advisory back inside its branch still fails exactly the
two field-access tests.

800/800. Comments and test identifiers only.
CI review round 7 found one real defect, reachable and reproduced.

The declaration semicolon fix raises the reserve by exactly one
around each declarator's value. That is right for the LAST
declarator, but a statement can hold several joined by `, `, and a
non-last declarator is followed by `, name` for each one still to
come before the `;`. So

    int result = someCallThatFillsToColumnSeventyNine(), other;

committed its first value at column 79, measured `79 + 1 <= 80`, and
let `, other;` push the line to 82. The statement-level advisory
caught it on disk, so it was not silent — but it was avoidable: with
the real suffix charged the cascade breaks at `=` and the line comes
out at 75.

The reserve is now charged from the names of the declarators still to
come. Identifiers emit verbatim, so that width is a function of the
AST, not of layout — the property this release exists to protect. A
later declarator carrying its own initializer remains under-charged
by that initializer's width; strictly better than charging nothing,
and noted as the remaining gap.

Zero corpus effect: the style does not occur in the 504-file corpus,
which is why six review rounds missed it. Output byte-identical in
all 504 files, over-80 1571, advisories 289, second-pass 1.
Fixture `method_decl_wrap/07_multi_declarator_reserves_its_suffix`
goes red when the suffix reserve is neutralised.

801/801.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Code Review — Part 1 of 3

I cross-checked the diff against the actual checked-out files (the working tree already reflects the merge, so line numbers below are from the real files) and ran static analysis on the trickiest new logic (multi-catch wrap, record header cascade, for/enhanced-for reserves, _line_length_exempt). Note: pytest execution was blocked by the sandbox's approval gate, so I could not directly execute the suite — findings on tests rely on diff inspection.

Code Quality

  • Style conventions — consistent with the file's existing idiom (docstrings, Final, snapshot/restore pattern for speculative emission). No issues found in the reviewed sections.
  • No commented-out code.
  • Meaningful names_arg_owns_its_rows, _line_length_exempt, _formal_param_name_col_offset, etc., are self-describing.
  • DRY_refuse_catch_parameter_modifiers (format_java.py:5943) and _attach_trailing_side_comments (format_java.py:2782) are properly extracted and shared rather than duplicated across the new call sites (_emit_class_body_members:1612, _emit_interface_body_members, _emit_catch_clause).
    • Minor nit: _LINE_LENGTH_EXEMPT_MARKERS (format_java.py:3414) lists both "a href" and "href", where the latter subsumes the former. I checked checkstyle/senzing-checkstyle.xml:17 — the redundancy exists in the upstream ignorePattern itself, so the Python list is a faithful (if redundant) mirror. Not a bug, not worth changing given the stated intent to mirror the XML exactly.
  • Defect hunting — I specifically chased three suspicious patterns and all three turned out correct on closer reading:
    • _emit_catch_clause's p1_fits check (format_java.py:5913) omits the emitter.line_count == p1_saved[0] guard that sibling cascades use elsewhere (e.g. the enhanced-for inline_fits check). This looked like a latent bug, but _emit_generic_type/_emit_type_arguments (format_java.py:5654-5684) never introduce newlines, so a catch type genuinely cannot cause the emitter to advance lines — the omission is harmless in practice.
    • The index, member = _attach_trailing_side_comments(...) + index += 1 pattern in the newly-converted _emit_class_body_members/_emit_interface_body_members loops looked like an off-by-one at first glance, but it's an exact reuse of the pre-existing _emit_indented_member_list pattern (format_java.py:2860-2871), which already proves it out.
    • The record-header params_wrapped / restore-and-re-emit logic (format_java.py:5498-5525) correctly gates on super_interfaces_node is not None, so a component list that overflows with no implements clause is left to its own (already-correct) cascade.
    • No confirmed correctness defects found in the reviewed portion of format_java.py.
  • No .claude/CLAUDE.md in this diff — the new .claude/070_REMAINING_SCOPE.md is not CLAUDE.md so the local-environment-specificity rule doesn't strictly apply, but as a minor organizational note: it reads as project history/decision-log content, which arguably belongs under docs/faqs/building/ alongside the two new sibling FAQ docs rather than in .claude/. Not a defect, just a placement question worth confirming is intentional for this repo's conventions.

Testing

  • ✅ Diff stat between the pre/post commits shows 142 files changed, +6636/−1096, dominated by new fixture pairs (expected.java/input.java) plus tooling/scripts/tests/test_format_java.py (+924/−… lines) — consistent with the changelog's claim of 234 fixture pairs and 41 new unit tests for the reflow helpers.
  • ⚠️ Could not execute pytest in this sandbox (command blocked pending approval), so I can't independently confirm the "801/801 passing" figure claimed in CHANGELOG.md. Recommend CI verification rather than trusting the changelog prose alone.
  • The new corpus-gate CI job (.github/workflows/pytest.yaml) is a genuine coverage improvement — it fixes a real gap (fuzz/perf tests silently skipping in standalone checkouts) rather than adding superficial tests.

Documentation

  • CHANGELOG.md updated extensively and follows Keep-a-Changelog structure.
  • ✅ Two new FAQ docs (formatter-python-environment.md, source-preservation-history.md) are well-organized with a "See also" cross-link section.
  • java-coding-standards.md updates are consistent with the code changes I traced (record headers, catch wrapping, enhanced-for, priority 2b, parameter alignment carve-outs).
  • The docs/faqs/building/java-formatting-standards.md superseded-notice banner is a good pattern for keeping historical rationale without misleading current readers.
  • Markdown formatting looks CommonMark-clean on visual inspection (proper fenced code blocks, consistent list markers); I was unable to run an automated trailing-whitespace/prettier check due to sandbox command restrictions, so this is a visual pass only, not a verified one.

Security

  • ✅ No hardcoded credentials found in this chunk.
  • ✅ No .lic files or AQAAAD-prefixed strings present in this chunk.
  • ✅ CI changes (corpus-gate job) pin the external checkout to a release tag (4.0.1) rather than main, with persist-credentials: false — reasonable supply-chain hygiene.

I'll continue with parts 2 and 3 to complete the Testing/coverage assessment (fixture content itself) and finish the defect scan across the rest of format_java.py.


Review Part 2 of 3

I've reviewed this diff (Part 2 of 3) covering changes to tooling/scripts/format_java.py, tooling/scripts/requirements.txt, tooling/scripts/tests/test_format_java.py, and numerous fixture files for what appears to be a Java source-formatting tool. Note: since I only have this slice of a 3-part diff, some checklist items (CHANGELOG, README, .claude/CLAUDE.md, and whether GRAMMAR_VERSION was actually bumped alongside requirements.txt) can't be fully verified from this chunk alone.

Code Quality

  • Style/naming — Consistent with the file's existing conventions (verbose rationale comments, Final[...] typed module constants, descriptive flag names like p1_invalid_wrap, p3_arg_escaped, chain_is_sole_arg).
  • No commented-out code — Old logic (_ESTIMATE_VERBATIM_NODE_TYPES, _estimate_normalize, _arg_list_single_line_estimate, _SEMANTIC_WRAP_ARG_TYPES, _chain_receiver_is_factory, emit_p1f_factory, ~124 lines of the old source-preserve column-remap) is fully deleted rather than left commented out, with matching test deletions (TestEstimateNormalize, TestArgListSingleLineEstimate in test_format_java.py).
  • ⚠️ DRYformat_java.py around the P1/P2/P3 cascades in _emit_argument_list (new p1_invalid_wrap, arg_invalid_wrap, p3_arg_invalid_wrap/p3_arg_escaped): the "did an argument wrap illegitimately" tracking pattern is reimplemented three times with slightly different mechanics (reject-whole-candidate vs. patch-in-place). This is explained in comments as intentional (different escalation semantics per tier), so it's a soft finding rather than a real defect.
  • Verify elsewhere in the diff: format_java.py deletes _chain_receiver_is_factory and the P1F tier (emit_p1f_factory) in the method-chain-wrap section (~line 10188 region). I don't see corresponding test removals for those symbols in this chunk (only TestEstimateNormalize/TestArgListSingleLineEstimate are shown deleted). If any TestChainReceiverIsFactory-style tests exist and weren't removed in part 1/3, they'll now fail on AttributeError. Worth confirming.
  • .claude/CLAUDE.md — not present in this diff chunk; can't evaluate against the "no local-environment-specific content" rule from this slice.

Defects found:

  1. tooling/scripts/format_java.py, _field_access_receiver_is_computed/_emit_field_access (~line 8490–8630) — The new "break before .field only for computed receivers" logic and its accompanying overflow-advisory path is exercised by fixture method_chain_wrap/25_field_access_breaks_before_dot, but that fixture only tests the computed-receiver case (methodBeingCalled(...).someFieldNameHere). The illustrative case in the code comment — a field_access whose receiver is a non-computed qualified name (java.util.Objects...) that still overflows and should fire the "no computed sub-expression to break before" advisory — isn't exercised by any new fixture. This is a coverage gap for a branch that's easy to get wrong (e.g., silently swallowing the advisory, or mis-detecting nested field_access receivers).

Testing

  • Unit tests for new functions — Good coverage added: TestIsNestedOrChainedCall for _is_nested_or_chained_call, plus ~20 new fixture pairs under arg_list_wrap/, nested_call_wrap/, method_chain_wrap/, record_header_wrap/, multi_catch_wrap/, switch_brace/, source_preserve_reanchor/, etc., each demonstrating a specific behavior change with before/after .java content.
  • Regression protectiontest_installed_versions_match_pins (test_format_java.py, new) is a solid addition: it catches environment drift where installed tree-sitter/tree-sitter-java versions don't match the pins, citing a real past incident (704 passing tests against a mismatched binding).
  • ⚠️ Edge case noted above (field_access non-computed-receiver overflow path) is untested in this chunk.
  • N/A Integration tests / 80% coverage — not meaningfully applicable to this fixture-driven formatter test style; the fixture-pair pattern is the project's existing test methodology and is followed correctly here.

Documentation

  • Inline comments for complex logic — Extensive, arguably the file's strongest quality: every new wrap-cascade tier (emit_p2b_packed, nested-call wrap rules 1–3, _arg_owns_its_rows, the field-access break rule) has a rationale comment with before/after examples and links to a building/source-preservation-history FAQ.
  • CHANGELOG.md — not visible in this chunk. Given the commit log shows dedicated "0.7.0" commits, it's likely updated elsewhere in the 3-part diff; flagging to confirm it captures this chunk's user-visible formatting changes (nested-call wrapping, field-access breaking, P1F removal, P2b-packed tier).
  • requirements.txt comment update (tooling/scripts/requirements.txt, top-of-file comment) — clearly documents the GRAMMAR_VERSION mirror requirement and why Dependabot PRs need a paired code change. Good practice, though it means this PR's tree-sitter==0.26.0 bump needs a matching GRAMMAR_VERSION["tree-sitter"] = "0.26.0" change — not visible in this chunk, please confirm it's in part 1 or 3.
  • N/A Markdown/CommonMark formatting — no .md files touched in this chunk.

Security

  • ✅ No hardcoded credentials, no logging of sensitive data — this is a source-formatting CLI tool operating on local file content only.
  • ✅ No .lic files or AQAAAD-prefixed strings present in this diff.
  • N/A Input validation — the "input" here is Java source parsed via tree-sitter; not a network/user-facing trust boundary.

Summary

The core logic changes (nested-call wrap rules, field-access dot-breaking, P2b-packed tier, removal of the fragile width-based source-preservation fallback and P1F factory-chain heuristic) are well-reasoned, thoroughly commented, and backed by targeted fixtures showing the actual before/after formatting output. The one gap worth addressing is test coverage for the non-computed-receiver overflow branch in _emit_field_access. The rest are cross-part verification items (matching GRAMMAR_VERSION bump, CHANGELOG, and confirming no dangling tests reference deleted _chain_receiver_is_factory/emit_p1f_factory) that depend on parts 1 and 3 of this diff, which I don't have visibility into.


Review Part 3 of 3

Reviewing this chunk (part 3 of 3 — I don't have parts 1/2 in this session, so this covers only the tests shown above; they appear to be new pytest classes added to a Java formatter's test suite in Python).

Code Quality

  • Style conventions — Consistent PEP 8 / pytest idioms throughout (parametrize usage, docstrings on test classes explaining intent).
  • ⚠️ Minor style nit: three consecutive blank lines appear before class TestFieldAccessCommitAndWarn: (after the TestSecondPassConvergence block), where the rest of the file uses the standard two blank lines between top-level definitions. Not a functional issue, just an inconsistency a formatter/linter (e.g., black/flake8) would normally flag.
  • No commented-out code — All comments are explanatory prose describing invariants/history of bugs, not disabled code.
  • Meaningful names — Test and helper names (_legacy, _passes, _warn_and_widths, _first_arg_list_of) are descriptive; short method names inside test fixture source strings (t(), u()) are intentional Java stand-ins, not production identifiers.
  • DRY — Shared setup factored into helpers (_legacy, _passes, _warn_and_widths, _FIXTURES) rather than duplicated per test.
  • Defects: No logic bugs found in the test code itself; assertions are internally consistent with the documented behavior (e.g., brace-depth tracking in TestSplitsInlineTag, greedy vs. balanced fill in TestMinRaggedLines).
  • ⚠️ Flag for follow-up, not a new defect: TestDeclarationSemicolonReserve.test_still_reports_a_value_with_no_split_point deliberately pins a known, pre-existing off-by-one bug (asserts "max line width 86" instead of the semantically correct 87), with a comment explaining a de-duplication issue causes it and that the assertion must be updated when fixed. This is a reasonable "pin the bug so it can't silently change" pattern, but it means a known defect is being codified into the test suite rather than tracked purely as an issue — worth confirming there's an actual tracking ticket referenced somewhere, since the comment says "tracked as its own task" without a ticket ID/link.

Testing

  • Unit tests for new functions — Extensive: _is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_boundary, and the declaration/receiver/field-access reserve logic all get dedicated test classes.
  • N/A Integration tests for new endpoints — not applicable; this is a source-formatting tool, not a service with endpoints.
  • Edge cases covered — Notably thorough: nested/unterminated inline tags, oversize tokens, infeasible packing, two-pass/second-pass convergence (fixed-point stability), and "unstable candidate" rejection for javadoc reflow.
  • Coverage > 80% — Cannot verify numerically without running a coverage tool against the full source; the breadth of scenarios suggests it's likely satisfied for the touched functions, but I can't confirm the percentage from the diff alone.

Documentation

  • N/A README / API docs / CHANGELOG — none touched in this chunk (may be handled in parts 1/2, which aren't visible here).
  • Inline comments for complex logic — Strong: docstrings explain why each test exists (e.g., "cancelling on the first } called these unsplit," "output becoming a function of previous output"), which is valuable given how subtle the reflow/stability logic is.
  • N/A Markdown/CommonMark formatting — no .md files in this chunk.

Security

  • ✅ No hardcoded credentials, no sensitive logging, no .lic files or AQAAAD-prefixed strings present in this chunk.
  • N/A Input validation / error handling — this diff is test code only; no new production input-handling logic to assess here.

Overall: This chunk is test-only and is well-constructed — heavy edge-case coverage and unusually good documentation of why each test exists (bug history, invariants). Only findings are a cosmetic blank-line inconsistency and a note to confirm the pinned pre-existing off-by-one bug has a real tracking reference. No blocking issues found in this part of the diff.



Automated code review analyzing defects and coding standards

Folds in what I had ticketed as a follow-up. On reflection the
deferral was poorly reasoned: I adopted CI's "worth a follow-up
ticket" framing instead of costing the fix myself, and this is a
correctness bug in an advisory THIS release introduced, with a test
asserting the wrong value.

`_fire_wrap_overflow_advisory` de-duplicates when a nested engine has
already fired for the same span, keeping the inner advisory as the
more actionable pointer. But the inner one measured before the outer
construct wrote its trailing characters, so the surviving number
could fall short of what checkstyle sees — a declaration whose `;`
lands in column 87 reported 86.

The dedup now carries the larger width across while keeping the
inner line, column and remedy. `FormatterWarning` gained a `width`
field so the comparison need not parse the number out of the prose.

Not the cosmetic off-by-one it looked like: 48 corpus advisories
change, and one had been reporting 82 for a 92-column line. Measured
against the widest line in each file, exact matches go 51 to 85 and
the count that over-state is unchanged at 44 — so this removes
under-reporting without introducing over-reporting. That metric is a
bound, not a per-construct measurement; the delta is the signal.

Output byte-identical in all 504 files, advisory count still 289,
over-80 1571, second-pass 1. The test that asserted 86 now asserts
87, and its accepted-debt note is gone.

801/801.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

I've now reviewed the complete diff (all 3 parts). Here's the review.

Senzing Code Review — format_java.py 0.7.0 release PR

Code Quality

  • Style conventions — Consistent with existing formatter code (type hints, docstrings, naming). No issues.
  • No commented-out code — None found; the removed _arg_list_single_line_estimate/_estimate_normalize/_SEMANTIC_WRAP_ARG_TYPES machinery is deleted outright rather than commented out, and their unit tests (TestEstimateNormalize, TestArgListSingleLineEstimate) are removed alongside, which is the right call.
  • Meaningful variable/function names — Names like _is_nested_or_chained_call, _arg_owns_its_rows, p3_arg_escaped are clear and self-documenting.
  • DRY — Good consolidation work: _refuse_catch_parameter_modifiers is now shared between the single- and multi-catch paths (format_java.py:5978 area), and _balanced_reflow_words is shared between // and javadoc reflow. _javadoc_reflow_words at format_java.py now delegates to _javadoc_balanced_reflow rather than duplicating logic.
  • Defect — stale fixture reference in new doc: .claude/070_REMAINING_SCOPE.md:25 cites the fixture name arg_list_wrap/18_arg0_wraps_but_paren_aligned_still_fits, but the fixture actually added in this PR (and referenced correctly elsewhere, e.g. docs/java-coding-standards.md's own commit message and the code comment at format_java.py:5476 area) is named arg_list_wrap/18_arg0_wraps_so_whole_list_breaks. A reader following this pointer to find the pinning fixture will not find it. Minor (docs-only), but worth a one-line fix before merge.
  • Defects/logic review — I traced several of the trickiest pieces of new logic for correctness:
    • Emitter.snapshot()/restore() (format_java.py:488-581): tuple grew from 9 to 11 fields for _anchor_escaped/_raw_rows_emitted; field order and count in the docstring match the actual tuple construction/unpacking. No mismatch found.
    • _emit_variable_declarator's _anchor_escaped/inline_orphan save-reset-restore dance (format_java.py:11347-11418): verified the flag is correctly scoped to "this value's own emission" via reset-before/restore-after, and that the monotonic (never-reset-to-False mid-cascade) nature of the flag means a nested declarator's escape correctly propagates to an enclosing one. No bug found.
    • _emit_formal_parameters' P2/P3 alignment-vs-width tradeoff (format_java.py:8145-8384): the aligned-vs-unaligned comparison logic and the p3_col >= paren_col skip-if-it-doesn't-help gate both check out against their documented rationale.
    • Both field_declaration and local_variable_declaration (and constant_declaration) dispatch to the same _emit_field_declaration (format_java.py:11484,11517,11527), confirming the new multi-declarator suffix-reservation fix applies uniformly as the CHANGELOG claims.
    • No hardcoded credentials, no unsafe eval/exec, no shell injection risk introduced (this is a pure-Python AST-based text formatter with no subprocess/network calls in the changed code).
  • Project CLAUDE.md — No .claude/CLAUDE.md exists in this repository (only in the unrelated build-resources scratch checkout in the working tree), so this item is not applicable here.

Testing

  • Unit tests for new functions — Very thorough: new test classes for _is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_boundary, the declaration-semicolon reserve, the chain-receiver-reserve two-cycle regression, second-pass convergence, and field-access commit-and-warn (tooling/scripts/tests/test_format_java.py, ~700 new lines). The old estimator's tests are removed along with the code they tested.
  • Edge cases — Explicitly covers curried lambdas, block- vs expression-bodied lambdas, oversize inline tags, nested brace depth in {@code {a,b}}, and idempotency/convergence over multiple passes — good discipline given this is a formatter where non-convergence is the worst-case failure mode.
  • N/A Integration tests for new endpoints — Not applicable; this is a formatter library, not a service.
  • ⚠️ Test coverage > 80% — Can't be verified without running coverage tooling (no network/execution access in this review), but given the density of new unit tests directly exercising the new helper functions, this is very likely satisfied. Not independently verified.
  • ✅ New CI job corpus-gate (.github/workflows/pytest.yaml) specifically closes a real gap: test_fuzz_corpus.py/test_performance.py were silently skip-marked in the standalone checkout used by the existing pytest job, so the AST round-trip/idempotency properties were never actually gated in CI before this PR. Pinning to a release tag (senzing-commons-java@4.0.1) rather than main is the right call to keep this repo's CI from breaking on an unrelated upstream commit.

Documentation

  • CHANGELOG.md updated — Extensive 0.7.0 entry, cross-referenced with fixture names and measured corpus effects.
  • Standards doc updateddocs/java-coding-standards.md gets new sections (enhanced-for wrapping, nested-call wrap, parameter alignment) matching the code changes.
  • New FAQ docsformatter-python-environment.md and source-preservation-history.md are well-organized and cross-linked ("See also" sections point to each other and the trial checklist).
  • Markdown formatting — Spot-checked the new/modified .md files (070_REMAINING_SCOPE.md, formatter-python-environment.md, source-preservation-history.md, consumer-trial-checklist.md) via direct read; no trailing whitespace or malformed structure observed, consistent ~80-column wrapping, proper heading hierarchy and code fences. (Note: I could not run prettier/a CommonMark linter directly in this sandboxed environment, so this is a visual check rather than a tool-verified one.)
  • ❌ See the stale-fixture-name issue noted above under Code Quality — it's a documentation defect specifically (.claude/070_REMAINING_SCOPE.md:25).

Security

  • No hardcoded credentials — none found.
  • No .lic files or AQAAAD-prefixed strings — grepped the entire diff; zero matches.
  • Error handlingNotImplementedError is raised deliberately for unhandled grammar shapes rather than silently mis-emitting source (existing, reinforced pattern, e.g. _refuse_catch_parameter_modifiers).
  • N/A Input validation — This tool operates on trusted local source trees via tree-sitter; not a network-facing input-validation surface. No new attack surface introduced (dependency bump tree-sitter 0.25.2 → 0.26.0 is pinned and gated by a new three-way version check, test_installed_versions_match_pins).

Summary

This is a large, well-tested, and unusually well-documented formatter release. I found one concrete defect suitable for a code-review comment: a stale/incorrect fixture-name cross-reference in .claude/070_REMAINING_SCOPE.md:25. Everything else checked out — I did not find logic bugs in the several dense/high-risk code paths I traced in detail (snapshot/restore state management, the anchor-escape orphan detection, and the parameter-alignment cascade).


Review Part 2 of 3

PR Code Review — Part 2 of 3

Code Quality

Style/naming/idioms: ✅ Consistent with the rest of the formatter (frozensets for type-membership tests, Final annotations, cascade-of-tiers pattern). Naming is verbose but descriptive and matches existing conventions (_arg_owns_its_rows, _is_nested_or_chained_call, etc.).

No commented-out code: ✅ The deletions (_estimate_normalize, _arg_list_single_line_estimate, _SEMANTIC_WRAP_ARG_TYPES, P1F factory tier) are clean removals, not commented-out remnants, and each removal is backed by a rationale comment explaining why the mechanism is now dead weight.

DRY: ✅ (mostly) _arg_owns_its_rows correctly consolidates what used to be duplicated logic (block-lambda / text-block / semantic-multi-row checks) across the arg-list emitter and the chain discriminator (_segment_emit_is_legitimately_multi_line).

Minor nit — redundant re-emission: In _emit_argument_list's single-arg cascade (around the if _is_block_body_lambda(args[0]) branch), when opener_ok and closer_ok is False, the code does emitter.restore(saved) and falls through to the nested-call-rule-1 check and then the else (standard cascade), which calls emit_p1() again from scratch. Since args[0] is a lambda_expression (not method_invocation/object_creation_expression), the nested-call branch is never taken, so this always re-runs emit_p1() a second time for the block-lambda-does-not-fit case. Harmless (deterministic same output) but wasted work — could reuse saved/skip to emit_p4_single_arg_block_indent directly.

Defects — worth verifying (medium confidence):

  1. Possible stale _anchor_escaped propagation in _emit_variable_declarator. In the Step-3 inline attempt:

    prev_escaped = emitter._anchor_escaped
    emitter._anchor_escaped = False
    with _extra_tail_reserve(emitter, 1):
        _emit_node(emitter, source, value)
    inline_orphan = emitter._anchor_escaped
    ...
    if not inline_overflow and not inline_orphan:
        emitter._anchor_escaped = prev_escaped
        ...
        return

    When the branch is not taken (i.e., inline_overflow or inline_orphan is True), execution falls through to the break-at-= backtrack, which re-emits value again — but nothing resets emitter._anchor_escaped to False before that second emission. If the first (discarded) inline attempt set the flag True (an orphan was detected), and the second (committed) backtrack emission does not itself orphan anything, the flag is left stale at True. For a value containing a nested _emit_variable_declarator call (e.g. inside a lambda block, which is exactly the scenario the surrounding comment calls out — "a declarator nested in this one's value"), this stale True would incorrectly read back to an enclosing declarator's own inline_orphan check, forcing an unnecessary break-at-= even though the actually-committed shape had no orphan. This looks like a genuine (if narrow) case where the flag isn't reset before the shape that actually gets committed is emitted. Worth double-checking against a fixture where an inline lambda body contains a chain that orphans on its first attempt but resolves cleanly on backtrack.

  2. emit_p2b_packed sets emitter._arg_list_p4_fired = True unconditionally, with no save/restore. Every other tier in this cascade (emit_p1, emit_p2_greedy, emit_p3_paren_one_per_line) carefully saves the incoming value of _arg_list_p4_fired, resets it, and restores it after rejecting a candidate — because the flag needs to reflect the state of the shape that's actually committed, not leak from an evaluated-and-discarded candidate. emit_p2b_packed does neither: it sets the flag True, and if the packed candidate doesn't fit and is rejected (emitter.restore(packed_snap)), the flag is never restored to its prior value before falling through to P3. Since P3 explicitly zeroes _arg_list_p4_fired before evaluating arg 0, this specific leak is likely masked in the visible code path — but it's inconsistent with the pattern used everywhere else and worth a second look to confirm there's no path where the stale True is read before P3's own reset (e.g. if P3 is skipped and P4 is reached directly).

  3. Over-conservative tail-reserve in _emit_field_access's broken-dot path (minor). In the non-inline branch:

    emitter.restore(saved)
    _emit_node(emitter, source, object_node)
    emitter.newline()
    ...

    object_node is re-emitted under the ambient emitter.tail_reserve (e.g., reserving for a trailing ;), even though its last line is immediately followed by a forced newline() rather than by whatever the ambient reserve represents. This can cause the receiver to wrap one tier more aggressively than necessary. Not a correctness bug (errs safe), just a potential over-wrap versus the canonical shape.

Project-memory / CLAUDE.md: Not present in this diff chunk; no comment.

Testing

Extensive new fixture coverage for every new behavior introduced: receiver parameters (method_decl_wrap/06), field-access dot-breaking (method_chain_wrap/25), nested-call wrap rules 1–3 (nested_call_wrap/0109), P2b packed tier (arg_list_wrap/17), whole-list escalation (arg_list_wrap/16, 18, 19, 20), and the new _anchor_escaped backoff (method_chain_wrap/21). Several existing fixtures were correctly renamed/updated to reflect new expected output.

test_installed_versions_match_pins (tests/test_format_java.py) is a good addition — it closes a real gap where the two pin sources (requirements.txt and GRAMMAR_VERSION) could agree with each other while both being stale relative to what's actually installed.

❌ Gap: No fixture directly exercises the interaction I flagged in finding #1 above (nested declarator inside a lambda block where the inner value's first inline attempt orphans but backtrack succeeds cleanly) — the existing 21_chain_rhs_backs_off_when_anchor_escapes fixture covers the single-level case, not the nested-declarator case. Worth adding if the concern above is confirmed.

Documentation

Inline comments are extensive to the point of being the primary design record (e.g., the building/source-preservation-history FAQ references). This matches the file's existing very-heavy-comment style, so it's consistent rather than excessive for this codebase.

requirements.txt (tooling/scripts/requirements.txt): Comment updates read fine and CommonMark isn't applicable (not a .md file). One consistency note: the file states tree-sitter is bumped to 0.26.0, and a new comment says pins are "mirrored by GRAMMAR_VERSION... TestGrammarVersionPins fails the build when the two drift" — this diff chunk doesn't show the corresponding GRAMMAR_VERSION dict update in format_java.py. If that edit isn't in part 1 or 3, test_grammar_version_values_match_requirements and the new test_installed_versions_match_pins will fail immediately.

Security

No hardcoded credentials, no logging concerns, no .lic files or AQAAAD-prefixed strings in this chunk. This is a source-formatting tool operating on local Java files; no new input-validation/error-handling surface of security relevance was introduced.


Summary

The core logic changes (nested-call wrap rules, P2b tier, field-access dot-breaking, receiver-parameter support, the widened "if an argument breaks, the list breaks" rule) are well-reasoned and thoroughly fixture-tested. The two items worth confirming before merge are the _anchor_escaped reset gap in _emit_variable_declarator's backtrack path and the missing save/restore of _arg_list_p4_fired around emit_p2b_packed — both are subtle state-leak risks rather than confirmed failures, and both are the kind of thing that would only surface on deeply nested constructs not yet covered by a fixture.


Review Part 3 of 3

PR Code Review — Part 3 of 3 (tooling/scripts/tests/test_format_java.py)

This chunk is test-only: it removes one stale test (test_nested_call_with_string_comma, superseded by the more thorough TestIsNestedOrChainedCall suite) and adds ~700 lines of new unit tests covering the 0.7.0 nested-call/javadoc-reflow/reserve logic introduced in parts 1–2.

Code Quality

  • Style conventions — Consistent with the rest of the test suite: pytest.mark.parametrize, clear class-per-concern grouping, docstrings that explain why a case exists rather than what it does.
  • No commented-out code — The one removal (test_nested_call_with_string_comma) is a clean deletion, not a comment-out.
  • Meaningful names — Test/class names double as a spec (TestDeclarationSemicolonReserve, TestReceiverReserveIgnoresArgumentLayout), which is good practice for a formatter's regression suite.
  • ⚠️ DRY (minor nit) — The tree-walking closure:
    def visit(node) -> None:
        if node.type == "argument_list":  # or "object_creation_expression"
            found.append(node)
        for child in node.children:
            visit(child)
    visit(tree.root_node)
    is duplicated verbatim three times: once in _first_arg_list_of (top of chunk) and twice more inside TestIsAnonymousClass.test_anonymous_class_detected / test_plain_constructor_is_not_anonymous. A shared _find_nodes(tree, node_type) helper would remove the duplication. Low severity — test-only, not shipped code — but worth a quick fix since it's three copies of the exact same 5 lines.
  • Defects/logic — I traced the trickier assertions for self-consistency:
    • TestIsNestedOrChainedCall.test_traversal collects all argument_list nodes via pre-order search and asserts any(results) is expected. For the True cases this correctly targets the inner (nested) call's arg list while the outer statement-level call's arg list stays False; for the False cases there's only ever one arg list in the snippet, so any reduces to a direct check. The parametrized cases (parenthesized cast, ternary, binary op, field access, array index) look like a deliberate and correct enumeration of parent shapes the predicate should not reach.
    • TestMinRaggedLines.test_infeasible_returns_none — six 4-char tokens with max_lines=1: three tokens exactly fill a 14-char line ("aaaa aaaa aaaa" = 14 chars), so six tokens need two lines minimum; with max_lines=1 this is correctly infeasible → None. Arithmetic checks out.
    • TestDeclarationSemicolonReserve.test_still_reports_a_value_with_no_split_point hardcodes "max line width 87" in the assertion — this pins an exact computed width rather than deriving it, which is intentional here (it's explicitly a regression lock per the docstring on the de-dup fix), so I don't consider it fragile in a bad way, just worth flagging as a magic number a future refactor must recompute deliberately.
    • No mismatched fixture/expected-output pairs or off-by-one errors found in TestSecondPassConvergence or TestReceiverReserveIgnoresArgumentLayout — both correctly assert both idempotency (once == twice) and convergence-to-the-same-shape (a == b) rather than just one or the other, which is the right bar for a formatter fixed-point regression.
  • N/A Project .claude/CLAUDE.md — not part of this chunk.

Testing

  • Unit tests for new functions — Direct, dedicated coverage for every helper named in the CHANGELOG-adjacent work: _is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_boundary, plus end-to-end format_source regression tests for the semicolon-reserve and receiver-reserve bugs.
  • Edge cases — Notably strong here: block- vs expression-bodied lambdas, curried lambdas, oversize/unterminated inline tags, nested {@code {a,b}} brace depth, empty input, hard-cap/max-lines boundaries, and a real two-cycle oscillation regression (TestReceiverReserveIgnoresArgumentLayout) with an explicit note on why the two fixtures were chosen (both are fixed points at the pre-fix baseline, so the test only goes red when the specific reserve computation regresses — not the whole release).
  • N/A Integration tests for new endpoints — not applicable (formatter library).
  • ⚠️ Test coverage > 80% — Not independently measurable without running coverage tooling in this review, but the density of tests directly exercising each new private helper makes this very likely satisfied.

Documentation

  • ✅ Docstrings throughout are unusually good test documentation — each class/method explains the failure mode being guarded against (e.g. TestJavadocReflowIsBoundary's note on @Override being miscategorized as prose, or TestFieldAccessCommitAndWarn's note on the silent-drop bug in the revert branch), which is exactly the kind of comment that earns its keep per the "why, not what" bar.
  • N/A No .md/README/CHANGELOG changes in this chunk.

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings.
  • N/A No new input-validation surface (test code operating on trusted literal Java snippets).

Summary

Solid, well-targeted test-only chunk with good edge-case discipline. One minor nit: the tree-walking visit closure is duplicated three times and could be factored into a single _find_nodes helper — worth a quick cleanup but not blocking.



Automated code review analyzing defects and coding standards

…sitives

CI round 8. One documentation defect, one spellcheck failure, and
three code concerns — all three of which turn out to be non-issues,
now documented as such so they stop being re-raised.

**Spellcheck** flagged "dedup", which I introduced in the previous
commit's comment. Reworded to "de-duplication".

**Stale fixture reference.** `.claude/070_REMAINING_SCOPE.md` cited
`arg_list_wrap/18_arg0_wraps_but_paren_aligned_still_fits`; the
fixture was renamed to `18_arg0_wraps_so_whole_list_breaks` during
the release and the census was not updated.

**Two flagged flag-leaks are covered by snapshot/restore**, and both
sites now say so. `_emit_variable_declarator`'s `_anchor_escaped`
reset looks like it could leak a stale True into the backtrack's
re-emission, but `saved` is snapshotted BEFORE the reset and the flag
is part of the snapshot tuple, so `restore(saved)` puts the incoming
value back; the explicit restore is only needed on the commit path,
which does not call `restore`. Likewise `emit_p2b_packed` sets
`_arg_list_p4_fired` without saving it, but the caller snapshots
immediately before invoking the tier — and an ACCEPTED candidate
should leave it set, because P4 genuinely fired. An earlier review
round had already verified the first of these independently; the
comments are there to stop a third pass at it.

The conditional fixture gap raised alongside those was contingent on
the first concern being real, so it does not apply.

Output byte-identical, 801/801, corpus unchanged.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Bash commands are being blocked by approval in this sandbox, so I'll proceed with a manual review based on careful reading of the diff (which is sufficient for this analysis). Here's my review of Part 1 of 3.

Review — Part 1 of 3

This part covers .claude/070_REMAINING_SCOPE.md, .github/dependabot.yml, .github/workflows/pytest.yaml, .vscode/cspell.json, CHANGELOG.md, three docs under docs/faqs/building/, docs/java-coding-standards.md, and roughly the first half of tooling/scripts/format_java.py. This is a documentation-heavy formatting-tool release; I focus scrutiny on the format_java.py logic.

Code Quality

  • ✅ Style/naming/idioms — Consistent with existing conventions (type hints, Final, docstrings). No issues found.

  • ✅ No commented-out code — All additions are prose comments explaining rationale, not disabled code. Comments are unusually long/dense in places (e.g. format_java.py:3134-3160 around the if-statement Tier 1 gate removal, and format_java.py:1684-1770 in _emit_field_declaration), but this matches the codebase's existing house style of heavily-annotated "why" comments, so not flagging as a defect.

  • ⚠️ DRYformat_java.py: _refuse_catch_parameter_modifiers (new, ~line 5975) is correctly factored out and shared between _emit_catch_clause and _emit_catch_formal_parameter — good catch by the author. _emit_class_body_members/_emit_interface_body_members both gained near-identical _attach_trailing_side_comments call sites (~line 1614-1645 and ~7767-7800) — duplication is pre-existing pattern (the two functions were already parallel), not introduced by this PR, so acceptable.

  • ❌ Possible defect — _line_length_exempt's structural static final check (format_java.py, added in the hunk around line 3441-3490, function body ~3470-3481):

    index = text.find("static final")
    if index >= 0:
        angle = text.find("<", index)
        if angle > index and text.find(">", angle) > angle:
            return True

    This is a plain substring/position search over the rendered line text, not an AST-aware check. A line that merely contains the literal substring static final followed later by <...> anywhere in the line (e.g. inside a string literal, a comment, or even a coincidental generic elsewhere on the same line) would be misclassified as an unbreakable constant declaration and have its overflow advisory silently suppressed. This mirrors checkstyle's own regex-based ignorePattern, so it's a faithful port of an already-coarse check, but it's worth flagging since the consequence (a real over-80 line silently dropped from advisories) is exactly the "silent under-formatting" failure mode this release otherwise took pains to eliminate elsewhere (e.g. the parameter-list and javadoc structural-line advisories added in this same diff). Low severity, but a re.match(r'.*static\s+final.*<.*>', ...)-style check anchored to statement start would be safer than an unanchored find.

  • ✅ Defect identification — declaration semicolon reserve (_emit_field_declaration, ~line 1684-1770): Traced the math for multi-declarator statements (int a = ..., b, c;). The new suffix reservation (sum of 2 + len(name) for each later declarator) combined with the pre-existing "+1 for semicolon" charged inside each declarator's own cascade correctly reconstructs the exact trailing text (, b, c;) for a non-last declarator, with no double-count. This looks correct.

  • _emit_for_statement header-span fix (~line 6116-6135): Switching source_was_multi_row from testing the body brace's row to testing the actual max end-row across inits/condition/updates is a legitimate bug fix and matches the changelog's stated defect.

  • ✅ Enhanced-for reserve arithmetic (_emit_enhanced_for_statement, ~line 6323-6390): The +3 reserve during the inline attempt (for ) {) and +1 reserve during the wrapped path (for the lone ), since the brace moves to its own Allman line) are each verified against how many characters are actually written afterward — correct.

  • _fire_wrap_overflow_advisory de-duplication rewrite (~line 802-895): The width-carrying dedup (dataclasses.replace + string-substitution on the message) is a bit fragile — it relies on the literal substring f"max line width {existing.width}" appearing verbatim in existing.message — but since existing.width is always set consistently at construction time (line ~895, width=max_on_disk), and this is the only place FormatterWarning is constructed in the code shown, this is internally consistent. Worth double-checking in parts 2/3 that no other call site constructs a FormatterWarning directly (bypassing the width field).

Testing

Not assessable from this part — no test files are included in this diff chunk (the fixture directory changes referenced in the changelog, e.g. arg_list_wrap/18, method_chain_wrap/26, etc., must be in part 2 or 3). Deferring this checklist section.

Documentation

  • ✅ CHANGELOG.md updated — Extensively so (adds ~1420 lines for the 0.7.0 entry). This is far larger than a typical changelog entry; while thorough and well-organized with headers, its sheer size (nearly the entire diff) makes it a de facto design/postmortem document rather than a changelog. Not a defect, but consider whether some of this belongs in the new FAQ docs instead (much of it already is duplicated there, e.g. source-preservation-history.md and 070_REMAINING_SCOPE.md restate material also in CHANGELOG.md).
  • ✅ New FAQ docs (formatter-python-environment.md, source-preservation-history.md) are clear, well-structured, and cross-link related docs appropriately.
  • ⚠️ CommonMark/prettier formatting — I was unable to run prettier/markdownlint in this sandbox (network access to fetch the package is disabled per this review's constraints, and direct grep calls were blocked by the approval system), so I could not mechanically verify whitespace/formatting compliance. From visual inspection of the diff, the new markdown files look properly formatted (consistent heading levels, fenced code blocks with language tags, no obvious stray trailing whitespace in the diff view). Recommend running the repo's actual lint/format check in CI rather than relying on this review for that item.
  • docs/java-coding-standards.md additions are consistent with the corresponding CHANGELOG.md narrative and read correctly against the code changes I traced above (enhanced-for wrapping, priority 2b, nested-call wrap rules, parameter alignment carve-outs).

Security

  • ✅ No hardcoded credentials, no sensitive data in logs.
  • ✅ No .lic files or AQAAAD-prefixed strings found anywhere in this diff.
  • .github/workflows/pytest.yaml new corpus-gate job — pins the external checkout (senzing-garage/senzing-commons-java) to a release tag (4.0.1) rather than main, with persist-credentials: false on both checkout steps — reasonable supply-chain hygiene. Minor note: it pins by tag rather than commit SHA, which is a lighter-weight guarantee than a full SHA pin (tags are mutable in principle), but this matches how the rest of the workflow presumably references actions (actions/checkout@v7.0.1 etc.), so it's consistent with existing project convention rather than a new weakness.

Summary so far: No blocking defects found in this part. One low-severity advisory-suppression edge case in _line_length_exempt's static final matching worth a second look. Everything else — the semicolon-reserve arithmetic, the for/enhanced-for header fixes, the switch-brace and catch-clause additions — checks out against the stated intent in the changelog and standards doc. Waiting for parts 2 and 3 (fixtures/tests and the remainder of format_java.py) before giving a final verdict on the Testing checklist section.


Review Part 2 of 3

Review — Part 2 of 3

This part covers the back half of tooling/scripts/format_java.py (parameter alignment, receiver parameters, field access wrapping, argument-list cascade rewrite, method-chain wrap tiers, method-invocation reserve, variable-declarator reserve), tooling/scripts/requirements.txt, and a large batch of new/renamed test fixtures plus test_format_java.py changes. I checked the current on-disk state (tooling/scripts/format_java.py, HEAD) against the diff to get accurate line numbers.

Code Quality

  • ✅ Style/naming/idioms — Consistent with the codebase's existing conventions (type hints, Final, closures with list-boxed flags for cross-closure state). No issues.
  • ✅ No commented-out code — All large comment blocks are prose rationale (including several "here's what used to be here and why it was removed" notes), consistent with this repo's established house style of exhaustive "why" comments. Verbose, but not a defect.
  • ✅ DRY — The single-arg-owns-its-rows concept (_arg_owns_its_rows) is correctly factored out and reused across P1/P2/P3 wrap tiers and the chain-segment "legitimately multi-line" discriminator, replacing what were previously separate, duplicated source-spanning checks (_SEMANTIC_WRAP_ARG_TYPES + _node_spans_multiple_rows). Good consolidation.
  • ⚠️ Minor: dead variableformat_java.py:9920, inside the block-body-lambda single-arg branch of _emit_argument_list:
    effective_max = _MAX_LINE - emitter.tail_reserve
    ...
    opener_ok = (len(emitter._lines[saved[0]]) <= _MAX_LINE)
    ...
    closer_ok = (emitter.column + emitter.tail_reserve <= _MAX_LINE)
    effective_max is computed but never referenced — both opener_ok and closer_ok inline the equivalent comparison against _MAX_LINE directly. Not a correctness bug (the checks are still right: opener_ok correctly uses the raw _MAX_LINE since that line isn't the emission's last line and carries no tail reserve), just a leftover local that should be deleted.
  • ✅ Defect-hunting on the cascade rewrite — I traced the trickiest parts by hand against the new fixtures:
    • _field_access_receiver_is_computed/_emit_field_access (format_java.py:8534-8646): inline-first-then-break-before-dot logic is internally consistent; the revert-to-inline path when breaking doesn't help, followed by an unconditional advisory, correctly covers both the "reverted" and "broken but still overflowing" cases.
    • _is_nested_or_chained_call, chain_is_sole_arg, p3_col anchor in _emit_method_chain_wrapped (format_java.py:~10300-10410, 10902-10937): the chain_is_sole_arg gating of P3F/P2/P2-greedy-dot-aligned tiers is correctly scoped to headless positional-argument chains only (verified against nested_call_wrap/07_identifier_receiver_chain_keeps_dot_align and 18_arg0_wraps_so_whole_list_breaks fixtures, which exercise exactly the "keep dot-align" vs. "force one-per-line" split).
    • emit_p4_multi_arg's per-argument tail-reserve handling (drop-and-replace with 1 for non-last args, add +1 for the last) matches its own comment's stated invariant and is exercised by fixture 19_middle_argument_keeps_full_budget.
    • _emit_method_invocation's reserve simplification (.NAME( prefix only, no longer the arguments' first source line) is a real bug fix — the old approach was provably non-idempotent (oscillating on .append(...) based on how the argument happened to be wrapped), and the new approach is covered by method_chain_wrap/26_receiver_reserve_ignores_arg_layout.
    • _extra_tail_reserve usage in _emit_variable_declarator/_emit_variable_declarator_with_array_rhs references a context manager defined elsewhere in the file (format_java.py:3728) — confirmed it exists, not a dangling reference.

No correctness defects, security issues, or crash/edge-case gaps found in this part beyond the dead-variable nit above.

Testing

  • Every new/changed formatting behavior in this chunk has a corresponding fixture pair (arg-list P2b/P4-packed/whole-list-escalation, receiver parameters, field-access dot-break, nested-call wrap rules 1–3, chain anchor-escape backoff, multi-catch, switch-brace, record headers, source-preserve reanchoring). This is unusually thorough coverage.
  • Fixture renames (e.g. 05_paren_align_binary_positional_arg05_binary_positional_arg_breaks_the_list) correctly pair a rename with an updated expected.java reflecting the new intended output, not an orphaned stale expectation.
  • test_format_java.py: TestEstimateNormalize and TestArgListSingleLineEstimate are removed in lockstep with the removal of _estimate_normalize/_arg_list_single_line_estimate from format_java.py — no dangling tests referencing deleted functions.
  • New test_installed_versions_match_pins (test_format_java.py) closes a real gap: the existing tests only compared requirements.txt to GRAMMAR_VERSION as two files, never against what's actually installed — the docstring cites a concrete prior incident (0.25.2 installed vs. 0.26.0 pinned, 704 tests still green).

Documentation

  • requirements.txt comment updates are accurate and add a useful cross-reference (GRAMMAR_VERSION dict / TestGrammarVersionPins) for why dependabot bumps need a matching code change.
  • Not applicable in this chunk: README/API docs/CHANGELOG (not part of this diff slice).

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings found in this chunk.
  • No sensitive data in logs/advisories — all FormatterWarning messages are static template text plus source positions.

Summary

One nit: remove the unused effective_max local at format_java.py:9920. Everything else in this chunk — the argument-list cascade rewrite, field-access wrapping, method-chain tier gating, and the reserve-accounting fixes — is logically sound, matches its extensive inline rationale, and is backed by targeted regression fixtures.


Review Part 3 of 3

Both fixtures exist and match the paths referenced in TestSecondPassConvergence. I have enough to finalize the review for this chunk.

Review — Part 3 of 3

This part covers the tail end of tooling/scripts/tests/test_format_java.py: removal of the old _arg_list_single_line_estimate multi-row tests, and a large batch of new unit tests for the 0.7.0 formatter internals (nested-call detection, anonymous-class detection, javadoc inline-tag grouping/reflow, minimum-raggedness fill, declaration/receiver semicolon reserves, second-pass convergence, field-access advisory coverage). I cross-checked every function name referenced by the new tests against tooling/scripts/format_java.py at HEAD and confirmed all exist at the cited definitions (_is_anonymous_class 9010, _is_nested_or_chained_call 9062, _javadoc_is_prose_line 3517, _balanced_reflow_words 3592, _greedy_fill 3750, _group_inline_tags 3770, _splits_inline_tag 3815, _min_ragged_lines 3853, _javadoc_reflow_is_boundary 3956, _javadoc_balanced_reflow 3992, _emit_field_access 8547). I also confirmed the two fixture files TestSecondPassConvergence reads from disk (fixtures/condition_wrap/12_for_clause_wrap_escalates_whole_header/input.java, fixtures/need_braces/23_wrapped_source_condition_still_collapses/input.java) exist.

Code Quality

  • ✅ Style/naming/idioms — Consistent with the rest of the test suite; docstrings follow the project's house style of explaining why a test exists (often citing the specific bug it guards against), not just what it does.
  • ✅ No commented-out code.
  • ✅ Meaningful names — Test and helper names are unusually descriptive (test_unstable_candidate_is_rejected, test_semicolon_does_not_land_in_column_81).
  • ⚠️ DRYtest_format_java.py:4133-4137 and 4149-4153 (TestIsAnonymousClass.test_anonymous_class_detected / test_plain_constructor_is_not_anonymous) each define an identical inline visit() pre-order walker to find the first object_creation_expression. This is the same pattern as _first_arg_list_of (test_format_java.py:4028-4051), just parametrized over node type. A shared _first_node_of(snippet, node_type) helper would remove three near-duplicate traversal blocks. Minor, no behavioral risk.
  • ✅ Defect-hunting — No bugs found in the test logic itself. Traced by hand:
    • TestIsNestedOrChainedCall.test_traversal's any(results) is expected check correctly handles the case of multiple argument_list nodes per snippet (only one needs to match True; none may for False) — verified against a couple of the trickier parametrized cases ("var x = (inner(a, b));", curried lambdas).
    • _min_ragged_lines (format_java.py:3853) — confirmed the DP correctly charges slack on every line including the last (per its docstring, the deliberate departure from classic minimum-raggedness), and that the end > start guard lets an over-wide single token still produce a solution rather than None, matching test_oversize_token_gets_its_own_line.
    • The removed tests (test_multi_row_source_collapses, test_string_with_comma_inside_multi_row_source, test_nested_call_with_string_comma, and the surrounding _arg_list_single_line_estimate class) correspond to the removal of _estimate_normalize/_arg_list_single_line_estimate from format_java.py, already confirmed clean in the Part 2 review (no dangling references to deleted functions).
  • N/A .claude/CLAUDE.md — not touched in this chunk.

Testing

  • ✅ Unit tests for new functions — Every new predicate/helper added in this release (_is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_boundary) gets a dedicated test class.
  • ✅ Edge cases covered — Notably thorough: the False-case table in TestIsNestedOrChainedCall (parenthesized, cast, ternary, binary, field-access, array-index parents) exists specifically to catch scope creep in the traversal predicate; TestMinRaggedLines covers empty input, infeasibility, the hard cap, and oversize tokens; TestJavadocBalancedReflow/TestJavadocReflowIsBoundary include an explicit oscillation/instability regression test pair (test_unstable_candidate_is_rejected + test_stability_check_sees_the_oscillation).
  • ✅ Regression coverage for prior silent bugsTestDeclarationSemicolonReserve, TestReceiverReserveIgnoresArgumentLayout, and TestFieldAccessCommitAndWarn each document (in their docstrings) a specific class of bug that shipped silently (off-by-one onto column 81, a two-cycle non-convergence, a missing advisory on a committed over-long line) and assert against exactly that failure mode.
  • ✅ Test coverage — This chunk is essentially additive-only test coverage; no production code is unreached by a corresponding test here.

Documentation

  • N/A for this chunk — no README/API docs/CHANGELOG/markdown files touched (all covered in Parts 1–2).

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings in this chunk.
  • ✅ No sensitive data in logs — test fixtures use synthetic identifiers only (SampleRequestHandler, Foo, Bar, etc.).

Summary

No blocking issues in this final part. One minor DRY nit (duplicated node-finding visit() helper in TestIsAnonymousClass, test_format_java.py:4133-4157) worth a follow-up cleanup but not worth blocking on. Combined with Parts 1–2, this PR's only outstanding items are: the low-severity _line_length_exempt substring-matching edge case (Part 1) and the unused effective_max local at format_java.py:9920 (Part 2, since renumbered — the argument-list cascade section this referred to). No security, correctness, or test-coverage blockers found across the full diff.



Automated code review analyzing defects and coding standards

CI round 9 reported no blocking issues. Its one flagged item was a
"possible defect" it also identified as a faithful port, and checking
it out confirms the latter reading.

`_line_length_exempt`'s `static final` test is a plain scan over the
rendered line, so it also matches `static final` inside a string
literal or a comment. That is fidelity rather than sloppiness: the
question the function answers is "will checkstyle skip this line",
and checkstyle asks `static final.*<.*>` of the same text. Compared
against checkstyle's own regex over eleven lines — including every
"misclassification" the review raised — the two agree on all of
them, zero mismatches. Being MORE precise would be the defect: the
formatter would advise about lines the build ignores, which is the
noise this check was added to remove.

Rather than argue that in prose, the agreement is now pinned by
`TestLineLengthExemptMatchesCheckstyle`, which asserts the two
verdicts match line-for-line and additionally fails if the
`ignorePattern` in `checkstyle/senzing-checkstyle.xml` drifts away
from the copy the test compares against.

Also removed an unused `effective_max` local in the block-body-lambda
branch of the argument-list cascade.

803/803. Output byte-identical, corpus unchanged: over-80 1571,
advisories 289, second-pass 1, third-pass 0.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

Code Review — 0.7.0 release PR

No CRITICAL security findings. No .lic files anywhere in the diff or repo; no string beginning AQAAAD in any added/modified content.

Verdict: approve with follow-ups. This is an unusually well-evidenced release — every behavior change is corpus-measured, convergence work is real (26→1 second-pass files), ~280 lines of provably-dead code removed, backed by 235 golden fixtures + ~40 unit tests. Nothing blocks on formatter correctness, but two CI/config items below are worth verifying before merge.


Code Quality

Item
Style conventions ⚠️
No commented-out code
Meaningful names
DRY
Defects ⚠️
CLAUDE.md scope ✅ N/A (no CLAUDE.md in repo root; new .claude/070_REMAINING_SCOPE.md is project-scoped only)
  • PEP 8 nits: format_java.py:3481 docstring line 84 chars; _extra_tail_reserve (:3738) missing return annotation; test_format_java.py:4666-4668 triple blank line (E303).
  • Defects reviewed, all hold up as sound but one is brittle: _fire_wrap_overflow_advisory's de-dup (:892-897) rewrites a warning message via string-replace on existing.width — correct today (invariant holds everywhere it's constructed) but nothing enforces that invariant going forward.
  • _min_ragged_lines DP, _javadoc_balanced_reflow floor/adopt logic, _LINE_LENGTH_EXEMPT_MARKERS (verified faithful to checkstyle's actual unanchored regex), and the multi-declarator suffix math all check out correct.
  • Minor inconsistency: _emit_record_declaration's terminal commit fires no overflow advisory of its own, unlike every sibling cascade.

Testing

✅ Fixtures for all cited golden files are in the diff (nested_call_wrap, record_header_wrap, multi_catch_wrap, etc.) — no claim/verification gap. Edge cases well covered. ⚠️ Coverage >80% unverifiable — CI runs pytest with no --cov flag.

Documentation

  • README.md:22 — still says "912-line rules doc"; docs/java-coding-standards.md is now 3580 lines.
  • README.md:23-27 — FAQ inventory omits both new files this PR adds.
  • docs/faqs/building/java-formatting-standards.md:81 — still pins tree-sitter==0.25.2, contradicting this same PR's bump to 0.26.0.
  • format_java.py:45 + docstring — says "234 fixture pairs", tree has 235.
  • ⚠️ A few doc/example inconsistencies (varargs-only carve-out text vs. code also excluding receiver params; CHANGELOG lines exceeding its own wrap width).
  • ✅ Inline comments are exceptional throughout.

Security

✅ All clear. corpus-gate CI job is well-built (empty top-level permissions, persist-credentials: false, pinned tag, external corpus only parsed as data, never executed). Minor hardening suggestions only (SHA vs tag pin, .corpus not gitignored).

Two items worth verifying before merge

  1. .github/dependabot.yml — the diff keeps /tooling/scripts and removes /tooling/scripts/tests, but tests/requirements.txt is the one with pytest>=9.1.1 plus a -r ../requirements.txt include for the rest — it's the superset, not the duplicate. As written, Dependabot may stop seeing pytest bumps. Worth double-checking Dependabot's actual directory-resolution behavior before merging.
  2. corpus-gate can go green having tested nothing — if .corpus/src fails to materialize, test_fuzz_corpus.py/test_performance.py just skip cleanly and the job reports success. A test -d guard before the pytest call would close this.

Review Part 2 of 3

I don't have write permission for that file, so I'll present the review directly here instead (matching the format already used in review-chunk-1.txt for consistency with the rest of this chunked review).

Code Review — Part 2 of 3 (parameter/field-access/argument-list/method-chain wrap engine)

No CRITICAL security findings. No .lic files, no AQAAAD-prefixed strings anywhere in this chunk.

Verdict: approve. This chunk covers the bulk of the wrap-engine rework: the parameter-list P3 alignment-vs-width tradeoff, a new receiver_parameter emitter (fixes a prior silent-drop bug for Foo this parameters), field_access gaining its own break-before-dot tier, a large simplification of _arg_list_takes_source_preserve_path (dropping a whole family of width-based fallback rules for two correctness-only triggers), a new P2b "packed" argument-list tier, several tail-reserve fixes, and removal of the 0.6.0 "P1F" factory-chain tier in favor of new chain-anchor/orphan-detection logic. I cross-checked the actual working tree (not just the diff) rather than trusting the comments at face value:

Code Quality

Item
Style conventions
No commented-out code
Meaningful names
DRY
Defects ✅ none found
  • tooling/scripts/format_java.py:8088 (_formal_param_name_col_offset) correctly bails out when a receiver_parameter is in the list (p.type != "formal_parameter" guard, line 8118) — matches _emit_receiver_parameter's docstring claim that receivers opt out of column alignment.
  • emitter._anchor_escaped (new, set at format_java.py:9475) is genuinely part of Emitter's snapshot/restore tuple (lines 265, 538, 579), so the "hand back the flag as we found it" logic in _emit_variable_declarator (~line 11431) is correct, not a stale-flag leak as a reviewer might suspect on first read.
  • _extra_tail_reserve (format_java.py:3738) is a proper @contextmanager restoring the prior reserve on exit; its use around RHS emission in _emit_variable_declarator/_emit_variable_declarator_with_array_rhs doesn't double-charge.
  • GRAMMAR_VERSION["tree-sitter"] (format_java.py:114) was bumped to "0.26.0" in sync with requirements.txt in this same diff — no drift within this chunk (the stale 0.25.2 reference chunk 1 flagged is in docs/faqs/..., a different file).
  • Verified every helper this chunk deletes (_chain_receiver_is_factory, emit_p1f_factory, _SEMANTIC_WRAP_ARG_TYPES, _arg_list_has_semantic_multi_row_arg, _arg_list_single_line_estimate, _estimate_normalize) has zero remaining references in the tree, and both call sites of _arg_list_takes_source_preserve_path were updated to its new no-column signature. No orphaned dead code.
  • Minor nit: the new P3 "escape" detection in emit_p3_paren_one_per_line (~format_java.py:9780) reaches into emitter._lines/emitter._current directly instead of a public accessor — the only spot in this chunk that pokes at emitter internals from outside the class. Functionally fine per its own fixture (18_arg0_wraps_so_whole_list_breaks), just an encapsulation wart.

Testing

✅ Every behavior change has a matching new/renamed fixture: parameter alignment-vs-width (method_decl_wrap/05_short_name_keeps_paren_align_over_double_indent), receiver parameters (method_decl_wrap/06_receiver_parameter_preserved), field-access dot-break (method_chain_wrap/25_field_access_breaks_before_dot), nested-call/chain-anchor rules (nested_call_wrap/0109, method_chain_wrap/18,21,24,26), P2b packed tier (arg_list_wrap/17), whole-list escalation (arg_list_wrap/16,18,19,20). Renamed fixtures (e.g. 18_p1f_factory_deep_dot18_factory_chain_breaks_at_first_dot) correctly ship updated expected.java rather than stale expectations.
✅ Removal of TestEstimateNormalize/TestArgListSingleLineEstimate tracks the corresponding production-code deletion — no orphaned tests.
✅ New test_installed_versions_match_pins (via importlib.metadata) closes a real gap — it would have caught the exact tree-sitter version mismatch chunk 1 flagged as having silently passed 704 tests.
⚠️ Coverage >80% still unverifiable from this chunk alone (no --cov flag visible).

Documentation

✅ Inline comments are thorough and, where spot-checked, accurate to the code rather than aspirational. No README/CHANGELOG/API docs in this chunk's diff — see chunk 1 for repo-wide doc drift findings (stale docs/faqs/.../java-formatting-standards.md:81 pin, fixture-count mismatch).

Security

✅ Clear — internal formatter logic and test fixtures only, no credentials/logging/license concerns.


One process note: review-chunk-2.txt exists as an empty placeholder in the working tree (alongside a populated review-chunk-1.txt), suggesting an external orchestration step expects this review written there — but I wasn't given write permission for it, so I'm returning the review as a message instead. Let me know if you'd like me to write it to that file.


Review Part 3 of 3

Code Review — Part 3 of 3 (test file changes)

This chunk is entirely test code: removal of a handful of _arg_list_single_line_estimate tests and a large addition of new test classes covering 0.7.0 formatter behavior (nested-call detection, javadoc reflow, line-length exemptions, etc.).

Code Quality

  • Style conventions: ✅ Consistent with existing test style (parametrize, docstring-as-rationale pattern already established in prior 0.7.0 commits per git log).
  • No commented-out code: ✅ None found; the verbose comments are explanatory docstrings, not dead code.
  • Meaningful variable/function names: ❌ _first_arg_list_of (new helper, ~line 4028 in the hunk) is misnamed/mis-documented. Its docstring says "Return the FIRST argument_list node in a method body", but the implementation does a full pre-order traversal, appends every matching node to found, and returns the whole list (~line 4050, return found). Every call site (arg_lists = _first_arg_list_of(snippet) then for node in arg_lists) relies on the "all matches" behavior, not "first." The name/docstring should be something like _all_arg_lists_of, otherwise a future reader will assume arg_lists is a single node and misuse it.
  • DRY: ❌ The pre-order "walk children, collect nodes of type X" closure is duplicated three times: once inside _first_arg_list_of, and again independently inside TestIsAnonymousClass.test_anonymous_class_detected and test_plain_constructor_is_not_anonymous (both define their own local visit to collect object_creation_expression nodes). Worth factoring into one small _nodes_of_type(tree, type_name) helper shared by all three.
  • Defects/logic: No functional bugs spotted in the test assertions themselves; the parametrized cases for _is_nested_or_chained_call, _splits_inline_tag, and _min_ragged_lines look internally consistent (widths, expected outputs, and stated rationale line up).
  • Test-removal note: The six removed tests (test_block_comment_with_comma_preserved, test_empty_arg_list, test_multi_row_source_collapses, test_string_with_comma_inside_multi_row_source, test_nested_call_with_string_comma, and the one implied just above the visible diff) exercised _arg_list_single_line_estimate. This chunk alone doesn't show whether that function was removed/renamed in the production source (likely in part 1/2). If the function still exists, this is a coverage regression worth double-checking against parts 1–2.
  • CLAUDE.md: N/A — no .claude/CLAUDE.md changes in this chunk.

Testing

  • Unit tests for new functions: ✅ Strong, targeted coverage for _is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_boundary, _line_length_exempt, and the field-access/declaration-semicolon regression fixes.
  • Edge cases: ✅ Good — empty input, infeasible packing, oversize tokens, unterminated tags, detached AST nodes, both-layouts-converge fixed-point checks.
  • Fragile test note: TestLineLengthExemptMatchesCheckstyle.test_pattern_is_still_the_one_in_the_config computes the checkstyle config path via Path(__file__).resolve().parents[3] and skips (rather than fails) if the file isn't found there. If the test file ever moves, parents[3] could resolve to some other existing-but-wrong directory that happens not to contain checkstyle/senzing-checkstyle.xml, in which case the test silently skips instead of failing — giving false confidence that the regex is still pinned to the real config. Consider asserting the resolved path is correct, or failing loudly instead of skipping when running inside the full repo.
  • Coverage > 80%: Not measurable from the diff alone, but the added surface area looks well covered.

Documentation

  • Not applicable to this chunk (no README/CHANGELOG/markdown changes visible here).
  • Docstrings are thorough and explain the "why" behind each regression test, consistent with this project's established style.

Security

  • ✅ No hardcoded credentials, no .lic files, no sensitive data — this chunk is pure test logic over synthetic Java snippets.


Automated code review analyzing defects and coding standards

Round 10 found no formatter defects. Six items, all documentation
staleness or test-code hygiene, all fixed.

**README was describing an older repo.** It called
`docs/java-coding-standards.md` "the canonical 912-line rules doc"
when it is now 3,580 lines, and its FAQ inventory listed four files
when there are eight — missing both this release's additions plus
`consumer-trial-checklist` and `cspell-word-list-policy`. The line
count is dropped rather than corrected: it was destined to go stale
again, and it carries no information a reader needs.

**A FAQ contradicted this PR's own dependency bump**, still pinning
`tree-sitter==0.25.2` in prose while `requirements.txt` and
`GRAMMAR_VERSION` moved to 0.26.0.

**The fixture-pair count** in `format_java.py` said 234 against 235
on disk.

**`_first_arg_list_of` was misnamed.** It promised the FIRST
`argument_list` and returned all of them — and callers depend on
that, since they assert over the whole list. Renamed
`_arg_lists_of`, with the docstring saying what it returns.

**Three copies of the same pre-order tree walk** collapsed onto one
`_nodes_of_type` helper: one in that helper's caller and two more in
`TestIsAnonymousClass`. A single local `visit` closure remains, the
one inside the shared helper.

Left alone deliberately: `CHANGELOG.md`'s "912-line standards doc"
sits inside the `[0.1.0]` section, where it is accurate history.

803/803. Output byte-identical across all 504 corpus files;
over-80 1571, advisories 289, second-pass 1, third-pass 0.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

PR Code Review — 0.7.0 formatting release

Reviewed the full diff (143 files changed: .claude/070_REMAINING_SCOPE.md, .github/dependabot.yml, .github/workflows/pytest.yaml, .vscode/cspell.json, CHANGELOG.md, README.md, four docs/faqs/** files, docs/java-coding-standards.md, tooling/scripts/format_java.py, requirements.txt, test_format_java.py, and ~50 new/moved fixture pairs), not just part 1 that was pasted into chat. I read pr_diff.txt from disk since it contained the complete diff.

Code Quality

  • Style conventions. Consistent with existing formatter code: type hints, Final, docstrings on every new function, comment style matches the surrounding file.
  • No commented-out code. Deleted code (the old P1F factory-chain tier, _arg_list_single_line_estimate, _estimate_normalize, etc.) is fully removed, not commented out — confirmed via git diff (shows as - lines, not disabled code left in place).
  • Meaningful variable names. p3_arg_escaped, chain_is_sole_arg, _arg_owns_its_rows, etc. are descriptive and match their extensive docstrings.
  • DRY. Shared helpers factored out well: _refuse_catch_parameter_modifiers (format_java.py:5863 area) is shared between the new multi-catch cascade and the pre-existing single-type path instead of being duplicated; _extra_tail_reserve context manager centralizes the semicolon-reserve pattern used in four call sites; _balanced_reflow_words is now shared between // and javadoc reflow.
  • Defects. I did not find a new correctness bug. Notably, most of what would normally be "review findings" have already been caught and fixed within this same PR — the CHANGELOG documents four pre-existing defects and five CI-review findings that were fixed during this release's own review rounds (inline argument comments producing invalid Java, dropped receiver parameters, field-access break-before-dot, declaration-semicolon reserve, for-header multi-row misdetection, etc.), each backed by a fixture. I independently traced several of the trickiest bits and they check out:
    • _line_length_exempt (format_java.py:3466) against the actual checkstyle/senzing-checkstyle.xml ignorePattern (^package.*|^import.*|a href|href|http://|https://|@snippet|static final.*<.*>) — the structural checks (startswith, the static final...<...> scan) and the substring marker list together cover exactly the 8 alternatives, and the new TestLineLengthExemptMatchesCheckstyle test pins this against the live XML file with a skip-guard for standalone clones.
    • The _fire_wrap_overflow_advisory de-duplication-with-width-upgrade logic (format_java.py:800) — verified only one FormatterWarning(...) construction site exists now (the string-replace-based width upgrade can't collide with a stale default, since every warning is created with its real width).
    • The record-header rejection logic in _emit_record_declaration (format_java.py:5453) — the "component list wrapped ⇒ always retry with implements on its own line" branch matches its own doc comment about the un-produced shape.
    • The nested-call lambda-transparency traversal in _is_nested_or_chained_call (format_java.py:9072) is covered by an explicit parametrized test table of True/False cases including the curried-lambda and block-lambda-opacity edge cases.
  • .claude/CLAUDE.md. No such file exists in the repository (only an unrelated, untracked build-resources/.claude/CLAUDE.md scratch directory outside the diff) — not applicable here.

Testing

  • Unit tests for new functions. Every new helper (_is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _line_length_exempt, etc.) has a dedicated test class in test_format_java.py, including deliberately-adversarial cases (nested brace depth, oversize tokens, unterminated tags).
  • Edge cases covered. ~50 new/renamed fixture pairs target specific edge cases (lambda-body boundary, receiver-parameter preservation, multi-declarator suffix reservation, chain anchor escape, etc.), each named for the exact behavior it locks.
  • New CI corpus gate. .github/workflows/pytest.yaml adds a corpus-gate job that checks out senzing-garage/senzing-commons-java@4.0.1 so test_fuzz_corpus.py/test_performance.py (AST round-trip + idempotency) actually run in CI instead of silently skipping in a standalone checkout — closes a real gap (these properties were previously unverified in CI). Pinning to a release tag rather than main is the right call to keep this repo's CI from going red on an unrelated upstream commit.
  • ⚠️ Test coverage > 80%. Can't measure a numeric percentage from a diff, but coverage is clearly extensive (235 fixture pairs vs. 83 pre-release, per format_java.py's own docstring update, plus 41 new unit tests for the reflow helpers). No action needed, just flagging that this line item isn't independently verifiable from the diff alone.
  • N/A Integration tests for new endpoints — not applicable; this is a formatter/CLI tool, not a service.

Documentation

  • CHANGELOG.md updated. Extremely thorough — every behavior change has a before/after example and a stated corpus-measured effect.
  • README.md updated to list the three new FAQ files.
  • Standards doc (docs/java-coding-standards.md) updated with new sections (enhanced-for wrapping, priority 2b, nested-call wrap, "if an argument breaks, the argument list breaks") that match what the code actually implements, plus corrections to two examples that had drifted from actual formatter output.
  • Markdown formatting. No trailing whitespace found in any new/changed .md file (070_REMAINING_SCOPE.md, the three new FAQ files, consumer-trial-checklist.md). Structure (headers, fenced code blocks, lists) reads as valid CommonMark. I don't have prettier available to run in this sandbox, so I could not mechanically confirm exact prettier formatting (e.g. line-wrap width, list-marker style) — worth a prettier --check pass in CI if that isn't already gated, but nothing I read by eye looks mis-formatted.
  • ℹ️ One documentation nit: docs/java-coding-standards.md's new "Priority 3" section (parameter double-indent skip) and the _formal_param_name_col_offset docstring both restate the same worked example (SomeExtremelyLongQualifiedTypeName) — intentional per the PR's style of mirroring doc examples in code comments, not a defect.

Security

  • No hardcoded credentials. Scanned all added lines for password/secret/api-key/credential patterns — only matches are the persist-credentials: false YAML keys in the new/existing GitHub Actions steps, which is the secure setting (explicitly disabling credential persistence), not a leaked credential.
  • No .lic files or AQAAAD-prefixed strings anywhere in the diff.
  • Input validation / error handling. Unrecognized grammar node types still raise NotImplementedError rather than silently passing through (pre-existing policy, unchanged). New emit-and-warn paths (parameter list, javadoc structural line, field access) fail safe: they emit something and advise rather than crash or silently corrupt output.
  • No sensitive data in logs. FormatterWarning messages only ever contain line/column numbers and construct names — no source content beyond what's already in the user's own file.
  • New CI job correctly scopes permissions: contents: read on the corpus-gate job and uses persist-credentials: false on both checkouts (own repo and the external senzing-commons-java corpus) — least-privilege, no lingering token that a later step in the same job could exfiltrate.

Summary

No blocking issues found. This is an unusually well-self-audited PR — the CHANGELOG itself documents the review process finding and fixing several defects (including one genuine infinite oscillation bug in the method-chain receiver reserve) before this diff was finalized, each backed by a named regression fixture. The dependabot.yml cleanup and new corpus-gate CI job are both small, well-justified infra fixes with clear rationale recorded in comments. Nothing here should block merge.


Review Part 2 of 3

Code Review — PR Diff (Part 2 of 3)

Note: This is a review of a diff fragment (tooling/scripts/format_java.py + fixtures/tests). I don't have parts 1 or 3, so some checklist items (overall README/CHANGELOG state, full test-suite coverage, whether other hunks touch security-sensitive code) can't be fully assessed from this fragment alone.

Code Quality

  • Style/idioms — Consistent with the rest of the file (type hints, Final[...], docstrings explaining why not what). No issues.
  • No commented-out code — The large deletions (old _estimate_normalize, _arg_list_single_line_estimate, _SEMANTIC_WRAP_ARG_TYPES, P1F factory tier, the 124-line source-preserve remap) are genuine removals with prose explaining the removal, not disabled code left in place.
  • Namingp3_arg_escaped, p3_arg_invalid_wrap, chain_is_sole_arg, etc. are verbose but self-documenting, consistent with the file's existing convention of state flags named for exactly what they track.
  • ⚠️ DRYemit_p1, emit_p2_greedy, and emit_p3_paren_one_per_line each re-implement the same "did this argument wrap when it shouldn't have" tracking (*_invalid_wrap flags) with near-identical bodies. This is likely intentional (each tier has slightly different bookkeeping alongside it — e.g. P3 also tracks p3_arg_escaped), so I'm not flagging it as a defect, just noting it as the largest duplication in this hunk.
  • Possible defect — stale _anchor_escaped leaking across the backtrack path in _emit_variable_declarator (around the inline_orphan logic near the end of the function): prev_escaped is saved and emitter._anchor_escaped reset to False only immediately before the inline emit attempt. On the early-return ("fits, no orphan") path, the flag is explicitly restored to prev_escaped. But when execution falls through to the break-at-= backtrack (inline_overflow or inline_orphan), the flag is left at whatever the inline attempt set it to (possibly True), and the subsequent backtrack emission never resets/re-scopes it before writing = + value again — it only gets corrected if emitter.restore(saved) happens to restore _anchor_escaped as part of the snapshot tuple. The in-code comment claims "Reviewers have read this as a stale-flag leak more than once — it is not one," attributing correctness to restore(saved) including _anchor_escaped in the snapshot. I could not verify that guarantee from this fragment (the Emitter.snapshot()/restore() definitions aren't in this diff chunk). If _anchor_escaped is not part of the snapshot tuple, a variable declarator nested inside another construct's value expression could report a spurious escape to its enclosing caller after backtracking. Recommend confirming _anchor_escaped is included in Emitter.snapshot() before trusting this comment; if it isn't, this is a real latent bug.
  • CLAUDE.md — Not touched in this fragment.

Testing

  • ✅ New/changed behavior is paired with fixture pairs in essentially every case (e.g. 05_binary_positional_arg_breaks_the_list, 13_defect_3_nested_wrap_breaks_the_list, 18_arg0_wraps_so_whole_list_breaks, 21_chain_rhs_backs_off_when_anchor_escapes, 25_field_access_breaks_before_dot, 06_receiver_parameter_preserved). This is a strong pattern — each behavioral change has a corresponding before/after fixture.
  • test_installed_versions_match_pins (new in test_format_java.py) directly closes a real gap: pinned files can drift from the installed environment silently. Good addition, well-justified in its own docstring (cites a concrete false-pass incident: 704 tests green with a mismatched tree-sitter version).
  • ⚠️ No visible test for the _anchor_escaped cross-call leak scenario flagged above (a variable declarator whose value itself contains a nested declarator/lambda body with its own escape). Given the comment explicitly anticipates reviewers raising this, a regression test locking in the intended snapshot/restore behavior would be cheap insurance.

Documentation

  • requirements.txt comment updates accurately reflect the version bump and add a useful new invariant note (GRAMMAR_VERSION dict mirroring, dependabot bump instructions).
  • N/A CHANGELOG.md / README — not present in this diff fragment.

Security

  • ✅ No hardcoded credentials, no .lic files, nothing resembling AQAAAD....
  • ✅ No user input handling in this fragment (pure AST-driven code formatter internals).

Summary

This fragment is a mature, heavily self-documented change (0.7.0 line) — nearly every non-obvious decision has an inline rationale, and almost every behavioral change ships with a fixture. The one item worth a second pair of eyes before merge is the _anchor_escaped flag lifecycle in _emit_variable_declarator: the code explicitly acknowledges this is a recurring point of reviewer confusion, which is itself a signal it's worth independently verifying that Emitter.snapshot()/restore() actually captures _anchor_escaped (not visible in this diff part) rather than taking the comment's word for it.


Review Part 3 of 3

I don't have write permission for that scratch file, so here's the review directly.

Code Review — PR Diff (Part 3 of 3)

Scope: This fragment is entirely tooling/scripts/tests/test_format_java.py — a large test-class removal followed by ~750 lines of new test classes for the 0.7.0 nested-call / javadoc-reflow / line-length-exemption work. I cross-checked the specific claims against the current working tree (tooling/scripts/format_java.py and the test file itself) rather than relying on the diff text alone.

Code Quality

  • Style/idioms — Consistent with the rest of the file: type hints, parametrized tests, docstrings on every class explaining why the coverage exists, not just what it asserts.
  • ⚠️ Minor style nittooling/scripts/tests/test_format_java.py:4667-4669: three blank lines separate test_wrapped_condition_still_collapses_to_tier_1 from class TestFieldAccessCommitAndWarn: (line 4670), where the rest of the file uses two. PEP8 E303-territory, but the repo runs no flake8/pylint/ruff/black step in .github/workflows/, so it's cosmetic only — won't fail CI.
  • No commented-out code — The large deletion (the old test class covering _arg_list_single_line_estimate) is a genuine removal. I confirmed _arg_list_single_line_estimate no longer exists anywhere in format_java.py, so the removed tests correspond to a function that was actually deleted, not orphaned coverage.
  • Meaningful names_nodes_of_type, _arg_lists_of, TestIsNestedOrChainedCall, etc. The _arg_lists_of docstring even documents a prior naming mistake ("an earlier name promised the FIRST one while the body returned the whole list") rather than silently renaming without a trace.
  • DRY_nodes_of_type and _arg_lists_of are shared helpers factored out once and reused across TestIsNestedOrChainedCall and TestIsAnonymousClass, instead of each test class re-declaring its own visit() closure.
  • Defects — I traced the referenced production functions (_is_nested_or_chained_call at format_java.py:9072, _is_anonymous_class:9020, _line_length_exempt:3466, _javadoc_balanced_reflow:4002, _javadoc_reflow_is_boundary:3966, _min_ragged_lines:3863, _splits_inline_tag:3825, _group_inline_tags:3780) and confirmed they all exist at the lines the tests imply. Hand-traced the trickiest parametrized table (test_format_java.py:4077-4125) — e.g. outer(inner(a, b)); correctly resolves to True (outer's own arg list isn't nested, inner's is) and run(() -> { inner(a, b); }); correctly resolves to False (block-lambda shields both). No logic errors found.
  • CLAUDE.md — Not touched in this fragment.

Testing

  • Unit tests for new functions — Every helper referenced above has a dedicated test class.
  • Edge cases coveredTestMinRaggedLines covers empty input, infeasibility (None return), the hard cap, and oversize-token overflow; TestGroupInlineTags/TestSplitsInlineTag cover nested-brace depth, unterminated tags, and prose braces that aren't tags (a case the docstring says previously false-positived); TestDeclarationSemicolonReserve and TestReceiverReserveIgnoresArgumentLayout each lock in a specific two-pass-oscillation bug with a fixed-point assertion, not just a single-shot check.
  • Regression-test disciplineTestJavadocBalancedReflow.test_unstable_candidate_is_rejected + its companion test_stability_check_sees_the_oscillation independently verifies the mechanism (replaying one pass over the rejected candidate reproduces the instability), guarding against the fix being right for the wrong reason.
  • N/A Integration tests for new endpoints — not applicable.
  • ⚠️ Coverage % — not independently measurable from this fragment alone.

Documentation

  • N/A — pure test code in this fragment; no README/CHANGELOG/markdown changes here.

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings.
  • ✅ No sensitive data — all fixtures are synthetic Java snippets.

Summary

No blocking issues. The only nit is the triple blank line at test_format_java.py:4667-4669 (trivial, no CI impact). The removed test class matches a genuinely removed function, and the new nested-call/javadoc-reflow/second-pass-convergence tests all check out against the current implementation.



Automated code review analyzing defects and coding standards

CI review round 11 found no blocking issues. Its only actionable
item was three blank lines before `TestFieldAccessCommitAndWarn`
where PEP 8 wants two.

Its one flagged defect is the third raise of the same
`_anchor_escaped` claim — that the backtrack path re-emits the value
without resetting the flag, leaking a stale True. It does not: the
snapshot is taken BEFORE the reset and `_anchor_escaped` is part of
the snapshot tuple, so `restore(saved)` on the backtrack path puts
the incoming value back; the explicit `prev_escaped` restore exists
only for the commit path, which never calls `restore`. That is
already stated in a comment directly above the code, which even
notes it has been misread before. Verified independently in an
earlier round. No change.

Also declined: extracting the shared `*_invalid_wrap` tracking from
`emit_p1` / `emit_p2_greedy` / `emit_p3_paren_one_per_line`. Same
reasoning as the member-iteration loops — a refactor of the
most-exercised cascade in a release that is otherwise converged,
where the value is future-proofing rather than any present defect.

803/803, output unchanged.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Combined Code Review

Review Part 1 of 3

I'll give my assessment of Part 1 now, but flag upfront: this review will be incomplete until Parts 2 and 3 arrive — the diff cuts off mid-function in format_java.py (inside _emit_formal_parameters, priority 3 logic), and I haven't yet seen any test files, which the Testing section of the checklist depends on entirely. I'll finalize once the remaining parts are provided.

Part 1 Review

Code Quality

  • Style/naming/idioms — Consistent with existing conventions in format_java.py (docstrings, Final typing, snapshot/restore pattern reused correctly for new state fields _anchor_escaped/_raw_rows_emitted).
  • No commented-out code — None found.
  • ⚠️ Minor DRY nitformat_java.py:~3440 (_LINE_LENGTH_EXEMPT_MARKERS): both "a href" and "href" are listed as substring markers. Since "a href" always contains "href" as a substring, the in text check on "href" alone already matches every case "a href" would; the "a href" entry is dead weight. Not a bug, just redundant.
  • Defect fixes look sound — The four "pre-existing defects" (inline argument comments producing invalid Java, dropped receiver parameters, field-access dot-break, semicolon-reserve) are well-reasoned and each is paired with a fixture reference. The _fire_wrap_overflow_advisory de-dup width-carry-forward logic (format_java.py:~875-905) correctly widens rather than narrows reported width, and uses existing.width (always populated at construction) rather than parsing the message string — reasonable.
  • CLAUDE.md/project config — No .claude/CLAUDE.md was touched in this diff (the new .claude/070_REMAINING_SCOPE.md is a design-history doc, not the memory file), so nothing to flag there.
  • 🔲 Full correctness of format_java.py — Cannot complete; the diff is truncated mid-function (_emit_formal_parameters priority-3 logic breaks off at if p3_col >= paren_col:). Need part 2 to review the rest of that function and everything after it (record declarations already shown look fine, e.g. the params_wrapped guard against a wrapped component list trailing an implements clause is a sensible defensive check).

Testing

  • 🔲 Not assessable from Part 1 — No test files appear in this chunk. format_java.py's docstring says fixtures grew from 83 to 235 pairs, but I can't verify the actual test additions/coverage until they appear in Part 2/3.

Documentation

  • README updated — Doc tree diagram correctly reflects the two new FAQ files (formatter-python-environment.md, source-preservation-history.md).
  • CHANGELOG.md updated — Extensive 0.7.0 entry. Unusually long by typical changelog conventions, but consistent with this project's established style for prior releases (0.6.0 etc. per file context), so not flagging as a defect.
  • Inline comments — The new comments in format_java.py explaining non-obvious history (e.g. why the for-header multi-row check moved from body-brace row to header-span row, why tail-reserve is applied only around value emission) are exactly the kind of "why, not what" comments that earn their keep.
  • ⚠️ Markdown formatting — New files (070_REMAINING_SCOPE.md, formatter-python-environment.md, source-preservation-history.md) look CommonMark-clean structurally (no unclosed fences, consistent heading levels, proper list syntax) from visual inspection, but I have not run prettier/a linter against them — can't fully confirm "no extra whitespace" per the checklist item without tooling access.
  • No obviously broken cross-references between the new FAQs (they link to each other and to consumer-trial-checklist.md correctly).

Security

  • No hardcoded credentials anywhere in this chunk.
  • No .lic files added, and no strings matching AQAAAD appear in this diff.
  • CI supply-chain hygiene.github/workflows/pytest.yaml's new corpus-gate job pins the external corpus checkout to a release tag (4.0.1) rather than main, with an explicit rationale comment about not letting an unrelated consumer commit turn this repo's CI red — good practice. Both checkout steps set persist-credentials: false.
  • Input validation / error handling — N/A for this chunk (documentation + a formatter's internal wrap logic, not user-facing input handling); the NotImplementedError refusals for unsupported grammar shapes (e.g. _refuse_catch_parameter_modifiers) are a reasonable "fail loud" pattern already established in the codebase.
  • No sensitive data in logs — the new FormatterWarning.width field and advisory messages only ever contain line/column numbers and static remedy text.

I'll continue with Parts 2 and 3 once they arrive to complete the Testing section and validate the remainder of the format_java.py changes.


Review Part 2 of 3

Review — Part 2 of 3 (format_java.py argument-list/chain/field-access wrap engine, requirements.txt, test fixtures)

Code Quality: ✅ No defects found after tracing the two areas most likely to hide state-management bugs:

  • emit_p2b_packed (format_java.py:9835) sets emitter._arg_list_p4_fired = True without saving the prior value first. Traced the caller (format_java.py:10149-10161): the snapshot/restore around it is correctly ordered, so a rejected candidate restores the prior flag and an accepted one legitimately leaves it True. Matches the inline comment's claim.
  • _emit_field_access's new break-before-dot path (format_java.py:~8576-8670) omits the .field-width tail-reserve when re-emitting the receiver on the broken path, unlike emit_inline. Confirmed this is correct: the field moves to its own line, so the receiver's own cascade no longer needs that reserve, and the ambient/outer tail_reserve is left untouched throughout.

No commented-out code, no obvious logic errors, naming is meaningful and consistent with the surrounding style (this is an internal formatter tool, not application code, so most "security" checklist items — credentials, input validation, sensitive logs — don't apply).

Testing: ✅ Extensive new/renamed fixture pairs accompany essentially every behavioral change in this chunk (new P2b tier, field-access wrap, receiver-parameter handling, method-chain sole-arg suppression, etc.), each with input.java/expected.java. test_installed_versions_match_pins (test_format_java.py) is a good defensive addition — closes a real gap (a stale local env silently validating against an unpinned tree-sitter version).

Documentation: ✅ requirements.txt and GRAMMAR_VERSION in format_java.py are consistent (tree-sitter==0.26.0 / tree-sitter-java==0.23.5 both places). Inline rationale comments are unusually thorough (arguably more than needed for future maintainers, but not wrong).

Security: ✅ No hardcoded credentials, no .lic files, nothing logging sensitive data in this chunk.

One thing I can't fully verify from this diff alone: several removed helpers (_estimate_normalize, _arg_list_single_line_estimate, _SEMANTIC_WRAP_ARG_TYPES, _chain_receiver_is_factory, the P1F tier) are deleted along with their dedicated test classes — I'd want to confirm in part 1 or 3 that no other call site still references them (a stale reference would be a NameError at import time, easy for CI to catch, so low risk).

No blocking findings to report for this chunk.


Review Part 3 of 3

Now I have concrete line numbers to cite. Here's my review of this chunk (Part 3 of 3 — pure test-file changes in tooling/scripts/tests/test_format_java.py).

Code Quality

  • Style conventions: Consistent with the rest of the file — 79-char-ish wrapping, docstrings on every class/method explaining why a case exists rather than what it does. Matches the surrounding codebase's established idiom (visible in prior commits like d70c488, cf8d8c1).
  • No commented-out code: The large block of removed lines is a clean deletion of TestArgListSingleLineEstimate, not commented-out code. Verified _arg_list_single_line_estimate no longer exists anywhere in format_java.py — the removal correctly tracks a deleted production function, not orphaned dead tests.
  • Meaningful names: _nodes_of_type (test_format_java.py:4028) and _arg_lists_of (4047) are well-named; the latter's docstring even explains a prior name was misleading ("earlier name promised the FIRST one") — good self-documentation of history.
  • DRY: _nodes_of_type is factored out and reused by _arg_lists_of and directly in TestIsAnonymousClass, rather than re-declaring a visit closure per test class.
  • Defects: I cross-checked every function referenced by the new tests (_is_nested_or_chained_call, _is_anonymous_class, _group_inline_tags, _splits_inline_tag, _min_ragged_lines, _javadoc_balanced_reflow, _javadoc_reflow_is_boundary, _line_length_exempt, _emit_field_access, _emit_variable_declarator_with_array_rhs) against format_java.py — all exist with matching names. I also independently verified TestLineLengthExemptMatchesCheckstyle (test_format_java.py:4732)'s CHECKSTYLE_PATTERN and its parents[3] path resolution against the real checkstyle/senzing-checkstyle.xml: both the pattern text and the path arithmetic (tests/ → scripts/ → tooling/ → repo root) check out correctly.
  • N/A .claude/CLAUDE.md: The only CLAUDE.md present (build-resources/.claude/CLAUDE.md) is an untracked scratch artifact from the review tooling itself, not part of this PR's diff — not evaluated as project config.

Testing

  • Unit tests for new functions: Every new/changed helper from the 0.7.0 nested-call work gets a dedicated test class with both positive and negative cases (e.g., TestIsNestedOrChainedCall at 4066 explicitly tests parent shapes that should stay False, which the docstring rightly calls "as important as the True ones").
  • Edge cases covered: Detached/root nodes (test_never_raises_on_detached_node), empty input (test_empty_input in TestMinRaggedLines), infeasible packing (test_infeasible_returns_none), oversize unsplittable tokens, unstable reflow candidates — all thoughtfully covered.
  • ⚠️ Can't verify numeric coverage (>80%) or that the suite actually passes — sandbox blocked me from running pytest in this session, and only part 3 of the diff was provided so I can't see the corresponding production-code changes these tests target. The commit trail (cf8d8c1: "803/803... Output byte-identical") suggests the full suite passes as of this branch, for what that's worth.

Documentation

  • N/A No README/API-doc/CHANGELOG/Markdown changes appear in this chunk.
  • ✅ Inline rationale is excellent throughout — e.g. TestReceiverReserveIgnoresArgumentLayout (4540) and TestSecondPassConvergence (4597) both explain the specific two-cycle/second-pass bug being locked down, not just the mechanics of the test.

Security

  • ✅ No hardcoded credentials, no .lic files, no AQAAAD-prefixed strings anywhere in this diff. (I did find AQAAAD in a few untracked scratch files — build-resources/.claude/CLAUDE.md, .vscode/cspell.json, prompt-header.md, etc. — but on inspection these are just this checklist's own instruction text being echoed back, not real license content. Not a finding.)
  • N/A Input validation / error handling / log hygiene: not applicable to test-only changes.

One thing worth a second look (not blocking)

TestLineLengthExemptMatchesCheckstyle.LINES (test_format_java.py:4752) pins equivalence with checkstyle's ignorePattern, but its fixture only exercises ^package.*/^import.* with a well-formed trailing space ("package com.senzing.example;", "import java.util.Map;"). The implementation (format_java.py:3490, unchanged by this diff) checks text.startswith("package ")/"import " — stricter than the regex, which matches any line merely starting with the literal substring package/import (no space required). In practice this is unreachable for valid Java (the package/import keywords are always followed by whitespace, and nothing else can legally sit unindented at column 0), so it's not a functional bug — just flagging it since the test's stated goal is to "pin" full equivalence and this particular branch pair isn't actually exercised by an adversarial case.

Overall: this chunk is a well-scoped, well-reasoned batch of test additions with a clean deletion of superseded tests. No defects found in what's reviewable from this slice alone. I don't have parts 1–2 (the production-code diff these tests exercise), so I can't fully close out the "Defects"/"Security" items against the actual implementation changes — only against what's referenced from here.



Automated code review analyzing defects and coding standards

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.

2 participants