ID

R334

Status

Backlog

Bucket

Backlog

Priority

3

Theme

model-cleanup

Created

2026-06-18

Updated

2026-09-08

Generated argument extraction is unreadable nested-ternary one-liners

Every generated @condition argument is inlined as a nested ternary directly in the WHERE chain, e.g. env.getArgument("filter") instanceof Map<?, ?> map1 ? (String) map1.get("brukerId") : null, repeated once per argument across a single .and(...) term. A method with several condition args renders as one dense, hard-to-read, hard-to-breakpoint expression (flagged by a consumer as "an eyesore" that "violates our principle of readable and debuggable code"). R330 made the surrounding .and(...) chains and the FK-target EXISTS multi-line, but did not touch the per-argument extraction, which is cross-cutting: it lives in ArgCallEmitter.buildArgExtraction and feeds every WHERE-emitting site (the QueryConditions shim plus the inline / lookup / split fetcher emitters). The fix likely extracts each argument into an explicitly-typed named local (never var; GeneratedSourcesLintTest.emittedSourcesDoNotUseVar bans it in emitted code) before the call, or routes through a small generated helper, so the call site reads as Conditions.method(table, brukerId) and each extraction is independently debuggable. Scope: emitter-only, generated output changes shape but not behaviour; pipeline tests must not assert on generated method bodies, so coverage stays at the compile/execution tier.

Narrowed for the condition family (2026-07-28, R552 pickup)

R552 (condition-command) absorbs this item’s fix for @condition argument extraction: the glue body’s one-local-per-argument convention delivers the named-locals shape by construction (R552 slice 1 for the root family, slice 2 for every inline call site), so the ternary chains stop appearing at condition call sites entirely. Everything below in the expanded scope stays here: the mutation insert-value ternaries, the polymorphic discriminator expression, and the $fields-mapper deep-path extraction with its inputs/*.fromMap reuse finding are not condition content and remain this item’s. Do not delete this item when R552 closes.

Expanded scope (2026-07-24 audit)

A full audit of the graphitron-sakila-example generated tree found the same expression-over-statement pattern beyond @condition extraction; this item now covers all of it (same emitter family, same fix shape: hoist to explicitly-typed named locals per path segment, named after the GraphQL argument or column):

  • Deep input-path extraction in fetchers and $fields mappers: nested instanceof Map/List ternary chains up to ~600 characters on one line (QueryFetchers.filmsByNestedListPath, the two ~330-char .where(...) arguments in occupantsByFilter, types/Store.$fieldsGrouped). Some sites re-check the same instanceof twice or test in != null after in.get(...) was already dereferenced; the locals-first rewrite removes the redundancy. Note the emitter already generates typed inputs/*.fromMap classes for many of these shapes and then ignores them, re-digging raw maps at the use site; reusing the input classes where one exists deletes the pattern outright.

  • Insert-value ternaries: in.containsKey("x") ? DSL.val(...) : DSL.defaultValue(...) emitted once per column per mutation (39 occurrences in MutationFetchers). A named local per column, or a tiny emitted valOrDefault(in, "x", FIELD) helper, restores the column list’s readability.

  • The polymorphic discriminator expression DSL.field(table.getQualifiedName().append(DSL.name("content_type")), Object.class) is rebuilt inline up to four times within a single method (29 occurrences across the root fetcher classes); bind it once to a named local per method.

R521 (generated-output-hygiene-sweep) tracks the complementary naming/dedup/hygiene findings from the same audit and explicitly excludes statement-form defects in favour of this item.

The descent is now safe to splice, and that does not discharge this item (2026-09-08)

The nested-map descent that reads a wire value (WireMapChain.of, and the presence-test sibling TypeFetcherGenerator.nestedContainsKeyExpr) now returns a primary expression: parenthesised at the producer, so a caller may splice it into any operand slot. That closed a correctness defect, not a readability one: before it, splicing the descent into an instanceof pattern test or a == null comparison emitted Java javac rejects, and the mutation DML decode-locals walks did exactly that at six emit statements. The fix is one paren pair, deliberately chosen over the statement-form migration so a shipped consumer blocker did not ride on a rewrite of those methods.

So the ternaries this item is about are unchanged in shape, and the expanded scope above still names them. What changed is that they are no longer a trap for the next caller. The statement-form end state remains this item’s: routing the mutation DML sites through ArgPathHelperRegistry collapses each call site to a method invocation (a primary expression by construction) and turns the descent into readable statements in a private static helper, which also discharges the "statement form over expression tricks" and the throwaway-pattern-variable naming rule that the _s-prefixed decode locals violate today. It was deferred there because TypeFetcherGenerator’s mutation walk is a chain of `private static methods that would each grow a registry parameter, which is a signature sweep rather than a fix. When that lands, the paren pair becomes redundant at those sites and can go with them; it stays load-bearing for every other consumer of the descent until then.

R85 (helper-emission-non-fetcher-hosts) reshapes the same method: it fixes the ContextArg arm of ArgCallEmitter.buildArgExtraction that fails to emit the graphitronContext helper on non-fetcher hosts, while this item extracts the per-argument extraction into named locals or a helper for readability. Whichever lands first changes the other’s diff; sequence knowingly (distinct deliverables, not a merge).