Skip to content

feat(web): Choy UI kit rewrite and Element Plus retirement - #514

Merged
buke merged 81 commits into
mainfrom
feat/choy-ui-kit
Oct 26, 2026
Merged

buke merged 81 commits into
mainfrom
feat/choy-ui-kit

Conversation

@buke

@buke buke commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

User description

Summary

  • Replace Element Plus–era chrome with the Choy / shadcn-vue kit: dual Guest/App shells, sidebar/header density, Tailwind FullCSS + design tokens, and CI gates that forbid legacy UI imports.
  • Rewrite the field family onto ChoyFieldBase / Choy* widgets, unify the open viewKind registry, and polish list / kanban / chart / form surfaces (virtual lanes, quieter cards, chart hover, form density).
  • Land auth surfaces on ChoyFormView (login/register/preferences), drop Element Plus adaptive-height dialog stubs, honor password input types, and type O2M kanban #card slots from relation rows (includes feat(web): align auth and shell chrome with shadcn blocks #470–fix(web): type O2M kanban #card slot so item is not {} #513 close-out).

Test plan

  • CI green on this PR against main
  • Smoke: login / register / guest home vs app shell routing
  • Smoke: sandbox record form sections (divided titles), O2M table + kanban card slot fields
  • Smoke: list / kanban / chart views and company logo where present
  • Confirm no remaining Element Plus / el-* product imports outside allowed vendor paths

PR Type

Enhancement, Bug fix, Tests


Description

  • Retire Element Plus: rewrite web UI onto Choy/shadcn-vue kit with new shells, fields, views, and Tailwind FullCSS generation.

  • Go core: add Tailwind generate/digest pipeline, exact package pins, forbidden UI import policy, and typecheck gate.

  • Go core fixes: hotreload lease retry, seed cascade uninstall, gzip/document MIME, parser and loader corrections.

  • Tests: broad Go and TypeScript/unit coverage for new kit, Tailwind, policy, and lifecycle paths.


File Walkthrough

Relevant files
Tests
51 files
webBuilder_test.go
Rewrite web builder tests for Choy shells, Tailwind hook, exact pins
+951/-91
tailwind_generate_test.go
Add Tailwind generate, scan, digest and scoping tests       
+1390/-0
tailwind_generate_coverage_test.go
Add coverage tests for Tailwind generate edge paths           
+920/-0 
forbidden_ui_import_coverage_test.go
Add coverage tests for forbidden UI import policy               
+875/-0 
uninstaller_seed_cascade_test.go
Add uninstaller seed-row cascade and savepoint tests         
+766/-0 
parser_test.go
Add SFC parser masking and self-closing tag tests               
+926/-0 
server_hotreload_test.go
Add hotreload ignore, lease-conflict retry tests                 
+376/-0 
forbidden_ui_import_test.go
Adds tests for forbidden UI import classification and disk scan
+536/-0 
host_bundle_coverage_test.go
Updates FE stub coverage for Choy kit packages and Page shell
+94/-49 
network_test.go
Tests held-response replay and unknown-method handling     
+165/-20
input_digest_test.go
Tests Tailwind digest inputs, engine version, and schema salt
+189/-0 
p1_coverage_test.go
Adds Postgres index and sqlite origin-unique coverage       
+192/-2 
vue_pin_test.go
Tests Vue host bare import pin resolution                               
+170/-0 
lease_test.go
Tests lease acquire error branches and unique detection   
+156/-0 
script_test.go
Tests Vue pin helpers and package version sync                     
+146/-0 
server_serve_test.go
Tests serve resilience to watch handler and lease failures
+170/-0 
uninstaller_model_data_test.go
Updates uninstaller test to seed effective model                 
+2/-94   
ast_test.go
Tests destructured export name extraction                               
+102/-0 
parse_ctx_test.go
Tests destructured binding export names in parse ctx         
+109/-0 
handler_test.go
Tests gzip bypass for document paths and header handling 
+95/-0   
package_pins_test.go
Tests exact pin extraction from package.json                         
+131/-0 
sandbox_amount_currency_field_test.go
Tests sandbox amount currency and scale field parsing       
+130/-0 
gateway_test.go
Updates gateway tests to assert no wire principal               
+6/-29   
loader_test.go
Tests numeric record index tie-break in topo order             
+59/-0   
bundle_test.go
Tests e2e bundle exact pin error paths                                     
+63/-0   
typecheck_app_test.go
Tests typecheck forbidden UI import gate                                 
+64/-0   
bundles_translation_term_test.go
Tests bundles entry dir creation error                                     
+38/-0   
resolver_test.go
Tests BareImportPin accessor and prerelease pins                 
+26/-0   
parse_choy_scan_test.go
Adds repo-wide Choy Vue SFC parse smoke test                         
+62/-0   
control_height_theme_test.go
Locks control height theme utility emission                           
+39/-0   
auth_test.go
Asserts base.Language read allowlist on auth entries         
+20/-0   
mime_policy_test.go
Tests ASCII filename in content disposition                           
+38/-0   
builder_test.go
Tests backend exact pin option application                             
+24/-0   
module_index_sync_test.go
Updates bundles entry path expectation                                     
+1/-1     
bundles_app_setting_test.go
Updates bundles entry path expectation                                     
+1/-1     
bundles_field_default_test.go
Updates bundles entry path expectation                                     
+1/-1     
bundles_property_definition_test.go
Updates bundles entry path expectation                                     
+1/-1     
vue_component_tsgo_parser_test.go
Tests property name empty-text fallback                                   
+14/-0   
uninstaller_property_definition_test.go
Tests narrowed missing-table error detection                         
+7/-0     
Search.behavior.test.ts
Adds Search behavior unit tests for Choy kit                         
+1379/-0
ChoyManyToManyRefField.mount.test.ts
Adds M2M ref field mount tests                                                     
+543/-0 
chartViewHelpers.test.ts
Adds unit tests for chart view helper utilities                   
+1096/-0
ChoyManyToOneField.mount.test.ts
Adds mount tests for ManyToOne field behavior                       
+861/-0 
ChoyManyToManyRefTagsField.mount.test.ts
Adds mount tests for ManyToMany ref tags field                     
+830/-0 
ChoyManyToManyTagsField.mount.test.ts
Adds mount tests for ManyToMany tags field                             
+831/-0 
useFormViewLifecycle.test.ts
Adds lifecycle tests for form view composable                       
+787/-0 
ChoyFieldBase.test.ts
Adds mount tests for field base chrome                                     
+739/-0 
ChoyNativeScalarFields.mount.test.ts
Adds mount tests for native scalar field widgets                 
+639/-0 
ChoyManyToManyField.mount.test.ts
Adds mount tests for ManyToMany table field                           
+609/-0 
breadcrumbStore.test.ts
Adds breadcrumb store and shell trail tests                           
+659/-0 
ChoyKanbanView.chrome.test.ts
Adds chrome and drag tests for kanban view                             
+608/-0 
Enhancement
25 files
tailwind_generate.go
Add Go-native Tailwind FullCSS generate and digest pipeline
+1004/-0
input_digest.go
Fold Tailwind dialect, candidates and engine into web digest
+80/-2   
forbidden_ui_import.go
Adds forbidden kit import policy with cutover bans             
+395/-0 
webBuilder.go
Wires Tailwind generation, exact pins, and type-only import filtering
+220/-1 
forbidden_ui_import_scan.go
Adds on-disk web tree scan for forbidden imports                 
+323/-0 
host_bundle.go
Swaps FE stubs to Choy kit packages and adds exact pins   
+93/-26 
uninstaller_seed_cascade.go
Adds seed row cascade delete with savepoints                         
+255/-0 
gen_vue_version.go
Adds generator for Vue version from web package.json         
+184/-0 
fe_unit_stubs.go
Replaces Element Plus stubs with Choy kit stubs                   
+56/-23 
server_hotreload_dispatch.go
Defers watch reloads on lease conflict with retries           
+110/-0 
script.go
Adds Vue pin helpers and exact SemVer validation                 
+123/-2 
ast.go
Extracts destructured export names and prefers Text()       
+53/-14 
server_hotreload.go
Adds watch path ignore rules and lease conflict detection
+58/-0   
package_pins.go
Adds exact pin extraction and merge from package.json       
+80/-0   
parse_ctx.go
Extracts destructured binding names for exports                   
+51/-12 
builder.go
Applies exact package pins to backend resolver                     
+19/-0   
bundle.go
Applies exact pins without vue to e2e bundle                         
+33/-2   
typecheck.go
Adds forbidden UI import gate to typecheck                             
+14/-1   
policy.go
Allows base.Language read for login and refresh                   
+4/-0     
server_hotreload_state.go
Tracks per-file lease retry counters                                         
+24/-0   
resolver.go
Adds BareImportPin accessor to resolver                                   
+8/-0     
vue_version.go
Adds generated Vue package version constant                           
+12/-0   
server_hotreload_pipeline.go
Ignores generated artifacts when enqueuing watch events   
+6/-0     
chartViewHelpers.ts
Adds chart view helper functions and tooltip formatting   
+874/-0 
useMessageScroller.ts
Adds vendored message scroller composable engine                 
+1043/-0
Bug fix
12 files
parser.go
Mask PascalCase raw-text tags and expand self-closing HTML tags
+639/-1 
mime_policy.go
Strip non-ASCII from filename Content-Disposition parameter
+10/-1   
network.go
Holds responses until request method is known                       
+64/-13 
inspect.go
Adds Postgres index inspection and sqlite origin uniqueness
+116/-6 
lease.go
Uses ON CONFLICT DO NOTHING for lease insert                         
+37/-32 
uninstaller.go
Cascades seed deletes and narrows missing-table detection
+28/-8   
loader.go
Fixes numeric record index tie-break in topo order             
+24/-6   
handler.go
Skips gzip for document paths and fixes header handling   
+23/-1   
bundles.go
Moves bundles entry into dedicated subdirectory                   
+8/-1     
server_lifecycle.go
Keeps server running on watch reload failures                       
+22/-1   
field_resolved.go
Propagates scaleField and currencyField to meta field       
+13/-0   
vue_component_tsgo_parser.go
Prefers Text() for property names to avoid comments           
+4/-2     
Refactor
6 files
upload_handler.go
Drops wire principal from upload authorize and commit       
+6/-27   
download_handler.go
Drops wire principal from download resolve                             
+3/-4     
helpers.go
Removes unused enabled company ID helper                                 
+0/-40   
ambient_subpath.go
Removes Element Plus and echarts ambient stubs                     
+0/-32   
source_reader.go
Drops wire principal from binding source read                       
+1/-2     
index.ts
Renames i18n modules and drops Element Plus locale loading
+14/-19 
Additional files
101 files
.coderabbit.yaml +23/-0   
action.yml +4/-2     
codeql-config.yml +3/-0     
mainline-verify.yml +5/-0     
pr-gate.yml +30/-0   
.pr_agent.toml +17/-0   
.sourcery.yaml +12/-0   
codecov.yml +9/-0     
go.mod +1/-0     
auth_store.js +17/-1   
codemirror.js +8/-0     
codemirror_lang_json.js +14/-0   
codemirror_lint.js +12/-0   
codemirror_state.js +23/-0   
codemirror_view.js +186/-0 
echarts.js +0/-16   
element_plus.js +0/-215 
element_plus_icons.js +0/-76   
lucide_vue_next.js +105/-0 
reka_ui.js +459/-0 
tiptap_starter_kit.js +7/-2     
tiptap_vue3.js +112/-3 
unovis_ts.js +37/-0   
unovis_vue.js +37/-0   
vicons_material.js +0/-34   
vue_echarts.js +0/-14   
vue_router.js +39/-5   
vue_sonner.js +62/-0   
vuedraggable.js +0/-14   
register.spec.ts +4/-4     
timezone_wallclock.spec.ts +23/-16 
login.ts +10/-7   
switchCompany.ts +96/-55 
auth.pot +479/-387
zh_CN.po +506/-437
package.json +0/-3     
_record_rule_eval.ts +41/-4   
permission_state.test.ts +11/-11 
record_rule_eval_edges.test.ts +33/-0   
AuthPanel.test.ts +25/-0   
AuthPanel.vue +22/-0   
ChoyAppShell.vue +328/-0 
ChoyGuestShell.vue +99/-0   
OHeader.vue +0/-107 
OSwitchCompany.test.ts +0/-26   
OSwitchCompany.vue +0/-417 
SwitchCompany.test.ts +118/-0 
SwitchCompany.vue +534/-0 
auth_header_popup_state.test.ts +11/-0   
auth_header_popup_state.ts +10/-0   
collapsed_identity_tooltip.test.ts +19/-0   
collapsed_identity_tooltip.ts +16/-0   
o_switch_company_draft.ts +39/-0   
popup_escape_focus.test.ts +42/-0   
popup_escape_focus.ts +17/-0   
reuse_parent_setup_state.test.ts +67/-0   
reuse_parent_setup_state.ts +32/-0   
switch_company_option_pick.ts +25/-0   
OPreferencesDialog.vue +0/-267 
PreferenceDraftSeeder.test.ts +142/-0 
PreferenceDraftSeeder.ts +75/-0   
PreferencesDialog.vue +314/-0 
dialog_focus_restore.test.ts +19/-0   
dialog_focus_restore.ts +16/-0   
dialog_focus_trap.test.ts +230/-0 
dialog_focus_trap.ts +94/-0   
preferences_submit.test.ts +162/-0 
preferences_submit.ts +84/-0   
index.ts +4/-2     
applyPermissionToMenus.test.ts +30/-0   
applyPermissionToMenus.ts +11/-0   
menus.ts +3/-3     
AuthPageErrorClear.vue +33/-0   
Login.mount.test.ts +103/-5 
Login.vue +171/-167
Logout.mount.test.ts +121/-0 
Logout.vue +66/-128
Register.mount.test.ts +197/-0 
Register.vue +247/-253
Role.vue +3/-3     
RoleFieldRule.vue +3/-3     
RoleFieldRuleList.vue +5/-6     
RoleList.vue +5/-6     
RoleMethodAccess.vue +3/-3     
RoleMethodAccessList.vue +5/-6     
RoleRecordRule.vue +3/-3     
RoleRecordRuleList.vue +5/-6     
RoleUiResource.vue +3/-3     
RoleUiResourceList.vue +5/-6     
Session.vue +3/-3     
SessionList.vue +5/-6     
Token.vue +3/-3     
TokenChart.vue +28/-0   
TokenKanban.vue +3/-9     
TokenList.vue +3/-12   
User.vue +3/-3     
UserList.vue +5/-6     
login_form.test.ts +344/-0 
login_form.ts +264/-0 
logout_page.test.ts +32/-0   
Additional files not shown

Summary

  • Replace Element Plus–era chrome with the Choy / shadcn-vue kit: dual Guest/App shells, sidebar/header density, Tailwind FullCSS + design tokens, and CI gates that forbid legacy UI imports.
  • Rewrite the field family onto ChoyFieldBase / Choy* widgets, unify the open viewKind registry, and polish list / kanban / chart / form surfaces (virtual lanes, quieter cards, chart hover, form density).
  • Land auth surfaces on ChoyFormView (login/register/preferences), drop Element Plus adaptive-height dialog stubs, honor password input types, and type O2M kanban #card slots from relation rows (includes feat(web): align auth and shell chrome with shadcn blocks #470–fix(web): type O2M kanban #card slot so item is not {} #513 close-out).

Test plan

  • CI green on this PR against main
  • Smoke: login / register / guest home vs app shell routing
  • Smoke: sandbox record form sections (divided titles), O2M table + kanban card slot fields
  • Smoke: list / kanban / chart views and company logo where present
  • Confirm no remaining Element Plus / el-* product imports outside allowed vendor paths

Notes

  • Seed loader tie-break: batch topo order now sorts by file, then numeric RecordIndex, then module.name. This replaces lexicographic string keys where "10" sorted before "2". Intentional correctness fix (see loader.go + loader_test.go); application order for same-file multi-record seeds follows declaration index. FK/ref edges still drive dependency order — only independent-node tie-breaks change.
Open in Web Open in Cursor 

buke added 30 commits September 22, 2026 15:54
* ci(pr-gate): also run on PRs targeting feat/choy-ui-kit

Stacked kit PRs merge into the integration branch first; without this
base in the pull_request filter, PR Gate never starts (only main matched).

* ci(pr-gate): run on every pull request base

Drop the pull_request.branches allowlist so stacked PRs into integration
branches get the same discover-routed gate as PRs into main.
* feat(choy_ui): scaffold isolation kit with Tailwind Go gate

Add installable choy_ui module (tokens, gallery shell, /web/__choy_gallery),
wire tailwind-go generate into web builds, and fold dialect/candidate hashes
into the web input digest so skip stays stable under the 500ms budget.

* chore(choy_ui): stop tracking Tailwind generated CSS

Keep choy-tailwind.generated.css as a web-build artifact only so class
churn does not pollute git; EnsureChoyTailwindCSS still writes it before bundle.

* chore: re-sync PR #430 to trigger PR Gate

