Skip to content

fix(console): show validated IBKR account ID as account name - #1172

Draft
Pigbibi wants to merge 1 commit into
mainfrom
fix/account-display-name-20261114
Draft

Pigbibi wants to merge 1 commit into
mainfrom
fix/account-display-name-20261114

Conversation

@Pigbibi

@Pigbibi Pigbibi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

AI assistance

Claude Code, model Claude Opus 5.5 (claude-opus-5-5). Implementation of the owner-approved (2026-11-14) presentation fix scoped by the read-only alias audits.

What changed

Display-only change: on authenticated surfaces an IBKR account is named by its validated broker account ID instead of a strategy-derived key, label or target name.

  • Shared helper. ibkrAccountName(option) in lib/accountDirectory.ts returns the matched option's account_selector only when it is a complete IBKR ID (^(?:U|DU)[0-9]+$, the same rule account facts use). useDisplayNames().account() gives it precedence for ibkr; when it is missing or invalid it returns the new localized common.account_id_unavailable ("Account ID unavailable" / "账户号不可用"). Label sanitization for other platforms is unchanged.
  • Raw-alias renderers. The overview decision queue (OverviewPage.tsx) and the cockpit decision card (cockpit.tsx) now use the shared resolver. The queue also shows the platform as its own field.
  • ControlsTab. For IBKR, the stop confirmation's visible target and typed challenge use the resolved broker ID (new en/zh-CN copy). When no ID can be resolved, the stop action is disabled with an explanation and nothing is sent. Requests, loadRunControls and the response identity check still use the internal key. The non-IBKR challenge is unchanged.
  • Desk. accounts.js gains accountRef(), shared with switch_targets.js (its duplicate helper and label map are removed): IBKR targets with a valid selector return the full ID as account_ref; other cases keep the masked form. readback.js shows valid raw IBKR selector values in full, keeps the mask otherwise, and keeps full-value comparison and unknown-state rules. Target IDs, account_suffix and schema types are unchanged. Desk overview rows (overview.js, outside the binding) still show the masked form.
  • Display mappings. Removed only account_aliases.soxl and account_aliases.tqqq, then regenerated display_names_data.js and python/scripts/display_names.json with build_display_names.py.
  • Examples/docs. account-options.example.json uses synthetic U00000001/U00000002 in account_selector and label. The READMEs replace DEMO_IBKR_PRIMARY and explain key vs account_selector vs strategy_profile. Compatibility notes were added to docs/display-names.md, docs/console-api.md, docs/console-information-design.zh-CN.md, and the unfrozen header of docs/desk-api-contract-v1-frozen.md (frozen bytes unchanged; the SHA check passes).
  • Tests. account_config_label_validation.mjs was rewritten: valid U, DU paper, 17 missing/invalid selector forms, platform isolation, unchanged selection/lookup/grouping identity, plus a ControlsTab harness asserting the challenge, the blocked state and unchanged request bodies. Desk switch-targets, N1 and readback validations were extended. Display-name tests (Node and Python) assert that the SOXL/TQQQ removal leaves other aliases intact.

How verified

  • npm run build + npm run sync:v2-assets (frontend) + node tests/console_v2_assets_validation.mjs: pass
  • npm run test:operations (console; includes the i18n, tokens/theme, desk mock and cockpit validations): pass
  • Bound tests individually: account_config_label, display_names, desk_switch_targets, desk_n1_projection, desk_readback, reconciliation_material_presentation: pass
  • python3 python/scripts/build_display_names.py --check, build_config.py --check, build_platform_config.py --check, runtime_settings.py validate: pass
  • python3 -m unittest discover -s python/tests: 1205 tests OK (10 skipped), including the digest roster and Cloud Run digest regressions
  • python scripts/check_style.py: 0 blocking. check:style-guards: 1 warn-only finding, the same one as on main
  • All 133 node commands from validate.yml, compared against a clean main worktree: two new failures (below). The other failures also fail on main locally (git-history/release-pin checks absent from this clone), or are Node 24 rejecting --experimental-default-type; those pass with the flag removed.
  • Mutation check: the new ControlsTab assertions fail against the original ControlsTab.tsx.

