fix(ui): harden drift audit
This commit is contained in:
parent
c4fec49b8b
commit
9ea72f7a71
@ -7,15 +7,17 @@ Live source of truth for migrating `learning_ai_invt_trdg` UI/UX and platform cl
|
||||
- Product repo: `/Users/saravana/BytelystAI/learning_ai/learning_ai_invt_trdg`
|
||||
- Common platform repo: `/Users/saravana/BytelystAI/learning_ai/learning_ai_common_plat`
|
||||
- Local package toggle: `BYTELYST_PACKAGE_SOURCE=common-plat pnpm install -r`
|
||||
- Current common-platform usage: `@bytelyst/api-client`, `@bytelyst/config`, `@bytelyst/errors`, `@bytelyst/kill-switch-client`, `@bytelyst/react-auth`, `@bytelyst/react-native-platform-sdk`, `@bytelyst/telemetry-client`
|
||||
- UI duplicates to migrate: local `web/src/components/ui/*`, raw HTML controls, legacy global surface classes, hardcoded color literals, local status/surface/list/table patterns
|
||||
- Common UI target: `@bytelyst/ui`
|
||||
- Design token target: `@bytelyst/design-tokens` with product aliases to `--bl-*`
|
||||
- Current common-platform usage: `@bytelyst/api-client`, `@bytelyst/design-tokens`, `@bytelyst/errors`, `@bytelyst/kill-switch-client`, `@bytelyst/react-auth`, `@bytelyst/react-native-platform-sdk`, `@bytelyst/telemetry-client`, `@bytelyst/ui`
|
||||
- UI adapter in code: `web/src/components/ui/Primitives.tsx` imports from `@bytelyst/ui`, re-exports common primitives, and owns product status-to-tone mapping.
|
||||
- Design token foundation in code: `web/src/index.css` imports `@bytelyst/design-tokens/generated/tokens.css` and defines product `--bl-*` aliases.
|
||||
- Remaining UI duplicates to migrate: raw HTML controls in portfolio/screener/simple trade plan/chat surfaces, legacy global badge/surface CSS, hardcoded color literals, and local status/surface/list/table patterns where still product-local.
|
||||
- Common UI target: `@bytelyst/ui` through the product adapter only; direct `@bytelyst/ui` imports outside the adapter remain disallowed.
|
||||
- Design token target: `@bytelyst/design-tokens` with product aliases to `--bl-*`; chart/domain colors may remain product-owned when they encode trading semantics.
|
||||
|
||||
## Component Ownership
|
||||
|
||||
- Common platform core UI (`learning_ai_common_plat/packages/ui`): product-agnostic primitives such as Button, IconButton, Card, Panel, Surface, Badge, StatusBadge, StatusDot, Input, Textarea, Select, Label, Checkbox, RadioGroup, Switch, Tabs, SegmentedControl, DropdownMenu, Tooltip, Modal, ConfirmDialog, DataList, DataTable, Timeline, DiffCard, EmptyState, LoadingSpinner, Sidebar/AppShell base pieces.
|
||||
- Product UI adapter (`web/src/components/ui/Primitives.tsx`, to be added): imports from `@bytelyst/ui`, maps product defaults, status tones, density, radius, focus behavior, and preserves trading-domain composition locally.
|
||||
- Product UI adapter (`web/src/components/ui/Primitives.tsx`): imports from `@bytelyst/ui`, maps product defaults/status tones, exposes common primitives to product code, and preserves trading-domain composition locally.
|
||||
- Product-owned UI: trading workflows, strategy/risk/execution domain composition, charts, order lifecycle views, and product-specific copy/state.
|
||||
|
||||
## Common-Platform Inventory
|
||||
@ -37,6 +39,18 @@ Live source of truth for migrating `learning_ai_invt_trdg` UI/UX and platform cl
|
||||
| Datastore/config | Backend Cosmos repositories and `@bytelyst/config` bootstrap | `@bytelyst/datastore`, `@bytelyst/cosmos`, `@bytelyst/config`, `@bytelyst/backend-config` | Existing `@bytelyst/cosmos`/`config` usage stays; consider `datastore` only for reusable storage helpers. |
|
||||
| Operational shells/components | Local AppShell, Sidebar, cards, lists, status surfaces | `@bytelyst/ui`, `@bytelyst/dashboard-shell`, `@bytelyst/dashboard-components` | Prefer `@bytelyst/ui` first; evaluate dashboard packages only if they fit product-agnostic shell/list composition. |
|
||||
|
||||
## Code Sync Snapshot
|
||||
|
||||
Reviewed against current product code on 2026-05-06:
|
||||
|
||||
- `web/package.json` includes `@bytelyst/ui` and `@bytelyst/design-tokens`.
|
||||
- `web/src/components/ui/Primitives.tsx` is present and is the only direct `@bytelyst/ui` import found in `web/src`.
|
||||
- Product code currently imports adapter primitives in `Login`, `ResetPassword`, `PositionsTab`, `MyStrategiesTab`, and `VisualRuleBuilder`.
|
||||
- `web/src/index.css` imports shared generated design tokens and defines `--bl-*` aliases for product surfaces/status/focus/radius.
|
||||
- Remaining raw-control/product-local areas observed include `views/PortfolioView.tsx`, `views/ScreenerView.tsx`, `views/SimpleView.tsx`, and `components/ChatControl.tsx`.
|
||||
- Remaining legacy CSS/status areas observed include `web/src/App.css`, `web/src/components/SymbolCard.css`, and `web/src/components/SymbolCard.tsx`.
|
||||
- `scripts/ui-drift-audit.sh` currently depends on `rg`. In an environment where `rg` is missing, the report can show false-zero counts while printing `rg: command not found`; strict-audit verification is not trustworthy until the script fails fast or provides a portable fallback.
|
||||
|
||||
## Common UI Gap Closure Decision
|
||||
|
||||
No shared `@bytelyst/ui` code change is required before the product adapter phase. The common UI package already exports the primitives required for the first product migration passes:
|
||||
@ -66,7 +80,18 @@ Note: common platform had a pre-existing local modification in `packages/ui/src/
|
||||
| [x] | 7. Forms, modals, and settings migration | product | Migrated auth reset/sign-in/sign-up controls to adapter Button/Input primitives while preserving labels, placeholders, button names, and provider-error behavior. | Passed: `pnpm --filter @bytelyst/trading-web exec vitest run src/components/Login.dom.test.tsx src/components/ResetPassword.dom.test.tsx`; `pnpm --filter @bytelyst/trading-web typecheck`; `pnpm run audit:ui`; `git diff --check`. | `6511326a3e5de3e6d2cf4f5ec29c30c3ea98cfe7` | Audit raw controls reduced from 32 to 25. Settings/share/create/edit form debt remains for dashboard/workspace and cleanup phases. |
|
||||
| [x] | 8. Dashboard, search, and workspace migration | product | Migrated My Strategies dashboard card action controls, diagnostic toggle, engine toggle, wizard back control, and empty-state/header CTAs to adapter Button/IconButton primitives. | Passed: `pnpm --filter @bytelyst/trading-web exec vitest run src/tabs/MyStrategiesTab.dom.test.tsx`; `pnpm --filter @bytelyst/trading-web typecheck`; `pnpm run audit:ui`; `git diff --check`. | `1ee57d5aeb548aefd505f53fa7d08d47cc8f4db7` | Audit raw controls reduced from 25 to 17. Dense operational layout and existing card composition preserved. |
|
||||
| [x] | 9. Advanced product-specific surfaces | product | Migrated Visual Strategy Builder controls to adapter Select/Input/Button/IconButton primitives while keeping dnd-kit composition and strategy rule logic product-owned. | Passed: `pnpm --filter @bytelyst/trading-web exec vitest run src/components/strategy/VisualRuleBuilder.dom.test.tsx`; `pnpm --filter @bytelyst/trading-web typecheck`; `pnpm run audit:ui`; `git diff --check`. | `bb4efc2b0d5f8e2ebbd7487b5731e0e6446ec4fb` | Audit raw controls reduced from 17 to 13. Charts, rule semantics, and strategy execution remain product-owned. |
|
||||
| [ ] | 10. Legacy global cleanup and strict audit | product | Remove or greatly reduce old global classes and hardcoded literals; enable strict audit only after accepted debt is cleared. | `pnpm run audit:ui:strict`; `pnpm verify`; `pnpm lint`; `git diff --check` | Pending | Strict mode intentionally fails until this phase. |
|
||||
| [ ] | 10. Audit tooling hardening | product | Make `scripts/ui-drift-audit.sh` fail fast when `rg` is unavailable, document the prerequisite, or add a portable fallback so report/strict modes cannot produce false-zero counts. | `pnpm run audit:ui`; intentionally test missing-`rg` behavior; `git diff --check` | Pending | Current shell run printed `rg: command not found` and reported zero findings, so audit counts in that environment are invalid. |
|
||||
| [ ] | 11. Remaining raw-control migration | product | Migrate remaining raw HTML controls in portfolio/screener/simple trade plan/chat surfaces to adapter primitives where behavior and accessibility can be preserved. | Targeted DOM tests for touched views; `pnpm --filter @bytelyst/trading-web typecheck`; `pnpm run audit:ui`; `git diff --check` | Pending | Keep test mocks out of production counts; keep trading workflow logic local. |
|
||||
| [ ] | 12. Legacy global cleanup and strict audit | product | Remove or greatly reduce old global classes and hardcoded literals; replace generic badge/status/surface CSS with adapter/common primitives or token aliases; enable strict audit only after accepted debt is cleared. | `pnpm run audit:ui:strict`; `pnpm verify`; `pnpm lint`; `git diff --check` | Pending | Strict mode intentionally remains disabled until audit tooling is reliable and accepted debt is cleared. |
|
||||
|
||||
## Remaining Work
|
||||
|
||||
- **Fix audit reliability**: Update `scripts/ui-drift-audit.sh` so missing `rg` exits non-zero instead of reporting zero findings.
|
||||
- **Migrate remaining production raw controls**: Prioritize `PortfolioView`, `ScreenerView`, `SimpleView`, and `ChatControl`.
|
||||
- **Clean legacy global CSS**: Reduce or replace `.badge`, `*-badge`, `surface-*`, and older dark-surface classes in `App.css` and `SymbolCard.css`.
|
||||
- **Reduce hardcoded colors**: Move reusable surface/status colors to token aliases; keep chart palette and trading semantic colors product-owned when appropriate.
|
||||
- **Expand adapter usage**: Continue routing product code through `web/src/components/ui/Primitives.tsx`; do not import `@bytelyst/ui` directly outside the adapter.
|
||||
- **Verify after each slice**: Run targeted DOM tests for touched views, web typecheck, UI audit, and `git diff --check`.
|
||||
|
||||
## Running Notes
|
||||
|
||||
|
||||
@ -10,55 +10,66 @@ if [ ! -d "${WEB_SRC}" ]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
RG_BASE='--glob=*.ts --glob=*.tsx --glob=*.css --glob=!**/*.test.ts --glob=!**/*.test.tsx --glob=!**/*.dom.test.tsx --glob=!**/test/** --glob=!**/assets/**'
|
||||
APPROVED_UI_GLOB='!**/components/ui/**'
|
||||
python3 - "${WEB_SRC}" "${MODE}" <<'PY'
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
section() {
|
||||
printf '\n== %s ==\n' "$1"
|
||||
}
|
||||
web_src = pathlib.Path(sys.argv[1])
|
||||
mode = sys.argv[2]
|
||||
extensions = {".ts", ".tsx", ".css"}
|
||||
excluded_suffixes = (".test.ts", ".test.tsx", ".dom.test.tsx")
|
||||
excluded_parts = {"test", "assets"}
|
||||
|
||||
count_matches() {
|
||||
pattern="$1"
|
||||
shift
|
||||
# shellcheck disable=SC2086
|
||||
rg -n $RG_BASE "$@" "${pattern}" "${WEB_SRC}" | wc -l | tr -d ' '
|
||||
}
|
||||
patterns = [
|
||||
("raw interactive controls outside approved primitives", "Raw Interactive Controls", re.compile(r"<(button|input|textarea|select)(\s|>)"), True),
|
||||
("legacy global surface classes", "Legacy Global Surface Classes", re.compile(r"\b(surface-card|surface-muted|badge|input-shell)\b"), False),
|
||||
("hardcoded color literals", "Hardcoded Color Literals", re.compile(r"(#[0-9a-f]{3,8}\b|rgba?\()", re.IGNORECASE), False),
|
||||
("direct @bytelyst/ui imports outside product adapter", "Direct Common UI Imports Outside Adapter", re.compile(r"@bytelyst/ui"), True),
|
||||
]
|
||||
|
||||
print_matches() {
|
||||
pattern="$1"
|
||||
shift
|
||||
# shellcheck disable=SC2086
|
||||
rg -n $RG_BASE "$@" "${pattern}" "${WEB_SRC}" || true
|
||||
}
|
||||
def included(path: pathlib.Path) -> bool:
|
||||
if path.suffix not in extensions:
|
||||
return False
|
||||
name = path.name
|
||||
if name.endswith(excluded_suffixes):
|
||||
return False
|
||||
rel_parts = set(path.relative_to(web_src).parts)
|
||||
return rel_parts.isdisjoint(excluded_parts)
|
||||
|
||||
raw_controls_count="$(count_matches '<(button|input|textarea|select)(\s|>)' --glob="${APPROVED_UI_GLOB}")"
|
||||
legacy_classes_count="$(count_matches '\b(surface-card|surface-muted|badge|input-shell)\b')"
|
||||
hardcoded_colors_count="$(count_matches '(#[0-9a-f]{3,8}\b|rgba?\()')"
|
||||
direct_common_ui_count="$(count_matches '@bytelyst/ui' --glob="${APPROVED_UI_GLOB}")"
|
||||
def approved_ui(path: pathlib.Path) -> bool:
|
||||
return "components" in path.parts and "ui" in path.parts
|
||||
|
||||
echo "UI drift audit mode=${MODE}"
|
||||
echo "raw interactive controls outside approved primitives: ${raw_controls_count}"
|
||||
echo "legacy global surface classes: ${legacy_classes_count}"
|
||||
echo "hardcoded color literals: ${hardcoded_colors_count}"
|
||||
echo "direct @bytelyst/ui imports outside product adapter: ${direct_common_ui_count}"
|
||||
def collect(pattern: re.Pattern[str], exclude_approved_ui: bool) -> list[tuple[str, int, str]]:
|
||||
matches = []
|
||||
for path in sorted(web_src.rglob("*")):
|
||||
if not path.is_file() or not included(path):
|
||||
continue
|
||||
if exclude_approved_ui and approved_ui(path):
|
||||
continue
|
||||
try:
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
rel = path.relative_to(web_src).as_posix()
|
||||
for line_number, line in enumerate(lines, start=1):
|
||||
if pattern.search(line):
|
||||
matches.append((rel, line_number, line.rstrip()))
|
||||
return matches
|
||||
|
||||
section "Raw Interactive Controls"
|
||||
print_matches '<(button|input|textarea|select)(\s|>)' --glob="${APPROVED_UI_GLOB}"
|
||||
results = [(label, title, collect(pattern, exclude_approved_ui)) for label, title, pattern, exclude_approved_ui in patterns]
|
||||
total = sum(len(matches) for _, _, matches in results)
|
||||
|
||||
section "Legacy Global Surface Classes"
|
||||
print_matches '\b(surface-card|surface-muted|badge|input-shell)\b'
|
||||
print(f"UI drift audit mode={mode}")
|
||||
for label, _, matches in results:
|
||||
print(f"{label}: {len(matches)}")
|
||||
|
||||
section "Hardcoded Color Literals"
|
||||
print_matches '(#[0-9a-f]{3,8}\b|rgba?\()'
|
||||
for _, title, matches in results:
|
||||
print(f"\n== {title} ==")
|
||||
for rel, line_number, line in matches:
|
||||
print(f"{rel}:{line_number}:{line}")
|
||||
|
||||
section "Direct Common UI Imports Outside Adapter"
|
||||
print_matches '@bytelyst/ui' --glob="${APPROVED_UI_GLOB}"
|
||||
|
||||
total_findings=$((raw_controls_count + legacy_classes_count + hardcoded_colors_count + direct_common_ui_count))
|
||||
|
||||
if [ "${MODE}" = "strict" ] && [ "${total_findings}" -gt 0 ]; then
|
||||
echo "error: strict UI drift audit failed with ${total_findings} findings" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
if mode == "strict" and total > 0:
|
||||
print(f"error: strict UI drift audit failed with {total} findings", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
PY
|
||||
|
||||
Loading…
Reference in New Issue
Block a user