* ci(pr-gate): run gate on every pull request (#431)

* ci(pr-gate): also run on PRs targeting feat/choy-ui-kit

Stacked kit PRs merge into the integration branch first; without this
base in the pull_request filter, PR Gate never starts (only main matched).

* ci(pr-gate): run on every pull request base

Drop the pull_request.branches allowlist so stacked PRs into integration
branches get the same discover-routed gate as PRs into main.

* fix(choy_ui): address PR #430 review on isolation and Tailwind scan

Drop multi-line TS/JS scanner leaks from generated CSS, scope gallery
theme/density to .choy-gallery-root, tighten digest/write/stat handling,
and keep route registration idempotent.

* fix(choy_ui): address PR suggestions and raise Tailwind patch coverage

Adopt Sync-before-rename atomic writes, rename the directory-theme test,
and assert theme-mapped utilities. Expand unit coverage so the PR1
Tailwind/digest/webBuilder hook patch lines are fully exercised.

* fix(choy_ui): scope Tailwind theme/utilities and harden generate checks

Propagate LoadCSS errors, emit ThemeCSS aliases with utilities, scope both
under .choy-gallery-root for isolation, and fail tests if preflight reset CSS
appears in the generated output.

* fix(choy_ui): harden CSS scoping and hit 100% Tailwind patch coverage

Scope nested @media utilities, split selectors only at top level, skip
quoted braces/comments, guard AppLayout, fail incomplete kits loudly, and
make chmod permission tests root-tolerant.

* fix(choy_ui): scope past bare at-rules and harden theme/tests

Copy block-less @-rules through before brace scanning, normalize all
:root/:host spellings, reuse the generated CSS name constant, rename the
shadowed setupApp param, and make chmod-based permission tests root-tolerant.

* fix(choy_ui): skip leading CSS comments and harden write-failure tests

Emit leading /* */ before selector scoping, force rename failure when the
generated CSS path is a directory, and warn when AppLayout is missing.

* fix(choy_ui): restore router if-parens and align Tailwind digest guards

Repair a Go-style if that broke typecheck, and mirror EnsureChoyTailwindCSS
path checks in TailwindInputDigest for non-dir web/ and dialect paths.

* test(choy_ui): cover Tailwind digest path guards and no-brace CSS

Exercise missing-dialect and permission-denied digest paths, plus selector
fragments without a block, so patch coverage on tailwind_generate.go is 100%.

* fix(choy_ui): scope unknown at-rules and spare functional :host

Default-scope nested selectors in unrecognized block at-rules while keeping
declaration bodies verbatim, and rebind only standalone :root/:host tokens.

* fix(choy_ui): scope tokens to gallery and tighten Tailwind digests

Move light tokens off :root onto .choy-gallery-root, exclude only the kit's
generated CSS from source walks, and include the tailwind-go module version
in the web input digest schema (v7).

* fix(choy_ui): quote-aware bare at-rules and cover digest version helper

Scan block-less @-rule terminators with quote/paren awareness, raise the
candidate length cap for long arbitrary utilities, and unit-test the
injectable tailwind-go version reader for 100% digest patch coverage.

* fix(choy_ui): match relative kit CSS paths and harden CSS helpers

Recognize root-relative choy_ui/web generated CSS in digest exclusion, close
temp files before cleanup on write failure, and skip commas inside selector
comments when splitting.

* fix(choy_ui): drop empty selectors and honor go.mod replaces

Omit empty slots from scoped selector lists, skip comments when finding
bare at-rule terminators, and prefer replace version/path for the
tailwind-go digest stamp.

* fix(choy_ui): rebind utility :root/:host and tighten dialect checks

Stamp replace as path@version, map bare :root/:host onto the gallery scope
in utility lists, error when theme.css is a directory, and add SPDX to
theme.css.

* fix(choy_ui): tighten :root/:host boundaries and log budget overruns

Reject identifier continuations when rebinding theme selectors, add SPDX
headers to tokens/policy CSS, and warn when Tailwind generation exceeds the
soft wall-clock budget.

* fix(choy_ui): reject empty Tailwind output and stamp engine in header

Refuse to write blank generated CSS, record the tailwind-go version in the
file header, and use t.Skip for permission probes that cannot run as root.

* test(choy_ui): cover Tailwind soft-budget warn path

Inject ensureChoyTailwindCSS so BuildCtx can exercise the over-budget Warn log without waiting on a slow generate.

* fix(choy_ui): align Tailwind scan extensions with digest sources

Scan .mts/.cts/.mjs/.cjs for class candidates, fail digest when theme.css
is a directory (same as Ensure), and assert engine bumps invalidate digests.

* fix(choy_ui): harden at-rule open-brace scan and digest helpers

Skip braces inside prelude comments/quotes when scoping nested utilities,
guard nil BuildInfo, and lock choy_ui_extra out of kit CSS exclusion.

* fix(choy_ui): tolerate vanished files during Tailwind candidate scan

Skip NotExist between WalkDir and ReadFile instead of aborting the build,
matching hashFile's missing-input behavior.
* feat(choy_ui): PR2 L2 primitives, Reka peers, and domain import bans

Vendor first-batch ui/* with cn() and VENDOR.json, pin reka-ui/lucide peers,
expand the gallery control matrix, and fail typecheck on forbidden domain
imports of ui/*, reka-ui, @unovis/*, and kit deep paths.

* refactor(choy_ui): nest L2 primitives under components/vendor/ui

Make the vendored shadcn tree obvious at the path level, update Gallery
imports and cn() relatives, extend forbidden-import matching, and point
PR-Agent/Codecov/CodeQL ignores at the new location.

* chore(choy_ui): keep vendor/ui cutover-ready for web merge

Use module-agnostic ignore globs, document the stable components/vendor/ui
layout, and structure kit-host exemption so only choy_ui is exempt until the
kit moves under modules/web.

* chore: ignore vendor/ui in CodeRabbit and Sourcery reviews

Exclude the module-agnostic L2 vendor tree from bot review noise, matching
PR-Agent/Codecov/CodeQL filters and surviving the choy_ui → web cutover.

* fix(choy_ui): address PR #432 review on policy scan and gallery

Harden forbidden UI import scanning (Vue line offsets, re-exports,
JSX virtual paths, missing roots) and fix gallery portal token scope
plus a few clear L2 bugs called out in review.

* ci: let CodeRabbit auto-review PRs to any base branch

Default auto_review only covers main; stacked bases like feat/choy-ui-kit
were skipped. Match all targets with base_branches: [".*"].

* fix(choy_ui): harden policy scan from review and hit 100% coverage

Accept PR-Agent items on HTML-comment scripts, declaration skips,
virtual-path uniqueness, type-only alignment, gallery host theme
restore, and typecheck web Stat errors; cover forbidden_ui_* and
TypecheckApp web gate at 100%.

* fix(policy): skip empty web sources and harden re-export scan

Empty .ts under web/ no longer fails typecheck; wildcard re-exports
fall back to the parent ModuleSpecPath; add disk coverage and SPDX on
CodeRabbit/Sourcery configs.

* fix(policy): tighten Vue script extraction and gallery color-scheme

Only blank HTML comments that embed <script>, require line-anchored
</script> so string literals cannot hide imports, keep color-scheme on
.choy-gallery-root, and cover type-only re-exports on disk.

* fix(choy_ui): harden web scan skips and portal dark color-scheme

Guard nil parse results, skip web/public and coverage trees, pin reka-ui
to the vendored 2.10.4 snapshot, and apply color-scheme: dark on the
html token scope so Reka portals match gallery dark mode.

* fix(choy_ui): dedupe import violations and harden Vue/script gates

Guard gallery theme restore without capture, dedupe per-line ban noise,
use RE2-safe script close matching for compact SFCs, align Input
v-model to string, pin lucide, and run the web import gate before
type-asset prep.

* test(policy): cover violation sort by rule for dedupe path

* test(policy): cover SpecText sort when columns collide

* fix(choy_ui): CRLF-safe Vue script scan and toast duration ownership

Handle CRLF SFCs and quoted script attrs with '>', trim backtick
specifiers, let Reka ToastRoot own duration, widen gallery checkbox
model typing, and avoid clobbering host theme on unmount.

* test(policy): assert backtick import specs via appendForbiddenUiSpec

* fix(choy_ui): tighten kit bans, SFC close tags, and toast duration zero

Allow </script > whitespace, ban bare choy_ui package imports, map toast
duration 0 to Infinity, fix decorative Separator roles, keep vendor/ui
in CodeQL, and replace hand-rolled itoa / fmt.Errorf with stdlib helpers.

* fix(choy_ui): same-line Vue scripts, toast clear, and small a11y fixes

Rewind after script matches that consume a trailing '<', clear the toast
store on Toaster unmount, gate disabled to real buttons, omit :0 columns
in ban errors, and add SPDX fields on VENDOR.json.

* fix(choy_ui): full scan coverage, indent-safe scripts, toast timer hygiene

- Drop unreachable Vue script-scan stall branches so patch coverage is 100%.
- Preserve first-line script indentation when extracting SFC bodies.
- Track toast removal timers and clear them in clearToasts.
- Narrow review/coverage ignores to vendored *.vue (Sourcery stays dir-wide).

* fix(policy): fail closed when Vue script blocks fail to parse

Refuse to skip .vue files that still contain a real <script> tag after
comment masking but yield no captured blocks, so the kit import ban
cannot fail open on awkward close-tag shapes.
* feat(choy_ui): PR3 L1 shells, FormView skeleton, and DogfoodLogin

Add public ChoyLayout/Page/Card/Grid/Tabs/Button/Message and a chrome-only
ChoyFormView, plus gallery coverage and a dogfood login route for button
and toast smoke.

* fix(choy_ui): harden L1 page IO, tabs selection, and dogfood styles

Forward ChoyPage import/export events, reconcile tab selection when
panels unregister, re-register on value changes, clamp grid cols/span,
fix bell a11y/clicks, and load Choy styles on DogfoodLogin.

* fix(choy_ui): defer default tab pick and tidy L1 edge cases

Wait for a late-registering defaultValue before auto-selecting tabs, skip
disabled-only picks, normalize bell counts, read Card header slots at
render time, announce page loading, and drop redundant dogfood clearToasts.

* fix(choy_ui): refine tab selection edges and form loading a11y

Only defer defaultValue while nothing is selected, rename tabs in place
on value change, watch defaultValue, cover disabled/stale cases, ceil
bell counts, and announce FormView loading with role=status.

* fix(choy_ui): block duplicate tab rename and embed form border

Reject ChoyTab value renames that collide with an existing registration,
apply FormView border classes mutually exclusively for embedded mode,
and cover the empty registration-list selection path.

* fix(choy_ui): own tab registrations and clamp grid spans

Track whether a ChoyTab owns its registration so refused duplicates do
not unregister survivors, bind panes to registeredValue, and extract
resolveChoyColSpan with unit coverage.

* refactor(choy_ui): render-time slots, shared grid cols, testable tabs registry

Read Layout/Page slot visibility at render time, map ChoyMessage levels
via an exhaustive Record, extract createChoyTabsContext and
normalizeChoyGridCols with direct unit coverage.

* fix(choy_ui): harden tab ownership edges and unread a11y

Fail update on unknown values, hide duplicate ChoyTab panes, announce
exact unread counts, treat null span as full width, and cover the
unknown registry path.

* fix(choy_ui): retry refused tabs and tidy page/message helpers

Allow duplicate-refused ChoyTabs to re-register on value change, keep
tab identity when patch.value is undefined, shallow-watch the registry,
omit unset ChoyMessage options, extract ChoyPageTitleActions, and cover
defaults.

* fix(choy_ui): retry free tabs, clamp grid, inert loading body

Watch the shared registry so refused ChoyTabs reclaim a freed value;
merge tab updates field-by-field; reject empty values; cap grid cols;
mark loading page/form bodies inert for a11y.

* fix(choy_ui): inert chrome while loading, cover tab reclaim

Keep FormView header/button-box and Page header/toolbar/footer
unreachable during load; pin duplicate-register recovery after unregister.

* fix(choy_ui): retry collided tab renames; inert messages

Retry pending value renames once the conflict clears; drop redundant
Tabs default-value under v-model; inert FormView messages while loading.

* fix(choy_ui): clamp toast duration; sync label on rename miss

Reject non-finite/negative ChoyMessage durations before the store, and
keep label/disabled patched when a tab value rename collides.

* fix(choy_ui): settle tab default, isolate loading status

Fall back after ChoyTabs mounts when defaultValue never registers; keep
loading status outside aria-busy; fix failed-rename label; drop dead
Dogfood submitting guard; plain pane without ChoyTabs parent.

* fix(choy_ui): announce bell count; drop sticky invalid duration

Add string span/cols coercion coverage; live-status unread count on the
notification bell; leave invalid ChoyMessage durations to the store default.

* fix(choy_ui): breadcrumb with header slot; clarify dogfood toast

Render breadcrumb above a custom header slot; mark dogfood success as
no-auth; cover stale→default tab selection and sub-ms toast duration.

* fix(choy_ui): blank grid attrs, toast bounds, rename partials

Treat blank cols/span as unset; trim toast titles and cap duration at
the 32-bit timer limit; apply label/disabled on colliding tab renames.
* feat(choy_ui): PR4 L3 DataTable, DatePicker, RelationCombobox

Add TanStack table/virtualizer and @internationalized/date peers with
internal engines for Gallery dogfood; keep helpers unit-tested and out
of the public barrel for PR5 List/Date/ManyToOne assembly.

* fix(choy_ui): harden L3 DataTable and RelationCombobox edges

Reject empty row ids, restore original selection id types, sync header
scroll, show indeterminate select-all, fix search races, and pin the
selected relation label across remote pages.

* fix(choy_ui): cover L3 helpers and harden review edges

Reach full helper coverage; catch relation search failures; harden row
ids; fix clear/select emit duplication and calendar clear key bubbling.

* fix(choy_ui): address L3 a11y, filter, and selection review edges

Move DatePicker clear out of the trigger; ignore remote combobox
filtering; prune stale row selection; encode typed row keys; debounce
and surface relation search failures.

* fix(choy_ui): harden L3 id decode, date parse, and NameSearch map

Decode typed row keys on registry miss, reject non-finite ids, validate
impossible calendar days, dedupe relation options, and keep Clear for
unparsable models.

* fix(choy_ui): polish L3 scroll sync, prune, and id edge cases

Compare scrollLeft for header/body sync, extract selection pruning,
trim string row ids, broaden row-click guards, and keep search errors
generic in the UI.

* fix(choy_ui): support controlled DataTable selection and stale search reset

Declare rowSelection for v-model pairing, clear RelationCombobox caches
when the search target changes, and cover the blank-query NameSearch path.

* fix(choy_ui): stop open-search cancel and polish L3 UX edges

Do not bump searchSeq while the combobox is open; keep prior options
visible during loading; skip controlled selection echo emits; and expose
aria-sort on column headers.

* fix(choy_ui): harden controlled DataTable selection and a11y

Keep prop-authoritative selection with off-page id merge, complete table
roles plus keyboard row activation, and clear RelationCombobox host
errors whenever local error state resets.

* test(choy_ui): cover date parse clamp rejection path

Inject a clamping parser in unit tests so the exact YYYY-MM-DD parts
guard stays reachable under the current @internationalized/date behavior.

* fix(choy_ui): normalize selection ids and polish RelationCombobox UX

Trim controlled row ids before encoding/merging, show selected labels via
display-value, skip empty virtual items, pad years for date round-trip,
and clear debounce timers on watcher cleanup.

* fix(choy_ui): sort Date columns by timestamp in DataTable

Default sortingFn uses compareDataTableValues; compare Date instances via
getTime so weekday-name string ordering no longer wins.

* fix(choy_ui): stabilize DataTable columns and RelationCombobox edges

Memoize column defs and defaultColumn, measure virtual rows, abort
stale searches on watcher cleanup, normalize NameSearch rows, and avoid
emitting select(null) for unresolved ids.

* fix(choy_ui): harden L3 selection merge, a11y, and RelationCombobox UX

Dedupe controlled selection ids, memoize presentKeys, announce virtual
table counts, keep search options on error, and round-trip date/key edges.

* fix(choy_ui): align date/relation edges and dogfood controlled selection

Clear unformattable dates to null, normalize upsert ids, coerce Date/number
sorts, pin labels on close, and bind gallery DataTable via v-model.

* fix(choy_ui): tighten date/row-key edges and virtualizer identity

Validate day-of-month on format, ignore object NameSearch labels, guard
malformed n: keys, key virtual rows by id, and clamp Relation debounce.

* fix(choy_ui): trim relation ids and deep-watch controlled selection

Match padded model ids to pinned/selectedOption labels, skip blank
DisplayName overshadowing label, and deep-watch rowSelection/presentKeys.

* fix(choy_ui): detect duplicate row ids and stabilize Relation search

Reject colliding DataTable ids, tolerate null selection, skip false-only
prune writes, hide empty-on-error, and watch searchKey for target identity.

* fix(choy_ui): drop ghost selection ids and polish DataTable a11y

Skip unnormalizable controlled ids on merge, re-measure on estimateSize
change, align leaf headers, and coerce DatePicker display/clear guards.

* fix(choy_ui): keep invalid date picks and stabilize list scroll

Reject out-of-range DatePicker writes without clearing, reset pins only
on searchIdentity, preserve option raw, and reset scroll on data/search.

* fix(choy_ui): fix empty-table ARIA count and Infinity sort

Count the empty-state placeholder row, compare numbers with < / >, disable
non-sortable headers, and validate month length via UTC leap-safe days.

* fix(choy_ui): align date year parse and use grid for selection a11y

Reject out-of-range years on parse, switch DataTable to role=grid with
gridcell, clamp estimateSize, and add encode/decode round-trip coverage.

* fix(choy_ui): advertise multi-select and harden numeric string sort

Add aria-multiselectable, compare numeric-looking strings without ICU,
clear stale sort when columns swap, and route Today through calendarValue.

* fix(choy_ui): reset scroll on key signature and label calendar nav

Watch presentKeys instead of data identity for scroll reset, key Relation
virtual rows by option id, and cover throwing date parsers.

* fix(choy_ui): break unsafe-int string ties and Space-toggle rows

Tie-break collapsed MAX_SAFE_INTEGER string ids, ignore unusable ids in
selection equality, Space toggles selection, and bound the DatePicker calendar.
* feat(choy_ui): PR5 Form/List/Search and field main set

Wire ChoyFormView/ListView/SearchView/Pagination/Breadcrumb and the
field main set onto L3 engines, with DogfoodCompanyForm and Gallery dogfood.

* fix(choy_ui): address PR5 review a11y/bugs and helper coverage

Restore image preview object-URL ownership, wire field control a11y IDs,
harden dogfood save/pagination, and cover remaining helper branches.

* fix(choy_ui): address follow-up PR5 review a11y and dogfood save

Round monetary commits, wire field slot a11y across controls, keep
invalid number drafts, sync pagination page, and harden dialog/dogfood saves.

* fix(choy_ui): harden monetary rounding, draft sync, and field a11y ids

Keep invalid drafts visible, round decimal strings half-away-from-zero,
unique-suffix control ids, and avoid disabling readonly text inputs.

* fix(choy_ui): sync monetary draft on host model and polish PR5 edges

Clear stale edited drafts on unfocused model updates, normalize search
keywords, unify numeric monetary rounding, and fix breadcrumb/number UX.

* fix(choy_ui): preserve host monetary updates and tighten field a11y

Allow unfocused-or-unedited model sync while typing, expose aria-required,
keep non-exponential number drafts, and filter empty Select options.

* fix(choy_ui): sync monetary host updates and cover helper edges

Only keep focused drafts when they match the host value, harden checkbox
typing, drop blank search fields, and exercise remaining helper branches.

* test(choy_ui): cover exponential monetary string fallback branches

Exercise Number()-canonicalized decimals and -0 formatting so
formatChoyMonetary patch coverage reaches 100%.

* fix(choy_ui): cover decimal carry paths and tighten monetary edges

Exercise full rounding carry, drop an unreachable frac pad loop, reject
non-decimal literals, and prefer local image object URLs over stale hosts.

* fix(choy_ui): harden PR5 field edges from review

Keep host clears over stale monetary drafts, guard readonly date/time
inputs, reject lossy monetary magnitudes and non-image picks, expose
help via sr-only aria-describedby, reset page on pageSize change, and
normalize empty selection to null.

* fix(choy_ui): address PR435 follow-up review edges

Move Binary/Image a11y attrs onto the visible Choose control, reject
lossy float/decimal parses and monetary display fallbacks, commit number
drafts on Enter, guard monetary input when readonly, and keep list
rowSelection as a non-null array.

* fix(choy_ui): reject unrepresentable fractions near 2^52

Guard parse/round helpers when typed fractions vanish at unit spacing,
reuse choyTotalPages in dogfood paging, and watch only File for image
object URLs.

* fix(choy_ui): skip monetary rewrite on idle blur

Do not round the host model on focus/blur without edits, align number
formatting with the string lossy-reject path, clear list selection on
search in dogfood/gallery, and drop the unused keywordFields prop.

* fix(choy_ui): drop dogfood USD fallback and pin round-trip

Stop implying USD when currency is cleared, and assert
roundChoyDecimal text still parses back through parseChoyNumber.

* fix(choy_ui): tolerate trailing dots and harden select/enter

Normalize trailing decimal points on parse/round commit paths, de-dupe
selection option values, prevent Enter form submit on number fields,
and announce pagination changes to assistive tech.

* fix(choy_ui): mark invalid drafts and export view helpers

Surface uncommitted invalid monetary/number drafts via aria-invalid,
navigate Gallery→dogfood by named route, re-export search/pagination
helpers from the public barrel, and pin negative half-away round-trip.

* fix(choy_ui): ignore idle exponential blur and tighten format

Skip invalidDraft when the number draft still mirrors the host value,
reject lossy exponential monetary strings, fall back to a single list
checkbox on dogfood save, and drop unsupported aria-required on statusbar.

* fix(choy_ui): quiet virtual attrs and harden field edges

Disable VirtualField attr inheritance, deep-watch dialog rows while
pristine, hide non-finite binary sizes, and skip non-string selection
option values.

* fix(choy_ui): restore select placeholder and leading-dot parse

Bridge null↔empty for Reka Select so unset shows the placeholder, accept
leading-dot decimals in parse/round helpers, and surface invalid number
drafts as field error text.

* fix(choy_ui): tighten dogfood search and file accept checks

Clear selectedRowId on search, accept leading +, validate empty-MIME
images by extension, enforce Binary accept tokens, and disable Save
while the mock request is pending.

* fix(choy_ui): pin round stability, block SVG, export helpers

Add idempotent roundChoyDecimal coverage, reject SVG image uploads by
default, bind statusbar controlId for label association, and re-export
field helpers from the public barrel.

* fix(choy_ui): normalize -0.00 display and file reject UX

Collapse signed-zero monetary formatting, keep invalid amount visible on
focus, align image accept with raster validation, surface rejected file
picks as field errors, and skip non-primitive cells in keyword filter.

* fix(choy_ui): stop lossy monetary fallback and harden accept

Restrict monetary numeric fallback to exponential text, fix Binary
accept wildcards and empty MIME, clear stale file errors on host model
updates, require visible page rows for dogfood save, and allow named
routes in breadcrumbs.

* fix(choy_ui): reject inexact doubles and keep form save target

Replace the 2^52 fraction guard with a canonical round-trip check so
values in [2^51, 2^52) cannot commit a different amount, and validate
dogfood selectedRowId against allRows while checkbox stays page-local.

* fix(choy_ui): harden field UX and cover decimal expansion

- map image/jpeg empty-type fallback to .jpg/.jpeg; allow empty MIME on wildcards
- normalize datetime-local display; keep unmatched selection values visible
- commit monetary draft on Enter; pin list row ids; export choyFieldChromeDefaults
- expand exponential literals in canonicalChoyDecimal and cover all branches

* fix(choy_ui): align monetary e-paths and harden field edges

- gate exponential string formatting on digit exactness so 1e21 matches the number path
- bound expandExponentialDecimalText padding to avoid RangeError
- guard number draft updates when readonly/disabled; skip datetime display echo writes
- type Gallery select as string | null instead of weakening Select empty→null

* fix(choy_ui): harden accept, IME Enter, and e-form exactness

- accept *.ext and map image/svg+xml empty-type fallback to .svg
- filter empty statusbar options; skip Enter while IME is composing
- require expandable exponential literals so 9007199254740993.e0 stays rejected

* fix(choy_ui): harden datetime/time, accept, and e-forms

- compare datetime/time writes at input precision; sanitize field control ids
- align image validation with accept; normalize *.ext for the file picker
- skip selection ''→null on mount; revalidate number draft on mode change; dedupe statusbar
- expand leading-dot exponentials; drop unreachable expandFiniteNumber fallback

* fix(choy_ui): pad time hours and keep monetary draft

- pad single-digit hours for native time inputs; treat octet-stream as extension fallback
- keep focused monetary draft when it already matches the committed value
- cover primitive number/boolean keyword filter matching

* fix(choy_ui): GB sizes, stable row ids, mode-invalid

- add GB tier for binary size; reset pagination to page 1 explicitly
- hoist stable list row-id callbacks; drop SelectionField ''→null watcher
- flag number draft invalid when mode cannot parse the host value
- drop unreachable expand empty-digits / isSafeInteger pad branches for coverage

* fix(choy_ui): cover expandFiniteNumber and harden picks

- export/test expandFiniteNumberToPlainDecimal for zero and exponential paths
- ignore bare accept tokens unless a restrictive token fails to match
- flag number mode/value mismatch on mount; unify image raster allow-list

* fix(choy_ui): validate host number by value not String()

- avoid marking exponential host values like 1e-22 invalid on mount/mode switch
- keep mode incompatibility (e.g. 12.5 under integer) flagged for host and edits
* feat(choy_ui): PR6 Kanban, O2M/M2M, Html/Json/Properties

Add isolated data-view and remaining field main path for cutover
门槛: Kanban board, relation widgets, TipTap Html, Json, and Properties.

* fix(choy_ui): address PR6 review bugs and reach helper coverage

Fix nested JSON key loss, O2M edit/remove guards, Properties defaults,
Html plaintext separators, Kanban same-lane drop index, and TipTap
initial sync; expand helper unit tests for Codecov patch 100%.

* fix(choy_ui): harden JSON sort and close coverage gaps

Preserve __proto__/toJSON in stringify, skip TipTap empty reset, pin
TipTap peers, and cover buildFullPropertiesMap default branch.

* fix(choy_ui): validate Html links, dedupe lanes and property names

Reject non-http(s)/mailto link schemes before TipTap setLink; skip
duplicate Kanban laneDefs; reject duplicate property definition names.

* fix(choy_ui): harden sanitize/stringify and normalize selections

Guard missing purify.sanitize with tag strip, coalesce undefined
JSON.stringify results, and persist only usable selection options.

* fix(choy_ui): correct Kanban move index and link/host parsing

Emit post-move Kanban toIndex, treat host:port as host not scheme,
separate void hr in plaintext, and uniquify synthetic O2M row ids.

* fix(choy_ui): harden no-DOM strip and keep unrenderable props

Strip unterminated tags when purify.sanitize is missing, and carry
forward previous values for non-V1 property schema items on rewrite.

* fix(choy_ui): sync JSON draft on reset and skip blank defaults

Let external model updates overwrite invalid JSON drafts, ignore
whitespace-only numeric defaults, assert rel via sanitize hook, and
re-seed definition drafts only when items content changes.

* fix(choy_ui): fix protocol-relative links and kanban add races

Reset hr-only Html on null, preserve generated Id when titleField is Id,
and compare normalized property definition drafts.

* fix(choy_ui): fail-closed sanitize and tighten definition defaults

Hide Kanban create when readonly and reject selection defaults outside options.
* feat(choy_ui): PR7 Chatter, theme persistence, and xpath dogfood

Add isolation ChoyChatter*, applyChoyThemePreference + theme.override.css,
and Partner-style data-anchor dogfood with xpath-injected Bank tab.

* fix(choy_ui): cover timeline id sort and harden theme prefs

Rebind xpath dogfood setup after IMD merge and clear dogfood timers on unmount.

* fix(choy_ui): parse digit epoch strings and clear gallery timers

Persist normalized theme modes; use locale-independent id tie-breaks.

* fix(choy_ui): accept signed digit epoch timestamp strings

* fix(choy_ui): reject array theme prefs and allow field-change labels

* fix(choy_ui): default timeline entries and expose chatter clear

* fix(choy_ui): merge stored theme prefs on partial apply

Keep Gallery auto mode when only density changes.

* fix(choy_ui): reject unsafe epoch strings and prune dogfood timers

* fix(choy_ui): bind partner tab anchor and validate persisted theme

Reject unsafe numeric timestamps consistently with digit-string parsing.

* fix(choy_ui): ignore invalid theme prefs and case-fold change kinds

Keep empty action: labels aligned with product; cover fieldFallback.

* fix(choy_ui): reject out-of-range timestamps and avoid theme clobber

Dedupe timeline identities and preserve drafts on empty-string post errors.

* fix(choy_ui): skip unset theme attrs on partial preference apply

* test(choy_ui): cover partial theme apply leaving host dark class

* fix(choy_ui): persist only expressed theme preference fields

* fix(choy_ui): restore document theme after dogfood partner unmount

* fix(choy_ui): do not invent theme when gallery persists density

* fix(choy_ui): restore dogfood theme only when still page-owned

* test(choy_ui): pin cross-kind chatter timeline id dedupe

* fix(choy_ui): do not invent density when gallery persists theme

* fix(choy_ui): accept only ISO chatter wire timestamps

* fix(choy_ui): clear chatter draft on resId change; require ISO T separator

* fix(choy_ui): pin naive ISO times to UTC; deterministic timeline dedupe

* fix(choy_ui): enforce ISO timestamp case; guard stale post draft clear

* fix(choy_ui): sync-flush chatter posting watcher for same-tick hosts

* fix(choy_ui): capture in-flight posting on chatter mount

* fix(choy_ui): defer chatter draft clear past same-tick postError

* test(choy_ui): fix year-pad assertion for formatChoyUtcIso

* fix(choy_ui): static IMD tab anchor; reject impossible ISO dates

* fix(choy_ui): reject out-of-range chatter ISO time components

* fix(choy_ui): keep chatter composer mounted when disabled

* fix(choy_ui): ignore chatter submit shortcut during IME composition

* fix(choy_ui): validate ISO offset ranges; ignore overlapping gallery posts

* fix(choy_ui): default timeline author resolver; treat empty postError as ok

* test(choy_ui): cover author label overrides; guard dogfood overlap posts

* fix(choy_ui): keep timeline on refresh; clear draft on model change

* fix(choy_ui): truncate ISO fractional seconds; IME-safe submit prevent

* fix(choy_ui): normalize minute-precision chatter ISO timestamps
* feat(choy_ui): PR8 ChartView with Unovis (no ECharts)

* fix(choy_ui): address PR8 review, Tailwind gate, and chart coverage

Wire Unovis click via events, sort before percent stack, unique series keys,
and fix the Tailwind leak check false positive on clip-path.

* fix(choy_ui): correct Unovis click callbacks and chartType guard

Align bar/line/pie handlers with VisEventCallback(datum, event, index),
resolve stacked stackIndex and grouped flat series, and reject unsupported
host chartType values.

* fix(choy_ui): harden chart metric default, line click, and finite values

Fall back to the first metric when the alias is unknown, bail on unresolved
line-click SVG targets, and coerce non-finite series values to 0 for Unovis.

* fix(choy_ui): polish chart pie slices, metrics watch, and click helpers

Filter non-positive pie slices, deep-watch metrics, label sort toggles, and
extract unit-tested Unovis click index mapping helpers.

* fix(choy_ui): gate disallowed chart types and harden percent/click mapping

Skip rendering when the host allowlist empties availableTypes, clamp negatives
out of percent stacks, and prefer row.index for stacked click categories.

* fix(choy_ui): empty pie state, safer grouped clicks, round percents

Treat all-non-positive pie slices as empty, only map grouped series when the
flat index agrees with the row category, and round percent stacks to 2 dp.

* fix(choy_ui): cover rowCategoryIndex edge and polish chart type UX

Cover invalid row.index paths for patch coverage, allow pie when multi-series
even with groupDepth 0, map line clicks via path bounds, and preserve host
chartTypes order.

* fix(choy_ui): chart overlay precedence, shared fallback color, line click helper

Prefer error over loading overlay, harden Tailwind *-path leak sanitization,
centralize chart fallback color, and unit-test line-click category mapping.

* fix(choy_ui): harden line click width, path leak detect, percent align

Bail on zero-width line clicks, detect bare path: leaks without stripping
CSS *-path, pin sort-before-percent, and align percent series to categories.

* fix(choy_ui): finite sort totals and validate chart click indexes

Coerce Infinity/NaN before sorting and percent math, bounds-check emitted
series indexes, and guard non-finite line-click positions.

* fix(choy_ui): preserve pie categoryIndex and harden chart helpers

Resolve adapters via registry own-keys, optional-chain sparse series data,
and emit the original category index after pie slice filtering.

* fix(choy_ui): touch line clicks, axis metric label, slot disabled

Normalize line-click X from changedTouches, surface metricLabel on the Y
axis, and forward disabled into stacked/sort switcher slots.

* fix(choy_ui): harden stacked/pie clicks and clientX fallback

Guard undefined stacked-bar datums, prefer pie slice key/name identity,
and fall through to event.clientX when touch X is non-finite.

* fix(choy_ui): percent column remainder, blank index, noop emits

Use largest-remainder percent rounding so stacks hit 100, reject
whitespace-only row indexes, and skip redundant type/sort change events.

* fix(choy_ui): disambiguate pie click indexes and validate metrics

Emit distinct category vs series indexes for single/multi-series pies,
validate metric alias selection like other controls, and tighten the
Tailwind path: leak check to only exempt clip/offset-path.

* test(web): use hyphen-aware path: leak regex again

Prefer [^a-z-] so any CSS *-path: (including vendor-prefixed) is
exempted without an ad-hoc clip/offset Replacer list.

* fix(choy_ui): stable sorted categoryIndex and safer stubs/ticks

Guard FE stub resolve on lookup ok, map XY click indexes through the
sort permutation, and skip fractional/out-of-range axis tick labels.

* fix(choy_ui): key-first pie match and overflow-safe percents

Resolve donut clicks by unique slice key before name/index, coerce
non-finite values on the unsorted sort path, and scale percent totals
before the 10000 factor so large finite inputs cannot overflow.
* chore(choy_ui): put template before script in product SFCs

IMD/xpath reviewers open markup first; keep vendor/ui on shadcn's
script-first order so upstream diffs stay small.

* docs(agents): document Vue SFC template-first convention

Record IMD-first template → script → style for product SFCs, with
vendor/ui keeping shadcn script-first; point at the local Cursor rule.

* revert(docs): drop Vue SFC convention note from AGENTS.md

Keep the rule local under gitignored .cursor/rules only.

* fix(web): escape ErrorView message; clarify SFC order docs

Render error details as text so route query cannot inject HTML, and
document that template-first applies to module web pages too.
* feat(web): PR9a merge Choy kit tree into modules/web

Move vendor/view/field/layout/chatter/internal, styles, gallery/dogfood
into modules/web; keep a thin choy_ui re-export shell. Prefer web for
Tailwind generation with kit-only candidate scan, exempt web as kit
host, and mask PascalCase Textarea tags so template-first SFCs parse.

* fix(web): pin exact package.json peers for esm.sh web builds

Bare @tanstack/vue-table resolved to latest v9 (useTable) and broke
DataTable named imports after the kit merged into web. Apply exact
peer/dependency versions from the module package.json as bare-import
pins so global web build stays on the declared 8.21.3 API.

* fix(web): pin kit peers in FE host bundle; harden gallery setup

unit-shard (web) still resolved versionless @tanstack/vue-table via the
vue host bundle. Merge exact modules/web peers into host/e2e esm pins,
and skip Choy gallery registration when router is missing so setupApp
unit tests stay isolated.

* fix(web): scope SFC raw-text mask to templates; raise PR441 patch coverage

Limit PascalCase Textarea/Title masking to unquoted template bodies so script
literals and attributes stay intact; declare choy_ui→web depends and cover the
Codecov patch gaps.

* fix(web): harden SFC raw-text mask and e2e peer pins

Only skip quotes inside tags so apostrophes in template text cannot leave
Textarea unmasked; ignore script/style-embedded template literals; skip
test/spec files in Tailwind scans; fail e2e bundles on pin read errors.

* fix(web): harden SFC template depth, pins, and kit host guards

Ignore self-closing nested templates when masking; fail when a kit tree
lacks theme.css; gate web Reka exemption on vendor/ui; propagate FE pin
errors; dedupe ExactPins wiring in webBuilder.

* fix(web): require kit ui path to be a directory

Harden vendor/ui probes with IsDir, skip the Choy Vue parse scan when the
kit tree is absent, and assert ExactPins are actually consumed in builds.

* test(web): cover remaining PR441 patch branches

Exercise nil ExactPins receiver, empty modulesPath kit-host gate,
self-closing tag edges, and FE host bundle pin read failures.

* fix(web): harden SFC mask tag detection and kit scan fallbacks

Require a real tag name after '<' so interpolations cannot derail quoting;
pair script/style blocks independently; prefer-web kit root test; Choy*
fallback for unlisted kit dirs; derive FE pin asserts from package.json.

* fix(web): confirm tag openers before masking; guard addRoute

Require a closing '>' (quote-aware) before treating '<' as a tag so
comparisons like '{{ a <b }}' cannot derail Textarea masking; assert
Tailwind walk errors return no partials; require router.addRoute.

* fix(web): broaden Choy page scan; cover SFC tag-opener edges

Allow pages/Choy* in kit Tailwind candidates; normalize generated CSS
exclusions case-insensitively; harden escape handling and unit-cover
looksLikeHTMLTagOpener branches for Codecov patch 100%.

* fix(web): quote-aware SFC openers; prefer dependency pins

Make template/script/style open tags quote-aware so '>' inside attrs does
not truncate matches; let exact dependencies override peers; skip
__tests__/ paths in Tailwind candidate scans.

* fix(web): harden SFC mask boundaries and kit UI Stat errors

Skip HTML comments when scanning template depth and masking; clamp
unbalanced fallback before script/style; cover mixed-case raw-text tags;
unmask comment nodes; surface non-not-exist vendor/ui Stat errors.

* fix(web): ignore script/style when scanning HTML comments

An unclosed "<!--" inside a script string must not open a comment span
through EOF that skips masking the following template Textarea.

* test(web): cover SFC mask comment edge branches

Exercise close-inside-comment, unclosed HTML comments, and OutsideQuotes
unclosed-comment paths so parser.go patch coverage reaches 100%.

* fix(web): ignore commented script/style openers in SFC mask

A <!-- <script> --> must not pair with a later real </script> and treat
the template as script body, leaving PascalCase raw-text tags unmasked.

* fix(web): prefer nested template open before comment close

Checking inComment(close) first skipped a preceding nested <template>
opener when the next </template> sat inside a comment, corrupting depth.

* fix(web): protect template depth from mustache/attr literals

Ignore </template> inside mustaches and quoted attrs when scanning SFC
template depth; cover choy_ui kit fallback; avoid digest path allocs.

* test(web): cover mustache/attr range helper edges

* fix(web): restore chatter edge tests; harden kit host + unmask

Restore formatFieldChangeSummary edge assertions; derive ModulesPath from
ModuleRoot for kit-host exemption; strip VueSfcRaw masks from text nodes.

* fix(web): merge kit pins; harden SFC script/mustache ranges

Domain module builds inherit modules/web exact pins (own pins win);
drop mustache ranges that start in script/style; ignore cross-script
comment markers when discovering script blocks.

* fix(web): Clean ModuleRoot; mask ALLCAPS; warn pin conflicts

Derive kit-host ModulesPath via filepath.Clean; mask all-caps raw-text
tags; log when a module exact pin overrides the kit host version.

* fix(web): unmask only tag sentinels; harden pins and choy fallback

Restore VueSfcRaw only after < or </ in text/comments; Clean module path
before kit-host pin lookup; accept lowercase choy* in unknown-dir scan.

* perf(web): skip SFC raw-text masking when no candidate tags

Avoid the multi-pass masker when pascalCaseRawTextTag does not match,
which is the common case for product SFCs without Textarea-like tags.

* test(web): cover top-level self-closing template mask path

* fix(web): drop comment mustaches; harden kit host Stat and O*.vue

Ignore mustache ranges that start in HTML comments; treat only IsNotExist
as non-kit for web; limit capital-O exclusion to .vue product views.

* fix(web): guard empty pin path; case-insensitive raw-text mask

Bail on blank module Path before kit pin lookup; match generated CSS by
exact kit-host suffix; mask any non-lowercase raw-text tag spelling;
treat only permission Stat errors as web kit-host.

* fix(web): fail closed on kit Stat; TrimSpace pin paths

Any kit-dir Stat failure keeps the web import ban; trim module Path
before Clean so whitespace-padded paths still resolve kit pins.

* fix(web): drop exact vue pins from module package.json

Mirror vueHostBareImportPins so module builds cannot pin a second Vue
instance away from the embedded host.

* fix(web): harden kit path/mask edges from PR review

Require a directory boundary for generated CSS digest skips, scope
Tailwind scan to vendor/ui, interleave comment/mustache discovery so
attr <!-- and comment {{ cannot unmask Textarea, drop e2e vue pins,
and tighten the kit Stat fail-closed test.
* feat(web): PR9b main-bundle Choy CSS, product scan, cutover bans

Emit unscoped Tailwind theme/utilities into the SPA entry, scan kit plus
domain web trees for candidates, and add gated cutover import bans
(element-plus / @/web lib) while keeping dual-stack typecheck green.

* fix(web): harden PR9b product scan and cutover lib bans

Drop Parallel on the shared ReadDir hook test, align exported Generate
hashes with product digest under modules/, and normalize import paths so
bare and ../-escaped @/web/web/lib deep imports stay banned.

* fix(web): align kit root, CSS order, symlink scan, query strip

Resolve preferred kit root before generate so dialect/hash match digest,
import generated utilities after index.scss, scan symlink modules, and
strip ?/# before cutover lib-path normalization.

* fix(web): skip file symlinks, filter sibling kits, normalize EP bans

Stat module roots before web/ so ENOTDIR cannot abort product scans,
route sibling kit hosts through kit-filtered candidates, and normalize
element-plus specs the same way as deep-lib bans.

* fix(web): accept custom modules dirs; assert domain Stat errors

Resolve-back modulesPathForKitRoot so Generate matches digest outside a
literal modules/ name, and force domain web Stat failures via the test
seam instead of flaky chmod 000 probes.

* test(web): cover modulesPathForKitRoot parent==root edge

* fix(web): normalize import specs before all forbidden UI bans

Hoist path cleaning so ui/internal/choy_ui-deep rules cannot be bypassed
with ./ ../ or ?/#, matching the cutover element-plus / lib protection.

* fix(web): reject directory sibling dialects; cover cutover scan

Treat a directory theme.css on sibling kits as an error (same as preferred
kit resolve), replace flaky chmod probes with deterministic fixtures, and
assert cutover bans plus kit-host exemption through AssertNoForbiddenUiImports.

* fix(web): gate sibling kits on vendor/ui; scope dark tokens

Align product Tailwind sibling-kit detection with isKitHostModule so
domain modules that only ship theme.css keep full scanning, and drop
unanchored .dark / [data-density] selectors that could theme EP subtrees.

* fix(web): pin theme override cascade; anchor web-lib bans

Import theme.override.css from the app entry after tokens so it is not
reorderable by route chunks, and match cutover web/web/lib bans by
leading segments to avoid embedded-path false positives.

* fix(web): keep dir trailing slash in import bans; digest domain classes

Preserve directory-style trailing slashes after path.Clean so @/choy_ui/
still matches choy_ui-deep markers, ban bare @/choy_ui, and assert
TailwindInputDigest changes when a domain module adds a class.

* fix(web): close web-lib prefix bypass; kit-filter without dialect

Strip @/, ../, and ./ in any order for cutover web/web/lib bans, and
treat vendor/ui ownership as the kit-host marker independent of
styles/theme.css so sibling hosts stay kit-filtered.

* fix(web): align custom kit-root product scan; harden host filtering

Accept either web or choy_ui under a custom modules parent for product
scans, treat choy_ui as a kit host by name, load theme.override after
index.scss, and wrap per-module candidate scan errors with context.

* fix(web): ignore non-kit directory dialects; lock domain spec skip

Keep directory-dialect hard errors for web/choy_ui only so a stray
styles/theme.css directory on an unrelated sibling cannot abort product
Tailwind generation, and assert Ensure skips domain *.spec*/__tests__ classes.

* fix(web): ban package-form @choysum-dev/web/web/lib deep imports

Rewrite @choysum-dev/web/* package subpaths into web/* so cutover
web-lib-deep matches the published package specifier the same way as
@/web/web/lib aliases.
…443)

* feat(web): PR9c cut shell to Choy runtime (setupApp, peers, barrel)

Drop Element Plus from the web SPA host and CSS entry, apply Choy theme
preferences at startup, route shell pages through ChoyShellLayout, and
trim echarts/EP icon peers while keeping the public barrel Choy*-only.

* fix(web): restore echarts peers; cover shell; restore theme on boot

Keep echarts/vue-echarts peers for dual-stack OChartView typecheck,
apply persisted Choy theme at startup, keep link hover/active distinct
via color-mix, and add HomeView/ChoyShellLayout unit coverage.

* fix(web): resolve ErrorView codes from path; allow shell default slot

Static /error/403 redirects had no :code param, so ErrorView always fell
through to 404. Parse the path segment, and wrap router-view in a default
slot so hosts can override the main pane.

* fix(web): prefer ErrorView path code over query; clarify tick read

Static /error/NNN redirects must win over a stray ?code=; mark the
HomeView tick dependency with void for intentional reactive reads.

* fix(web): hide empty shell aside; harden ErrorView codes and link hover

Skip aside chrome unless a slot is provided, normalize array route codes,
and add plain color fallbacks before color-mix for older engines.

* fix(web): restore EP for dual-stack; harden ErrorView query picks

Keep Element Plus registered for O*/TerminologyEditor during cutover,
normalize repeated ErrorView query values, and store HomeView prefs in a ref.

* fix(web): restore reactive ElConfigProvider; clarify AppLayout sidebar

Dual-stack O* locale switching needs a live ConfigProvider; AppLayout
keeps showSidebar off until menu chrome can fill the aside slot.

* fix(web): gate empty footer; resync Home theme; sanitize ErrorView from

Skip footer chrome without a slot, refresh keepAlive Home prefs on
activate, and only offer Go back for same-app absolute from paths.

* fix(web): restore shell KeepAlive; harden from paths; drop App data-test

Honor route.meta.keepAlive like OLayout, reject backslash open-redirect
forms, and remove a non-inheritable data-test on ElConfigProvider.

* fix(web): keep KeepAlive mounted; drop dead footer; cover WelcomeView

Preserve cached shell views across non-keepAlive navigations, remove
route showFooter that cannot fill slots, and add WelcomeView step tests.

* fix(web): skip shell view render when router-view Component is empty

AppLayout and similar hosts can match with no child component; guard both
KeepAlive and plain branches to avoid invalid :is="undefined" vnodes.

* fix(web): key keepAlive by fullPath; mix hover from base primary

Parameterized cached routes need distinct instances per fullPath, and
color-mix hover/active should start from --el-color-primary not a light tint.

* fix(web): key keepAlive views by path, not fullPath

Reuse one cached instance across query variants while still remounting
when parameterized path segments change.

* fix(web): honor link theme tokens; harden ErrorView and shell meta

Nest color-mix under primary-light/dark vars, reject /error from targets,
optional-chain keepAlive meta, and open external links with noopener.

* fix(web): harden ErrorView from paths; cap KeepAlive cache

Reject control chars and backslashes anywhere in redirect targets, and
bound shell KeepAlive retention so parameterized paths cannot grow unbounded.

* revert(web): drop KeepAlive max cap on ChoyShellLayout

Cache bounding is unnecessary for the current keepAlive route set.

* fix(web): gate color-mix with @supports; harden sameAppPath encodings
* feat(web): PR9d store-bound Choy engine (Page/Form/List/fields)

Wire optional WebModelStore binding into ChoyPage/Form/List/Search and
key fields via store+prop (hosting O* engines); keep chrome/Dogfood
defineModel paths when store is absent. Export ChoyVColumn/ViewScope/
ButtonBox/StatInfo from the public barrel.

* fix(web): restore Choy chrome attrs fallthrough and cover store hosts

inheritAttrs:false was dropping chrome-mode attrs/listeners; ManyToOne
chrome now uses a noop search fallback. Mount tests exercise store-mode
hosts and patch paths for Codecov.

* fix(web): forward Choy store engine events and page-store fields

Declared emits were not forwarded in store mode; attrs+v-on double-bound
listeners. Fields under ChoyPage now resolve the page store; VColumn
forwards scoped slot props; nested pages keep the ancestor store.

* fix(web): strip on prefix in splitChoyAttrsListeners for v-on

Vue toHandlers prepends on again; keep v-on object keys as event names
so undeclared store-mode listeners actually fire.

* fix(web): forward Search store props and conditional VColumn slot

Store-mode ChoySearchView now passes placeholder/disabled to OSearchView
(inert when disabled). ChoyVColumn forwards the cell slot only when the
consumer provides one. Field hosts share useChoyStoreFieldBinding.

* fix(web): align O2M inheritAttrs and listener on* detection

ChoyOneToManyField now disables attr fallthrough like sibling fields.
splitChoyAttrsListeners matches Vue's /^on[^a-z]/ heuristic so camelCase
props such as onchangeSessionId stay in bind. Mount tests assert forwarded prop.

* fix(web): share ViewScope provide keys and harden filter flatten

OViewScope/ChoyViewScope provide via shared viewScopeKeys constants.
flattenChoySearchFilters accepts a singleton group without throwing.

* fix(web): keep List chrome under page store; harden filter leaves

Explicit columns/data force ChoyListView chrome even when ChoyPage provides
a store. Leaf filters with empty children are no longer dropped. ViewScope
uses toRef; O2M row helpers use Record indexing.

* fix(web): harden disabled search fallback and list row-click

Add pointer-events/select-none when OSearchView is disabled for browsers
without inert, and skip nullish store row-click payloads instead of
forwarding the wrapper object.

* fix(web): block disabled search keys; omit chrome model from storeBind

Capture keydown when OSearchView is disabled so older browsers without
inert cannot submit via keyboard, and strip defineModel keys from
storeBind so chrome defaults do not reach the O* engine.
* feat(auth): PR9e cut domain web to Choy* from @/web

Replace auth O*/Element Plus product surface with store-bound Choy*
(Page/Form/List/Search/fields) and Choy chrome for Login/Register/Logout.
Wire header actions into ChoyShellLayout via data-anchor xpath; host
TokenKanban on ChoyKanbanView lanes. Split public @/web barrel to kit.ts
so domain imports do not pull the web app instance.

* refactor(auth): rename local O* components without O prefix

Auth-owned layout/preferences components are no longer named like the
legacy web O* stack: AuthHeader, SwitchCompany, PreferencesDialog.

* fix(auth): harden PR9e cutover review follow-ups and i18n CI

Remove Enter double-submit, wire real notifications, fix token kanban
query/flat-lane/move races, hide stub IO actions, restore Login patch
coverage, and sync auth/web gettext catalogs for CI.

* fix(auth): address PR9e open-redirect and kanban resync reviews

Restrict login/register redirect to same-origin paths, drain pending
kanban lane syncs, dismiss the user menu on Escape, and tighten Login
success assertions.

* fix(auth): harden login redirect against backslash open redirects

Parse redirect targets with URL origin checks, reject backslash payloads,
and tighten Login mount tests to assert real form affordances.

* fix(auth): trim login username and harden kanban/company UX

Trim username before login submit, navigate kanban cards only on real
record ids, and replace the multi-select company list with additive
checkboxes.

* feat(web): add ChoyKanbanView lane load-more API

Expose lane.remain and lane-load-more on the Choy kanban chrome, wire
TokenKanbanView through kanbanController.loadMoreLane, and cover the
helper contract with unit tests.

* fix(auth): close login redirect bypasses and cover login_form 100%

Reject normalization-induced protocol-relative redirects, harden kanban
card ids/moves, localize New, restore SwitchCompany Escape, and expand
login_form unit coverage for origin/error edge paths.

* feat(web): host inbox engine behind ChoyNotificationBell

ChoyNotificationBell omits deep O* imports from auth by hosting
ONotificationBell when count is unset; Gallery keeps chrome mode via
explicit count, and AuthHeader imports the public kit export only.

* fix(auth): accept raw kanban rows and dim preferences backdrop

Normalize RecordRow and raw model records into card payloads, and add a
semi-transparent PreferencesDialog overlay for modal contrast.

* fix(web): cover notification bell chrome and harden PR9e follow-ups

- Extract ChoyNotificationBell badge/aria helpers with unit and mount coverage
- Assert login redirect target via router.replace spy
- Resync kanban lanes when move record id cannot be resolved
- Cap SwitchCompany company list height with overflow scroll

* fix(auth): harden kanban labels, search filters, and dialog a11y

- Preserve string UserId fallback on token kanban username labels
- Keep singleton appliedFilters objects in ChoySearchQuery payloads
- Restore focus to popup triggers on Escape; trap Tab in PreferencesDialog

* fix(auth): clear typecheck errors and cover dialog focus trap

- Cast dialog root for trapDialogTabKey; cover all focus-trap branches
- Avoid [object Object] username labels; trim kanban card ids
- Always resync lanes after a failed move even if reload rejects
- Exclude hidden inputs from dialog Tab order

* fix(auth): harden kanban move/load races and list row navigation

- Resync optimistic lanes when a move is skipped (pending/unknown lane)
- Re-apply late first-frame search after the initial empty kanban load
- Fail closed on blank list row ids; normalize notification badge counts

* fix(auth): normalize login origin and tighten company/focus UX

- Compare login redirects against a normalized origin base
- Treat any negative tabindex as outside dialog Tab order
- Lock the active company checkbox so active stays in enabled

* fix(auth): harden login redirect decoding and focus/inbox polish

- Reject percent-encoded // and \\ in login redirect paths
- Skip hidden controls in the preferences dialog focus trap
- Forward ChoyNotificationBell class in inbox mode
- Prefer row key over synthetic id when payload Id is blank

* fix(auth): cover login origin catch and polish PR9e follow-ups

- Cover normalizeLoginRedirectOrigin parse failures; clear errors before validate
- Disable kanban load-more while a move write is in flight
- Normalize notification aria counts; skip aria-hidden ancestor tab stops

* fix(auth): reset header popups and harden kanban/login edges

- Clear AuthHeader menu/preferences on logout; focus dialog before load
- Reject malformed percent-encoded login redirects; prefer non-blank usernames
- Block kanban moves during search apply; treat summary/contenteditable as tabbable
- Disable TokenListView attr fallthrough onto the wrapper root

* fix(auth): harden multi-decode redirects and dialog focus restore

- Decode login redirect paths until stable before safety checks
- Allow hosts to localize kanban load-more labels via a formatter
- Skip focus restore when the pre-dialog trigger is detached

* test(auth): cover multi-pass login redirect decode fallthrough
* feat(base,meta): PR9f cut domain web to Choy* from @/web

Migrate base/meta pages and views off O*/Element Plus onto named Choy*
imports, with ModuleKanban hosted on createKanbanController + ChoyKanbanView.

* fix(base,meta): harden PR9f review follow-ups and patch coverage

Wire ChoyNumberField/ChoyDateField store hosts, fix ModuleKanban dialog
a11y and search races, restore list route guard, and expand FE mount/unit
coverage for cutover pages and helpers.

* fix(web,meta): cover ChoyNumberField chrome helpers and date parse edges

Extract number-mode helpers with unit tests, harden kanban date formatting
for epoch numbers, and exercise chrome NumberField inputmode in mounts.

* fix(web,meta,base): cover NumberField chrome and PR-Agent follow-ups

- exercise ChoyNumberField chrome commit/host sync for patch coverage
- fall back empty op summary message to code; log lane preload errors
- assert Lucide menu icon identity; drop vacuous ModuleDetailView smoke

* fix(web,base,meta): unbreak typecheck and harden id/nav follow-ups

- replace Array.at in NumberField mount test for current TS lib
- reject non-scalar list/kanban ids; share resolver; catch lane sync errors
- unmount page coverage wrappers in finally with named failure errors

* fix(web,base,meta): harden review follow-ups for ids, dates, and plan races

- fail-closed non-finite ids; keep epoch-0 dates; scalar-only op summaries
- gate PlanOperation by dialog session; date-only store valueFormat
- route float to ONumberField (default decimal); collect page mount failures

* fix(meta,web): coalesce lane sync waiters and fail-closed card/number ids

- reuse fail-closed id helper for kanban card keys; await in-flight lane sync
- count recovery search as in-flight; reject silently-rounded integer drafts

* fix(meta): treat non-finite timestamps as empty in kanban date format

* fix(web): drop unreachable integer BigInt parse guard for patch coverage

Number.isSafeInteger already rejects drafts that Number rounds past the safe range.

* fix(meta): harden kanban chrome fallbacks and invalidate plan on unmount

- ignore in-flight PlanOperation after unmount; unwrap {row} for card ModuleName
- avoid object/[hostile] string fallthrough in date and op summary formatters
* feat(partner): PR9g cut domain web to Choy* + xpath anchors

Migrate partner/partner_bank/partner_commercial product surfaces from O*/EP
to @/web Choy*, hang partner.detail.tab-panels for IMD, and fill store-bound
O2M kanban + ChoyChatter / FormView slot gaps needed by the cut.

* fix(partner,web): PR9g typecheck, chatter races, and patch coverage

Fix PartnerListView row-click typing and ChoyOneToMany kanban slot typing
for CI typecheck; harden ChoyChatter bindStore post/follower races; cover
ChoyChatter plus partner list navigation helpers at 100% patch.
* feat(domain): PR9h close remaining domain cutover (peers + e2e)

document/task/message/audit stay service-only; drop stale Element Plus peers
from cut domain modules and align auth/meta e2e selectors with Choy chrome.

* fix(web,auth,meta): PR9h forward ChoyButton attrs and harden e2e hooks

ChoyButton/L2 Button now forward data-testid and aria-* so auth header
e2e can find company switch and user menu. Also apply review follow-ups:
wait for company options, register-terms testid, and scoped op-status rows.

* fix(auth,meta,web): harden PR9h e2e selectors from review follow-ups

Exclude JWT-active company when picking a switch target, pin module op
status/result badges via data-testid, scope kanban search by data-anchor,
and always unmount ChoyButton mount wrappers.

* fix(auth,web): merge AuthHeader into ChoyShellLayout for e2e hooks

Auth header actions never reached the shell because AuthHeader was a
standalone SFC; extend ChoyShellLayout so xpath merge and testids work,
and apply PR-agent attr/testid hardenings with coverage.

* refactor(web,auth): rename ChoyShellLayout to ChoyWebShell

Align file and component names on both sides of the xpath inheritance
chain; keep choy.shell.header-actions anchors unchanged.

* fix(auth,web): harden company-switch pick and reload e2e wait

Preserve an already-selected alternative on switch-company retries, and
restore auto-waiting reload-row assertion in module management e2e.

* fix(i18n,web,auth): clear pot drift and harden review follow-ups

Resync base/meta catalogs for ModuleKanban strings, re-emit ChoyButton
click through the nested host, and tighten web-builder/e2e guards from
review suggestions.

* fix(auth,web): harden company pick and ChoyButton handleClick coverage

Require a known JWT active company before reusing the draft selection,
scope switch-company DOM lookups to the open panel, and cover
ChoyButton.handleClick for patch coverage.

* fix(web,auth): suppress disabled ChoyButton clicks

Keep non-button disabled hosts from emitting click, and use an explicit
.ts import for the e2e company-pick helper.

* fix(web,auth): inert disabled anchors and tighten e2e races

PreventDefault on disabled non-button hosts, read JWT active after the
company draft, flush before timezone select success, and restore stub attrs.

* fix(web): keep disabled link buttons out of tab order

Non-button hosts ignore disabled: styles; set tabindex=-1 with
aria-disabled and cover the enabled as=a click re-emit path.

* fix(web,auth): drop disabled link href and harden review follow-ups

Prevent middle-click navigation on disabled as=a hosts, scope company
select visibility to the switch panel, settle timezone draft before
Save, and match xpath placeholders with whitespace-tolerant regex.

* fix(web,auth): style disabled link hosts and harden company pick

Apply pointer-events/opacity on disabled non-button Button hosts, skip
disabled company options, and re-assert the select value inside the poll.

* fix(auth,web): tighten shell merge assert and setup reuse

Require the parent double-quoted header-actions anchor after merge, poll
JWT active company through token rotation, and only reuse plain parent
setup state (skip script-setup render functions).

* fix(web): stop disabled click bubble and broaden xpath assert

Match leftover Xpath placeholders in any components-object position,
and stopPropagation on disabled Button/ChoyButton hosts so ancestors
do not treat the control as enabled.

* fix(auth,web): assert ChoyLayout registration and plain setup reuse

Require ChoyLayout inside the merged components map, surface the active
company JWT error via a bounded wait loop, and reject non-plain parent
setup results (Map/class/Ref).

* fix(auth,web): re-read company draft and fail async setup reuse

Re-read the company select after the JWT wait, reject Promise parent
setup results instead of silently dropping them, and hoist merged
components regexes used by the shell merge asserts.

* fix(web,auth): settle company draft and harden disabled nav attrs

Flush Vue before declaring the company select settled, drop to/target
on disabled non-button hosts, fail fast when the reload status row is
missing, and reject thenable parent setup results.

* fix(web,auth): refresh company options and harden disabled clicks

Re-read company option values after the JWT wait, guard disabled
handleClick against non-Event payloads, and bind data-anchor explicitly
so caller attrs cannot override the framework anchor.

* fix(auth): pick company option from a single post-wait snapshot

Drop the pre-wait values fallback so current and options always come
from one post-JWT-wait read, and cover picks when current is absent
from the values list.

* fix(auth,web): stabilize JWT reads and tighten setup/key checks

Require two consecutive identical active-company JWT reads, settle the
company select draft before Apply, drop reactive/readonly parent setup
state, and anchor merged-components regexes on real component keys.

* fix(web,auth): harden JWT retry, noopener blank targets, and setup copy

Catch transient evaluate failures in the active-company JWT loop, add
noopener for enabled _blank hosts (and strip rel when disabled), and
return a shallow copy from reuseParentSetupState.

* fix(web,auth): merge noopener into rel and harden setup/JWT diagnostics

Merge noopener into existing _blank rel tokens, fail fast when the base
shell exposes no setup, surface last JWT read errors after the retry
budget, and use a realm-agnostic plain-object prototype check.

* test(web): lock components regex against later value references

Assert that ChoyLayout as a value after another entry does not satisfy
the merged-components key matcher (the claimed false-positive case).
…451)