Known failing (binding exceptions, not edited):

  1. tests/console_grouping_runs_validation.mjs:85 (run by console_design_api_validation.mjs) expects IBKR accounts without a selector to display their labels ("Alpha"/"Zulu"). They now show "Account ID unavailable".
  2. tests/website_mobile_gaps_validation.mjs:108 expects an unconfigured IBKR account to display its raw alias. It now shows "Account ID unavailable".

Both pin the old behavior that this change removes. They need synthetic valid selectors or updated expectations; that is outside this PR's file binding and needs owner approval.

Why

soxl/tqqq and similar strategy-family names were shown as IBKR account names through option labels/keys, generated account aliases, raw account_alias renderers and masked Desk refs. The owner asked for the native broker ID instead, without renaming the internal key.

Risk

Presentation-only. No changed path is CODEOWNERS-protected. account_alias/option.key, selection values, platform/key lookups, settings identity, target IDs, source IDs, request shapes, history grouping, facts joins, runtime-daily fingerprints and amounts are unchanged; the tests above assert this where it is observable. Behavior changes to note:

  • Authenticated responses and UI now carry the full IBKR ID where a valid selector is configured.
  • IBKR stop is blocked in the console when the selector is missing or invalid.
  • Several IBKR accounts without valid selectors all display the same "Account ID unavailable" text (platform and strategy remain visible).
  • Desk static mock fixtures still use masked refs, because console_desk_mock_validation.mjs (unbound) pins that format.

No labels, merge or deploy. Draft until the binding exceptions are resolved.

🤖 Generated with Claude Code

Authenticated surfaces now name an IBKR account by the validated
account_selector (U or DU followed by digits) of the matched account
option instead of a strategy-derived key, label or target name. A
missing or invalid selector shows a localized "Account ID unavailable".

- One shared helper (ibkrAccountName) feeds useDisplayNames; the
  overview queue and cockpit decision card use the shared resolver.
- ControlsTab confirms IBKR stops against the broker ID and blocks the
  stop when it cannot be resolved; requests keep the internal key.
- Desk accounts and switch-targets return the validated IBKR ID as
  account_ref; readback shows valid IBKR selectors in full. Target IDs,
  suffix fields, comparison and unknown-state rules are unchanged.
- Remove the SOXL/TQQQ account_aliases mappings and regenerate.
- Synthetic example IDs, README guidance and compatibility notes.

Display only: account_alias/option.key, selection, requests, grouping,
bindings and amounts are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Pigbibi

Pigbibi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Posted by the coordinator on behalf of the independent Astra review session (session-review receipt, not an official Codex GitHub App attestation):

Independent end-of-round verdict: FAIL

PR #1172 — QuantStrategyLab/QuantRuntimeSettings
Head: b7ccc9102299c663311eae203c31f929c3388994
Base: d5bb6df4863efd7046b59aad03604efa09f6373b
Reviewer: Codex Astra, independent read-only session requested by the owner.

This is a prepared session-review receipt, not an official Codex GitHub App attestation or owner approval. Reviewer authentication and native gates remain separate. This session has not posted it.