* feat(web): PR9z cutover — absorb engines, drop EP/echarts/choy_ui, CI bans

Finish the Choy UI kit cutover on the integration branch: merge O* engines
into Choy*/neutral internals, remove Element Plus/echarts/choy_ui dual-stack,
wire Dialog confirm + Lucide, enable CutoverImportBans with CI grep gates.

BREAKING CHANGE: out-of-tree O*/el-* and legacy slot anchors are intentionally
removed ahead of feat/choy-ui-kit → main.

* fix(web): unblock PR9z CI after choy_ui cutover

Add missing lucide FE stub exports, close PropertiesField textarea for SFC parse, and align Tailwind kit tests with web-only resolveChoyKitModuleRoot.

* refactor(web): camelCase helpers/i18n modules and Choy* test names

Align web file names with component-side camelCase, retarget Choy* SUT tests, and drop obsolete ohtml/oproperties/tiptap helpers.

* feat(web): finish PR9z Choy field absorb and domain wiring

- Absorb store engines into Choy* fields; drop parallel *Engine/Page/Form/List leftovers
- Add Int/Bigint/Decimal/Ref/Kanban field exports; restore store StatusbarField
- Merge properties helpers; unify bare field i18n scopes and sync PO
- Wire domain views to correct Choy* types (decimal, ManyToOne vs Ref)