Four unresolved findings (P2):

  1. ReconciliationMaterials bypasses the validated resolver (E1). ReconciliationMaterials.tsx:36 still uses the display map. Actual component probes with a protected synthetic selector U00000001 render generic Account by default, or TQQQ when the supported build override maps that account key to TQQQ. The shared resolver in the same provider renders U00000001. The data-URL test harness constraint is real, but leaving this required renderer unchanged is not acceptable. Bundle it with real providers in tests/reconciliation_material_presentation_validation.mjs, then use the shared resolver and test valid/missing/invalid/override-resistant IBKR names.

  2. Grouping test pins obsolete IBKR labels (E2). console_grouping_runs_validation.mjs:85 fails on head and passes on base. This is a fixture/expectation defect, not a demonstrated product grouping regression. Minimal fix: give synthetic-z selector U00000001 and synthetic-a selector U00000002; assert both IDs exist and order by those displayed IDs, retain raw-alias absence, and update the holdings Alpha expectation at line 133. Keep other amount/status/coverage assertions. Scratch candidate passes zh/en.

  3. Mobile returns test requires raw IBKR alias (E3). website_mobile_gaps_validation.mjs:108 fails on head and passes on base. Keep the no-config fixture; require localized Account ID unavailable and absence of fictional-native, preserving the partial-portfolio disclaimer. This is an expectation defect. Scratch candidate passes zh/en.

  4. Invalid Desk selectors become apparent account names. accounts.js:35 falls back to a mask when validation fails. Accounts/switch targets return IBKR •••• for a missing selector and IBKR ••••tqqq / IBKR ••••soxl for those invalid selectors; Desk renderers print them directly. The brief requires unavailable presentation. Return explicit unavailable text for invalid IBKR account names, preserve suffix/target/identity fields and other-platform behavior, and update the bound tests/docs. An explicit bilingual unavailable account_ref can fit the existing string contract and binding.

Positive evidence: all 27 changed files are bound. The validated frontend helper, valid U/DU precedence, ControlsTab visible/typed ID challenge, original request keys and response-identity checks, valid Desk refs, full-value readback comparisons and mapping removals work in the exercised cases. A base-ControlsTab mutation makes the new regression fail as expected. Independent base/head JSON-byte comparisons preserve selection/grouping, settings identity, Desk target IDs, facts joins and amount strings, runtime-daily attribution/source IDs/fingerprints, and readback fields outside intended presentation changes. Identity-producing source trees and Desk golden fixtures are unchanged. The rebuilt asset map is byte-identical, including all 45 assets.

Exceptions/residuals: E1's constraint is verified but its “never strategy name” claim is refuted. E2/E3 require test corrections. E4's masked mock constraint is verified and accepted as a bounded residual. Residuals 1–7 are accepted as source-scope/evidence limitations, with residual 3 retained as a release-readiness condition: multi-selector targets are supported by backend identity/request construction yet blocked by the new UI. This follows the explicit brief. No production inventory proves whether deployed targets are affected; an authorized release operator must establish usable stop coverage before deployment. Overview masking remains an explicit unbound omission, not full-surface completion.

Checks independently run: PASS account_config_label, display_names, desk_switch_targets, desk_n1_projection, console_account_state, reconciliation_material_presentation, account_settings_state, facts totals/history currencies/source lineage, runtime-daily multi-target/worker/health, frontend i18n, light/dark tokens, Desk mocks, Python display names (4), digest roster (6), unified digest presentation (27), generator check, TypeScript/Vite/budget, asset validation and documentation style (one advisory). E2/E3 reproduce red on head and green on base. Full desk_readback, account_facts, broker_executions and the console operations aggregate encounter sandbox localhost EPERM; the readback pure assertion prefix passes and base has the same socket restriction. Frontend operations reaches a style-guard subprocess assertion that fails identically on base/head; direct guard execution passes with one advisory. These incomplete aggregates are not represented as passing. Hosted CI and browser/production verification were not performed.

Required owner decision: widen the binding to the three test files named above, fix B1–B3, and require the already-bound Desk fallback correction for B4. Two test fixes alone do not clear this review. Review the resulting new head and obtain green required checks in a suitable environment. This receipt grants no merge or deployment authority.

The supplied checkout remains clean at the exact reviewed head. No tracked edits or external mutations were made.

@Pigbibi

Pigbibi commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Owner decision recorded by the coordinator (2026-11-14, in the main conversation): the owner approved the Astra review's required decision verbatim — widen the binding to the three test files named in the review (tests/reconciliation_material_presentation_validation.mjs, tests/console_grouping_runs_validation.mjs, tests/website_mobile_gaps_validation.mjs), fix findings B1–B3, and make the already-bound Desk fallback correction for B4.

This approval covers the fix implementation on this branch only. It is not a merge approval; merge will be requested separately on the resulting head after a fresh review.

This branch has not been deployed

No deployments
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.

1 participant