* refactor(web): drop product SCSS and ambient dayjs plugin types

- Convert modules/web styles to plain CSS; strip lang=scss from web/auth Vue SFCs
- Declare dayjs/plugin/* (and locale/*) for IDE; CLI typecheck already stubbed these

* refactor(web): route list tables through DataTable via ChoyTableHost

Replace the legacy VTable engine with an adapter host so List, O2M, and M2M share the L3 DataTable path while ChoyVColumn registration stays unchanged. Gallery keeps a direct DataTable dogfood.

* refactor(web): move column DSL to components/table as ChoyTableColumn

* refactor(web): rename ChoyVColumn to ChoyTableColumn across kit and domains

Finish the public column DSL rename after the table/ path move so List, O2M/M2M, FieldBase, and domain list views share one semantic export.

* refactor(web): rename table composable and choy-* CSS classes

- rename useVTable to useTable and TableColumnMeta helpers
- replace o-* DOM/CSS and --o-* tokens with choy-*
- drop dead el-tag deep styles in Search and M2M tags
- refresh gallery boot comment after choy_ui removal

* feat(web): add optional store engine to ChoyKanbanView

- extract useChoyKanbanStoreEngine and shared kanban store helpers
- migrate Token/Module kanban hosts off chrome-only lane glue
- keep Gallery chrome-only via v-model:lanes without store

* refactor(web)!: drop Gallery/Dogfood and make views store-only

- remove Gallery/Dogfood pages, choyGallery routes, and gallery token scopes
- require store for Kanban/Search; wire ChartView to createChartController
- document Form/List as store-only; keep field dual-mode for FE unit

BREAKING CHANGE: /web/__choy_gallery and Dogfood pages are gone; ChoyKanbanView,
ChoySearchView, and ChoyChartView require :store or a page store (no chrome dual-mode).

* fix(web): address PR #451 review bugs and raise field coverage

Wire native field bindings (value/onInput, checkbox checked/change), confirm
cancel/dismiss + pending settle, SearchFilterGroup AND/OR, import remapping,
image/binary drop, properties date ISO round-trip, and kanban bootstrap retry.
Add mount tests for scalar/temporal/relation Choy fields to close Codecov gaps.

* test(web): tighten M2O and O2M field mount coverage

Cover remaining ManyToOne search/keydown edges via setupState and expand
OneToMany add/display/ensureArray paths so patch misses stay near zero.

* fix(web): address PR review, typecheck, and patch coverage

Drop inert FieldBase cell prop/rules bindings, catch Chart bootstrap and
List search/sort/paginate rejections, fix O2M handle typecheck, and raise
field/tailwind unit coverage toward Codecov patch 100%.

* fix(web): resolve ViewScope tag, typecheck, and server sort toggle

Align ChoyViewScope imports with template tags, fix M2M mount typecheck,
mark CommitStrategy as type-only, cycle server-side DataTable sort, and
drop leftover Search tooltip attrs.

* fix(ci): clear web i18n pot-dirty and harden legacy-import gate

Re-extract Search after dropping _t('Search'), scope skip-dir checks to
modules_root, and strip comments only outside string literals.

* fix(web): restore client field rules and harden IO/image UX

Wire FieldBase RuleItem evaluation into FormView submit, warn on
Import/Export without a model, align attachment value types, and reject
non-raster image uploads.

* fix(web): drop remaining el-* fields, gate tags, unblock partner e2e

Replace M2O/Tags select-v2 with RelationCombobox and M2M tree with
RelationTree; scan Vue el-* tags in CI; allow Language:read on Login
and wait for ChoyPage loading in e2e login.

* fix(web): clear i18n pot-dirty, tighten validation/gate, cover Tags/Tree

Sync web pot/po after RelationCombobox cutover; treat empty arrays as
required-empty; ban export-star re-exports; add Tags/Tree mount coverage.

* fix(web): native textarea, per-instance validators, drop v-loading

Replace invalid input type=textarea, isolate FieldBase client-validator
map keys, replace Element Plus v-loading with aria-busy, and keep search
filter field select controlled via the parent update path.

* fix(web): accurate el-tag lines, keep host blob URLs, enforce binary accept

Blank HTML comments in place for vue tag line mapping; stop revoking
host-owned blob previews on image replace/remove; reject files outside
the accept allow-list for binary upload/drop.

* fix(web): carry template quotes in import gate; honor Ref baseIndex

Keep backtick string state across lines in the legacy-import scanner,
unwrap Ref baseIndex in ChoyTableHost, drop duplicate StatInfo class,
and rename FieldBase defineOptions off the legacy OFieldBase name.
* refactor(web): migrate Vue styles off --el-* onto --choy-* + Tailwind

Replace dead Element Plus CSS vars/selectors with Choy tokens, move layout chrome
to utilities, share field display styles, and CI-ban leftover --el-/.el- in product vue/css.

* refactor(web): clear product Vue styles via Tailwind (waves 1–4)

- migrate auth/meta/web scoped CSS to utilities; drop field-display.css
- keep BEM/test hooks (Search, Filter, tags, image preview, etc.)

* refactor(web): point boot spinner at --choy-* with hex fallbacks

* refactor(web): slim styles to tokens + index (+ theme dialect)

- drop empty preflight/override and unused transition/a11y/variable CSS
- fold direction defaults and fade/a11y residuals into tokens/index
- unify motion tokens; simplify link hover/active colors

* fix(web): address PR #452 review on a11y, truncate, el-* scan

- theme-aware high-contrast focus outline
- inline-block max-w-full so display truncate can ellipsize
- detect .el-* in compound CSS selectors; extend scanner tests

* fix(web): address PR-agent style suggestions from #452

- map info-subtle in theme; restore dialog/upload/properties/inline layouts
- bold m2m suggestion hits; highlight unread notifications; drop dead mdd-card

* fix(web): typecheck DropdownMenuItem class; split m2o display colors

- avoid object-key with spaces on unread menu item class binding
- mutually exclusive text-foreground vs text-primary for clickable m2o

* test(web): target search leading button by BEM class in behavior test
* fix(web): pin host Vue for reka-ui single-instance mounts

Web builds skipped a host vue bare-import pin, so reka-ui peers floated
extra Vue runtimes and Toaster/ConfirmHost crashed with renderSlot `.ce`.

* fix(web): take exact vue pin from modules/web package.json

Make modules/web's exact vue peer the SSOT for host Vue/@vue/* pins;
choysummount.VuePackageVersion remains fallback + sync-checked.

* fix(web): narrow host Vue pin skip and cover BareImportPin

Address PR review: only skip the five host-owned Vue runtime packages,
normalize VueBareImportPinsFor input, and add tests for patch coverage.

* refactor(web): generate VuePackageVersion from modules/web

Keep modules/web/package.json as the only hand-edited Vue pin; go generate
writes the choysummount fallback const and syncs bootstrap-web.

* fix(web): dedupe Vue host pins and cover kit-host build path

Address PR review: shared host package list, stricter exactVuePin,
cached package.json reads, fallback warn, ReplaceAll bootstrap sync,
and tests for building modules/web as the kit host.

* fix(web): tighten exactVuePin SemVer checks for host pins

Reject surplus core segments and empty prerelease/build suffixes so
malformed versions fall back to the generated host pin; cover script.go fully.

* fix(web): align Vue pin generator with exactVuePin rules

Use IsExactVuePin in go generate, find repo root via go.mod, suppress
misleading missing-pin warns on unreadable package.json, and assert
domain @vue/runtime-dom cannot override the host.

* fix(web): harden Vue pin fallback warn and generator safety

- Warn when kit exact vue fails host exactVuePin and falls back
- Resolve gen output via runtime.Caller; require unique bootstrap vue rewrite

* fix(web): harden Vue pin validation and generator ordering

- Fail FE host when kit exact vue fails IsExactVuePin
- Normalize declared vue before rejection warn; sync bootstrap before write
- Assert IsExactVuePin with explicit expected booleans

* fix(web): harden Vue pin sync formatting and SemVer checks

- Tolerate compact JSON when syncing bootstrap vue; write vue_version.go atomically
- Fail FE host when kit package.json lacks an exact vue pin
- Reject leading-zero SemVer core components in exactVuePin

* fix(web): align Vue pin warn with IsExactVuePin and harden generate

- Reuse IsExactVuePin for kit rejection warn; chmod generated file to 0644
- Assert full fallback Vue bare-import pin map in FE host tests

* fix(web): atomic bootstrap vue sync and pin prerelease coverage

- Write bootstrap package.json via temp+rename like vue_version.go
- Assert WithBareImportPins keeps prerelease/build Vue host pins
* fix(web): avoid aria-hidden on focused ChoyPage during loading

- Prefer inert + aria-busy over aria-hidden so login submit focus cannot warn
- Blur focused descendants before loading masks the page chrome

* fix(web): unstick ChoyPage loading focus typecheck and coverage

- Widen blur root typing to avoid ParentNode vs Vue/TipTap HTMLElement mismatch
- Blur on immediate watch + onMounted; cover nested/throwing blur and loading reverse

* test(web): assert ChoyPage loading watcher invokes blur helper

- Add a test seam so mount tests observe blurFocusedDescendant calls
- Clarify sync-throw coverage; skip speculative aria-hidden/thenable changes
* feat(web): emerald shell redesign with built-in sidebar and denser chrome

Wire ChoyWebShell to own brand header + sidebar menu, switch tokens to
emerald primary, and polish Login/Register/Home plus Form/List density
so the ERP shell reads as one usable product surface.

* fix(web): address PR #455 review findings and patch coverage

Expand inactive sidebar groups in useMenu, skip disabled shortcut
entries, harden soft router guards, fix auth calc/@layer/separator,
and add Home/Grid/Shell tests for full patch coverage.

* test(web): cover useMenu sidebar expand and harden shortcuts

Add useMenu/ChoyWebShell coverage for manual submenu expand/collapse,
accept root-relative shortcut paths with cycle guards, and fall back
sidebar width to 15rem when the layout token is unset.
* feat(web): bind L2 controls to density control-height tokens

Introduce --choy-control-height (32/28) and theme utilities h-control /
size-control so Button, Input, Select, Combobox, and Tabs share one ruler
without stray h-9/h-10, and use logical inset/padding for RTL overlays.

* docs(web): record vendor/ui product patch history

Document the allowed L2 contract-patch policy and the control-height /
logical-chrome changes so upstream refreshes can replay them from
vendor/ui/HISTORY.md instead of rediscovering diffs.

* fix(web): address W0 review feedback on control ruler

Unify sm/lg under --choy-control-height-*, make Switch LTR/RTL thumb
rules mutually exclusive, align ToastClose to focus-visible, fix the
HISTORY rg word-boundary pattern, and lock h-control emission with a
Go theme dialect test (stock TW spacing rename would invent p-control).
* feat(web): W1 Dense Admin App shell with rail attribution

Wire ChoyWebShell to layoutStore for expanded/collapsed/mobile drawer,
pin open-source attribution in the nav rail foot, keep auth header-actions
xpath anchors, and stub brand land path via resolveDefaultLandPath until W4.

* fix(web): bind Top bar height to control ruler

Dense Admin contract expects Top bar height ≈ control; point
--choy-layout-header-height at --choy-control-height so comfortable/
compact density stays one source of truth.

* fix(web): harden W1 mobile drawer a11y and patch coverage

Address PR #457 review feedback: modal drawer semantics, header above
backdrop, body scroll lock, Escape guard, deterministic layoutStore
viewport pins, and FE tests to cover the shell/layout patch at 100%.

* fix(web): preserve scroll lock and inert header on drawer

Address PR-agent follow-ups: restore prior body overflow on unlock,
make pinViewportWidth writable, mark overlay header inert, and reset
overflow after drawer tests.

* fix(web): post-flush drawer focus and robust viewport media pin

Restore menu-trigger focus after inert clears (watch flush post), and
teach pinViewportWidth to ignore non-width media clauses and fractional
px thresholds used by VueUse breakpoints.
* feat(web): W2 Page chrome, Feedback, Pagination, table density

Dense Admin W2: densify Page chrome, add ActionTray/Skeleton/Badge,
export Confirm with destructive option, unify kit Pagination with
pageSize reset, bind list/table row defaults to control height, and
fix Boolean display to plain text (no disabled input wall).

* fix(web): address PR #458 review and patch coverage gaps

Wire destructive confirms on delete callers, include description in
ChoyPage header, merge out-of-range pageSize into select options, and
cover ActionTray/Badge/ConfirmHost/Pagination/estimateSize helpers.
* feat(web): W3 List/Form/Search chrome and remaining field families

Wire Action tray on List/Form, adapt ListPagination to ChoyPagination,
add Search Clear-all with locked instant strategy, summarize O2M/M2M in
list cells, and densify Properties/scalars/Statusbar/Terminology.

* fix(web): address PR #459 review feedback

Singular relation counts, defer ListPagination until total is known,
name action toolbars, cancel Clear-all debounce, coerce pageSize, and
tighten Clear-all / Properties a11y assertions.

* fix(web): tighten pagination guards and Properties a11y

Sanitize non-finite totals, skip no-op pageSize emits, short-circuit
empty Clear-all, use dt/dd for readonly Properties, and assert exact
relation summary text.
* feat(web): W3b Kanban/Chart dense page chrome

Rewrite Kanban with ActionTray, vuedraggable, mobile lane select, and
move-to-lane menu; densify Chart controls with i18n empty/load/error.

* fix(web): address W3b review and raise Kanban patch coverage

Scope card Enter to .self, clear chart no-group stale errors, and cover
drag/move/error/mobile paths plus the vuedraggable FE stub.
* feat(web): W4 default land path and retire Home/Welcome

Implement menu DFS → Module Board land path for /, catch-all, and brand
click; register Error routes; remove Home/Welcome/shortcuts and /home
soft-land special case.

* fix(web): harden default land path and cover remaining branches

Add menu DFS cycle guards, skip route-denied leaves at runtime via
canRoute, strengthen Home retirement assertions, and hit order-tie /
empty-subtree coverage paths.

* fix(web): harden land-path catch-all and fail-closed canNavigate

Prevent CatchAll self-redirect loops, filter nullish menu nodes before sort,
and skip unverifiable resolve paths so DFS can fall through; cover to 100%.
buke and others added 10 commits October 19, 2026 21:01
…es (#497) (#502)

* chore: bump the go-minor-patch group across 1 directory with 20 updates

Bumps the go-minor-patch group with 20 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/antchfx/htmlquery](https://github.com/antchfx/htmlquery) | `1.3.6` | `1.3.7` |
| [github.com/chromedp/cdproto](https://github.com/chromedp/cdproto) | `0.0.0-20260714215040-dc233986426f` | `0.158.0` |
| [github.com/chromedp/chromedp](https://github.com/chromedp/chromedp) | `0.16.0` | `0.21.0` |
| [github.com/leonelquinteros/gotext](https://github.com/leonelquinteros/gotext) | `1.7.2` | `1.8.0` |
| [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) | `7.3.0` | `7.4.1` |
| [github.com/shopspring/decimal](https://github.com/shopspring/decimal) | `1.4.0` | `1.5.0` |
| [go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.71.0` | `0.72.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [go.opentelemetry.io/otel/exporters/stdout/stdoutmetric](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [go.opentelemetry.io/otel/exporters/stdout/stdouttrace](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [go.opentelemetry.io/otel/sdk/metric](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [golang.org/x/crypto](https://github.com/golang/crypto) | `0.57.0` | `0.58.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.59.0` | `0.60.0` |
| [golang.org/x/sync](https://github.com/golang/sync) | `0.23.0` | `0.24.0` |
| [golang.org/x/term](https://github.com/golang/term) | `0.46.0` | `0.47.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.42.0` | `0.43.0` |
| [go.opentelemetry.io/otel/metric](https://github.com/open-telemetry/opentelemetry-go) | `1.46.0` | `1.47.0` |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.41.0` | `0.42.0` |



Updates `github.com/antchfx/htmlquery` from 1.3.6 to 1.3.7
- [Release notes](https://github.com/antchfx/htmlquery/releases)
- [Commits](antchfx/htmlquery@v1.3.6...v1.3.7)

Updates `github.com/chromedp/cdproto` from 0.0.0-20260714215040-dc233986426f to 0.158.0
- [Changelog](https://github.com/chromedp/cdproto/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chromedp/cdproto/commits/v0.158.0)

Updates `github.com/chromedp/chromedp` from 0.16.0 to 0.21.0
- [Release notes](https://github.com/chromedp/chromedp/releases)
- [Commits](chromedp/chromedp@v0.16.0...v0.21.0)

Updates `github.com/leonelquinteros/gotext` from 1.7.2 to 1.8.0
- [Release notes](https://github.com/leonelquinteros/gotext/releases)
- [Commits](leonelquinteros/gotext@v1.7.2...v1.8.0)

Updates `github.com/minio/minio-go/v7` from 7.3.0 to 7.4.1
- [Release notes](https://github.com/minio/minio-go/releases)
- [Commits](minio/minio-go@v7.3.0...v7.4.1)

Updates `github.com/shopspring/decimal` from 1.4.0 to 1.5.0
- [Release notes](https://github.com/shopspring/decimal/releases)
- [Changelog](https://github.com/shopspring/decimal/blob/master/CHANGELOG.md)
- [Commits](shopspring/decimal@v1.4.0...v1.5.0)

Updates `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` from 0.71.0 to 0.72.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go-contrib@zpages/v0.71.0...zpages/v0.72.0)

Updates `go.opentelemetry.io/otel` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `go.opentelemetry.io/otel/sdk/metric` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `go.opentelemetry.io/otel/trace` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `golang.org/x/crypto` from 0.57.0 to 0.58.0
- [Commits](golang/crypto@v0.57.0...v0.58.0)

Updates `golang.org/x/net` from 0.59.0 to 0.60.0
- [Commits](golang/net@v0.59.0...v0.60.0)

Updates `golang.org/x/sync` from 0.23.0 to 0.24.0
- [Commits](golang/sync@v0.23.0...v0.24.0)

Updates `golang.org/x/term` from 0.46.0 to 0.47.0
- [Commits](golang/term@v0.46.0...v0.47.0)

Updates `golang.org/x/text` from 0.42.0 to 0.43.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](golang/text@v0.42.0...v0.43.0)

Updates `go.opentelemetry.io/otel/metric` from 1.46.0 to 1.47.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](open-telemetry/opentelemetry-go@v1.46.0...v1.47.0)

Updates `golang.org/x/mod` from 0.41.0 to 0.42.0
- [Commits](golang/mod@v0.41.0...v0.42.0)

---
updated-dependencies:
- dependency-name: github.com/antchfx/htmlquery
  dependency-version: 1.3.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: github.com/chromedp/cdproto
  dependency-version: 0.158.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/chromedp/chromedp
  dependency-version: 0.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/leonelquinteros/gotext
  dependency-version: 1.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/minio/minio-go/v7
  dependency-version: 7.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/shopspring/decimal
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  dependency-version: 0.72.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel/exporters/stdout/stdoutmetric
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel/exporters/stdout/stdouttrace
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel/sdk/metric
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/crypto
  dependency-version: 0.58.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/net
  dependency-version: 0.60.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/sync
  dependency-version: 0.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/term
  dependency-version: 0.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/text
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: go.opentelemetry.io/otel/metric
  dependency-version: 1.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/mod
  dependency-version: 0.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
...



* fix(e2e): adapt CDP wrapper to chromedp v0.21 generic API

Dependabot's chromedp/cdproto bump replaced Action/Run builders and
ListenTarget with Do/Run generics, Command+Call, and Events iterators.
Update the e2e CDP host so the go-minor-patch PR compiles again.

* fix(e2e): cover pageEvaluate errors and harden network finish race

Exercise the default pageEvaluate error path for Codecov patch
coverage, and remember early LoadingFinished IDs so ResponseReceived
still completes waiters under chromedp's unordered Events iterators.

* fix(e2e): subscribe chromedp Events before enable/wait

Hoist Events construction onto the calling goroutine so Fetch/Network
cannot miss events between Enable and ranging; document Go 1.27.x
required by gotext.

* fix(e2e): bound finishedEarly and fail NewPage setup errors

Only record LoadingFinished in finishedEarly for pending requests, and
clear pending/early entries on match filter; propagate cookie clear and
about:blank reset failures from NewPage.

* test(e2e): cover NewPage setup errors and finishedEarly pending

Add injectable hooks for cookie clear / about:blank reset, and unit-test
noteFinishedEarlyIfPending so patch coverage reaches 100%.

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* refactor(web): collapse field chrome into FieldBase

- Inline vendor Field chrome in the FieldBase form branch
- Make ChoyHtmlField a store field
- Remove ChoyFormItem, ChoyFieldBase, and their kit exports

* fix(web): typecheck Html aggregates and cover the editor

- Pass one type argument to NarrowAggProp so web typecheck succeeds
- Keep the editor id on the content node and forward aria-required
- Exercise TipTap edit, link prompts, and empty-doc sync in FE tests
- Size the reserved error line like FieldError

* fix(testing): hold CDP responses until request method is known

- Stop inventing ResponseMatch.Method from the filter when RequestWillBeSent races behind ResponseReceived
- Put Html field a11y attrs on the TipTap contenteditable node
- Merge Statusbar formItemProps.class through cn; avoid empty FieldLabel markup

* fix(testing): finish held CDP responses after early LoadingFinished

- Keep a pending placeholder while the method is unknown so LoadingFinished is not lost
- Bind the TipTap stub view.dom to the mounted contenteditable for live a11y attrs
- Cover matching hold replay and stub attribute helpers
* refactor(web): rename FieldBase to ChoyFieldBase

Align the field shell with the Choy*Field naming so third-party modules
can treat ChoyFieldBase as the stable kit extension point. Export the
component and FormItemProps/FieldStateExpr from kit.ts; keep
choy-field-base CSS/anchor tokens unchanged.

* fix(web): sync ChoyFieldBase i18n scopes after rename

Update zh_CN.po/web.pot msgctxt and source refs so translate lookups
match createTranslate scope, and clarify the component-name test title.
* refactor(web): field FW1 naming and display hygiene

Hard-cut Choy* defineOptions/i18n scopes, rename O*Cell internals,
drop EP display padding magic, and align destructive text tokens.

* feat(web): field form chrome, Combobox selection, and temporal pickers

- horizontal ChoyFieldBase label|value with orientation and start-align so errors do not shift labels
- rewrite scalar cells onto L2 controls; Selection uses Combobox with sticky search and check
- Date/Time/Datetime Popover pickers with UI locale, i18n, and in-trigger clear
- record Combobox kit patches in vendor/ui HISTORY and sync zh_CN terms

* feat(web): typed view switching, row payloads, and sandbox kitchen-sink

- add opt-in sandbox module (list/kanban/chart/form) with FE unit coverage
- introduce useChoyViewRouteSwitch plus route meta (viewKind/viewPaths/defaultGroups)
- type list/kanban row clicks via RowEventPayload; drop ad-hoc list_row_nav helpers
- fix data-loader topo tie-break to compare recordIndex numerically
- clear column metadata on virtual related fields so repository SELECT stays honest
- harden Register mount selectors for Reka checkbox and submit button

* feat(web): polish Text-family fields with Char wrapper and editor chrome

- add ChoyCharField thin wrap and trim char pads for accurate word counts
- wire word-limit, autosize resize-none, form empty placeholders, varchar widgets
- HTML toolbar icons with tooltips and link popover; JSON edit shell plus Format

* fix(web): show selection Combobox search text as option label

* fix(core): resolve monetary currencyField and ManyToOneRef write ids

Keep Amount.currencyField through parser/codegen, fall back to the row
currency when the patch companion is display-only, soft-skip onchange
preview stamp without a resolvable currency, and treat ManyToOneRef like
ManyToOne in update diffs so hydrated objects do not enter write patches.

* fix(web): attach monetary currency companions on form submit

When Amount (or other monetary fields) is written, ensure the currencyField
id is stamped from draft/original so display-only CurrencyId objects cannot
clear the FK and trip scale-stamp E1 on create/update.

* fix(web): keep CurrencyId selectable with nested monetary companions

exportFieldSelection previously dropped bare CurrencyId once nested paths
like CurrencyId.DecimalDigits were registered, so Browse omitted the FK and
ManyToOneRef never hydrated DisplayName. Keep parent leaf paths, register
DisplayName with currency siblings, and load labels via field-local Browse
into options without rewriting draft.

* feat(web): align numeric columns end and media/boolean columns center

Drive list/form cell justification from field type helpers so Number and
Monetary values sit at the end, while boolean/binary/image cells center.
Normalize index columns to align end across host tables.

* chore(sandbox): use FlaskConical icon for field playground menu

Distinguish the kitchen-sink sandbox entry from meta's Boxes icon and
stub FlaskConical in the FE lucide stub set.

* fix(web): toast on copyable VarcharField clipboard copy

Surface success/failure via ChoyMessage so form display copy is not silent.

* feat(web): add seconds column to DateTimePicker

Align datetime wire format with TimePicker (YYYY-MM-DDTHH:mm:ss) while
still accepting legacy minute-only values.

* feat(web): restyle Statusbar as chevron ribbon and unify field help

- replace ToggleGroup with a radiogroup chevron ribbon using primary soft tokens
- share Tooltip + cursor-help for form and inline help; drop native title doubles

* feat(web): rewrite Html/Json fields with TipTap polish and CodeMirror

- expand Html toolbar (headings, quote, code, undo/redo) and editor chrome
- edit Json with CM6 + localized lint tips; keep vue-json-pretty for form display

* fix(web): clear field label a11y issues and polish attachments

- Wire FieldBase label for/id/name; composite fields use aria-labelledby
- Extract AttachmentFieldChrome for Binary/Image upload and preview
- Drop TipTap extension-link peer (StarterKit already includes Link)
- Coerce legacy RoleRecordRule domain lists; wrap message RR in And
- Skip gzip on /_document/ and ASCII-sanitize Content-Disposition filenames
- Default image Bind disposition to inline; enrich descriptors with sizeBytes

* feat(web): hard-cut Properties to L2 single path (FW8)

- Merge definition editor onto L2+RPC; delete Draft parallel UI
- Rewrite ChoyPropertiesField with L2 controls, display formatting, and text numeric drafts (resync like Int/Number)
- Add sandbox PropertyDefinition model plus bootstrap schema for kitchen-sink hand tests

* fix(lifecycle): cascade-delete MetaModelData seed rows on uninstall

Hard-delete business rows pointed at by a module's xml_id mappings before
clearing the mappings (E15), so reinstall can Create without unique conflicts.
Use SAVEPOINTs on Postgres/MySQL so FK multi-pass deletes do not abort the
uninstall transaction.

* chore(sandbox): drop handwritten PropertyDefinition store

Rely on the forced per-app PropertyDefinition inject instead of a
duplicate hand-written model.

* fix(web): stop properties update patches from wiping sibling keys

Form UpdateById used nested microdiff for Properties maps, so changing
one key sent a partial column that the BE whole-replace write cleared.
Send original∪current for properties/jsonobject maps, collapse nested
plain-map diffs, and keep prior values when ResolveProperties is empty.

* fix(orm): inject related/compute on Browse and blank empty displays

Browse left LineCount/CategoryName empty because compute/related inject could
not see $rel$ (M2O FK scalars shadowed objects); keep PrimaryLineId virtual
and stop wrapping injected M2O records as {Id: object}. Form/StatInfo/chatter
empty states render blank instead of em dash or "No content".

* fix(web): polish field controls and Odoo-like M2O typeahead

- Unify Input/Select/Combobox/DateTime open chrome (thin border-ring, flush popover)
- RelationCombobox: keep options on reopen, skip short-list virtual flicker
- Search with empty keyword until the user edits (full page on focus)

* perf(web): memoize identical ManyToOne NameSearch requests

- Reuse the last NameSearch when query, searchKey, and pageSize match
- Short-circuit empty-prefix queries; fingerprint domain in searchKey

* fix(web): align M2O Search more with NameSearch conditions

- thread relationConditionSource through List Search/Count (and BE Count)
- use forcedCondition in the picker; reset leftover keyword/filters on open
- hide page title and selection column; drop blank combobox options

* fix(web): polish Search more and calendar selected hover

- style Search more as a primary action like Create
- keep selected calendar days on primary through hover/focus

* feat(web): add same-app M2M Tags/Tree fields and harden relation pickers

- ship ChoyManyToManyTagsField and ChoyManyToManyTreeField with shared helpers
- polish M2M list chrome, empty-state layout, and Add a line… copy
- hide picker empty New; close picker before Create; add Tag create routes

* feat(sandbox): wire same-app M2M Tags and Tree widgets

- add TagChips and Categories ManyToMany fields with join models
- bind ChoyManyToManyTagsField / ChoyManyToManyTreeField on the form
- cover metadata and form widget wiring with unit tests

* feat(web): harden M2M Tags/Tree widgets and adaptive tree height

- rebuild Tags/RefTags with badge shell and plain RelationCombobox chrome
- land RelationTree checkbox cascade and keep TagChips sparse M2M diffs safe
- default treeHeight to auto via shared resolveTreeBoxStyle; drop min-height padding
- softDelete false on sandbox M2M joins; i18n for Tag Chips/Categories

* fix(server): keep run alive on hotreload upgrade failure

- soft-fail watch upgrade/build errors instead of exiting serve
- write bundles entry under _choysum_entry/ so it is not colocated with Outfile

* fix(web): align RelationTree first row with form field labels

* fix(web): remove double border above M2M add-line footer

* feat(web): restore M2M Ref list field and polish relation empty UX

- add ChoyManyToManyRefField (kit export, mount tests, sandbox TagTableRefIds)
- default vertical orientation and hideLabel on FieldBase / M2M / O2M list fields
- display empty: Tags blank; M2M/M2MRef/O2M keep header only (no None/No data)
- extract and translate RefField zh_CN terms (Add a line…, Actions, …)

* feat(web): polish O2M table/kanban and fix select-only saves

- strip select-only computes from Create/Update and collection follow-up writes
- mark FieldsGet select-only as isReadonly; Form/diff omit them from patches
- O2M kanban: host delete chrome, safe dialog delete, hover actions, Sequence drag
- ChoyTab forceMount keeps sibling O2M fields registered across table/kanban tabs
- align table confirm-on-remove; Sandbox Lines CASCADE and dual-tab wiring
- hotreload: ignore *.test.* and auto-retry module_management LEASE_CONFLICT

* feat(web): virtualize capped O2M/M2M tables and O2M kanban boards

- auto-virtualize embedded tables only when height is capped
- add wrap-grid row virtualization for O2M kanban (threshold 40)
- seed VIRT-LINES and Sandbox fill/clear tools for hand testing

* feat(sandbox): add kitchen-sink onchange handlers

- demo domain, selection, value writes, and warn messages on SandboxRecord
- clamp SandboxLine Quantity with onchange warnings for hand tests
- cover handlers with backend unit tests and Form hint copy

* fix(web): remasure virtual tables after layout and tab show

- schedule virtualizer measure when virtualize turns on or height changes
- observe scrollport resize; notify on forceMount tab activation
- remasure O2M kanban boards when virtualization enables

* feat(web): align onchange messages with Odoo-like toast vs field errors

Clarify warn/info toast vs error+field inline delivery, style Sonner by
level tokens, and update Sandbox demos (silent Quantity clamp, IntValue
field error, Progress statusbar via selection+disabled).

* feat(web): close FW12 field a11y, real-chrome samples, and FE fixes

Wire describedBy/invalid through FieldBase and composite fields, drop
field el-button stubs, silence unconfigured M2M picker toasts, and fix
blob download attributes plus buildDiffFieldsMeta caching.

* fix(ci): unblock PR #508 typecheck, unit, and seed-cascade reviews

- add @tiptap/extension-link and tighten Vue/TS casts for web/sandbox typecheck
- align base/core tests with DisplayName strip and join-link success semantics
- harden seed cascade savepoints, SQL idents, and missing-table detection

* fix(ci): clear i18n pot drift, harden reviews, raise patch coverage

- extract/sync web+sandbox catalogs and fill new zh_CN msgstr
- validate related.path leaf; chunk blob→data URL; pass undefined original on create
- cover seed-cascade savepoints and lease-retry edges; ignore FE test stubs in Codecov

* fix(ci): address Tags review nits and close remaining patch coverage gaps

- make __rowKey redefinable; tighten selectedIds; guard O2M kanban reorder write
- expand seed-cascade/gzip/hotreload/diff tests; cover AttachmentFieldChrome
- ignore sandbox kitchen-sink view shells in Codecov (demo-only hosts)

* fix(ci): harden seed-cascade/hotreload reviews and close patch misses

Narrow Postgres missing-table detection, uniquify savepoints, clear lease
retry on dropped enqueue, and normalize gzip document bypass paths; add
targeted tests so remaining Go patch lines are covered.

* fix(ci): clear lease retry on abort and close FE patch coverage gaps

Clear hotreload leaseRetries when a retry is canceled, and add FE unit
coverage for core/utils/diff plus Attachment/Binary/Datetime edge paths
so Codecov JS patch hits reach 100%.
* test(web): cover layout nav/guest chrome and tidy flyout props

Add dedicated FE tests for AppRailFlyout, GuestHeader/MobileNav, ShellHeaderTools,
and PageTitleActions; rename flyout on* callbacks to leafSelect/hoverEnter/hoverLeave
so mount helpers do not treat them as vnode listeners; refresh componentHint example.

* fix(ci): refresh typecheck pkg cache and harden layout FE tests

Bump CLI pkg cache to v7 and type-fetch per typecheck shard so poisoned
v6 entries missing @codemirror types cannot fail web typecheck; strengthen
Guest/TitleActions assertions and soft-guard optional flyout leafSelect.
* test(web): cover view chrome and drop Element Plus stubs

Add dedicated FE tests for ViewBusyHost/ActionBar/Container/ButtonBox and
viewBusy helpers; remove leftover El* mount stubs and el-* class names from
List/Form/Search/StatInfo tests now that Choy chrome is the real surface.

* fix(web): harden view chrome tests and skip empty container bands

Collapse empty header/footer slots in ChoyViewContainer, mark ActionBar
leading column with data-slot, and tighten BusyHost/ActionBar/Container
assertions so regressions cannot pass on wrappers alone.

* fix(web): unblock view chrome typecheck and harden FE asserts
* fix(web): drop Element Plus adaptive-height and test stubs

Point dialog auto/container height at ChoyDialog data-slot surfaces and remove leftover El* smoke stubs.

* test(web): harden adaptive-height dialog footer coverage
* fix(auth): stack login fields and honor password input type

* docs(web): record Input type-vs-attrs vendor patch in HISTORY

* fix(web): keep varchar widget default while honoring type= aliases
* fix(web): type O2M kanban #card slot so item is not {}

* fix(sandbox): give record form sheet breathing room and clearer phases

* fix(web): type O2M kanban card slots from relation rows

Derive #card/#toolbar item types from V and expose remove as
void | Promise<boolean> so consumers get real field checking.

* fix(web): give ChoySection divided titles a trailing rule with gap

Replace legend-on-border hairlines with a plain title plus flex trailing
rule, and apply the same chrome to the sandbox Identity section.

* fix(web): keep ChoySection divided rule when title row is absent

Divided sections with #header or no title skipped the hairline after the
title-row redesign; render a full-width rule in that case.

Co-authored-by: Brian Wang <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry, we are unable to review this pull request

The GitHub API does not allow us to fetch diffs exceeding 300 files, and this pull request has 1464

@coderabbitai

coderabbitai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Walkthrough

Warning

Review details and warnings were omitted to fit the comment limit.

Comment thread scripts/web/vendor_shadcn_ui_w0.py Fixed
@github-actions

Copy link
Copy Markdown

Standalone PR Review

PR-Agent could not safely update the persistent review. This standalone result will not replace the canonical review.

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • internal/parser/vueparser/vuesfchtmlparser/parser.go
  • internal/module/policy/forbidden_ui_import_test.go
  • internal/testing/frontend/host_bundle_coverage_test.go
  • internal/server/server_hotreload_test.go
  • internal/module/policy/forbidden_ui_import.go
  • internal/testing/frontend/host_bundle.go
  • internal/testing/e2e/cdp/network_test.go
  • internal/module/artifact/build/web/webBuilder.go
  • internal/module/policy/forbidden_ui_import_scan.go
  • internal/module/artifact/build/web/input_digest_test.go
  • internal/module/evolution/schema/p1_coverage_test.go
  • internal/module/lifecycle/uninstaller_seed_cascade.go
  • internal/testing/frontend/fe_unit_stubs.go
  • internal/testing/frontend/vue_pin_test.go
  • internal/testing/e2e/cdp/network.go
  • internal/state/lease/lease_test.go
  • pkg/jsengine/scripts/choysummount/script_test.go
  • internal/module/artifact/build/web/input_digest.go
  • internal/module/evolution/schema/inspect.go
  • pkg/jsengine/scripts/choysummount/gen_vue_version.go
  • internal/server/server_serve_test.go
  • internal/document/gateway/upload_handler.go
  • internal/document/gateway/gateway_test.go
  • internal/server/server_hotreload_dispatch.go
  • internal/parser/ast_test.go
  • internal/parser/tsgoctx/parse_ctx_test.go
  • internal/esmresolver/package_pins_test.go
  • internal/server/middleware/gzip/handler_test.go
  • internal/parser/backendtsparser/sandbox_amount_currency_field_test.go
  • pkg/jsengine/scripts/choysummount/script.go
  • internal/module/lifecycle/uninstaller.go
  • internal/state/lease/lease.go
  • internal/parser/ast.go
  • internal/module/evolution/data/loader_test.go
  • internal/testing/e2e/bundle_test.go
  • internal/parser/tsgoctx/parse_ctx.go
  • internal/testing/typecheck/typecheck_app_test.go
  • internal/server/server_hotreload.go
  • internal/esmresolver/package_pins.go
  • internal/server/middleware/gzip/handler.go
  • internal/module/artifact/build/backend/builder.go
  • internal/document/gateway/download_handler.go
  • internal/testing/e2e/bundle.go
  • internal/module/lifecycle/bundles_translation_term_test.go
  • internal/esmresolver/resolver_test.go
  • internal/parser/vueparser/vuesfchtmlparser/parse_choy_scan_test.go
  • internal/document/gateway/helpers.go
  • internal/module/lifecycle/bundles.go
  • internal/testing/typecheck/typecheck.go
  • internal/typecheck/ambient_subpath.go
    ... and 1301 more

@github-actions

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Sanitize quoted filename param safely

Stripping non-ASCII runes can leave a filename that is only an extension (e.g.
截屏.png → .png) or empty, and "/</code> are still emitted unescaped into the quoted
filename= param. Replace disallowed runes with _ and drop "/</code> so the quoted value
stays valid and non-degenerate.

internal/document/gateway/mime_policy.go [118-127]

 	// filename= must stay ASCII; non-ASCII is carried by filename*=UTF-8''.
 	var b strings.Builder
 	b.Grow(len(clean))
 	for _, r := range clean {
-		if r < 0x20 || r > 0x7e {
+		if r < 0x20 || r > 0x7e || r == '"' || r == '\\' {
+			b.WriteByte('_')
 			continue
 		}
 		b.WriteRune(r)
 	}
 	return strings.TrimSpace(b.String())
Suggestion importance[1-10]: 6

__

Why: The suggestion raises a real concern: stripping non-ASCII can yield a degenerate value like .png, and "/\ are not filtered, which could break the quoted filename= param. Replacing with _ and dropping quote/backslash is a reasonable hardening, though the surrounding buildContentDisposition may already handle quoting.

Low
Possible bug
Do not swallow broken-kit resolve errors

A resolve error is silently ignored here, so a broken kit (e.g. theme.css as a
directory) falls back to kit-only scanning instead of surfacing the problem.
Propagate the error so GenerateChoyTailwindForModule fails loudly on a broken kit.

internal/module/artifact/build/web/tailwind_generate.go [766-773]

-	if resolved, err := resolveChoyKitModuleRoot(parent); err == nil && resolved != "" {
+	resolved, err := resolveChoyKitModuleRoot(parent)
+		if err != nil {
+			return ""
+		}
+		if resolved != "" {
 			// Accept the web kit module under the custom parent.
 			for _, name := range []string{"web"} {
 				if filepath.Clean(filepath.Join(parent, name)) == root {
 					return parent
 				}
 			}
 		}
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies that a resolve error is silently ignored, causing a broken kit to fall back to kit-only scanning. However, the proposed improved_code returns "" on error rather than propagating the error, which does not match the stated intent of failing loudly. The direction is reasonable but the fix is incomplete.

Low
Retry deferred lease-conflict reloads

The LEASE_CONFLICT branch only logs and continues, so a busy lease never retries the
event even though nextLeaseRetry/clearLeaseRetry were added for exactly that.
Requeue the file (bounded by nextLeaseRetry) so the change is eventually applied
instead of silently dropped.

internal/server/server_lifecycle.go [28-42]

 			if err := s.handleQueuedWatchEvent(file); err != nil {
 				// Never tear down the server on a watch reload failure — the
 				// operator can fix the module and save again. LEASE_CONFLICT is
 				// already soft-failed with a defer message in the dispatch path;
 				// other upgrade/build errors still reach here after ✗ logging.
 				if isModuleLeaseConflict(err) {
 					if s.runtimeScope != nil {
 						s.runtimeScope.Logger().Warn(
 							"watch reload deferred: module management lease busy",
 							"event", file,
 							"error", err,
 						)
 					}
+					if attempt := s.hotreloadState().nextLeaseRetry(file); attempt <= maxLeaseRetries {
+						s.enqueueWatchEvent(file)
+					} else {
+						s.hotreloadState().clearLeaseRetry(file)
+					}
 					continue
 				}
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly notes that nextLeaseRetry/clearLeaseRetry are added but the LEASE_CONFLICT branch only logs and continues, leaving the retry helpers unused in this path. However, the retry may be wired elsewhere (e.g. the dispatch path), and the improved code references undefined symbols (maxLeaseRetries, hotreloadState()), so it's not directly applicable.

Low
Surface unexpected read errors before rewrite

The early-return only skips the rewrite when the read succeeds; a non-IsNotExist
read error (e.g. permission denied) is silently swallowed and the code proceeds to
overwrite the file. Surface unexpected read errors so a broken output file is not
masked.

internal/module/artifact/build/web/tailwind_generate.go [843-846]

 func writeFileAtomicIfChanged(path, content string) error {
-	if prev, err := os.ReadFile(path); err == nil && string(prev) == content {
-		return nil
+	if prev, err := os.ReadFile(path); err == nil {
+		if string(prev) == content {
+			return nil
+		}
+	} else if !os.IsNotExist(err) {
+		return err
 	}
Suggestion importance[1-10]: 4

__

Why: The suggestion is technically valid: a non-IsNotExist read error is currently swallowed and the file is overwritten. However, the atomic write path would still succeed in most cases, so the impact is limited to masking a pre-existing read problem. It's a reasonable hardening but not a critical bug.

Low
Possible issue
Bound lease retry map growth

leaseRetries grows unbounded: entries are only deleted via clearLeaseRetry, so any
path that keeps hitting LEASE_CONFLICT (or whose retry is abandoned) leaks a map
entry for the process lifetime. Cap the map size or prune stale entries when it
exceeds a small bound.

internal/server/server_hotreload_state.go [219-228]

 // nextLeaseRetry increments and returns the 1-based retry attempt for path.
 func (h *hotreloadState) nextLeaseRetry(path string) int {
 	h.leaseRetryMu.Lock()
 	defer h.leaseRetryMu.Unlock()
 	if h.leaseRetries == nil {
 		h.leaseRetries = make(map[string]int)
 	}
+	if len(h.leaseRetries) >= maxLeaseRetryEntries {
+		// Bound memory: drop the whole table; retry counters are advisory.
+		h.leaseRetries = make(map[string]int)
+	}
 	h.leaseRetries[path]++
 	return h.leaseRetries[path]
 }
Suggestion importance[1-10]: 4

__

Why: The leaseRetries map is only pruned via clearLeaseRetry, so unbounded growth is plausible, but the impact is minor (small string→int entries) and the fix is speculative. It's a reasonable hardening suggestion but low importance.

Low
Run UI import ban unconditionally

The new UI-import ban only runs when /web exists, so a module that imports a
forbidden kit from a non-web directory (e.g. service/ or root TS) bypasses the
check. Consider running AssertNoForbiddenUiImports unconditionally and letting the
policy decide which paths to scan.

internal/testing/typecheck/typecheck.go [220-230]

 	// Fail fast on domain kit-import bans before type-asset preparation.
-	webDir := filepath.Join(modulesRoot, app, "web")
-	if st, err := osStat(webDir); err == nil {
-		if st.IsDir() {
-			if err := policy.AssertNoForbiddenUiImports(modulesRoot, app); err != nil {
-				return xfmt.Errorf("typecheck: %w", err)
-			}
-		}
-	} else if !os.IsNotExist(err) {
-		return xfmt.Errorf("typecheck: stat web dir: %w", err)
+	if err := policy.AssertNoForbiddenUiImports(modulesRoot, app); err != nil {
+		return xfmt.Errorf("typecheck: %w", err)
 	}
Suggestion importance[1-10]: 4

__

Why: The observation that the ban only runs when <app>/web exists is valid, but whether AssertNoForbiddenUiImports is meant to scan non-web dirs is a policy decision not verifiable from the diff. It's a plausible gap but speculative and could change intended behavior.

Low
Avoid double-closing temp file on rename failure

After a successful Close, the deferred cleanup still calls tmp.Close() again when
Rename fails. That double-close is harmless on Unix but can mask the real error on
some platforms; track the closed state so the deferred cleanup only removes the temp
file.

internal/module/artifact/build/web/tailwind_generate.go [868-873]

 	if err := atomicWriteClose(tmp); err != nil {
 			return err
 		}
+		closed := true
 		if err := atomicWriteRename(tmpName, path); err != nil {
 			return err
 		}
Suggestion importance[1-10]: 2

__

Why: The improved_code introduces a closed := true variable that is never used, so it does not actually implement the described fix. The double-close is harmless on Unix and the deferred cleanup already tolerates the error via _ = tmp.Close(). The suggestion is incomplete and low impact.

Low
Maintainability
Wrap primary kit scan error with context

The primary kit scan error is returned unwrapped while the per-module scans below
wrap with context. Wrap this one too so build failures point at the kit root,
matching the surrounding error style.

internal/module/artifact/build/web/tailwind_generate.go [160-166]

 	if kitRoot != "" {
-			kitCandidates, err := choyScanKitCandidates(filepath.Join(kitRoot, "web"))
+			kitWebRoot := filepath.Join(kitRoot, "web")
+			kitCandidates, err := choyScanKitCandidates(kitWebRoot)
 			if err != nil {
-				return nil, err
+				return nil, fmt.Errorf("scan kit candidates under %s: %w", kitWebRoot, err)
 			}
 			add(kitCandidates)
 		}
Suggestion importance[1-10]: 3

__

Why: A correct and consistent improvement matching the surrounding error-wrapping style, but it only affects error message readability, not correctness or behavior.

Low

⚠️ Suggestion coverage: 1273 file(s) were not analyzed because of the token budget or maximum chunk calls: modules/web/web/components/field/ChoyFieldBase.test.ts, modules/web/web/components/field/ChoyNativeScalarFields.mount.test.ts, modules/web/web/components/field/ChoyManyToManyField.mount.test.ts, modules/web/web/stores/breadcrumbStore/breadcrumbStore.test.ts, modules/web/web/components/view/ChoyKanbanView.chrome.test.ts, modules/web/web/components/field/ChoyStoreModeFields.mount.test.ts, modules/web/web/components/internal/dataTableHelpers.test.ts, modules/web/web/components/field/ChoyManyToManyRefField.mount.test.ts, modules/web/web/components/field/ChoyStatusbarField.test.ts, modules/web/web/composables/useMenu.test.ts, modules/web/web/components/layout/ChoyAppRail.test.ts, modules/web/web/components/field/relationTypeahead.NameSearch.test.ts, modules/web/web/components/field/ChoyImageField.test.ts, modules/web/web/stores/breadcrumbStore/index.ts, modules/web/web/components/view/search/SearchFilterCondition.test.ts, modules/web/web/components/internal/dataTableHelpers.ts, modules/web/web/composables/useMenu.logic.test.ts, modules/web/web/components/layout/ChoyGuestShell.test.ts, modules/sandbox/service/models/sandbox_record.ts, modules/web/web/stores/i18nStore/locales.ts, modules/web/web/components/chatter/ChoyChatter.test.ts, modules/web/web/components/view/ChoyChartView.coverage.test.ts, modules/web/web/components/field/ChoyJsonField.test.ts, modules/web/web/components/field/ChoyTemporalFields.mount.test.ts, modules/web/web/components/view/ChoyPagination.test.ts, modules/web/web/components/field/ChoyPropertiesField.test.ts, modules/web/web/kit.ts, modules/web/web/composables/useFormViewSubmit.test.ts, modules/web/web/components/field/ChoyOneToManyField.handle.test.ts, modules/web/e2e/module_management.spec.ts, modules/web/web/composables/useMenu.ts, modules/web/web/components/field/fieldHelpers.ts, modules/web/web/composables/useFormViewLifecycle.ts, modules/web/web/components/internal/DataTable.mount.test.ts, modules/web/web/components/field/ChoyBinaryField.test.ts, modules/web/web/components/field/fieldHelpers.test.ts, modules/web/web/components/view/ChoyStatInfo.test.ts, modules/web/web/router/resolveRuntimeDefaultLandPath.test.ts, modules/web/web/components/field/ChoyFieldCompanyValuesDialog.test.ts, modules/auth/web/pages/login_form.test.ts, modules/web/web/components/view/ChoyFormView.workspace_height.test.ts, modules/web/web/composables/useChoyKanbanStoreEngine.ts, modules/web/web/components/field/ChoySelectionField.test.ts, modules/web/web/components/field/attachmentFieldHelpers.ts, modules/web/web/components/field/ChoyFieldTranslationsDialog.test.ts, modules/web/web/components/layout/ChoyPageIoMenu.test.ts, modules/core/utils/diff.ts, modules/web/web/components/field/propertiesHelpers.ts, modules/web/web/composables/usePageContext.test.ts, modules/web/web/composables/search/useUserFilters.ts, and 1223 more.

@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Harden ASCII filename fallback

Stripping non-ASCII runes can leave a filename that is only an extension (e.g.
截屏.png → .png) or empty, and " / </code> are still allowed through into the quoted
filename= parameter. Drop the quote/backslash characters and fall back to a safe
default when nothing printable remains.

internal/document/gateway/mime_policy.go [118-127]

 	// filename= must stay ASCII; non-ASCII is carried by filename*=UTF-8''.
 	var b strings.Builder
 	b.Grow(len(clean))
 	for _, r := range clean {
-		if r < 0x20 || r > 0x7e {
+		if r < 0x20 || r > 0x7e || r == '"' || r == '\\' {
 			continue
 		}
 		b.WriteRune(r)
 	}
-	return strings.TrimSpace(b.String())
+	out := strings.TrimSpace(b.String())
+	if strings.Trim(out, ".") == "" {
+		return "download"
+	}
+	return out
Suggestion importance[1-10]: 7

__

Why: Stripping non-ASCII runes can indeed leave a bare extension like .png, and quotes/backslashes in the quoted filename= parameter are a legitimate header-injection concern. The fallback logic is reasonable, though the strings.Trim(out, ".") check is a bit narrow.

Medium
Possible issue
Distinguish read errors from missing file

Any read error (not just NotExist) is silently treated as "content changed" and the
file is rewritten. A permission error on an existing file would then surface as a
confusing rename failure; distinguish NotExist from other read errors so real I/O
problems are reported at the right step.

internal/module/artifact/build/web/tailwind_generate.go [844-846]

-	if prev, err := os.ReadFile(path); err == nil && string(prev) == content {
-		return nil
+	if prev, err := os.ReadFile(path); err == nil {
+		if string(prev) == content {
+			return nil
+		}
+	} else if !os.IsNotExist(err) {
+		return fmt.Errorf("read existing %s: %w", path, err)
 	}
Suggestion importance[1-10]: 5

__

Why: Treating any read error as "changed" can mask real I/O problems and produce a confusing downstream rename failure. Distinguishing NotExist from other errors is a reasonable hardening of the atomic-write path.

Low
Reset retry state after reload

Swallowing every reload error means a persistently broken module now fails silently
forever (only a log line), and the new nextLeaseRetry/clearLeaseRetry hooks are not
wired here, so the retry counter is never advanced or reset. At minimum, clear the
retry counter on success and on non-lease failures so the state does not go stale.

internal/server/server_lifecycle.go [28-50]

 			if err := s.handleQueuedWatchEvent(file); err != nil {
 				// Never tear down the server on a watch reload failure — the
 				// operator can fix the module and save again. LEASE_CONFLICT is
 				// already soft-failed with a defer message in the dispatch path;
 				// other upgrade/build errors still reach here after ✗ logging.
 				if isModuleLeaseConflict(err) {
 					if s.runtimeScope != nil {
 						s.runtimeScope.Logger().Warn(
 							"watch reload deferred: module management lease busy",
 							"event", file,
 							"error", err,
 						)
 					}
 					continue
 				}
 				if s.runtimeScope != nil {
 					s.runtimeScope.Logger().Error(
 						"watch reload failed; server keeps running",
 						"event", file,
 						"error", err,
 					)
 				}
+				if state := s.hotreloadState(); state != nil {
+					state.clearLeaseRetry(file)
+				}
 				continue
 			}
+			if state := s.hotreloadState(); state != nil {
+				state.clearLeaseRetry(file)
+			}
Suggestion importance[1-10]: 5

__

Why: The observation that nextLeaseRetry/clearLeaseRetry are not wired in this loop is accurate and worth addressing, but the suggestion relies on an unverified s.hotreloadState() accessor and mixes in a broader concern about swallowing errors that is a deliberate design choice in the PR.

Low
Bound lease retry map growth

leaseRetries grows unbounded: entries are only removed by clearLeaseRetry, which is
never called on the paths that matter (ignored events, dropped events, or a
successful retry after the map was populated). Add a cap so a long-running watcher
cannot leak one map entry per distinct path.

internal/server/server_hotreload_state.go [220-228]

 // nextLeaseRetry increments and returns the 1-based retry attempt for path.
 func (h *hotreloadState) nextLeaseRetry(path string) int {
 	h.leaseRetryMu.Lock()
 	defer h.leaseRetryMu.Unlock()
 	if h.leaseRetries == nil {
 		h.leaseRetries = make(map[string]int)
 	}
+	if len(h.leaseRetries) >= maxLeaseRetryEntries {
+		// Bound memory: drop the whole map rather than leak per-path counters.
+		h.leaseRetries = make(map[string]int)
+	}
 	h.leaseRetries[path]++
 	return h.leaseRetries[path]
 }
Suggestion importance[1-10]: 4

__

Why: The leaseRetries map is only cleared via clearLeaseRetry, and the PR does not wire that call anywhere, so unbounded growth is a plausible concern. However, the proposed fix (dropping the entire map when a cap is hit) is a blunt approach and references an undefined maxLeaseRetryEntries constant, so it is not directly applicable.

Low
Use errors.Is for stat check

The new ban check only runs when /web exists, so a module that keeps UI imports in
another directory (or a symlinked web dir) silently skips the policy. Also,
os.IsNotExist does not unwrap wrapped errors; prefer errors.Is(err, fs.ErrNotExist)
for robustness.

internal/testing/typecheck/typecheck.go [220-230]

 	// Fail fast on domain kit-import bans before type-asset preparation.
 	webDir := filepath.Join(modulesRoot, app, "web")
 	if st, err := osStat(webDir); err == nil {
 		if st.IsDir() {
 			if err := policy.AssertNoForbiddenUiImports(modulesRoot, app); err != nil {
 				return xfmt.Errorf("typecheck: %w", err)
 			}
 		}
-	} else if !os.IsNotExist(err) {
+	} else if !errors.Is(err, fs.ErrNotExist) {
 		return xfmt.Errorf("typecheck: stat web dir: %w", err)
 	}
Suggestion importance[1-10]: 4

__

Why: Switching os.IsNotExist to errors.Is(err, fs.ErrNotExist) is a minor robustness improvement, but the primary claim about modules keeping UI imports elsewhere is speculative and not evidenced by the diff. This is a low-impact style/robustness tweak.

Low
Maintainability
Wrap generated CSS write error

The write error is returned bare, so callers cannot tell which file failed to write.
Wrap it with the output path for actionable diagnostics (the build test asserts on
the wrapped message from BuildCtx, so keep the underlying error chain intact).

internal/module/artifact/build/web/tailwind_generate.go [830-832]

 	if err := writeFileAtomicIfChanged(outPath, header+css); err != nil {
-		return nil, err
+		return nil, fmt.Errorf("write generated Tailwind CSS %s: %w", outPath, err)
 	}
Suggestion importance[1-10]: 4

__

Why: Wrapping the error with the output path improves diagnostics and preserves the error chain via %w. It is a minor maintainability improvement rather than a correctness fix.

Low
Possible bug
Guard against mismatched resolved kit root

modulesPathForKitRoot returns "" for isolated temp kit roots, so
generateChoyTailwindForModule falls back to kit-only scanning. But when the caller
passes a web root under a custom modules dir, the product scan may resolve a
different kit root than moduleRoot, silently writing CSS for the wrong module.
Guard against a resolved root that differs from the requested one.

internal/module/artifact/build/web/tailwind_generate.go [746-748]

 func GenerateChoyTailwindForModule(moduleRoot string) (*ChoyTailwindGenerateResult, error) {
-	return generateChoyTailwindForModule(moduleRoot, modulesPathForKitRoot(moduleRoot))
+	modulesPath := modulesPathForKitRoot(moduleRoot)
+	if modulesPath != "" {
+		if resolved, err := resolveChoyKitModuleRoot(modulesPath); err == nil && resolved != "" &&
+			filepath.Clean(resolved) != filepath.Clean(strings.TrimSpace(moduleRoot)) {
+			return nil, fmt.Errorf("choy kit module root %q does not match resolved kit %q under %s", moduleRoot, resolved, modulesPath)
+		}
+	}
+	return generateChoyTailwindForModule(moduleRoot, modulesPath)
 }
Suggestion importance[1-10]: 3

__

Why: The concern is speculative: modulesPathForKitRoot only returns a parent when it resolves back to the same web kit root, so a mismatch is unlikely. The added guard is defensive but low-impact and not clearly a real bug.

Low
Normalize candidates before hashing

candidates is not deduplicated or sorted here, so callers passing unsorted/duplicate
input produce different ContentHash values for identical class sets (the digest
hashes the joined slice). Normalize before hashing/emitting to keep the generated
file stable across scan orderings.

internal/module/artifact/build/web/tailwind_generate.go [403-405]

 	if strings.TrimSpace(dialectCSS) == "" && len(candidates) == 0 {
 		return "", time.Since(start), fmt.Errorf("tailwind generate called without dialect CSS or candidates")
 	}
+	if len(candidates) > 0 {
+		uniq := make(map[string]struct{}, len(candidates))
+		norm := make([]string, 0, len(candidates))
+		for _, c := range candidates {
+			c = strings.TrimSpace(c)
+			if c == "" {
+				continue
+			}
+			if _, ok := uniq[c]; ok {
+				continue
+			}
+			uniq[c] = struct{}{}
+			norm = append(norm, c)
+		}
+		sort.Strings(norm)
+		candidates = norm
+	}
Suggestion importance[1-10]: 2

__

Why: The suggestion targets GenerateTailwindCSS, but ContentHash is computed in generateChoyTailwindForModule from already-sorted, deduplicated candidates. Normalizing here would not affect the digest and is largely redundant.

Low

⚠️ Suggestion coverage: 1273 file(s) were not analyzed because of the token budget or maximum chunk calls: modules/web/web/components/field/ChoyFieldBase.test.ts, modules/web/web/components/field/ChoyNativeScalarFields.mount.test.ts, modules/web/web/components/field/ChoyManyToManyField.mount.test.ts, modules/web/web/stores/breadcrumbStore/breadcrumbStore.test.ts, modules/web/web/components/view/ChoyKanbanView.chrome.test.ts, modules/web/web/components/field/ChoyStoreModeFields.mount.test.ts, modules/web/web/components/internal/dataTableHelpers.test.ts, modules/web/web/components/field/ChoyManyToManyRefField.mount.test.ts, modules/web/web/components/field/ChoyStatusbarField.test.ts, modules/web/web/composables/useMenu.test.ts, modules/web/web/components/layout/ChoyAppRail.test.ts, modules/web/web/components/field/relationTypeahead.NameSearch.test.ts, modules/web/web/components/field/ChoyImageField.test.ts, modules/web/web/stores/breadcrumbStore/index.ts, modules/web/web/components/view/search/SearchFilterCondition.test.ts, modules/web/web/components/internal/dataTableHelpers.ts, modules/web/web/composables/useMenu.logic.test.ts, modules/web/web/components/layout/ChoyGuestShell.test.ts, modules/sandbox/service/models/sandbox_record.ts, modules/web/web/stores/i18nStore/locales.ts, modules/web/web/components/chatter/ChoyChatter.test.ts, modules/web/web/components/view/ChoyChartView.coverage.test.ts, modules/web/web/components/field/ChoyJsonField.test.ts, modules/web/web/components/field/ChoyTemporalFields.mount.test.ts, modules/web/web/components/view/ChoyPagination.test.ts, modules/web/web/components/field/ChoyPropertiesField.test.ts, modules/web/web/kit.ts, modules/web/web/composables/useFormViewSubmit.test.ts, modules/web/web/components/field/ChoyOneToManyField.handle.test.ts, modules/web/e2e/module_management.spec.ts, modules/web/web/composables/useMenu.ts, modules/web/web/components/field/fieldHelpers.ts, modules/web/web/composables/useFormViewLifecycle.ts, modules/web/web/components/internal/DataTable.mount.test.ts, modules/web/web/components/field/ChoyBinaryField.test.ts, modules/web/web/components/field/fieldHelpers.test.ts, modules/web/web/components/view/ChoyStatInfo.test.ts, modules/web/web/router/resolveRuntimeDefaultLandPath.test.ts, modules/web/web/components/field/ChoyFieldCompanyValuesDialog.test.ts, modules/auth/web/pages/login_form.test.ts, modules/web/web/components/view/ChoyFormView.workspace_height.test.ts, modules/web/web/composables/useChoyKanbanStoreEngine.ts, modules/web/web/components/field/ChoySelectionField.test.ts, modules/web/web/components/field/attachmentFieldHelpers.ts, modules/web/web/components/field/ChoyFieldTranslationsDialog.test.ts, modules/web/web/components/layout/ChoyPageIoMenu.test.ts, modules/core/utils/diff.ts, modules/web/web/components/field/propertiesHelpers.ts, modules/web/web/composables/usePageContext.test.ts, modules/web/web/composables/search/useUserFilters.ts, and 1223 more.

Repair partner* O2M kanban typing, auth e2e against kit checkbox/combobox,
CodeQL HTML/regex findings, sandbox i18n pot drift, and minor review cleanups.

Co-authored-by: Brian Wang <[email protected]>
cursoragent and others added 2 commits October 26, 2026 10:35
Satisfy noImplicitAny on the preferences Combobox assertion helper.

Co-authored-by: Brian Wang <[email protected]>
Build the breadcrumb computed via a helper so items is assigned once.

Co-authored-by: Brian Wang <[email protected]>
Avoid the overwritten identity stub that code-quality flagged.

Co-authored-by: Brian Wang <[email protected]>
@buke
buke merged commit 7d0244b into main Oct 26, 2026
55 checks passed
@buke
buke deleted the feat/choy-ui-kit branch October 26, 2026 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants